fix itemmovementmaster

update qrmaster
This commit is contained in:
ameerulrasyid 2025-02-25 16:23:45 +08:00
parent 0f2b065c66
commit ca46776473
6 changed files with 362 additions and 765 deletions

View File

@ -407,23 +407,23 @@
// Generate QR codes after rows are rendered // Generate QR codes after rows are rendered
const api = this.api(); const api = this.api();
api.rows().every(function () { api.rows().every(function () {
const data = this.data(); // Row data const data = this.data();
const containerId = `qr${data.id}`; const containerId = `qr${data.id}`; //containerid is by increments from API
const container = $(`#${containerId}`); const container = $(`#${containerId}`);
container.empty(); container.empty();
container.append(`${data.item.itemId}`); container.append(`${data.item.itemId}`);
// console.log(container[0]); if (container.length) {
if (container) {
// Generate QR code only if not already generated
new QRCode(container[0], { new QRCode(container[0], {
text: data.qrString, text: data.qrString,
width: 100, width: 100,
height: 100, height: 100,
colorDark: "#000000", colorDark: "#000000",
colorLight: "#ffffff", colorLight: "#ffffff",
correctLevel: QRCode.CorrectLevel.M correctLevel: QRCode.CorrectLevel.M,
}); });
} }
// container.on('click', function() { // container.on('click', function() {

View File

@ -20,7 +20,7 @@
</div> </div>
<div class="row card"> <div class="row card">
<div class="card-header"> <div class="card-header">
<h2>Item Request</h2> <h2>Complete Request</h2>
@* <button id="addRequestBtn" class="btn btn-success col-md-3 col-lg-3 m-1 col-12"><i class="fa fa-plus"></i>&nbsp;Add Request</button> *@ @* <button id="addRequestBtn" class="btn btn-success col-md-3 col-lg-3 m-1 col-12"><i class="fa fa-plus"></i>&nbsp;Add Request</button> *@
</div> </div>
<div class="card-body"> <div class="card-body">
@ -240,6 +240,31 @@
{ {
"title": "Document/Picture", "title": "Document/Picture",
"data": "document", "data": "document",
"render": function (data, type, full, meta) {
if (!data) {
return "No Document";
}
// Check if the document is an image based on file extension
var isImage = /\.(jpeg|jpg|png|gif)$/i.test(data);
var isPdf = /\.pdf$/i.test(data);
if (isImage) {
return `<a href="${data}" target="_blank" data-lightbox="image-1">
<img src="${data}" alt="Image" class="img-thumbnail" style="width: 100px; height: 100px;" />
</a>`;
}
else if (isPdf) {
return `<a href="${data}" target="_blank">
<img src="https://upload.wikimedia.org/wikipedia/commons/8/87/PDF_file_icon.svg"
alt="PDF Document" class="img-thumbnail"
style="width: 50px; height: 50px;" />
<br>View PDF
</a>`;
} else {
return `<a href="${data}" target="_blank">Download File</a>`;
}
},
}, },
{ {
"title": "User Remark", "title": "User Remark",
@ -331,6 +356,31 @@
{ {
"title": "Document/Picture", "title": "Document/Picture",
"data": "document", "data": "document",
"render": function (data, type, full, meta) {
if (!data) {
return "No Document";
}
// Check if the document is an image based on file extension
var isImage = /\.(jpeg|jpg|png|gif)$/i.test(data);
var isPdf = /\.pdf$/i.test(data);
if (isImage) {
return `<a href="${data}" target="_blank" data-lightbox="image-1">
<img src="${data}" alt="Image" class="img-thumbnail" style="width: 100px; height: 100px;" />
</a>`;
}
else if (isPdf) {
return `<a href="${data}" target="_blank">
<img src="https://upload.wikimedia.org/wikipedia/commons/8/87/PDF_file_icon.svg"
alt="PDF Document" class="img-thumbnail"
style="width: 50px; height: 50px;" />
<br>View PDF
</a>`;
} else {
return `<a href="${data}" target="_blank">Download File</a>`;
}
},
}, },
{ {
"title": "User Remark", "title": "User Remark",
@ -361,7 +411,6 @@
// data-remark="${row.remark || ''}"> // data-remark="${row.remark || ''}">
// Reject // Reject
// </button>`; // </button>`;
// }, // },
// "className": "align-middle", // "className": "align-middle",
// }, // },

View File

@ -20,16 +20,8 @@
<div id="registerItem" class="row"> <div id="registerItem" class="row">
<div class="row card"> <div class="row card">
<div class="card-header"> <div class="card-header">
@* <button id="addItemBtn" class="btn btn-success col-md-3 col-lg-3 m-1 col-12"><i class="fa fa-plus"></i>&nbsp;Add Item</button> *@
</div> </div>
<div class="card-body"> <div class="card-body">
@* <div v-if="loading">
<div class="spinner-border text-info" role="status">
<span class="visually-hidden">Loading...</span>
</div>
</div> *@
@* <table class="table table-bordered table-hover table-striped no-wrap" id="itemDatatable" style=" width:100%;border-style: solid; border-width: 1px"></table> *@
<div id="app" data-aos="fade-right"> <div id="app" data-aos="fade-right">
<h1 data-aos="fade-right">QR & Barcode Scanner</h1> <h1 data-aos="fade-right">QR & Barcode Scanner</h1>
<div id="reader" data-aos="fade-right"></div> <div id="reader" data-aos="fade-right"></div>
@ -117,7 +109,7 @@
<div class=" register" data-aos="fade-right"> <div class=" register" data-aos="fade-right">
<div class="row" data-aos="fade-right"> <div class="row" data-aos="fade-right">
<form v-on:submit.prevent="addItemMovement" data-aos="fade-right">
@*Right Side*@ @*Right Side*@
<div class="col-md-12"> <div class="col-md-12">
<div class="tab-content" id="myTabContent"> <div class="tab-content" id="myTabContent">
@ -133,6 +125,7 @@
<select class="btn btn-primary dropdown-toggle col-md-10 offset-sm-1" v-model="selectedAction" required> <select class="btn btn-primary dropdown-toggle col-md-10 offset-sm-1" v-model="selectedAction" required>
<option class="btn-light" value="" disabled selected>Select Action</option> <option class="btn-light" value="" disabled selected>Select Action</option>
<option class="btn-light" value="user">Assign to User</option> <option class="btn-light" value="user">Assign to User</option>
<option class="btn-light" value="store">Assign to Store</option>
<option class="btn-light" value="supplier">Assign to Supplier</option> <option class="btn-light" value="supplier">Assign to Supplier</option>
<option class="btn-light" value="faulty">Faulty</option> <option class="btn-light" value="faulty">Faulty</option>
</select> </select>
@ -141,7 +134,7 @@
</div> </div>
<div v-if="selectedAction === 'user'"> <div v-if="selectedAction === 'user'">
<form v-on:submit.prevent="addItemMovement" data-aos="fade-right">
<div class="row register-form"> <div class="row register-form">
<div class="col-md-3"></div> <div class="col-md-3"></div>
<div class="col-md-6"> <div class="col-md-6">
@ -159,22 +152,10 @@
</div> </div>
</div> </div>
<div class="form-group row"> <div class="form-group row">
<label class="col-sm-4 col-form-label">To Store:</label> <label for="invoiceDate" class="col-sm-4">Assign Date:</label>
<div class="col-sm-8"> <div class="col-sm-8">
<select class="btn btn-primary dropdown-toggle col-md-10" v-model="selectedStore" > <input type="date" id="assigndate" name="assigndate" class="form-control" v-model="assigndate">
<option class="btn-light" value="" disabled selected>Select Store</option>
<option class="btn-light" v-for="(store, index) in storelist" :key="index" :value="store.id">{{ store.StoreName}}</option>
</select>
</div>
</div>
<div class="form-group row">
<label class="col-sm-4 col-form-label">Remark:</label>
<div class="col-sm-8">
<input type="text" id="remark" name="remark" v-model="remark" class="form-control" required />
</div> </div>
</div> </div>
@ -189,39 +170,117 @@
</div> </div>
</div> </div>
</div> </div>
</form>
</div>
<div v-if="selectedAction === 'store'">
<div class="row register-form">
<div class="col-md-3"></div>
<div class="col-md-6">
<div class="form-group row">
<label class="col-sm-4 col-form-label">To Store:</label>
<div class="col-sm-8">
<select class="btn btn-primary dropdown-toggle col-md-10" v-model="selectedStore">
<option class="btn-light" value="" disabled selected>Select Store</option>
<option class="btn-light" v-for="(store, index) in storelist" :key="index" :value="store.id">{{ store.storeName}}</option>
</select>
</div>
</div>
<div class="form-group row">
<label for="invoiceDate" class="col-sm-4">Assign Date:</label>
<div class="col-sm-8">
<input type="date" id="assigndate" name="assigndate" class="form-control" v-model="assigndate">
</div>
</div>
</div>
@* Submit and Reset Buttons *@
<div class="form-group row">
<div class="col-sm-8 offset-sm-3">
<button type="button" v-on:click="resetForm" class="btn btn-secondary m-1">Reset</button>
<button type="submit" class="btn btn-primary m-1">Submit</button>
</div>
</div>
</div>
</div> </div>
<div v-if="selectedAction === 'supplier'"> <div v-if="selectedAction === 'supplier'">
<form v-on:submit.prevent="addItemMovement" data-aos="fade-right">
<div class="row register-form"> <div class="row register-form">
<div class="col-md-3"></div> <div class="col-md-3"></div>
<div class="col-md-6"> <div class="col-md-6">
<div class="form-group row"> <div class="form-group row">
<label class="col-sm-4 col-form-label">To supplier:</label> <label class="col-sm-4 col-form-label">To Supplier:</label>
<div class="col-sm-8"> <div class="col-sm-8">
<div class="dropdown"> <div class="dropdown">
<select class="btn btn-primary dropdown-toggle col-md-10" v-model="selectedUser" required> <select class="btn btn-primary dropdown-toggle col-md-10" v-model="selectedSupplier" required>
<option class="btn-light" value="" disabled selected>Select User</option> <option class="btn-light" value="" disabled selected>Select Supplier</option>
<option class="btn-light" v-for="(user, index) in userlist" :key="index" :value="user.id">{{user.fullName}}</option> <option class="btn-light" v-for="(supplier, index) in supplierlist" :key="index" :value="supplier.supplierCompName">{{supplier.supplierCompName}}</option>
</select> </select>
</div> </div>
</div> </div>
</div> </div>
<div class="form-group row"> <div class="form-group row">
<label class="col-sm-4 col-form-label">To Store:</label> <label class="col-sm-4 col-form-label">To Other:</label>
<div class="col-sm-8"> <div class="col-sm-8">
<select class="btn btn-primary dropdown-toggle col-md-10" v-model="selectedStore"> <div class="dropdown">
<option class="btn-light" value="" disabled selected>Select Store</option> <select class="btn btn-primary dropdown-toggle col-md-10" v-model="selectedOther" required>
<option class="btn-light" v-for="(store, index) in storelist" :key="index" :value="store.id">{{ store.StoreName}}</option> <option class="btn-light" value="" disabled selected>Select</option>
<option class="btn-light" value="Repair">Repair</option>
<option class="btn-light" value="Calibration">Calibration</option>
</select> </select>
</div> </div>
</div> </div>
</div>
<div class="form-group row">
<label for="invoiceDate" class="col-sm-4">Assign Date:</label>
<div class="col-sm-8">
<input type="date" id="assigndate" name="assigndate" class="form-control" v-model="assigndate">
</div>
</div>
<div class="form-group row">
<label class="col-sm-4 col-form-label">Remark:</label>
<div class="col-sm-8">
<input type="text" id="remark" name="remark" v-model="remark" class="form-control" required />
</div>
</div>
</div>
@* Submit and Reset Buttons *@
<div class="form-group row">
<div class="col-sm-8 offset-sm-3">
<button type="button" v-on:click="resetForm" class="btn btn-secondary m-1">Reset</button>
<button type="submit" class="btn btn-primary m-1">Submit</button>
</div>
</div>
</div>
</div>
<div v-if="selectedAction === 'faulty'">
<div class="row register-form">
<div class="col-md-3"></div>
<div class="col-md-6">
<div class="form-group row">
<label class="col-sm-4 col-form-label">Consignment Note</label>
<div class="col-sm-8">
<input type="file" id="document" name="document" class="form-control-file" v-on:change="handleFileUpload" accept="image/png, image/jpeg, application/pdf" />
</div>
</div>
<div class="form-group row"> <div class="form-group row">
<label class="col-sm-4 col-form-label">Remark:</label> <label class="col-sm-4 col-form-label">Remark:</label>
@ -241,71 +300,13 @@
</div> </div>
</div> </div>
</div> </div>
</form>
</div> </div>
<div v-if="selectedAction === 'faulty'">
<form v-on:submit.prevent="addItemMovement" data-aos="fade-right">
<div class="row register-form">
<div class="col-md-3"></div>
<div class="col-md-6">
<div class="form-group row">
<label class="col-sm-4 col-form-label">To User:</label>
<div class="col-sm-8">
<div class="dropdown">
<select class="btn btn-primary dropdown-toggle col-md-10" v-model="selectedUser" required>
<option class="btn-light" value="" disabled selected>Select User</option>
<option class="btn-light" v-for="(user, index) in userlist" :key="index" :value="user.id">{{user.fullName}}</option>
</select>
</div>
</div>
</div>
<div class="form-group row">
<label class="col-sm-4 col-form-label">To Store:</label>
<div class="col-sm-8">
<select class="btn btn-primary dropdown-toggle col-md-10" v-model="selectedStore">
<option class="btn-light" value="" disabled selected>Select Store</option>
<option class="btn-light" v-for="(store, index) in storelist" :key="index" :value="store.id">{{ store.StoreName}}</option>
</select>
</div>
</div>
<div class="form-group row">
<label class="col-sm-4 col-form-label">Faulty:</label>
<div class="col-sm-8">
<input type="text" id="remark" name="remark" v-model="remark" class="form-control" required />
</div>
</div>
</div>
@* Submit and Reset Buttons *@
<div class="form-group row">
<div class="col-sm-8 offset-sm-3">
<button type="button" v-on:click="resetForm" class="btn btn-secondary m-1">Reset</button>
<button type="submit" class="btn btn-primary m-1">Submit</button>
</div> </div>
</div> </div>
</div> </div>
</form> </form>
</div> </div>
</div> </div>
</div>
</div>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -341,10 +342,15 @@
debounceTimeout: null, debounceTimeout: null,
userlist: null, userlist: null,
storelist: null, storelist: null,
supplierlist: null,
selectedUser: "", selectedUser: "",
selectedStore: "", selectedStore: "",
selectedAction: "", selectedAction: "",
assigndate: null,
selectedSupplier: "",
selectedOther: "",
remark: "",
document: null,
companies: [ companies: [
{ {
companyId: 1, companyId: 1,
@ -358,66 +364,12 @@
}, },
], ],
company: "", company: "",
// Dept: null,
// teamTypes: ["Continuous", "Manual"],
// teamType: "",
// productName: null,
// imageProduct: null,
// productCategory: null,
// serialNumber: "",
// quantity: 1,
// supplierName: null,
// purchaseDate: null,
// PO: null,
// currency: "MYR",
// DefaultPrice: 0.01,
// currencyRate: 1,
// convertPrice: 0.01,
// DONo:null,
// DODate: null,
// warranty: 0,
// EndWDate: null,
// invoiceNo: null,
// invoiceDate: null,re
// partNumber: null,
// products: [],
// depts: [],
// suppliers: [
// {
// supplierId: 1,
// supplierName: "Pang",
// },
// {
// supplierId: 2,
// supplierName: "Ms Kim",
// },
// ],
// isModalOpen: false,
// selectedProduct: "",
// selectedSupplier: "",
// selectedCompany: "",
// selectedDepartment: "",
// selectedTeamType: "",
// currencies: {},
// showItemModal: false,
// loading: false,
// thisQRInfo: {
// uniqueID: null,
// departmentName: null,
// serialNumber: null,
// endWDate: null,
// },
// items: [],
currentUser: null, currentUser: null,
currentUserCompanyDept: null, currentUserCompanyDept: null,
} }
}, },
mounted() { mounted() {
this.fetchUser(); this.fetchUser();
this.fetchItems();
this.fetchCurrencyData();
this.fetchCompanies();
this.fetchProducts();
this.fetchSuppliers(); this.fetchSuppliers();
this.startScanner(); this.startScanner();
this.fetchUsers(); this.fetchUsers();
@ -445,35 +397,58 @@
}, },
}, },
methods: { methods: {
handleFileUpload(event) {
const file = event.target.files[0];
if (file) {
const reader = new FileReader();
reader.onload = (e) => {
this.document = e.target.result.split(',')[1]; // Get Base64 string (remove metadata)
};
reader.readAsDataURL(file);
} else {
this.document = null;
}
},
async addItemMovement() { async addItemMovement() {
if (this.showProduct.category == "Disposable") { if (this.showProduct.category == "Disposable") {
this.serialNumber = ""; this.serialNumber = "";
} }
const now = new Date();
const formData = { const formData = {
toUser: this.selectedUser, ...(this.selectedAction === 'user' ? { toUser: this.currentUser.id, toOther: 'On Delivery', SendDate: this.assigndate, lastUser: this.selectedUser, } : {}),
toStore: this.selectedStore, ...(this.selectedAction === 'store' ? { toUser: this.currentUser.id, toOther: 'On Delivery', SendDate: this.assigndate, lastUser: this.selectedStore} : {}),
Remark: this.remark, ...(this.selectedAction === 'supplier' ? { toUser: this.currentUser.id, toOther: this.selectedOther, SendDate: this.assigndate, Remark: this.remark + '. Item sent to ' + this.selectedSupplier + ' for ' + this.selectedOther,lastUser: this.currentUser.id, } : {}),
...(this.selectedAction === 'faulty' ? { toUser: this.currentUser.id,toOther: 'Faulty', Date: new Date(now.getTime() + 8 * 60 * 60 * 1000).toISOString(), Remark: this.remark, ConsignmentNote: this.document } : {}),
ItemId: this.thisItem.itemID,
Action: 'Stock Out',
Quantity: 1,
MovementComplete: false,
}; };
try { try {
// Additional specific checks // Additional specific checks
if (this.showSerialNumber) { // if (this.showSerialNumber) {
this.quantity = 0; // this.quantity = 0;
if (this.serialNumber === null || this.serialNumber === '') { // if (this.serialNumber === null || this.serialNumber === '') {
alert('Serial Number Error', 'Serial Number must be filled when selecting Item or Part.', 'warning'); // alert('Serial Number Error', 'Serial Number must be filled when selecting Item or Part.', 'warning');
return; // return;
} // }
} // }
else { // else {
this.serialNumber = null; // this.serialNumber = null;
if (this.quantity === 0 || this.quantity === null || this.quantity === '') { // if (this.quantity === 0 || this.quantity === null || this.quantity === '') {
alert('quantity Error', 'Quantity is required when selecting Disposable.', 'warning'); // alert('quantity Error', 'Quantity is required when selecting Disposable.', 'warning');
return; // return;
} // }
} // }
// Proceed to send the data to the API // Proceed to send the data to the API
const response = await fetch('/InvMainAPI/AddItemMovement', { const response = await fetch('/InvMainAPI/AddItemMovement', {
@ -488,12 +463,12 @@
// If the form submission was successful, display a success message // If the form submission was successful, display a success message
alert('Success!', 'Item form has been successfully submitted.', 'success'); alert('Success!', 'Item form has been successfully submitted.', 'success');
const updatedItem = await response.json(); const updatedItem = await response.json();
this.items.push(updatedItem); // this.items.push(updatedItem);
this.fetchItem();
// Reset the form // Reset the form
this.resetForm(); this.resetForm();
// window.location.href = '/Inventory/InventoryMaster/ItemMovement';
} else { } else {
throw new Error('Failed to submit form.'); throw new Error('Failed to submit form.');
} }
@ -548,6 +523,7 @@
if (response.ok) { if (response.ok) {
this.thisItem = await response.json(); this.thisItem = await response.json();
} else { } else {
console.error('Failed to fetch item information'); console.error('Failed to fetch item information');
this.responseMessage = await response.text(); this.responseMessage = await response.text();
@ -556,300 +532,6 @@
console.error('Error fetching item information:', error); console.error('Error fetching item information:', error);
} }
}, },
async addItem() {
if (this.showProduct.category == "Disposable") {
this.serialNumber = "";
}
const formData = {
CompanyId: this.selectedCompany,
DepartmentId: this.selectedDepartment,
ProductId: this.selectedProduct,
SerialNumber: this.serialNumber,
Quantity: this.quantity,
Supplier: this.selectedSupplier,
PurchaseDate: this.purchaseDate,
PONo: this.PO,
Currency: this.currency,
DefaultPrice: this.DefaultPrice,
CurrencyRate: this.currencyRate,
ConvertPrice: this.convertPrice,
DODate: this.DODate,
Warranty: this.warranty,
EndWDate: this.EndWDate,
InvoiceDate: this.invoiceDate,
CreatedByUserId: this.currentUser.id,
TeamType: this.selectedTeamType,
PartNumber: this.partNumber,
};
try {
// Additional specific checks
if (this.showSerialNumber) {
this.quantity = 0;
if (this.serialNumber === null || this.serialNumber === '') {
alert('Serial Number Error', 'Serial Number must be filled when selecting Item or Part.', 'warning');
return;
}
}
else {
this.serialNumber = null;
if (this.quantity === 0 || this.quantity === null || this.quantity === '') {
alert('quantity Error', 'Quantity is required when selecting Disposable.', 'warning');
return;
}
}
// Proceed to send the data to the API
const response = await fetch('/InvMainAPI/AddItem', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
// 'Authorization': `Bearer ${this.token}`
},
body: JSON.stringify(formData)
});
if (response.ok) {
// If the form submission was successful, display a success message
alert('Success!', 'Item form has been successfully submitted.', 'success');
const updatedItem = await response.json();
this.items.push(updatedItem);
this.fetchItems();
// Reset the form
this.resetForm();
} else {
throw new Error('Failed to submit form.');
}
} catch (error) {
console.error('Error:', error);
// Displaying error message
alert('Inventory PSTW Error', `An error occurred: ${error.message}`, 'error');
}
},
initiateTable() {
self = this;
this.itemDatatable = $('#itemDatatable').DataTable({
"data": this.items,
"columns": [
{
"title": "Unique Id",
"data": "uniqueID",
"createdCell": function (td, cellData, rowData, row, col) {
// Assign a unique ID to the <td> element
$(td).attr('id', `qr${cellData}`);
},
},
{
"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;
},
"className": "align-middle",
},
{
"title": "Item Short Name",
"data": "productShortName",
},
{
"title": "Serial Number",
"data": "serialNumber",
},
{
"title": "Part Number",
"data": "partNumber",
},
{
"title": "Category",
"data": "category",
},
{
"title": "Quantity",
"data": "quantity",
},
{
"title": "Supplier",
"data": "supplier",
},
{
"title": "Purchase Date",
"data": "purchaseDate",
},
{
"title": "Price(RM)",
"data": "convertPrice",
},
{
"title": "Invoice Date",
"data": "invoiceDate",
},
{
"title": "Warranty Until",
"data": "warranty",
"render": function (data, type, full, meta) {
if (data > 0) { return full.endWDate }
else { return data }
}
},
{
"title": "Location",
"data": "currentUser",
"render": function (data, type, full, meta) {
currentUser = data ?? null;
currentStore = full.currentStore ?? 'N/A';
currentStation = full.currentStation ?? 'N/A';
return `User: ${currentUser}<br>
Store: ${currentStore}<br>
Station: ${currentStation}`
}
},
{
"title": "Delete",
"data": "itemID",
"render": function (data) {
var deleteButton = `<button type="button" class="btn btn-danger delete-btn" data-id="${data}">Delete</button>`;
return deleteButton;
},
"className": "align-middle",
}
],
responsive: true,
drawCallback: function (settings) {
// Generate QR codes after rows are rendered
const api = this.api();
api.rows().every(function () {
const data = this.data(); // Row data
const containerId = `qr${data.uniqueID}`;
const container = $(`#${containerId}`);
container.empty();
container.append(`${data.uniqueID}`);
// console.log(container[0]);
if (container) {
// Generate QR code only if not already generated
new QRCode(container[0], {
text: data.qrString,
width: 100,
height: 100,
colorDark: "#000000",
colorLight: "#ffffff",
correctLevel: QRCode.CorrectLevel.M
});
}
// container.on('click', function() {
// window.open(data.qrString, '_blank');
// });
});
},
})
// Attach click event listener to the delete buttons
$('#itemDatatable tbody').on('click', '.delete-btn', function () {
const itemId = $(this).data('id');
self.deleteItem(itemId);
});
$('#itemDatatable tbody').on('click', '.print-btn', function () {
const $button = $(this); // The clicked button
const $row = $button.closest('tr'); // The parent row of the button
const itemId = $button.data('id'); // Get the item ID from the button's data attribute
let imageSrc;
// 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: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:nth-child(1) img').attr('src');
}
if (imageSrc) {
self.printItem(itemId, imageSrc); // Call the print function with the itemId and imageSrc
} else {
console.error("Image source not found.");
}
});
this.loading = false;
},
async fetchItems() {
try {
// const token = localStorage.getItem('token'); // Get the token from localStorage
const response = await fetch('/InvMainAPI/ItemList', {
method: 'POST', // Specify the HTTP method
headers: {
'Content-Type': 'application/json', // Set content type
// 'Authorization': `Bearer ${token}` // Include the token in the headers
}
});
if (!response.ok) {
throw new Error('Failed to fetch item');
}
this.items = await response.json();
if (this.itemDatatable) {
this.itemDatatable.clear().destroy();
}
this.initiateTable();
}
catch (error) {
console.error('Error fetching item:', error);
}
},
async fetchProducts() {
try {
// const token = localStorage.getItem('token'); // Get the token from localStorage
const response = await fetch('/InvMainAPI/ProductList', {
method: 'POST', // Specify the HTTP method
headers: {
'Content-Type': 'application/json', // Set content type
// 'Authorization': `Bearer ${token}` // Include the token in the headers
}
});
if (!response.ok) {
throw new Error('Failed to fetch products');
}
this.products = await response.json();
}
catch (error) {
console.error('Error fetching products:', error);
}
},
async fetchCompanies() {
try {
const response = await fetch('/AdminAPI/GetDepartmentWithCompanyList', {
method: 'POST', // Specify the HTTP method
headers: {
'Content-Type': 'application/json'
}
});
if (!response.ok) {
throw new Error('Failed to fetch companies');
}
this.companies = await response.json();
} catch (error) {
console.error('Error fetching products:', error);
}
},
async fetchSuppliers() { async fetchSuppliers() {
try { try {
const response = await fetch('/InvMainAPI/SupplierList', { const response = await fetch('/InvMainAPI/SupplierList', {
@ -861,127 +543,26 @@
if (!response.ok) { if (!response.ok) {
throw new Error('Failed to fetch suppliers'); throw new Error('Failed to fetch suppliers');
} }
this.suppliers = await response.json(); // Get the full response object this.supplierlist = await response.json(); // Get the full response object
} catch (error) { } catch (error) {
console.error('Error fetching suppliers:', error); console.error('Error fetching suppliers:', error);
} }
}, },
async fetchCurrencyData() {
try {
// Fetch currency data from the API
const response = await fetch('https://openexchangerates.org/api/currencies.json'); // Example API
this.currencies = await response.json();
} catch (error) {
console.error('Error fetching currency data:', error);
}
},
convertCurrency() {
// Your currency conversion logic here
console.log('Selected currency:', this.currency);
},
resetForm() { resetForm() {
this.selectedUser = null; this.selectedUser = "";
this.selectedStore = null; this.selectedStore = "";
this.remark = ''; this.remark = "";
this.assigndate = "";
this.selectedStore = "";
this.selectedOther = "";
}, },
// FRONT END FUNCTIONS // FRONT END FUNCTIONS
//----------------------// //----------------------//
//Calculate Total Price
convertCurrency() {
const total = this.DefaultPrice * this.currencyRate;
this.convertPrice = total.toFixed(2);
this.DefaultPrice = this.DefaultPrice
// .replace(/[^0-9.]/g, '') // Remove non-numeric characters except decimal points
// .replace(/(\..*)\..*/g, '$1') // Allow only one decimal point
// .replace(/^(\d*\.\d{0,2})\d*$/, '$1'); // Limit to two decimal places
},
calculateWarrantyEndDate() {
// Check if DODate and warranty are valid
if (!this.DODate || isNaN(Date.parse(this.DODate))) {
this.EndWDate = null;
return;
}
const DODates = new Date(this.DODate);
const warrantyMonth = parseInt(this.warranty);
// Ensure warranty is a valid number
if (!isNaN(warrantyMonth)) {
DODates.setMonth(DODates.getMonth() + warrantyMonth);
this.EndWDate = DODates.toISOString().split('T')[0];
} else {
this.EndWDate = null;
}
},
async deleteItem(itemId) {
if (!confirm("Are you sure you want to delete this item?")) {
return;
}
try {
const response = await fetch(`/InvMainAPI/DeleteItem/${itemId}`, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
},
});
const result = await response.json();
if (result.success) {
alert(result.message);
// Remove the row from DataTables
this.itemDatatable
.row($(`.delete-btn[data-id="${itemId}"]`).closest('tr'))
.remove()
.draw();
} else {
alert(result.message);
}
}
catch (error) {
console.error("Error deleting item:", error);
alert("An error occurred while deleting the item.");
}
finally {
this.loading = false;
}
},
async printItem(itemId, imgSrc) {
try {
this.thisQRInfo.uniqueID = itemId;
const uniqueQR = itemId;
const container = document.getElementById("QrContainer");
if (!container) {
console.error("Container not found.");
return;
}
// Safely set image content
const sanitizedImgSrc = encodeURI(imgSrc); // Sanitize the URL
container.innerHTML = `<img src="${sanitizedImgSrc}" alt="QR Code" class="text-center " />`;
// Fetch QR information
const qrInfo = this.getPrintedQR(uniqueQR);
if (!qrInfo) {
console.error("QR Info not found.");
return;
}
this.thisQRInfo = qrInfo;
this.thisQRInfo.imgSrc = sanitizedImgSrc
this.thisQRInfo.imgContainer = container.innerHTML
$(`#QrItemModal`).modal('show'); // Show modal
}
catch (error) {
console.error("Error generating QR code:", error);
alert("An error occurred while generating the QR code.");
}
},
async fetchUser() { async fetchUser() {
try { try {
const response = await fetch(`/IdentityAPI/GetUserInformation/`, { const response = await fetch(`/IdentityAPI/GetUserInformation/`, {
@ -994,6 +575,7 @@
this.currentUserCompanyDept = companyDeptData; this.currentUserCompanyDept = companyDeptData;
this.selectedCompany = companyDeptData?.companyId || ""; this.selectedCompany = companyDeptData?.companyId || "";
this.selectedDepartment = companyDeptData?.departmentId || ""; this.selectedDepartment = companyDeptData?.departmentId || "";
} }
else { else {
console.error(`Failed to fetch user: ${response.statusText}`); console.error(`Failed to fetch user: ${response.statusText}`);
@ -1003,88 +585,27 @@
console.error('There was a problem with the fetch operation:', error); console.error('There was a problem with the fetch operation:', error);
} }
}, },
getPrintedQR(uniqueID) { async fetchStoreId() {
if (!this.items || !Array.isArray(this.items)) { try {
console.error("Items list is not available or is not an array."); const response = await fetch(`/IdentityAPI/GetUserInformation/`, {
return null; method: 'POST',
}
return this.items.find(item => item.uniqueID === uniqueID);
},
printQRInfo() {
// Create a virtual DOM element
const virtualElement = document.createElement('div');
virtualElement.style.width = '330px '; // Match label size for 2 inches at 203 DPI
virtualElement.style.height = '160px';
virtualElement.style.position = 'absolute';
virtualElement.style.left = '-9999px'; // Position offscreen to avoid rendering on the main UI
// virtualElement.style.border = '1px solid #000'; // Optional: Add a border for debugging dimensions
// Populate the virtual DOM with content
virtualElement.innerHTML = `
<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"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-left">
<div class="row-fluid">
<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>
</div>
`;
// Append the virtual DOM to the body (temporarily)
document.body.appendChild(virtualElement);
// Wait for the font to be loaded (important for custom fonts like OCR-A)
document.fonts.load('1em "OCR A"').then(() => {
// Use html2canvas to convert the virtual DOM to an image
html2canvas(virtualElement, {
scale: 1, // Increase scale for sharper images
}).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;
// console.log(imgData)
// Use printJS to print the image
printJS({
printable: imgData,
type: 'image',
css: '/../lib/bootstrap/dist/css/bootstrap.css',
style: `
@@media print {
@@page {
margin: 5px 5px 0px 5px;
}
body { margin: 0; }
}
`
}); });
if (response.ok) {
const data = await response.json();
this.currentUser = data?.userInfo || null;
const companyDeptData = await this.currentUser.department;
this.currentUserCompanyDept = companyDeptData;
this.selectedCompany = companyDeptData?.companyId || "";
this.selectedDepartment = companyDeptData?.departmentId || "";
// Remove the virtual DOM from the body after use }
document.body.removeChild(virtualElement); else {
}).catch((error) => { console.error(`Failed to fetch user: ${response.statusText}`);
console.error("Error generating image:", error); }
// Remove the virtual DOM if an error occurs }
document.body.removeChild(virtualElement); catch (error) {
}); console.error('There was a problem with the fetch operation:', error);
}).catch((error) => { }
console.error("Error loading font:", error);
// Remove the virtual DOM if font loading fails
document.body.removeChild(virtualElement);
});
}, },
async fetchUsers() { async fetchUsers() {
try { try {
@ -1125,6 +646,7 @@
this.storelist = await response.json(); this.storelist = await response.json();
} }
catch (error) { catch (error) {
console.error('Error fetching item:', error); console.error('Error fetching item:', error);

View File

@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Authorization; using Azure.Core;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
@ -643,7 +644,6 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
[HttpPost("AddItemMovement")] [HttpPost("AddItemMovement")]
public async Task<IActionResult> AddItemMovement([FromBody] ItemMovementModel itemmovement) public async Task<IActionResult> AddItemMovement([FromBody] ItemMovementModel itemmovement)
//public async Task<IActionResult> AddItemMovement()
{ {
@ -655,6 +655,32 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
try try
{ {
if (!string.IsNullOrEmpty(itemmovement.ConsignmentNote))
{
var bytes = Convert.FromBase64String(itemmovement.ConsignmentNote);
string filePath = "";
string uniqueName = $"{itemmovement.ItemId}_{Guid.NewGuid()}";
if (IsImage(bytes))
{
filePath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot/media/inventory/itemmovement", uniqueName + itemmovement.ItemId + "_Request.jpg");
itemmovement.ConsignmentNote = "/media/inventory/itemmovement/" + uniqueName + itemmovement.ItemId + "_Request.jpg";
}
else if (IsPdf(bytes))
{
filePath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot/media/inventory/itemmovement", uniqueName + itemmovement.ItemId + "_Request.pdf");
itemmovement.ConsignmentNote = "/media/inventory/itemmovement/" + uniqueName + itemmovement.ItemId + "_Request.pdf";
}
else
{
return BadRequest("Unsupported file format.");
}
await System.IO.File.WriteAllBytesAsync(filePath, bytes);
}
_centralDbContext.ItemMovements.Add(itemmovement); _centralDbContext.ItemMovements.Add(itemmovement);
await _centralDbContext.SaveChangesAsync(); // This generates the auto-incremented ItemID await _centralDbContext.SaveChangesAsync(); // This generates the auto-incremented ItemID

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB