Update
This commit is contained in:
parent
38c4629302
commit
d1682750dc
@ -8,6 +8,7 @@ namespace PSTW_CentralSystem.Areas.Inventory.Models
|
||||
[Key]
|
||||
public int ProductId { get; set; }
|
||||
public required string ProductName { get; set; }
|
||||
public required string ProductShortName { get; set; }
|
||||
public required int ManufacturerId { get; set; }
|
||||
public required string Category { get; set; }
|
||||
public required string ModelNo { get; set; }
|
||||
|
||||
@ -4,6 +4,14 @@
|
||||
Layout = "~/Views/Shared/_Layout.cshtml";
|
||||
}
|
||||
<style>
|
||||
@@font-face {
|
||||
font-family: 'OCR-A';
|
||||
src: url('../assets/fonts/ocraext.ttf');
|
||||
}
|
||||
|
||||
.QrPrintFont {
|
||||
font-family: 'OCR-A', monospace;
|
||||
}
|
||||
.table td img {
|
||||
display: block !important;
|
||||
}
|
||||
@ -45,17 +53,17 @@
|
||||
</div>
|
||||
<div class="col-7 d-flex align-items-center justify-content-center">
|
||||
<div class="text-center fs-4 text">
|
||||
<div class="col-12 my-3">
|
||||
{{thisQRInfo.uniqueID}}
|
||||
</div>
|
||||
<div class="col-12 my-3">
|
||||
{{thisQRInfo.departmentName}}
|
||||
</div>
|
||||
<div class="col-12 my-3">
|
||||
{{thisQRInfo.productName}}
|
||||
{{thisQRInfo.productShortName}}
|
||||
</div>
|
||||
<div class="col-12 my-3">
|
||||
{{thisQRInfo.endWDate}}
|
||||
{{thisQRInfo.serialNumber}}
|
||||
</div>
|
||||
<div class="col-12 my-3">
|
||||
{{thisQRInfo.partNumber}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -554,13 +562,29 @@
|
||||
},
|
||||
},
|
||||
{
|
||||
"title": "Category",
|
||||
"data": "category",
|
||||
"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": "Item Short Name",
|
||||
"data": "productShortName",
|
||||
},
|
||||
{
|
||||
"title": "Serial Number",
|
||||
"data": "serialNumber",
|
||||
},
|
||||
{
|
||||
"title": "Part Number",
|
||||
"data": "partNumber",
|
||||
},
|
||||
{
|
||||
"title": "Category",
|
||||
"data": "category",
|
||||
},
|
||||
{
|
||||
"title": "Quantity",
|
||||
"data": "quantity",
|
||||
@ -601,14 +625,6 @@
|
||||
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",
|
||||
"data": "productId",
|
||||
@ -640,9 +656,9 @@
|
||||
correctLevel: QRCode.CorrectLevel.M
|
||||
});
|
||||
}
|
||||
container.on('click', function() {
|
||||
window.open(data.qrString, '_blank');
|
||||
});
|
||||
// container.on('click', function() {
|
||||
// window.open(data.qrString, '_blank');
|
||||
// });
|
||||
});
|
||||
},
|
||||
})
|
||||
@ -663,10 +679,10 @@
|
||||
// Check if the table is collapsed
|
||||
if ($row.hasClass('child')) {
|
||||
// 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 {
|
||||
// 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) {
|
||||
@ -935,22 +951,22 @@
|
||||
|
||||
// Populate the virtual DOM with content
|
||||
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="col-5 text-center d-flex align-items-center justify-content-center">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<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 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="col-12 h4"><b>${this.thisQRInfo.departmentName}</b></div>
|
||||
<div class="col-12 h4"><b>${this.thisQRInfo.serialNumber??"-"}</b></div>
|
||||
<div class="col-12 h4"><b>${this.thisQRInfo.productName}</b></div>
|
||||
<div class="col-12 h4"><b>${this.thisQRInfo.endWDate}</b></div>
|
||||
<div class="col-12 h3"style="font-family: 'Verdana', monospace;"><b>${this.thisQRInfo.departmentName}</b></div>
|
||||
<div class="col-12 h4"style="font-family: 'Arial', monospace;"><b>${this.thisQRInfo.productShortName}</b></div>
|
||||
<div class="col-12 h4"style="font-family: 'Arial', monospace;"><b>${this.thisQRInfo.serialNumber??"-"}</b></div>
|
||||
<div class="col-12 h4"style="font-family: 'Arial', monospace;"><b>${this.thisQRInfo.partNumber}</b></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -968,7 +984,6 @@
|
||||
}).then((canvas) => {
|
||||
// Convert the canvas to an image
|
||||
const imgData = canvas.toDataURL('image/png');
|
||||
|
||||
// Open the image in a new tab for preview (optional)
|
||||
// const newWindow = window.open();
|
||||
// newWindow.location.href = imgData;
|
||||
|
||||
@ -28,6 +28,15 @@
|
||||
</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 *@
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-3">Manufacturer:</label>
|
||||
@ -126,6 +135,7 @@
|
||||
imageSrc: '',
|
||||
products: null,
|
||||
productDatatable: null,
|
||||
productShortName: null,
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
@ -142,6 +152,9 @@
|
||||
{ "title": "Product Name",
|
||||
"data": "productName",
|
||||
},
|
||||
{ "title": "Product Short Name",
|
||||
"data": "productShortName",
|
||||
},
|
||||
{ "title": "Model Number",
|
||||
"data": "modelNo",
|
||||
},
|
||||
@ -225,6 +238,7 @@
|
||||
// Create the payload
|
||||
const formData = {
|
||||
productName: this.productName,
|
||||
productShortName: this.productShortName,
|
||||
manufacturerId: this.manufacturer,
|
||||
category: this.category,
|
||||
modelNo: this.modelNo,
|
||||
|
||||
@ -17,17 +17,36 @@
|
||||
<div v-if="reportData">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-3">
|
||||
<h4>Statistic</h4>
|
||||
<select class="form-select shadow-none mt-3" v-model="selectedDepartment" v-on:change="">
|
||||
<option value="" disabled selected>Please select</option>
|
||||
<option v-for="(dept, index) in compDeptList" :key="index" :value="dept.departmentId">{{ dept.departmentName }}</option>
|
||||
</select>
|
||||
<div class="row col-10">
|
||||
<h4>Department</h4>
|
||||
<multiselect v-model="selectedDepartment" :options="compDeptList" :multiple="true" group-values="departments" group-label="companyName"
|
||||
:group-select="true" placeholder="Seach Department" track-by="departmentId" label="departmentName">
|
||||
</multiselect>
|
||||
<div class=""><button class="btn btn-danger" v-on:click="selectedDepartment = []">Clear</button></div>
|
||||
</div>
|
||||
</div>
|
||||
<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 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>
|
||||
@ -67,13 +86,16 @@
|
||||
<script>
|
||||
$(function () {
|
||||
app.mount('#invAdmin');
|
||||
|
||||
$('.closeModal').on('click', function () {
|
||||
// Show the modal with the ID 'addManufacturerModal'.
|
||||
$('.modal').modal('hide');
|
||||
});
|
||||
});
|
||||
const app = Vue.createApp({
|
||||
components: {
|
||||
'multiselect': window.VueMultiselect.default,
|
||||
VueDatePicker,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
currentUser: null,
|
||||
@ -83,13 +105,30 @@
|
||||
},
|
||||
reportData: null,
|
||||
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() {
|
||||
this.fetchUser();
|
||||
this.fetchProductList();
|
||||
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: {
|
||||
async fetchUser() {
|
||||
try {
|
||||
@ -142,25 +181,60 @@
|
||||
});
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
this.compDeptList = data.map(company => company.departments.map(department =>
|
||||
({
|
||||
companyId: company.companyId,
|
||||
companyName: company.companyName,
|
||||
departmentId: department.departmentId,
|
||||
departmentName: department.departmentName,
|
||||
departmentCode: department.departmentCode
|
||||
})
|
||||
)).flat();
|
||||
console.log(this.compDeptList);
|
||||
this.compDeptList = data;
|
||||
}
|
||||
else {
|
||||
console.error(`Failed to fetch comapny & department list: ${response.statusText}`);
|
||||
console.error(`Failed to fetch company & department list: ${response.statusText}`);
|
||||
}
|
||||
}
|
||||
catch (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>
|
||||
|
||||
@ -261,7 +261,7 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
|
||||
}
|
||||
|
||||
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>();
|
||||
// Get the item list
|
||||
if (isAdmin)
|
||||
@ -324,6 +324,7 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
|
||||
item.Department?.DepartmentName,
|
||||
CreatedBy=item.CreatedBy!.UserName,
|
||||
item.Product!.ProductName,
|
||||
item.Product!.ProductShortName,
|
||||
item.Product!.Category,
|
||||
//CurrentUser = item.Movement?.FromUser?.UserName,
|
||||
CurrentUser = item.Movement?.FromUser?.UserName,
|
||||
@ -495,6 +496,7 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
|
||||
item.Department?.DepartmentName,
|
||||
item.CreatedBy!.UserName,
|
||||
item.Product!.ProductName,
|
||||
item.Product!.ProductShortName,
|
||||
item.Product!.ImageProduct,
|
||||
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,
|
||||
CreatedBy = item.CreatedBy!.UserName,
|
||||
item.Product!.ProductName,
|
||||
item.Product!.ProductShortName,
|
||||
item.Product!.Category,
|
||||
//CurrentUser = item.Movement?.FromUser?.UserName,
|
||||
CurrentUser = item.Movement?.FromUser?.UserName,
|
||||
|
||||
@ -76,6 +76,7 @@ namespace PSTW_CentralSystem.Controllers.API.Reporting
|
||||
item.Department?.DepartmentName,
|
||||
CreatedBy = item.CreatedBy!.UserName,
|
||||
item.Product!.ProductName,
|
||||
item.Product!.ProductShortName,
|
||||
item.Product!.Category,
|
||||
//CurrentUser = item.Movement?.FromUser?.UserName,
|
||||
CurrentUser = item.Movement?.FromUser?.UserName,
|
||||
|
||||
@ -22,7 +22,8 @@
|
||||
type="image/png"
|
||||
sizes="16x16"
|
||||
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 -->
|
||||
<link rel="stylesheet" href="~/assets/libs/select2/dist/css/select2.min.css" />
|
||||
@ -38,6 +39,9 @@
|
||||
@* <script src="~/js/vue.global.prod.js"></script> *@
|
||||
<!-- QR Js -->
|
||||
<script src="~/lib/qrcode/qrcode.min.js"></script>
|
||||
|
||||
|
||||
|
||||
<!-- 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:// -->
|
||||
<!--[if lt IE 9]>
|
||||
@ -47,6 +51,7 @@
|
||||
</head>
|
||||
<body>
|
||||
<style>
|
||||
|
||||
.btn-teal {
|
||||
background-color: #20c997; /* Teal color */
|
||||
color: #ffffff; /* White text */
|
||||
@ -740,6 +745,12 @@
|
||||
<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.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 -->
|
||||
<script src="~/assets/libs/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<!-- slimscrollbar scrollbar JavaScript -->
|
||||
|
||||
BIN
wwwroot/assets/Fonts/OCRAEXT.TTF
Normal file
BIN
wwwroot/assets/Fonts/OCRAEXT.TTF
Normal file
Binary file not shown.
1
wwwroot/lib/Vue-DatePicker/mainVueDate.css
Normal file
1
wwwroot/lib/Vue-DatePicker/mainVueDate.css
Normal file
File diff suppressed because one or more lines are too long
1
wwwroot/lib/Vue-DatePicker/vue-datepicker.iife.js
Normal file
1
wwwroot/lib/Vue-DatePicker/vue-datepicker.iife.js
Normal file
File diff suppressed because one or more lines are too long
1358
wwwroot/lib/Vue-MultiSelect/vue-multiselect.js
Normal file
1358
wwwroot/lib/Vue-MultiSelect/vue-multiselect.js
Normal file
File diff suppressed because it is too large
Load Diff
1
wwwroot/lib/Vue-MultiSelect/vue-multiselect.min.css
vendored
Normal file
1
wwwroot/lib/Vue-MultiSelect/vue-multiselect.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
2
wwwroot/lib/Vue-MultiSelect/vue-multiselect.umd.min.js
vendored
Normal file
2
wwwroot/lib/Vue-MultiSelect/vue-multiselect.umd.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user