This commit is contained in:
MOHD ARIFF 2025-01-13 16:20:13 +08:00
parent 38c4629302
commit d1682750dc
13 changed files with 1533 additions and 51 deletions

View File

@ -8,6 +8,7 @@ namespace PSTW_CentralSystem.Areas.Inventory.Models
[Key] [Key]
public int ProductId { get; set; } public int ProductId { get; set; }
public required string ProductName { get; set; } public required string ProductName { get; set; }
public required string ProductShortName { get; set; }
public required int ManufacturerId { get; set; } public required int ManufacturerId { get; set; }
public required string Category { get; set; } public required string Category { get; set; }
public required string ModelNo { get; set; } public required string ModelNo { get; set; }

View File

@ -4,6 +4,14 @@
Layout = "~/Views/Shared/_Layout.cshtml"; Layout = "~/Views/Shared/_Layout.cshtml";
} }
<style> <style>
@@font-face {
font-family: 'OCR-A';
src: url('../assets/fonts/ocraext.ttf');
}
.QrPrintFont {
font-family: 'OCR-A', monospace;
}
.table td img { .table td img {
display: block !important; display: block !important;
} }
@ -45,17 +53,17 @@
</div> </div>
<div class="col-7 d-flex align-items-center justify-content-center"> <div class="col-7 d-flex align-items-center justify-content-center">
<div class="text-center fs-4 text"> <div class="text-center fs-4 text">
<div class="col-12 my-3">
{{thisQRInfo.uniqueID}}
</div>
<div class="col-12 my-3"> <div class="col-12 my-3">
{{thisQRInfo.departmentName}} {{thisQRInfo.departmentName}}
</div> </div>
<div class="col-12 my-3"> <div class="col-12 my-3">
{{thisQRInfo.productName}} {{thisQRInfo.productShortName}}
</div> </div>
<div class="col-12 my-3"> <div class="col-12 my-3">
{{thisQRInfo.endWDate}} {{thisQRInfo.serialNumber}}
</div>
<div class="col-12 my-3">
{{thisQRInfo.partNumber}}
</div> </div>
</div> </div>
</div> </div>
@ -554,13 +562,29 @@
}, },
}, },
{ {
"title": "Category", "title": "Print",
"data": "category", "data": "uniqueID",
"render": function (data, type, full, meta) {
var printButton = `<button type="button" class="btn btn-success print-btn" data-id="${data}">Print</button>`;
return printButton;
},
},
{
"title": "Item Short Name",
"data": "productShortName",
}, },
{ {
"title": "Serial Number", "title": "Serial Number",
"data": "serialNumber", "data": "serialNumber",
}, },
{
"title": "Part Number",
"data": "partNumber",
},
{
"title": "Category",
"data": "category",
},
{ {
"title": "Quantity", "title": "Quantity",
"data": "quantity", "data": "quantity",
@ -601,14 +625,6 @@
Station: ${currentStation}` Station: ${currentStation}`
} }
}, },
{
"title": "Print",
"data": "uniqueID",
"render": function (data, type, full, meta) {
var printButton = `<button type="button" class="btn btn-success print-btn" data-id="${data}">Print</button>`;
return printButton;
},
},
{ {
"title": "Delete", "title": "Delete",
"data": "productId", "data": "productId",
@ -640,9 +656,9 @@
correctLevel: QRCode.CorrectLevel.M correctLevel: QRCode.CorrectLevel.M
}); });
} }
container.on('click', function() { // container.on('click', function() {
window.open(data.qrString, '_blank'); // window.open(data.qrString, '_blank');
}); // });
}); });
}, },
}) })
@ -663,10 +679,10 @@
// Check if the table is collapsed // Check if the table is collapsed
if ($row.hasClass('child')) { if ($row.hasClass('child')) {
// For collapsed view: Look for the closest `.dtr-data` that contains the img // For collapsed view: Look for the closest `.dtr-data` that contains the img
imageSrc = $row.prev('tr').find('td:first-child img').attr('src'); imageSrc = $row.prev('tr').find('td:nth-child(1) img').attr('src');
} else { } else {
// For expanded view: Find the img in the first column of the current row // For expanded view: Find the img in the first column of the current row
imageSrc = $row.find('td:first-child img').attr('src'); imageSrc = $row.find('td:nth-child(1) img').attr('src');
} }
if (imageSrc) { if (imageSrc) {
@ -935,22 +951,22 @@
// Populate the virtual DOM with content // Populate the virtual DOM with content
virtualElement.innerHTML = ` virtualElement.innerHTML = `
<div class="container-fluid my-3" style="font-family: 'OCR A', monospace;"> <div class="container-fluid my-3 QrPrintFont" style="font-family: 'OCR A', monospace;">
<div class="row" > <div class="row" >
<div class="col-5 text-center d-flex align-items-center justify-content-center"> <div class="col-5 text-center d-flex align-items-center justify-content-center">
<div class="row"> <div class="row">
<div class="col-12"> <div class="col-12">
<div>${this.thisQRInfo.imgContainer}</div> <div>${this.thisQRInfo.imgContainer}</div>
<div class="col-12 h4"><b>${this.thisQRInfo.uniqueID}</b></div> <div class="col-12 h4"style="font-family: 'Arial', monospace;"><b>${this.thisQRInfo.uniqueID}</b></div>
</div> </div>
</div> </div>
</div> </div>
<div class="col-7 d-flex align-items-center justify-content-center"> <div class="col-7 d-flex align-items-center justify-content-left">
<div class="row-fluid"> <div class="row-fluid">
<div class="col-12 h4"><b>${this.thisQRInfo.departmentName}</b></div> <div class="col-12 h3"style="font-family: 'Verdana', monospace;"><b>${this.thisQRInfo.departmentName}</b></div>
<div class="col-12 h4"><b>${this.thisQRInfo.serialNumber??"-"}</b></div> <div class="col-12 h4"style="font-family: 'Arial', monospace;"><b>${this.thisQRInfo.productShortName}</b></div>
<div class="col-12 h4"><b>${this.thisQRInfo.productName}</b></div> <div class="col-12 h4"style="font-family: 'Arial', monospace;"><b>${this.thisQRInfo.serialNumber??"-"}</b></div>
<div class="col-12 h4"><b>${this.thisQRInfo.endWDate}</b></div> <div class="col-12 h4"style="font-family: 'Arial', monospace;"><b>${this.thisQRInfo.partNumber}</b></div>
</div> </div>
</div> </div>
</div> </div>
@ -968,7 +984,6 @@
}).then((canvas) => { }).then((canvas) => {
// Convert the canvas to an image // Convert the canvas to an image
const imgData = canvas.toDataURL('image/png'); const imgData = canvas.toDataURL('image/png');
// Open the image in a new tab for preview (optional) // Open the image in a new tab for preview (optional)
// const newWindow = window.open(); // const newWindow = window.open();
// newWindow.location.href = imgData; // newWindow.location.href = imgData;

View File

@ -28,6 +28,15 @@
</div> </div>
</div> </div>
@* Product Short Name *@
<div class="form-group row">
<label for="productName" class="col-sm-3">Product Short Name:</label>
<div class="col-sm-9">
<input type="text" id="productShortName" name="productShortName" class="form-control" maxlength="13" v-model="productShortName" required>
<p><em><small class="text-danger">* Product short name limited to 13 characters</small></em></p>
</div>
</div>
@* Manufacturer *@ @* Manufacturer *@
<div class="form-group row"> <div class="form-group row">
<label class="col-sm-3">Manufacturer:</label> <label class="col-sm-3">Manufacturer:</label>
@ -126,6 +135,7 @@
imageSrc: '', imageSrc: '',
products: null, products: null,
productDatatable: null, productDatatable: null,
productShortName: null,
} }
}, },
mounted() { mounted() {
@ -142,6 +152,9 @@
{ "title": "Product Name", { "title": "Product Name",
"data": "productName", "data": "productName",
}, },
{ "title": "Product Short Name",
"data": "productShortName",
},
{ "title": "Model Number", { "title": "Model Number",
"data": "modelNo", "data": "modelNo",
}, },
@ -225,6 +238,7 @@
// Create the payload // Create the payload
const formData = { const formData = {
productName: this.productName, productName: this.productName,
productShortName: this.productShortName,
manufacturerId: this.manufacturer, manufacturerId: this.manufacturer,
category: this.category, category: this.category,
modelNo: this.modelNo, modelNo: this.modelNo,

View File

@ -17,17 +17,36 @@
<div v-if="reportData"> <div v-if="reportData">
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-3"> <div class="col-3">
<h4>Statistic</h4> <div class="row col-10">
<select class="form-select shadow-none mt-3" v-model="selectedDepartment" v-on:change=""> <h4>Department</h4>
<option value="" disabled selected>Please select</option> <multiselect v-model="selectedDepartment" :options="compDeptList" :multiple="true" group-values="departments" group-label="companyName"
<option v-for="(dept, index) in compDeptList" :key="index" :value="dept.departmentId">{{ dept.departmentName }}</option> :group-select="true" placeholder="Seach Department" track-by="departmentId" label="departmentName">
</select> </multiselect>
<div class=""><button class="btn btn-danger" v-on:click="selectedDepartment = []">Clear</button></div>
</div>
</div> </div>
<div class="col-3"> <div class="col-3">
<h4>Item Registered </h4> <div class="row col-10">
<h4>Category</h4>
<multiselect v-model="selectedCategory" :options="categoryList" :multiple="true" placeholder="Seach Category">
</multiselect>
<div class=""><button class="btn btn-danger" v-on:click="selectedCategory = []">Clear</button></div>
</div>
</div> </div>
<div class="col-3"> <div class="col-3">
<h4>Item Stock Out </h4> <div class="row col-10">
<h4>Product</h4>
<multiselect v-model="selectedItem" :options="selectedCategory.length == 0 && selectedDepartment.length == 0 ? productList : filteredProduct " :multiple="true" placeholder="Seach Product" track-by="productId" label="productName">
</multiselect>
<div class=""><button class="btn btn-danger" v-on:click="selectedItem = []">Clear</button></div>
</div>
</div>
<div class="col-3">
<div class="row col-10">
<h4>Date filter</h4>
<vue-date-picker v-model="selectedMonth" month-picker range></vue-date-picker>
<div class=""><button class="btn btn-danger" v-on:click="selectedMonth = []">Clear</button></div>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -67,13 +86,16 @@
<script> <script>
$(function () { $(function () {
app.mount('#invAdmin'); app.mount('#invAdmin');
$('.closeModal').on('click', function () { $('.closeModal').on('click', function () {
// Show the modal with the ID 'addManufacturerModal'. // Show the modal with the ID 'addManufacturerModal'.
$('.modal').modal('hide'); $('.modal').modal('hide');
}); });
}); });
const app = Vue.createApp({ const app = Vue.createApp({
components: {
'multiselect': window.VueMultiselect.default,
VueDatePicker,
},
data() { data() {
return { return {
currentUser: null, currentUser: null,
@ -83,13 +105,30 @@
}, },
reportData: null, reportData: null,
compDeptList: {}, compDeptList: {},
selectedDepartment: null, productList: {},
categoryList:['Asset', 'Part', 'Disposable'],
monthList: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
filteredProduct: [],
selectedMonth: [],
selectedDepartment: [],
selectedItem: [],
selectedCategory: []
} }
}, },
mounted() { mounted() {
this.fetchUser(); this.fetchUser();
this.fetchProductList();
this.fetchDepartmentsCompaniesList(); this.fetchDepartmentsCompaniesList();
}, },
watch: {
//watch selectedDepartment. when selectedDepartment is changed, if selectedCategory is null filter productList based on selectedDepartment only. otherwise filter the productList based on selectedDepartment and selectedCategory
selectedDepartment() {
this.filterProducts();
},
selectedCategory() {
this.filterProducts();
}
},
methods: { methods: {
async fetchUser() { async fetchUser() {
try { try {
@ -142,25 +181,60 @@
}); });
if (response.ok) { if (response.ok) {
const data = await response.json(); const data = await response.json();
this.compDeptList = data.map(company => company.departments.map(department => this.compDeptList = data;
({
companyId: company.companyId,
companyName: company.companyName,
departmentId: department.departmentId,
departmentName: department.departmentName,
departmentCode: department.departmentCode
})
)).flat();
console.log(this.compDeptList);
} }
else { else {
console.error(`Failed to fetch comapny & department list: ${response.statusText}`); console.error(`Failed to fetch company & department list: ${response.statusText}`);
} }
} }
catch (error) { catch (error) {
console.error('There was a problem with the fetch operation:', error); console.error('There was a problem with the fetch operation:', error);
} }
}, },
async fetchProductList(){
try {
const response = await fetch(`/InvMainAPI/ItemList/`, {
method: 'POST',
});
if (response.ok) {
const data = await response.json();
this.productList = data;
}
else {
console.error(`Failed to fetch item list: ${response.statusText}`);
}
}
catch (error) {
console.error('There was a problem with the fetch operation:', error);
}
},
filterProducts() {
const selectedDepartmentIds = this.selectedDepartment.map(department => department.departmentId);
const selectedCategory = this.selectedCategory;
if (selectedDepartmentIds.length === 0 && selectedCategory.length === 0) {
// No filters applied
this.filteredProduct = this.productList;
}
else if (selectedDepartmentIds.length === 0) {
// Filter by category only
this.filteredProduct = this.productList.filter(product =>
selectedCategory.includes(product.category)
);
}
else if (selectedCategory.length === 0) {
// Filter by department only
this.filteredProduct = this.productList.filter(product =>
selectedDepartmentIds.includes(product.departmentId)
);
}
else {
// Filter by both department and category
this.filteredProduct = this.productList.filter(product =>
selectedDepartmentIds.includes(product.departmentId) &&
selectedCategory.includes(product.category)
);
}
}
}, },
}); });
</script> </script>

View File

@ -261,7 +261,7 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
} }
var userRole = await _userManager.GetRolesAsync(user); var userRole = await _userManager.GetRolesAsync(user);
var isAdmin = userRole.Contains("SystemAdmin") || userRole.Contains("SuperAdmin"); var isAdmin = userRole.Contains("SystemAdmin") || userRole.Contains("SuperAdmin") || userRole.Contains("Finance");
List<ItemModel> itemList = new List<ItemModel>(); List<ItemModel> itemList = new List<ItemModel>();
// Get the item list // Get the item list
if (isAdmin) if (isAdmin)
@ -324,6 +324,7 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
item.Department?.DepartmentName, item.Department?.DepartmentName,
CreatedBy=item.CreatedBy!.UserName, CreatedBy=item.CreatedBy!.UserName,
item.Product!.ProductName, item.Product!.ProductName,
item.Product!.ProductShortName,
item.Product!.Category, item.Product!.Category,
//CurrentUser = item.Movement?.FromUser?.UserName, //CurrentUser = item.Movement?.FromUser?.UserName,
CurrentUser = item.Movement?.FromUser?.UserName, CurrentUser = item.Movement?.FromUser?.UserName,
@ -495,6 +496,7 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
item.Department?.DepartmentName, item.Department?.DepartmentName,
item.CreatedBy!.UserName, item.CreatedBy!.UserName,
item.Product!.ProductName, item.Product!.ProductName,
item.Product!.ProductShortName,
item.Product!.ImageProduct, item.Product!.ImageProduct,
QRString = $"{HttpContext.Request.Scheme}://{HttpContext.Request.Host.Value}/I/{item.UniqueID}" // Generate QR String QRString = $"{HttpContext.Request.Scheme}://{HttpContext.Request.Host.Value}/I/{item.UniqueID}" // Generate QR String
}; };
@ -553,6 +555,7 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
item.Department?.DepartmentName, item.Department?.DepartmentName,
CreatedBy = item.CreatedBy!.UserName, CreatedBy = item.CreatedBy!.UserName,
item.Product!.ProductName, item.Product!.ProductName,
item.Product!.ProductShortName,
item.Product!.Category, item.Product!.Category,
//CurrentUser = item.Movement?.FromUser?.UserName, //CurrentUser = item.Movement?.FromUser?.UserName,
CurrentUser = item.Movement?.FromUser?.UserName, CurrentUser = item.Movement?.FromUser?.UserName,

View File

@ -76,6 +76,7 @@ namespace PSTW_CentralSystem.Controllers.API.Reporting
item.Department?.DepartmentName, item.Department?.DepartmentName,
CreatedBy = item.CreatedBy!.UserName, CreatedBy = item.CreatedBy!.UserName,
item.Product!.ProductName, item.Product!.ProductName,
item.Product!.ProductShortName,
item.Product!.Category, item.Product!.Category,
//CurrentUser = item.Movement?.FromUser?.UserName, //CurrentUser = item.Movement?.FromUser?.UserName,
CurrentUser = item.Movement?.FromUser?.UserName, CurrentUser = item.Movement?.FromUser?.UserName,

View File

@ -22,7 +22,8 @@
type="image/png" type="image/png"
sizes="16x16" sizes="16x16"
href="/assets/images/favicon.png" /> href="/assets/images/favicon.png" />
<link href="https://fonts.googleapis.com/css2?family=OCR+A&display=swap" rel="stylesheet"> <a href="~/assets/fonts/ocraext.ttf">~/assets/fonts/ocraext.ttf</a>
<!-- Custom CSS --> <!-- Custom CSS -->
<link rel="stylesheet" href="~/assets/libs/select2/dist/css/select2.min.css" /> <link rel="stylesheet" href="~/assets/libs/select2/dist/css/select2.min.css" />
@ -38,6 +39,9 @@
@* <script src="~/js/vue.global.prod.js"></script> *@ @* <script src="~/js/vue.global.prod.js"></script> *@
<!-- QR Js --> <!-- QR Js -->
<script src="~/lib/qrcode/qrcode.min.js"></script> <script src="~/lib/qrcode/qrcode.min.js"></script>
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries --> <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// --> <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]> <!--[if lt IE 9]>
@ -47,6 +51,7 @@
</head> </head>
<body> <body>
<style> <style>
.btn-teal { .btn-teal {
background-color: #20c997; /* Teal color */ background-color: #20c997; /* Teal color */
color: #ffffff; /* White text */ color: #ffffff; /* White text */
@ -740,6 +745,12 @@
<script src="~/assets/libs/jquery/dist/jquery.min.js"></script> <script src="~/assets/libs/jquery/dist/jquery.min.js"></script>
@* <script src="~/dist/js/jquery.ui.touch-punch-improved.js"></script> *@ @* <script src="~/dist/js/jquery.ui.touch-punch-improved.js"></script> *@
<script src="~/dist/js/jquery-ui.min.js"></script> <script src="~/dist/js/jquery-ui.min.js"></script>
<!-- VUE Multiselect-->
<script src="~/lib/vue-multiselect/vue-multiselect.js"></script>
<link href="~/lib/vue-multiselect/vue-multiselect.min.css" rel="stylesheet" />
<!-- VUE Date Picker-->
<link href="~/lib/vue-datepicker/mainvuedate.css" rel="stylesheet" />
<script src="~/lib/vue-datepicker/vue-datepicker.iife.js"></script>
<!-- Bootstrap tether Core JavaScript --> <!-- Bootstrap tether Core JavaScript -->
<script src="~/assets/libs/bootstrap/dist/js/bootstrap.bundle.min.js"></script> <script src="~/assets/libs/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
<!-- slimscrollbar scrollbar JavaScript --> <!-- slimscrollbar scrollbar JavaScript -->

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long