Merge branch 'misya' into Beta_1.2
This commit is contained in:
commit
b9a830f86a
@ -26,6 +26,12 @@ namespace PSTW_CentralSystem.Areas.Inventory.Models
|
||||
public DateTime? approvalDate { get; set; }
|
||||
public int? RequestQuantity { get; set; }
|
||||
public string? Document { get; set; }
|
||||
public int? fromStoreItem { get; set; }
|
||||
[ForeignKey("fromStoreItem")]
|
||||
public virtual StoreModel? Store { get; set; }
|
||||
public int? assignStoreItem { get; set; }
|
||||
[ForeignKey("assignStoreItem")]
|
||||
public virtual StoreModel? Stores { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -49,7 +49,7 @@
|
||||
margin-left: auto !important; /* Push Complete/Incomplete to right */
|
||||
}
|
||||
</style>
|
||||
@await Html.PartialAsync("~/Areas/Inventory/Views/_InventoryPartial.cshtml");
|
||||
@await Html.PartialAsync("~/Areas/Inventory/Views/_InventoryPartial.cshtml")
|
||||
<div id="registerItem" class="row">
|
||||
<div class="row mb-3" >
|
||||
<h2 for="sortSelect" class="col-sm-1 col-form-h2" style="min-width:140px;">Sort by:</h2>
|
||||
@ -118,7 +118,8 @@
|
||||
<h2>Item Movement List</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<table class="table table-bordered table-hover table-striped no-wrap" id="itemDatatable" style=" width:100%;border-style: solid; border-width: 1px"></table>
|
||||
<table class="table table-bordered table-hover table-striped no-wrap" id="itemDatatable"
|
||||
style=" width:100%;border-style: solid; border-width: 1px"></table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -131,7 +132,9 @@
|
||||
</div>
|
||||
<div v-for="(group, itemId) in filteredItems" :key="itemId" class="row card">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h2>Item : {{ group.uniqueID }}</h2>
|
||||
@* <h2>Item : {{ group.uniqueID }}</h2> *@
|
||||
<h2 v-if="group.uniqueID">Item : {{ group.uniqueID }}</h2>
|
||||
<h2 v-else>Item : No Item Tracked</h2>
|
||||
<button class="btn btn-light" v-on:click="toggleCategory(itemId)">
|
||||
<i :class="categoryVisible[itemId] ? 'fas fa-chevron-up' : 'fas fa-chevron-down'"></i> Show Details
|
||||
</button>
|
||||
@ -250,7 +253,10 @@
|
||||
'text-weird': movement.action === 'Register'}"
|
||||
class="flex-shrink-0 text-nowrap" style="max-width:140px; min-width:90px;">
|
||||
|
||||
{{ movement.toOther === 'Return' ? 'Return' : (movement.toOther === 'On Delivery' ? 'Receive' : ( movement.toStation !== null ? 'Change' : ( movement.toOther == 'Faulty' || movement.toOther == 'Calibration' || movement.toOther == 'Repair' ? movement.toOther : ( movement.action == 'Register' ? 'Register' : 'Assign')))) }}
|
||||
{{ movement.toOther === 'Return' ? 'Return' : (movement.toOther === 'On Delivery' ? 'Receive' :
|
||||
( movement.toStation !== null ? 'Change' :
|
||||
( movement.toOther == 'Faulty' || movement.toOther == 'Calibration' || movement.toOther == 'Repair' ? movement.toOther :
|
||||
( movement.action == 'Register' ? 'Register' : 'Assign')))) }}
|
||||
</h3>
|
||||
|
||||
<!-- Send Date -->
|
||||
@ -283,9 +289,13 @@
|
||||
</button>
|
||||
|
||||
<!-- Completion Status -->
|
||||
<h4 :class="movement.action == 'Register' ? 'text-success' : movement.movementComplete == 1 && movement.latestStatus !== 'Ready To Deploy' ? 'text-success' : movement.toOther === 'Repair' || movement.toOther === 'Calibration' && movement.latestStatus === 'Ready To Deploy' ? 'text-success' :'text-danger'"
|
||||
<h4 :class="movement.action == 'Register' ? 'text-success' : movement.movementComplete == 1 && movement.latestStatus !== 'Ready To Deploy' ? 'text-success' :
|
||||
movement.toOther === 'Repair' || movement.toOther === 'Calibration' && movement.latestStatus === 'Ready To Deploy' ? 'text-success' :'text-danger'"
|
||||
class="text-nowrap ms-3">
|
||||
{{ movement.action == 'Register' ? 'Complete' : (movement.movementComplete == 1 && movement.latestStatus !== 'Ready To Deploy' ? 'Complete' : ( movement.toOther === 'Repair' || movement.toOther === 'Calibration' && movement.latestStatus === 'Ready To Deploy' ? 'Complete' : (movement.latestStatus === 'Ready To Deploy' ? 'Canceled' : 'Incomplete'))) }}
|
||||
{{ movement.action == 'Register' ? 'Complete' :
|
||||
(movement.movementComplete == 1 && movement.latestStatus !== 'Ready To Deploy' ? 'Complete' :
|
||||
( movement.toOther === 'Repair' || movement.toOther === 'Calibration' && movement.latestStatus === 'Ready To Deploy' ? 'Complete' :
|
||||
(movement.latestStatus === 'Ready To Deploy' ? 'Canceled' : 'Incomplete'))) }}
|
||||
</h4>
|
||||
</div>
|
||||
|
||||
@ -341,7 +351,8 @@
|
||||
|
||||
<!--------------------------------------------STATION CATEGORY---------------------------------------------------------------------->
|
||||
<div v-if="sortBy === 'station'">
|
||||
<div v-for="(items, station) in filteredStation" :key="stationName" :class="{'bg-light-gray': station === 'Unassign Station', 'bg-white': station !== 'Unassign Station'}" class="station-category card mt-3">
|
||||
<div v-for="(items, station) in filteredStation" :key="stationName"
|
||||
:class="{'bg-light-gray': station === 'Unassign Station', 'bg-white': station !== 'Unassign Station'}" class="station-category card mt-3">
|
||||
<!-- Station Header -->
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h3>{{ station }}</h3>
|
||||
@ -355,7 +366,9 @@
|
||||
<div v-for="(group, itemId) in items" :key="itemId" class="row card">
|
||||
<!-- Item Header -->
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h2>Item : {{ group.uniqueID }}</h2>
|
||||
@* <h2>Item : {{ group.uniqueID }}</h2> *@
|
||||
<h2 v-if="group.uniqueID">Item : {{ group.uniqueID }}</h2>
|
||||
<h2 v-else>Item : No Item Tracked</h2>
|
||||
<button class="btn btn-light" v-on:click="toggleCategory(itemId)">
|
||||
<i :class="categoryVisible[itemId] ? 'fas fa-chevron-up' : 'fas fa-chevron-down'"></i> Show Details
|
||||
</button>
|
||||
@ -664,26 +677,13 @@
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
console.log(grouped);
|
||||
|
||||
// Sort items from newest to oldest & filter them
|
||||
for (let itemId in grouped) {
|
||||
let movements = grouped[itemId].movements
|
||||
.sort((a, b) => b.id - a.id); // Newest to oldest
|
||||
|
||||
// console.log(movements);
|
||||
// let stopIndex = movements.findIndex(m =>
|
||||
// m.toOther === 'Return' && m.movementComplete == 1
|
||||
// );
|
||||
|
||||
// if (stopIndex !== -1) {
|
||||
// movements = movements.slice(0, stopIndex + 1);
|
||||
// }
|
||||
|
||||
// Ensure at least 3 movements before stopping
|
||||
let stopIndex = movements.slice(3).findIndex(m =>
|
||||
m.toOther === 'Return' && m.movementComplete == 1
|
||||
);
|
||||
let stopIndex = movements.slice(3).findIndex(m => m.toOther === 'Return' && m.movementComplete == 1);
|
||||
|
||||
if (stopIndex !== -1) {
|
||||
stopIndex += 3; // Adjust index since we sliced after the first 3
|
||||
@ -692,7 +692,6 @@
|
||||
|
||||
grouped[itemId].movements = movements;
|
||||
}
|
||||
|
||||
return grouped;
|
||||
},
|
||||
|
||||
@ -716,9 +715,7 @@
|
||||
.sort((a, b) => b.id - a.id); // Newest → Oldest
|
||||
|
||||
// Ensure at least 3 movements before stopping
|
||||
let stopIndex = movements.slice(3).findIndex(m =>
|
||||
m.toOther === 'Return' && m.movementComplete == 1
|
||||
);
|
||||
let stopIndex = movements.slice(3).findIndex(m => m.toOther === 'Return' && m.movementComplete == 1);
|
||||
|
||||
// Remove older movements
|
||||
if (stopIndex !== -1) {
|
||||
@ -730,9 +727,7 @@
|
||||
let latestMovement = movements[0];
|
||||
let station = latestMovement.lastStationName || latestMovement.toStationName || "Not Assigned";
|
||||
|
||||
if (!groupedByStation[station]) {
|
||||
groupedByStation[station] = {};
|
||||
}
|
||||
if (!groupedByStation[station]) { groupedByStation[station] = {}; }
|
||||
|
||||
groupedByStation[station][itemId] = { uniqueID: itemId, movements };
|
||||
}
|
||||
@ -752,8 +747,6 @@
|
||||
|
||||
return sortedGrouped;
|
||||
|
||||
|
||||
|
||||
},
|
||||
filteredItems() {
|
||||
if (!this.searchQuery.trim()) {
|
||||
@ -781,9 +774,7 @@
|
||||
filtered[station] = grouped[station];
|
||||
}
|
||||
});
|
||||
|
||||
return filtered;
|
||||
|
||||
},
|
||||
|
||||
},
|
||||
@ -914,8 +905,6 @@
|
||||
}
|
||||
if(this.currentRole == "Super Admin"){
|
||||
this.items = await response.json();
|
||||
|
||||
console.log(this.items);
|
||||
this.initAllTables();
|
||||
|
||||
} else {
|
||||
@ -929,10 +918,6 @@
|
||||
|
||||
this.initAllTables();
|
||||
|
||||
|
||||
// console.log(this.items);
|
||||
|
||||
|
||||
}
|
||||
|
||||
if (this.itemDatatable) {
|
||||
@ -982,16 +967,10 @@
|
||||
this.renderTables();
|
||||
},
|
||||
renderTables() {
|
||||
// if (this.sortBy === "logs") {
|
||||
// // this.initAllTables();
|
||||
// this.initiateTable();
|
||||
// } else
|
||||
// if (this.sortBy === "all") {
|
||||
// this.initAllTables();
|
||||
// // this.initiateTable();
|
||||
// }
|
||||
|
||||
this.initAllTables();
|
||||
this.initiateTable();
|
||||
|
||||
},
|
||||
initAllTables() {
|
||||
if (this.itemMovementNotCompleteDatatable) {
|
||||
|
||||
@ -603,8 +603,8 @@
|
||||
"data": "convertPrice",
|
||||
},
|
||||
{
|
||||
"title": "Invoice Date",
|
||||
"data": "invoiceDate",
|
||||
"title": "Register Date",
|
||||
"data": "createDate",
|
||||
},
|
||||
{
|
||||
"title": "Warranty Until",
|
||||
@ -638,18 +638,27 @@
|
||||
],
|
||||
responsive: true,
|
||||
drawCallback: function (settings) {
|
||||
// Generate QR codes after rows are rendered
|
||||
setTimeout(() => {
|
||||
const api = this.api();
|
||||
api.rows().every(function () {
|
||||
const data = this.data(); // Row data
|
||||
const data = this.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], {
|
||||
const container = document.getElementById(containerId);
|
||||
|
||||
if (!container) {
|
||||
return;
|
||||
}
|
||||
|
||||
container.innerHTML = "";
|
||||
container.append(data.uniqueID);
|
||||
|
||||
// Ensure qrString is valid before generating QR code
|
||||
if (!data.qrString) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Generate QR Code
|
||||
new QRCode(container, {
|
||||
text: data.qrString,
|
||||
width: 100,
|
||||
height: 100,
|
||||
@ -657,20 +666,18 @@
|
||||
colorLight: "#ffffff",
|
||||
correctLevel: QRCode.CorrectLevel.M
|
||||
});
|
||||
}
|
||||
// container.on('click', function() {
|
||||
// window.open(data.qrString, '_blank');
|
||||
// });
|
||||
});
|
||||
},
|
||||
}, 100); // Small delay to ensure elements exist
|
||||
}
|
||||
})
|
||||
|
||||
// Attach click event listener to the delete buttons
|
||||
$('#itemDatatable tbody').on('click', '.delete-btn', function () {
|
||||
$('#itemDatatable tbody').off('click', '.delete-btn').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
|
||||
@ -686,9 +693,8 @@
|
||||
// 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
|
||||
self.printItem(itemId, imageSrc); //\ Call the print function with the itemId and imageSrc
|
||||
} else {
|
||||
console.error("Image source not found.");
|
||||
}
|
||||
@ -870,8 +876,6 @@
|
||||
.row($(`.delete-btn[data-id="${itemId}"]`).closest('tr'))
|
||||
.remove()
|
||||
.draw();
|
||||
} else {
|
||||
alert(result.message);
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
@ -887,7 +891,6 @@
|
||||
this.thisQRInfo.uniqueID = itemId;
|
||||
const uniqueQR = itemId;
|
||||
const container = document.getElementById("QrContainer");
|
||||
|
||||
if (!container) {
|
||||
console.error("Container not found.");
|
||||
return;
|
||||
@ -940,7 +943,11 @@
|
||||
console.error("Items list is not available or is not an array.");
|
||||
return null;
|
||||
}
|
||||
return this.items.find(item => item.uniqueID === uniqueID);
|
||||
|
||||
//FIX ERROR
|
||||
const foundItem = this.items.find(item => String(item.uniqueID).trim() === String(uniqueID).trim());
|
||||
|
||||
return foundItem ? JSON.parse(JSON.stringify(foundItem)) : null;
|
||||
},
|
||||
printQRInfo() {
|
||||
// Create a virtual DOM element
|
||||
@ -1019,7 +1026,6 @@
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -3,7 +3,7 @@
|
||||
Layout = "~/Views/Shared/_Layout.cshtml";
|
||||
}
|
||||
|
||||
@await Html.PartialAsync("~/Areas/Inventory/Views/_InventoryPartial.cshtml");
|
||||
@await Html.PartialAsync("~/Areas/Inventory/Views/_InventoryPartial.cshtml")
|
||||
<div id="app">
|
||||
<div class="row card">
|
||||
<div class="card-header">
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
string userId = ViewBag.UserId;
|
||||
}
|
||||
|
||||
@await Html.PartialAsync("~/Areas/Inventory/Views/_InventoryPartial.cshtml");
|
||||
@await Html.PartialAsync("~/Areas/Inventory/Views/_InventoryPartial.cshtml")
|
||||
<div class="row">
|
||||
<div id="registerProduct" class="card m-1">
|
||||
<div class="row" v-if="addSection == true">
|
||||
@ -145,7 +145,6 @@
|
||||
},
|
||||
methods: {
|
||||
initiateTable() {
|
||||
console.log(this.products)
|
||||
this.productDatatable = $('#productDatatable').DataTable({
|
||||
"data": this.products,
|
||||
"columns": [
|
||||
@ -180,7 +179,7 @@
|
||||
"title": "Delete",
|
||||
"data": "productId",
|
||||
"render": function (data) {
|
||||
var deleteButton = `<button type="button" class="btn btn-danger delete-btn" data-id="${data.productId}">Delete</button>`;
|
||||
var deleteButton = `<button type="button" class="btn btn-danger delete-btn" data-id="${data}">Delete</button>`;
|
||||
return deleteButton;
|
||||
},
|
||||
}
|
||||
|
||||
@ -145,7 +145,7 @@
|
||||
<div class="tab-pane fade show active" id="home" role="tabpanel" aria-labelledby="home-tab">
|
||||
<div style="text-align: center; margin: 20px 0; padding: 20px;">
|
||||
|
||||
<div v-if="itemlateststatus == 'On Delivery' && this.itemassignedtouser">
|
||||
<div v-if="itemlateststatus == 'On Delivery' && this.thisItem.toUser == this.currentUser.id">
|
||||
<h2 class="register-heading">Item is on Delivery</h2>
|
||||
<div class="col-sm-3"></div>
|
||||
<div class="col-sm-6 offset-sm-3">
|
||||
@ -169,6 +169,23 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="itemlateststatus == 'On Delivery' && thisItem.lastStore == currentUser.store">
|
||||
<h2 class="register-heading">Receive Item</h2>
|
||||
<div class="col-sm-3"></div>
|
||||
<div class="col-sm-6 offset-sm-3">
|
||||
<form v-on:submit.prevent="receiveItemMovement" data-aos="fade-right">
|
||||
<div class="row register-form">
|
||||
|
||||
<div style="display: flex; justify-content: center; margin-top: 20px;">
|
||||
<button type="submit" class="btn btn-primary" style="width: 200px; padding: 10px; font-size: 16px;">
|
||||
Receive Item
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="(itemlateststatus == 'Repair' || itemlateststatus == 'Calibration') && this.itemassignedtouser">
|
||||
<h2 class="register-heading">Receive Repair / Calibration</h2>
|
||||
<div class="col-sm-3"></div>
|
||||
@ -226,7 +243,7 @@
|
||||
@* </div> *@
|
||||
@* </div> *@
|
||||
|
||||
<div v-if="itemlateststatus == 'Faulty'">
|
||||
<div v-if="itemlateststatus == 'Faulty' && this.itemassignedtouser">
|
||||
<h2 class="register-heading">Add Item Movement</h2>
|
||||
<div class="col-sm-3"></div>
|
||||
<div class="col-sm-6 offset-sm-3">
|
||||
@ -240,7 +257,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="itemlateststatus == 'Ready To Deploy'">
|
||||
<div v-if="itemlateststatus == 'Ready To Deploy' && this.itemassignedtouser">
|
||||
<h2 class="register-heading">Add Item Movement</h2>
|
||||
<div class="col-sm-3"></div>
|
||||
<div class="col-sm-6 offset-sm-3">
|
||||
@ -730,8 +747,9 @@
|
||||
|
||||
})
|
||||
.then(() => { console.log("📷 Applied Constraintsss:", track.getSettings());
|
||||
}).catch(err =>
|
||||
// console.error("❌ Failed to apply constraints:", err)
|
||||
})
|
||||
.catch(err =>
|
||||
console.error("❌ Failed to apply constraints:", err)
|
||||
);
|
||||
|
||||
} else {
|
||||
@ -812,9 +830,9 @@
|
||||
|
||||
...(this.selectedAction === 'user' ? { toStore: this.currentUser.store, toUser: this.currentUser.id, toOther: 'On Delivery', SendDate: this.assigndate, lastUser: this.selectedUser, MovementComplete: false, Remark: this.remark, ConsignmentNote: this.document} : {}),
|
||||
...(this.selectedAction === 'station' ? { toStore: this.currentUser.store, toUser: this.currentUser.id, toOther: 'On Delivery', SendDate: this.assigndate, lastStation: this.selectedStation, lastUser: this.selectedStationPIC, MovementComplete: false, Remark: this.remark, ConsignmentNote: this.document} : {}),
|
||||
...(this.selectedAction === 'store' ? { toStore: this.currentUser.store, toUser: this.currentUser.id, toOther: 'On Delivery', SendDate: this.assigndate, lastUser: this.selectedStore, MovementComplete: false, Remark: this.remark, ConsignmentNote: this.document} : {}),
|
||||
...(this.selectedAction === 'supplier' ? { toStore: this.currentUser.store, toUser: this.currentUser.id, toOther: this.selectedOther, SendDate: this.assigndate, Remark: this.remark + '. Item sent to ' + this.selectedSupplier + ' for ' + this.selectedOther, ConsignmentNote: this.document, lastUser: this.currentUser.id, MovementComplete: false, } : {}),
|
||||
...(this.selectedAction === 'faulty' ? { toStore: this.currentUser.store, toUser: this.currentUser.id,toOther: 'Faulty', Date: new Date(now.getTime() + 8 * 60 * 60 * 1000).toISOString(), Remark: this.remark, ConsignmentNote: this.document, MovementComplete: true, } : {}),
|
||||
...(this.selectedAction === 'store' ? { toStore: this.currentUser.store, toUser: this.currentUser.id, toOther: 'On Delivery', SendDate: this.assigndate, lastStore: this.selectedStore, MovementComplete: false, Remark: this.remark, ConsignmentNote: this.document} : {}),
|
||||
...(this.selectedAction === 'supplier' ? { toStore: this.currentUser.store, toUser: this.currentUser.id, toOther: this.selectedOther, SendDate: this.assigndate, Remark: this.remark + '. Item sent to ' + this.selectedSupplier + ' for ' + this.selectedOther, ConsignmentNote: this.document, lastUser: this.currentUser.id, lastStore: this.currentUser.store, MovementComplete: false, } : {}),
|
||||
...(this.selectedAction === 'faulty' ? { toStore: this.currentUser.store, toUser: this.currentUser.id,toOther: 'Faulty', SendDate: new Date(now.getTime() + 8 * 60 * 60 * 1000).toISOString(), ReceiveDate: new Date(now.getTime() + 8 * 60 * 60 * 1000).toISOString(), Remark: this.remark, ConsignmentNote: this.document, MovementComplete: true, } : {}),
|
||||
|
||||
ItemId: this.thisItem.itemID,
|
||||
Action: 'Stock Out',
|
||||
@ -863,19 +881,23 @@
|
||||
this.serialNumber = "";
|
||||
}
|
||||
|
||||
if(this.thisItem.toOther === "On Delivery"){
|
||||
if(this.thisItem.toOther === "On Delivery" && this.thisItem.toUser == this.currentUser.id){
|
||||
if(!window.confirm("Are you sure you want to cancel item delivery?")){
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const now = new Date();
|
||||
console.log('currentuser'+this.currentUser.id);
|
||||
console.log('lastuser'+this.thisItem.lastuser);
|
||||
|
||||
const formData = {
|
||||
|
||||
Id: this.thisItem.movementId,
|
||||
ReceiveDate: new Date(now.getTime() + 8 * 60 * 60 * 1000).toISOString(),
|
||||
Remark: this.thisItem.toOther === "On Delivery" ? this.thisItem.remark + ". Inventory Master cancelled delivery with remark: " + this.remark : this.thisItem.remark,
|
||||
LatestStatus: this.thisItem.toOther === "Return" ? "Faulty" : (this.thisItem.toOther === "Calibration" || this.thisItem.toOther === "Repair" || this.thisItem.toOther === "On Delivery" ) ? "Ready To Deploy" : ""
|
||||
Remark: this.thisItem.toOther === "On Delivery" && this.thisItem.toUser == this.currentUser.id ? this.thisItem.remark + ". Inventory Master cancelled delivery with remark: " + this.remark : this.thisItem.remark,
|
||||
LastUser: this.thisItem.lastuser == null ? this.currentUser.id : this.thisItem.lastuser,
|
||||
LatestStatus: this.thisItem.toOther === "Return" ? "Faulty" : (this.thisItem.toOther === "Calibration" || this.thisItem.toOther === "Repair" || this.thisItem.toOther === "On Delivery" ) ? "Ready To Deploy" : (this.itemlateststatus == 'On Delivery' && this.thisItem.lastStore == this.currentUser.store ? "Delivered" : "")
|
||||
};
|
||||
|
||||
|
||||
@ -923,9 +945,19 @@
|
||||
// this.thisItem = await response.json();
|
||||
|
||||
this.thisItem = await response.json();
|
||||
console.log('last store'+this.thisItem.lastStore);
|
||||
|
||||
this.itemlateststatus = this.thisItem.latestStatus ? this.thisItem.latestStatus : this.thisItem.toOther;
|
||||
this.itemassignedtouser = this.thisItem.toUser == this.currentUser.id || this.thisItem.lastUser == this.currentUser.id ? true : false;
|
||||
this.itemassignedtouser = (this.thisItem.toUser === this.currentUser.id || this.thisItem.lastUser === this.currentUser.id ) && this.thisItem.lastStore === this.currentUser.store ? true : false;
|
||||
// console.log(this.thisItem);
|
||||
// console.log(this.itemassignedtouser);
|
||||
console.log(this.thisItem.lastStore);
|
||||
console.log( this.thisItem.lastStore == this.currentUser.store? true : false);
|
||||
console.log(this.thisItem.toUser == this.currentUser.id? true : false);
|
||||
console.log( this.thisItem.lastUser == this.currentUser.id ? true : false);
|
||||
console.log(((this.thisItem.toUser == this.currentUser.id) || ( this.thisItem.lastUser == this.currentUser.id)) ? true : false);
|
||||
console.log('currentuser store'+this.currentUser.store);
|
||||
|
||||
|
||||
} else {
|
||||
console.error('Failed to fetch item information');
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
Layout = "~/Views/Shared/_Layout.cshtml";
|
||||
}
|
||||
|
||||
@await Html.PartialAsync("~/Areas/Inventory/Views/_InventoryPartial.cshtml");
|
||||
@await Html.PartialAsync("~/Areas/Inventory/Views/_InventoryPartial.cshtml")
|
||||
<div id="registerStation">
|
||||
<form v-on:submit.prevent="addStation" data-aos="fade-right" id="registerStationForm" v-if="registerStationForm">
|
||||
<div class="container register" data-aos="fade-right">
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
Layout = "~/Views/Shared/_Layout.cshtml";
|
||||
}
|
||||
|
||||
@await Html.PartialAsync("~/Areas/Inventory/Views/_InventoryPartial.cshtml");
|
||||
@await Html.PartialAsync("~/Areas/Inventory/Views/_InventoryPartial.cshtml")
|
||||
<div id="registerSupplier">
|
||||
<form v-on:submit.prevent="addSupplier" data-aos="fade-right" id="registerSupplierForm" v-if="registerSupplierForm">
|
||||
<div class="container register" data-aos="fade-right">
|
||||
|
||||
@ -64,7 +64,8 @@
|
||||
<h2>Pending Item Movement</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<table class="table table-bordered table-hover table-striped no-wrap" id="itemMovementNotCompleteDatatable" style="width:100%;border-style: solid; border-width: 1px"></table>
|
||||
<table class="table table-bordered table-hover table-striped no-wrap" id="itemMovementNotCompleteDatatable"
|
||||
style="width:100%;border-style: solid; border-width: 1px"></table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -73,7 +74,8 @@
|
||||
<h2>Complete Item Movement</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<table class="table table-bordered table-hover table-striped no-wrap" id="itemMovementCompleteDatatable" style="width:100%;border-style: solid; border-width: 1px"></table>
|
||||
<table class="table table-bordered table-hover table-striped no-wrap" id="itemMovementCompleteDatatable"
|
||||
style="width:100%;border-style: solid; border-width: 1px"></table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -284,7 +286,8 @@
|
||||
|
||||
<!--------------------------------------------STATION CATEGORY---------------------------------------------------------------------->
|
||||
<div v-if="sortBy === 'station'">
|
||||
<div v-for="(items, station) in filteredStation" :key="stationName" :class="{'bg-light-gray': station === 'Unassign Station', 'bg-white': station !== 'Unassign Station'}" class="station-category card mt-3">
|
||||
<div v-for="(items, station) in filteredStation" :key="stationName"
|
||||
:class="{'bg-light-gray': station === 'Unassign Station', 'bg-white': station !== 'Unassign Station'}" class="station-category card mt-3">
|
||||
<!-- Station Header -->
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h3>{{ station }}</h3>
|
||||
@ -569,7 +572,8 @@
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
// Sort items from newest to oldest & filter them
|
||||
let filteredGrouped = {};
|
||||
|
||||
for (let itemId in grouped) {
|
||||
let movements = grouped[itemId].movements
|
||||
.sort((a, b) => b.id - a.id); // Newest to oldest
|
||||
@ -578,14 +582,27 @@
|
||||
m.toOther === 'Return' && m.movementComplete == 1
|
||||
);
|
||||
|
||||
let nextIndex = movements.findIndex(m =>
|
||||
m.latestStatus === 'Ready To Deploy' && m.movementComplete == 1
|
||||
);
|
||||
|
||||
if (stopIndex !== -1) {
|
||||
movements = movements.slice(0, stopIndex);
|
||||
}
|
||||
|
||||
grouped[itemId].movements = movements;
|
||||
if (nextIndex !== -1) {
|
||||
movements = movements.slice(0, nextIndex);
|
||||
}
|
||||
|
||||
return grouped;
|
||||
if (movements.length > 0) {
|
||||
filteredGrouped[itemId] = {
|
||||
uniqueID: grouped[itemId].uniqueID,
|
||||
movements: movements,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return filteredGrouped;
|
||||
},
|
||||
|
||||
groupedByStation() {
|
||||
@ -611,11 +628,18 @@
|
||||
m.toOther === 'Return' && m.movementComplete == 1
|
||||
);
|
||||
|
||||
// Remove older movements
|
||||
let nextIndex = movements.findIndex(m =>
|
||||
m.latestStatus === 'Ready To Deploy' && m.movementComplete == 1
|
||||
);
|
||||
|
||||
if (stopIndex !== -1) {
|
||||
movements = movements.slice(0, stopIndex);
|
||||
}
|
||||
|
||||
if (nextIndex !== -1) {
|
||||
movements = movements.slice(0, nextIndex);
|
||||
}
|
||||
|
||||
if (movements.length > 0) {
|
||||
let latestMovement = movements[0];
|
||||
let station = latestMovement.lastStationName || latestMovement.toStationName || "Self Assigned";
|
||||
@ -648,11 +672,17 @@
|
||||
return this.processedGroupedItems;
|
||||
}
|
||||
const searchLower = this.searchQuery.toLowerCase();
|
||||
return Object.fromEntries(
|
||||
Object.entries(this.processedGroupedItems).filter(([_, group]) =>
|
||||
group.uniqueID.toLowerCase().includes(searchLower)
|
||||
)
|
||||
);
|
||||
let grouped = this.processedGroupedItems;
|
||||
let filtered = {};
|
||||
|
||||
Object.keys(grouped).forEach(item => {
|
||||
if (item.toLowerCase().includes(searchLower)) {
|
||||
if (grouped[item] > 0) {
|
||||
filtered[item] = grouped[item];
|
||||
}
|
||||
}
|
||||
});
|
||||
return filtered;
|
||||
},
|
||||
|
||||
filteredStation() {
|
||||
@ -720,6 +750,10 @@
|
||||
}
|
||||
},
|
||||
|
||||
handleSorting() {
|
||||
this.renderTables();
|
||||
},
|
||||
|
||||
renderTables() {
|
||||
if (this.sortBy === "all") {
|
||||
this.initAllTables();
|
||||
@ -737,7 +771,7 @@
|
||||
this.stationDatatable.destroy();
|
||||
}
|
||||
|
||||
// Get latest movement per uniqueID
|
||||
// Get latest movement per uniqueID after filtering
|
||||
function getLatestMovements(data) {
|
||||
let latestMovements = {};
|
||||
data.forEach(movement => {
|
||||
@ -749,18 +783,42 @@
|
||||
return Object.values(latestMovements);
|
||||
}
|
||||
|
||||
// Distribute items based on priority
|
||||
// Filter movements based on conditions
|
||||
function filterMovements(movements) {
|
||||
let stopIndex = movements.findIndex(m =>
|
||||
m.toOther === 'Return' && m.movementComplete == 1
|
||||
);
|
||||
|
||||
let nextIndex = movements.findIndex(m =>
|
||||
m.latestStatus === 'Ready To Deploy' && m.movementComplete == 1
|
||||
);
|
||||
|
||||
if (stopIndex !== -1) {
|
||||
movements = movements.slice(0, stopIndex);
|
||||
}
|
||||
|
||||
if (nextIndex !== -1) {
|
||||
movements = movements.slice(0, nextIndex);
|
||||
}
|
||||
|
||||
return movements;
|
||||
}
|
||||
|
||||
let latestMovements = getLatestMovements(this.itemMovements);
|
||||
|
||||
let notCompleteData = [];
|
||||
let completeData = [];
|
||||
let assignedData = [];
|
||||
|
||||
latestMovements.forEach(movement => {
|
||||
let filteredMovements = filterMovements([movement]);
|
||||
|
||||
if (filteredMovements.length > 0) {
|
||||
if (movement.movementComplete == 0) {
|
||||
notCompleteData.push(movement);
|
||||
} else if (movement.movementComplete == 1) {
|
||||
completeData.push(movement);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Table 1: Not Complete Movements
|
||||
@ -833,6 +891,7 @@
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
toggleCategory(itemId) {
|
||||
this.categoryVisible[itemId] = !this.categoryVisible[itemId];
|
||||
|
||||
@ -857,9 +916,6 @@
|
||||
this.detailsVisible[movementId] = !this.detailsVisible[movementId];
|
||||
},
|
||||
|
||||
handleSorting() {
|
||||
this.renderTables();
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@ -440,193 +440,13 @@
|
||||
|
||||
|
||||
initiateTable() {
|
||||
self = this;
|
||||
this.requestDatatable = $('#requestDatatable').DataTable({
|
||||
"data": this.request.filter(request => request.status == "Requested"),
|
||||
"columns": [
|
||||
{
|
||||
"title": "Request ID",
|
||||
"data": "requestID",
|
||||
"createdCell": function (td, cellData, rowData, row, col) {
|
||||
// Assign a unique ID to the <td> element
|
||||
$(td).attr('id', `qr${cellData}`);
|
||||
},
|
||||
},
|
||||
{
|
||||
"title": "Product Name",
|
||||
"data": "productName",
|
||||
"render": function (data, type, full, meta) {
|
||||
if (!data) {
|
||||
return "No Document";
|
||||
}
|
||||
let self = this;
|
||||
|
||||
var imageSrc = full.productPicture;
|
||||
// Check if the document is an image based on file extension
|
||||
var isImage = /\.(jpeg|jpg|png|gif)$/i.test(imageSrc);
|
||||
var isPdf = /\.pdf$/i.test(imageSrc);
|
||||
// var imageSrc = full.productImage; Fallback to data if imgsrc is unavailable
|
||||
console.log(full);
|
||||
function renderDocument(data, full) {
|
||||
if (!data) return "No Document";
|
||||
|
||||
if (isImage) {
|
||||
return ` <div class="row"><td>${data}</td></div>
|
||||
<a href="${imageSrc}" target="_blank" data-lightbox="image-1">
|
||||
<img src="${imageSrc}" alt="Image" class="img-thumbnail" style="width: 100px; height: 100px;" />
|
||||
</a>`;
|
||||
}
|
||||
else if (isPdf) {
|
||||
return `<div class="row"><td>${data}</td></div>
|
||||
<a href="${imageSrc}" 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>`;
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
"title": "Product Category",
|
||||
"data": "productCategory",
|
||||
},
|
||||
{
|
||||
"title": "Request Quantity",
|
||||
"data": "requestQuantity",
|
||||
},
|
||||
|
||||
{
|
||||
"title": "Document / Picture",
|
||||
"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": "Remark",
|
||||
"data": "remarkUser",
|
||||
},
|
||||
{
|
||||
"title": "Station Deploy",
|
||||
"data": "stationName",
|
||||
"render": function (data, type, full, meta) {
|
||||
return data ? data : "Self Assign";
|
||||
}
|
||||
},
|
||||
{
|
||||
"title": "Request Date",
|
||||
"data": "requestDate",
|
||||
},
|
||||
{
|
||||
"title": "Status",
|
||||
"data": "status",
|
||||
},
|
||||
{
|
||||
"title": "Delete",
|
||||
"data": "requestID",
|
||||
"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,
|
||||
});
|
||||
this.requestDatatable = $('#settledrequestDatatable').DataTable({
|
||||
"data": this.request.filter(request => request.status !== "Requested"),
|
||||
"columns": [
|
||||
{
|
||||
"title": "Request ID",
|
||||
"data": "requestID",
|
||||
"createdCell": function (td, cellData, rowData, row, col) {
|
||||
// Assign a unique ID to the <td> element
|
||||
$(td).attr('id', `qr${cellData}`);
|
||||
},
|
||||
},
|
||||
{
|
||||
"title": "Status",
|
||||
"data": "status",
|
||||
},
|
||||
{
|
||||
"title": "Product Name",
|
||||
"data": "productName",
|
||||
"render": function (data, type, full, meta) {
|
||||
if (!data) {
|
||||
return "No Document";
|
||||
}
|
||||
|
||||
var imageSrc = full.productPicture;
|
||||
// Check if the document is an image based on file extension
|
||||
var isImage = /\.(jpeg|jpg|png|gif)$/i.test(imageSrc);
|
||||
var isPdf = /\.pdf$/i.test(imageSrc);
|
||||
// var imageSrc = full.productImage; Fallback to data if imgsrc is unavailable
|
||||
console.log(full);
|
||||
|
||||
if (isImage) {
|
||||
return ` <div class="row"><td>${data}</td></div>
|
||||
<a href="${imageSrc}" target="_blank" data-lightbox="image-1">
|
||||
<img src="${imageSrc}" alt="Image" class="img-thumbnail" style="width: 100px; height: 100px;" />
|
||||
</a>`;
|
||||
}
|
||||
else if (isPdf) {
|
||||
return `<div class="row"><td>${data}</td></div>
|
||||
<a href="${imageSrc}" 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>`;
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
"title": "Product Category",
|
||||
"data": "productCategory",
|
||||
},
|
||||
{
|
||||
"title": "Request Quantity",
|
||||
"data": "requestQuantity",
|
||||
},
|
||||
{
|
||||
"title": "Station Deploy",
|
||||
"data": "stationName",
|
||||
"render": function (data, type, full, meta) {
|
||||
return data ? data : "Self Assign";
|
||||
}
|
||||
},
|
||||
{
|
||||
"title": "Document / Picture",
|
||||
"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);
|
||||
let isImage = /\.(jpeg|jpg|png|gif)$/i.test(data);
|
||||
let isPdf = /\.pdf$/i.test(data);
|
||||
|
||||
if (isImage) {
|
||||
return `<a href="${data}" target="_blank" data-lightbox="image-1">
|
||||
@ -642,25 +462,43 @@
|
||||
} else {
|
||||
return `<a href="${data}" target="_blank">Download File</a>`;
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
"title": "Remark",
|
||||
"data": "remarkUser",
|
||||
},
|
||||
{
|
||||
"title": "Remark (Master)",
|
||||
"data": "remarkMasterInv",
|
||||
},
|
||||
{
|
||||
"title": "Request Date",
|
||||
"data": "requestDate",
|
||||
},
|
||||
{
|
||||
"title": "Approval Date",
|
||||
"data": "approvalDate",
|
||||
}
|
||||
|
||||
function renderDeleteButton(data) {
|
||||
return `<button type="button" class="btn btn-danger delete-btn" data-id="${data}">Delete</button>`;
|
||||
}
|
||||
|
||||
this.pendingRequestDatatable = $('#requestDatatable').DataTable({
|
||||
"data": this.request.filter(req => req.status === "Requested"),
|
||||
"columns": [
|
||||
{ "title": "Request ID", "data": "requestID", "createdCell": (td, cellData) => $(td).attr('id', `qr${cellData}`) },
|
||||
{ "title": "Product Name", "data": "productName", "render": (data, type, full) => renderDocument(full.productPicture) },
|
||||
{ "title": "Product Category", "data": "productCategory" },
|
||||
{ "title": "Request Quantity", "data": "requestQuantity" },
|
||||
{ "title": "Document / Picture", "data": "document", "render": (data, type, full) => renderDocument(data) },
|
||||
{ "title": "Remark", "data": "remarkUser" },
|
||||
{ "title": "Station Deploy", "data": "stationName", "render": (data) => data || "Self Assign" },
|
||||
{ "title": "Request Date", "data": "requestDate" },
|
||||
{ "title": "Status", "data": "status" },
|
||||
{ "title": "Delete", "data": "requestID", "render": renderDeleteButton, "className": "align-middle" }
|
||||
],
|
||||
responsive: true,
|
||||
});
|
||||
|
||||
this.settledRequestDatatable = $('#settledrequestDatatable').DataTable({
|
||||
"data": this.request.filter(req => req.status !== "Requested"),
|
||||
"columns": [
|
||||
{ "title": "Request ID", "data": "requestID", "createdCell": (td, cellData) => $(td).attr('id', `qr${cellData}`) },
|
||||
{ "title": "Status", "data": "status" },
|
||||
{ "title": "Product Name", "data": "productName", "render": (data, type, full) => renderDocument(full.productPicture) },
|
||||
{ "title": "Product Category", "data": "productCategory" },
|
||||
{ "title": "Request Quantity", "data": "requestQuantity" },
|
||||
{ "title": "Station Deploy", "data": "stationName", "render": (data) => data || "Self Assign" },
|
||||
{ "title": "Document / Picture", "data": "document", "render": (data, type, full) => renderDocument(data) },
|
||||
{ "title": "Remark", "data": "remarkUser" },
|
||||
{ "title": "Remark (Master)", "data": "remarkMasterInv" },
|
||||
{ "title": "Request Date", "data": "requestDate" },
|
||||
{ "title": "Approval Date", "data": "approvalDate" }
|
||||
],
|
||||
responsive: true,
|
||||
});
|
||||
@ -672,9 +510,9 @@
|
||||
self.deleteRequestItem(requestID);
|
||||
});
|
||||
|
||||
|
||||
this.loading = false;
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
async fetchRequest() {
|
||||
try
|
||||
|
||||
@ -145,14 +145,6 @@
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<!-- Station -->
|
||||
<!-- <li class="list-group-item d-flex justify-content-between align-items-center"> -->
|
||||
<!-- <span class="fw-bold"> -->
|
||||
<!-- <i class="fas fa-map-marker-alt me-2 text-secondary"></i>Station: -->
|
||||
<!-- </span> -->
|
||||
<!-- <span class="text-muted">{{ thisItem.currentStation || 'N/A' }}</span> -->
|
||||
<!-- </li> -->
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@ -542,15 +534,6 @@
|
||||
|
||||
async returnItemMovement() {
|
||||
|
||||
// const requiredFields = ['remark', 'consignmentNote'];
|
||||
|
||||
// for (let field of requiredFields) {
|
||||
// if (!this[field]) {
|
||||
// alert(`Request Error: Please fill in required field ${field}.`, 'warning');
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
|
||||
if (!confirm("Are you sure you want to return this item?")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
<h1 class="font-light text-white">
|
||||
<i class="mdi mdi-factory"></i>
|
||||
</h1>
|
||||
<h6 class="text-white">Manifacturer</h6>
|
||||
<h6 class="text-white">Manufacturer</h6>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
415
Areas/MMS/Controllers/MarineController.cs
Normal file
415
Areas/MMS/Controllers/MarineController.cs
Normal file
@ -0,0 +1,415 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using PSTW_CentralSystem.DBContext;
|
||||
//using PSTW_CentralSystem.Areas.MMS.Models;
|
||||
//using System.IO;
|
||||
//using System.Linq;
|
||||
using PSTW_CentralSystem.Areas.MMS.Models.PDFGenerator;
|
||||
using QuestPDF.Fluent;
|
||||
//using System.Threading.Tasks;
|
||||
//using System.Threading;
|
||||
//using System.Collections.Generic;
|
||||
//using Microsoft.Data.SqlClient;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using MySqlConnector;
|
||||
|
||||
namespace PSTW_CentralSystem.Areas.MMS.Controllers
|
||||
{
|
||||
public class TarballPdfDto //data transfer object, only holds data, used to move data between programs
|
||||
{
|
||||
// From tbl_marine_tarball
|
||||
public int Id { get; set; }
|
||||
public required string StationID { get; set; }
|
||||
public required string Longitude { get; set; }
|
||||
public required string Latitude { get; set; }
|
||||
public required DateTime DateSample { get; set; }
|
||||
public required TimeSpan TimeSample { get; set; }
|
||||
public required string ClassifyID { get; set; }
|
||||
public string? OptionalName1 { get; set; }
|
||||
public string? OptionalName2 { get; set; }
|
||||
public string? OptionalName3 { get; set; }
|
||||
public string? OptionalName4 { get; set; }
|
||||
public required string FirstSampler { get; set; }
|
||||
|
||||
// From joined tables
|
||||
public required string LocationName { get; set; } // From tbl_marine_station
|
||||
public required string StateName { get; set; } // From tbl_state
|
||||
public required string FullName { get; set; } // From tbl_user
|
||||
public required string LevelName { get; set; } // From tbl_level
|
||||
}
|
||||
|
||||
[Area("MMS")]
|
||||
public class MarineController : Controller
|
||||
{
|
||||
private readonly MMSSystemContext _context;//Used in TarBallForm and GeneratePdfResponse to query the database.
|
||||
private readonly NetworkShareAccess _networkAccessService;//used in GetImage and GeneratePdfResponse
|
||||
private const string PhotoBasePath = @"\\192.168.12.42\images\marine\manual_tarball";//used in GetImage and GeneratePdfResponse
|
||||
|
||||
public MarineController(MMSSystemContext context, NetworkShareAccess networkAccessService)
|
||||
{
|
||||
_context = context;
|
||||
_networkAccessService = networkAccessService;
|
||||
}
|
||||
|
||||
public IActionResult Index()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
public async Task<IActionResult> TarBallForm()//make it async in case of traffic/frequent usage
|
||||
{
|
||||
try
|
||||
{
|
||||
var marineTarballs = await _context.MarineTarballs
|
||||
.Select(t => new
|
||||
{
|
||||
id = t.Id,
|
||||
date = t.DateSample.ToString("yyyy/MM/dd"),
|
||||
station = t.StationID,
|
||||
time = t.TimeSample.ToString("hh\\:mm\\:ss")
|
||||
})
|
||||
.ToListAsync();
|
||||
|
||||
Console.WriteLine($"Marine Tarballs Count: {marineTarballs.Count}"); //???
|
||||
return View(marineTarballs);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Content($"Error: {ex.Message}<br/>{ex.StackTrace}", "text/html");
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet] // Explicitly mark as a GET endpoint
|
||||
//removal TBD===============!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
public IActionResult TestCredentials()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Use the EXACT same path/credentials as in Program.cs
|
||||
var testService = new NetworkShareAccess(
|
||||
@"\\192.168.12.42\images\marine\manual_tarball",
|
||||
"installer",
|
||||
"mms@pstw"
|
||||
);
|
||||
|
||||
testService.ConnectToNetworkPath();
|
||||
testService.DisconnectFromNetworkShare();
|
||||
|
||||
return Ok("Network credentials and path are working correctly!");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Log the full error (including stack trace)
|
||||
Console.WriteLine($"TestCredentials failed: {ex}");
|
||||
return StatusCode(500, $"Credentials test failed: {ex.Message}");
|
||||
}
|
||||
}
|
||||
//====================!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
|
||||
public IActionResult GetImage(string fileName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(fileName))
|
||||
{
|
||||
return BadRequest("Filename cannot be empty");
|
||||
}
|
||||
|
||||
// Sanitize filename to prevent path traversal attacks
|
||||
var sanitizedFileName = Path.GetFileName(fileName);
|
||||
if (sanitizedFileName != fileName)
|
||||
{
|
||||
return BadRequest("Invalid filename");
|
||||
}
|
||||
|
||||
int retryCount = 0;
|
||||
const int maxRetries = 3;
|
||||
bool connectionSuccess = false;
|
||||
|
||||
// Retry loop for network connection
|
||||
while (retryCount < maxRetries && !connectionSuccess)
|
||||
{
|
||||
try
|
||||
{
|
||||
Console.WriteLine($"Attempt {retryCount + 1} to connect to network share...");
|
||||
|
||||
// Connect to network share
|
||||
_networkAccessService.ConnectToNetworkPath();
|
||||
connectionSuccess = true;
|
||||
|
||||
Console.WriteLine("Network share connected successfully");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
retryCount++;
|
||||
Console.WriteLine($"Connection attempt {retryCount} failed: {ex.Message}");
|
||||
|
||||
if (retryCount >= maxRetries)
|
||||
{
|
||||
Console.WriteLine($"Max connection attempts reached. Last error: {ex}");
|
||||
return StatusCode(503, $"Could not establish connection to image server after {maxRetries} attempts");
|
||||
}
|
||||
|
||||
// Wait before retrying (1s, 2s, 3s)
|
||||
Thread.Sleep(1000 * retryCount);
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
string imagePath = Path.Combine(PhotoBasePath, sanitizedFileName);
|
||||
Console.WriteLine($"Attempting to access image at: {imagePath}");
|
||||
|
||||
// Verify file exists
|
||||
//removal TBD
|
||||
if (!System.IO.File.Exists(imagePath))
|
||||
{
|
||||
Console.WriteLine($"Image not found at path: {imagePath}");
|
||||
return NotFound($"Image '{sanitizedFileName}' not found on server");
|
||||
}
|
||||
|
||||
// Verify file is an image
|
||||
//consider removal if/since verification already happened during image uploading (other program)
|
||||
if (!IsImageValid(imagePath))
|
||||
{
|
||||
Console.WriteLine($"Invalid image file at path: {imagePath}");
|
||||
return BadRequest("The requested file is not a valid image");
|
||||
}
|
||||
|
||||
// Read and return the image
|
||||
byte[] imageBytes = System.IO.File.ReadAllBytes(imagePath);
|
||||
Console.WriteLine($"Successfully read image: {sanitizedFileName} ({imageBytes.Length} bytes)");
|
||||
|
||||
// Determine content type based on extension
|
||||
//removal TBD??? IF image type verification already done during image uploading =========!!!!!!!!!!!
|
||||
string contentType = "image/jpeg"; // default
|
||||
string extension = Path.GetExtension(sanitizedFileName).ToLower();
|
||||
//string extension = Path.GetExtension(sanitizedFileName)?.ToLower();
|
||||
|
||||
if (extension == ".png")
|
||||
{
|
||||
contentType = "image/png";
|
||||
}
|
||||
else if (extension == ".gif")
|
||||
{
|
||||
contentType = "image/gif";
|
||||
}
|
||||
|
||||
return File(imageBytes, contentType);
|
||||
}
|
||||
catch (UnauthorizedAccessException ex)
|
||||
{
|
||||
Console.WriteLine($"Access denied to image: {ex}");
|
||||
return StatusCode(403, "Access to the image was denied");
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
Console.WriteLine($"IO error accessing image: {ex}");
|
||||
return StatusCode(503, "Error accessing image file");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Unexpected error: {ex}");
|
||||
return StatusCode(500, "An unexpected error occurred while processing the image");
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
if (connectionSuccess)
|
||||
{
|
||||
Console.WriteLine("Disconnecting from network share...");
|
||||
_networkAccessService.DisconnectFromNetworkShare();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Warning: Error disconnecting from share: {ex.Message}");
|
||||
// Don't fail the request because of disconnect issues
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<IActionResult> GenerateReport(int id)//calls GeneratePdfResponse to generate a PDF for inline viewing
|
||||
{
|
||||
return await GeneratePdfResponse(id, true);
|
||||
}
|
||||
|
||||
//public async Task<IActionResult> DownloadPDF(int id)
|
||||
//{
|
||||
// return await GeneratePdfResponse(id, true);
|
||||
//}
|
||||
|
||||
public IActionResult ViewPDF(int id)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Add timeout for safety
|
||||
var task = Task.Run(() => GeneratePdfResponse(id, false));
|
||||
if (task.Wait(TimeSpan.FromSeconds(30))) // 30 second timeout
|
||||
{
|
||||
return task.Result;
|
||||
}
|
||||
return StatusCode(500, "PDF generation took too long");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"PDF VIEW ERROR: {ex}");
|
||||
return StatusCode(500, $"Error showing PDF: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<IActionResult> GeneratePdfResponse(int id, bool forceDownload)
|
||||
{
|
||||
Console.WriteLine($"Requested ID in {(forceDownload ? "GenerateReport" : "ViewPDF")}: {id}");
|
||||
|
||||
// Test network connection first
|
||||
try
|
||||
{
|
||||
_networkAccessService.ConnectToNetworkPath();
|
||||
_networkAccessService.DisconnectFromNetworkShare();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return StatusCode(500, $"Cannot access network: {ex.Message}");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_networkAccessService.ConnectToNetworkPath();
|
||||
|
||||
// ===== 1. Get Data from Database =====
|
||||
var query = @"
|
||||
SELECT
|
||||
marine.*,
|
||||
station.LocationName,
|
||||
state.StateName,
|
||||
user.FullName,
|
||||
level.LevelName
|
||||
FROM tbl_marine_tarball marine
|
||||
JOIN tbl_marine_station station ON marine.StationID = station.StationID
|
||||
JOIN tbl_state state ON station.StateID = state.StateID
|
||||
JOIN tbl_user user ON marine.FirstSampler = user.FullName
|
||||
JOIN tbl_level level ON user.LevelID = level.LevelID
|
||||
WHERE marine.Id = @id";
|
||||
|
||||
var tarball = await _context.Database
|
||||
.SqlQueryRaw<TarballPdfDto>(query, new MySqlParameter("@id", id))
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
if (tarball == null)
|
||||
return NotFound("Record not found");
|
||||
|
||||
// Prepare boolean values for PDF
|
||||
bool tarBallYes = tarball.ClassifyID != "NO";
|
||||
bool tarBallNo = tarball.ClassifyID == "NO";
|
||||
bool isSand = tarball.ClassifyID == "SD";
|
||||
bool isNonSandy = tarball.ClassifyID == "NS";
|
||||
bool isCoquina = tarball.ClassifyID == "CO";
|
||||
|
||||
// ===== 2. Get Images =====
|
||||
// For date (stored as DATE in DB → "2025-01-30" becomes "20250130")
|
||||
var sampleDateString = tarball.DateSample.ToString("yyyyMMdd");
|
||||
|
||||
// For time (stored as TIME in DB → "16:49:02" becomes "164902")
|
||||
var sampleTimeString = tarball.TimeSample.ToString("hhmmss");
|
||||
var stationFolder = Path.Combine(PhotoBasePath, tarball.StationID);
|
||||
|
||||
var stationImages = new Dictionary<string, string>();
|
||||
var foundAnyImages = false;
|
||||
|
||||
if (Directory.Exists(stationFolder))
|
||||
{
|
||||
var basePattern = $"{tarball.StationID}_{sampleDateString}_{sampleTimeString}_";
|
||||
var allImages = Directory.GetFiles(stationFolder, $"{basePattern}*");
|
||||
|
||||
foreach (var imagePath in allImages)
|
||||
{
|
||||
var fileName = Path.GetFileNameWithoutExtension(imagePath);
|
||||
var type = fileName.Split('_').Last();
|
||||
stationImages[type] = imagePath;
|
||||
foundAnyImages = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundAnyImages)
|
||||
{
|
||||
return StatusCode(404, "No images found for this record");
|
||||
}
|
||||
|
||||
// Verify mandatory images exist
|
||||
var mandatoryImages = new[] {
|
||||
"LEFTSIDECOASTALVIEW",
|
||||
"RIGHTSIDECOASTALVIEW",
|
||||
"DRAWINGVERTICALLINES",
|
||||
"DRAWINGHORIZONTALLINES"
|
||||
};
|
||||
|
||||
foreach (var type in mandatoryImages)
|
||||
{
|
||||
if (!stationImages.ContainsKey(type))
|
||||
{
|
||||
return StatusCode(400, $"Missing mandatory image: {type}");
|
||||
}
|
||||
}
|
||||
|
||||
// ===== 3. Generate PDF =====
|
||||
var pdf = new TarBallPDF(
|
||||
tarball.StateName,
|
||||
tarball.StationID,
|
||||
tarball.LocationName,
|
||||
tarball.Longitude,
|
||||
tarball.Latitude,
|
||||
tarball.DateSample,
|
||||
tarball.TimeSample,
|
||||
tarball.ClassifyID,
|
||||
tarBallYes,
|
||||
tarBallNo,
|
||||
isSand,
|
||||
isNonSandy,
|
||||
isCoquina,
|
||||
stationImages["LEFTSIDECOASTALVIEW"],
|
||||
stationImages["RIGHTSIDECOASTALVIEW"],
|
||||
stationImages["DRAWINGVERTICALLINES"],
|
||||
stationImages["DRAWINGHORIZONTALLINES"],
|
||||
stationImages.GetValueOrDefault("OPTIONAL01"),
|
||||
stationImages.GetValueOrDefault("OPTIONAL02"),
|
||||
stationImages.GetValueOrDefault("OPTIONAL03"),
|
||||
stationImages.GetValueOrDefault("OPTIONAL04"),
|
||||
tarball.OptionalName1,
|
||||
tarball.OptionalName2,
|
||||
tarball.OptionalName3,
|
||||
tarball.OptionalName4,
|
||||
tarball.FirstSampler,
|
||||
tarball.FullName,
|
||||
tarball.LevelName
|
||||
).GeneratePdf();
|
||||
|
||||
// ===== 4. Return PDF =====
|
||||
var downloadName = $"{tarball.StationID}_{sampleDateString}_{sampleTimeString}.pdf";
|
||||
return forceDownload
|
||||
? File(pdf, "application/pdf", downloadName)
|
||||
: File(pdf, "application/pdf");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return StatusCode(500, $"PDF generation failed: {ex.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
_networkAccessService.DisconnectFromNetworkShare();
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsImageValid(string imagePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var image = System.Drawing.Image.FromFile(imagePath))
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
Console.WriteLine($"Invalid image skipped: {imagePath}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
128
Areas/MMS/Models/NetworkAccessService.cs
Normal file
128
Areas/MMS/Models/NetworkAccessService.cs
Normal file
@ -0,0 +1,128 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.ComponentModel;
|
||||
using System.Threading;
|
||||
|
||||
public class NetworkShareAccess : IDisposable
|
||||
{
|
||||
private readonly string _networkPath;
|
||||
private readonly string _username;
|
||||
private readonly string _password;
|
||||
|
||||
private bool _isConnected = false;
|
||||
private static readonly object _lock = new object();
|
||||
|
||||
public NetworkShareAccess(string networkPath, string username, string password)
|
||||
{
|
||||
_networkPath = networkPath?.Trim().Replace('/', '\\') ?? throw new ArgumentNullException(nameof(networkPath));
|
||||
if (!_networkPath.StartsWith(@"\\"))
|
||||
throw new ArgumentException("Network path must start with \\\\");
|
||||
|
||||
_username = username ?? throw new ArgumentNullException(nameof(username));
|
||||
_password = password ?? throw new ArgumentNullException(nameof(password));
|
||||
}
|
||||
|
||||
public void ConnectToNetworkPath()
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (_isConnected) return;
|
||||
|
||||
int retries = 0;
|
||||
while (retries < 3)
|
||||
{
|
||||
try
|
||||
{
|
||||
var netResource = new NetResource
|
||||
{
|
||||
Scope = ResourceScope.GlobalNetwork,
|
||||
ResourceType = ResourceType.Disk,
|
||||
DisplayType = ResourceDisplayType.Share,
|
||||
RemoteName = _networkPath
|
||||
};
|
||||
|
||||
int result = WNetAddConnection2(netResource, _password, _username, 0);
|
||||
|
||||
if (result == 0)
|
||||
{
|
||||
_isConnected = true;
|
||||
Console.WriteLine($"Connected to {_networkPath}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (result == 1219) // Multiple connections
|
||||
{
|
||||
WNetCancelConnection2(_networkPath, 0, true);
|
||||
Thread.Sleep(1000);
|
||||
retries++;
|
||||
continue;
|
||||
}
|
||||
|
||||
throw new IOException($"Failed to connect. Error {result}: {GetNetworkErrorDescription(result)}");
|
||||
}
|
||||
catch
|
||||
{
|
||||
if (++retries >= 3) throw;
|
||||
Thread.Sleep(1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void DisconnectFromNetworkShare()
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (!_isConnected) return;
|
||||
|
||||
try
|
||||
{
|
||||
int result = WNetCancelConnection2(_networkPath, 0, true);
|
||||
if (result != 0)
|
||||
Console.WriteLine($"Warning: Disconnect failed (Error {result})");
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isConnected = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose() => DisconnectFromNetworkShare();
|
||||
|
||||
private string GetNetworkErrorDescription(int errorCode) => errorCode switch
|
||||
{
|
||||
5 => "Access denied",
|
||||
53 => "Network path not found",
|
||||
67 => "Network name not found",
|
||||
85 => "Network connection already exists",
|
||||
86 => "Invalid password",
|
||||
1219 => "Multiple connections to a server or shared resource not allowed",
|
||||
_ => new Win32Exception(errorCode).Message
|
||||
};
|
||||
|
||||
[DllImport("mpr.dll", CharSet = CharSet.Unicode)]
|
||||
private static extern int WNetAddConnection2(NetResource netResource, string password, string username, int flags);
|
||||
|
||||
[DllImport("mpr.dll", CharSet = CharSet.Unicode)]
|
||||
private static extern int WNetCancelConnection2(string name, int flags, bool force);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||
private class NetResource
|
||||
{
|
||||
public ResourceScope Scope;
|
||||
public ResourceType ResourceType;
|
||||
public ResourceDisplayType DisplayType;
|
||||
public int Usage;
|
||||
public string? LocalName; //? or required TBD
|
||||
public string? RemoteName; //
|
||||
public string? Comment; //
|
||||
public string? Provider; //
|
||||
}
|
||||
|
||||
private enum ResourceScope { Connected = 1, GlobalNetwork, Remembered, Recent, Context }
|
||||
private enum ResourceType { Any = 0, Disk = 1, Print = 2, Reserved = 8 }
|
||||
private enum ResourceDisplayType { Generic = 0x0, Domain = 0x01, Server = 0x02, Share = 0x03, File = 0x04, Group = 0x05 }
|
||||
}
|
||||
405
Areas/MMS/Models/PDFGenerator/TarBallPDF.cs
Normal file
405
Areas/MMS/Models/PDFGenerator/TarBallPDF.cs
Normal file
@ -0,0 +1,405 @@
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Infrastructure;
|
||||
using QuestPDF.Helpers;
|
||||
using Google.Protobuf.WellKnownTypes;
|
||||
using PSTW_CentralSystem.Models;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion.Internal;
|
||||
|
||||
namespace PSTW_CentralSystem.Areas.MMS.Models.PDFGenerator
|
||||
{
|
||||
public class TarBallPDF(string stateName, string stationID, string locationName,
|
||||
string longitude, string latitude, DateTime dateSample, TimeSpan timeSample,
|
||||
string classifyID, bool tarBallYes, bool tarBallNo, bool isSand, bool isNonSandy,
|
||||
bool isCoquina, string photoPath1, string photoPath2, string photoPath3, string photoPath4,
|
||||
string? photoPath5, string? photoPath6, string? photoPath7, string? photoPath8,
|
||||
string? optionalName1, string? optionalName2, string? optionalName3, string? optionalName4,
|
||||
string firstSampler, string fullName, string levelName
|
||||
)
|
||||
: IDocument
|
||||
{
|
||||
//have to be arranged accordingly(?)
|
||||
private readonly string _stateName = stateName;
|
||||
private readonly string _stationId = stationID;
|
||||
private readonly string _locationName = locationName;
|
||||
private readonly string _longitude = longitude;
|
||||
private readonly string _latitude = latitude;
|
||||
private readonly DateTime _dateSample = dateSample;
|
||||
private readonly TimeSpan _timeSample = timeSample;
|
||||
private readonly string _classifyID = classifyID;
|
||||
private readonly bool _tarBallYes = tarBallYes;
|
||||
private readonly bool _tarBallNo = tarBallNo;
|
||||
private readonly bool _isSand = isSand;
|
||||
private readonly bool _isNonSandy = isNonSandy;
|
||||
private readonly bool _isCoquina = isCoquina;
|
||||
private readonly string _photoPath1 = photoPath1;
|
||||
private readonly string _photoPath2 = photoPath2;
|
||||
private readonly string _photoPath3 = photoPath3;
|
||||
private readonly string _photoPath4 = photoPath4;
|
||||
private readonly string? _photoPath5 = photoPath5;
|
||||
private readonly string? _photoPath6 = photoPath6;
|
||||
private readonly string? _photoPath7 = photoPath7;
|
||||
private readonly string? _photoPath8 = photoPath8;
|
||||
private readonly string? _optionalName1 = optionalName1;
|
||||
private readonly string? _optionalName2 = optionalName2;
|
||||
private readonly string? _optionalName3 = optionalName3;
|
||||
private readonly string? _optionalName4 = optionalName4;
|
||||
private readonly string _firstSampler = firstSampler;
|
||||
private readonly string _fullName = fullName;
|
||||
private readonly string _levelName = levelName;
|
||||
|
||||
private Image? LoadImage(string? imagePath)
|
||||
{
|
||||
if (string.IsNullOrEmpty(imagePath)) //check if photo is missing
|
||||
{
|
||||
return null; // returns 'nothing' (safe)
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return Image.FromFile(imagePath); //load photo
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null; //if loading fails, returns 'nothing' (safe)
|
||||
}
|
||||
}
|
||||
|
||||
public DocumentMetadata GetMetadata() => new()
|
||||
{
|
||||
Title = "TARBALL SAMPLING FORM",
|
||||
Author = "PAKAR SCIENO TW Integrated Environmental Solutions",
|
||||
Subject = "Environmental Survey and Observations"
|
||||
};
|
||||
|
||||
// Compose the PDF content
|
||||
public void Compose(IDocumentContainer container)
|
||||
{
|
||||
container.Page(page =>
|
||||
{
|
||||
// Page Setup
|
||||
page.Size(PageSizes.A4);
|
||||
page.Margin(1.1f, Unit.Centimetre);
|
||||
page.DefaultTextStyle(x => x.FontFamily("Arial").FontSize(10));
|
||||
|
||||
//HEADER SECTION ==========================================================================
|
||||
//will be included in each page
|
||||
page.Header().Row(row =>
|
||||
{
|
||||
row.RelativeItem(1).Element(CellStyle)
|
||||
.AlignMiddle()
|
||||
.AlignCenter()
|
||||
.Image("wwwroot/assets/images/pstw-logo.jpg")
|
||||
.FitArea();
|
||||
|
||||
row.RelativeItem(1).Element(CellStyle)
|
||||
.AlignMiddle()
|
||||
.AlignCenter()
|
||||
.Text("TARBALL SAMPLING FORM")
|
||||
.FontSize(16)
|
||||
.FontColor("#4B0082");
|
||||
|
||||
row.RelativeItem(1).Column(column =>
|
||||
{
|
||||
column.Spacing(0);
|
||||
|
||||
column.Item().Row(innerRow =>
|
||||
{
|
||||
innerRow.RelativeItem(1).Element(CellStyle).Text("Document:")
|
||||
.AlignLeft();
|
||||
innerRow.RelativeItem(1).Element(CellStyle).Text("F-MM06")
|
||||
.AlignLeft().Bold();
|
||||
});
|
||||
column.Item().Row(innerRow =>
|
||||
{
|
||||
innerRow.RelativeItem(1).Element(CellStyle).Text("Effective Date:")
|
||||
.AlignLeft();
|
||||
innerRow.RelativeItem(1).Element(CellStyle).Text("1 April 2025")
|
||||
.AlignLeft();
|
||||
});
|
||||
column.Item().Row(innerRow =>
|
||||
{
|
||||
innerRow.RelativeItem(1).Element(CellStyle).Text("Revision No.")
|
||||
.AlignLeft();
|
||||
innerRow.RelativeItem(1).Element(CellStyle).Text("02")
|
||||
.AlignLeft();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
static IContainer CellStyle(IContainer container)
|
||||
=> container.Border(0.5f).Padding(5);
|
||||
|
||||
});
|
||||
//HEADER SECTION END ==========================================================================
|
||||
|
||||
|
||||
// CONTENT SECTION ============================================================================
|
||||
page.Content().Column(column =>
|
||||
{
|
||||
// Observations Table
|
||||
column.Item().Element(container =>
|
||||
{
|
||||
container
|
||||
.PaddingTop(20)
|
||||
.PaddingBottom(10)
|
||||
.Text("Please be informed that we have observed the following conditions:");
|
||||
});
|
||||
column.Item().Table(table =>
|
||||
{
|
||||
column.Spacing(0);
|
||||
|
||||
table.ColumnsDefinition(columns =>
|
||||
{
|
||||
columns.RelativeColumn(3);
|
||||
columns.RelativeColumn(3);
|
||||
});
|
||||
|
||||
table.Cell()
|
||||
.Background(Colors.Grey.Lighten2).Element(CellStyle).Text("STATE")
|
||||
.Bold();
|
||||
table.Cell().Element(CellStyle).Text(_stateName);
|
||||
|
||||
table.Cell()
|
||||
.Background(Colors.Grey.Lighten2).Element(CellStyle).Text("STATION ID")
|
||||
.Bold();
|
||||
table.Cell().Element(CellStyle).Text(_stationId);
|
||||
|
||||
table.Cell()
|
||||
.Background(Colors.Grey.Lighten2).Element(CellStyle).Text("LOCATION")
|
||||
.Bold();
|
||||
table.Cell().Element(CellStyle).Text(_locationName);
|
||||
|
||||
table.Cell()
|
||||
.Background(Colors.Grey.Lighten2).Element(CellStyle).Text("TARBALL SURVEY LOCATION LONGITUDE & LATITUDE")
|
||||
.Bold();
|
||||
table.Cell().Element(CellStyle).Text($"{_longitude}, {_latitude}");
|
||||
|
||||
table.Cell()
|
||||
.Background(Colors.Grey.Lighten2).Element(CellStyle).Text("DATE / TIME")
|
||||
.Bold();
|
||||
table.Cell().Element(CellStyle).Text($"{_dateSample:yyyy-MM-dd} {_timeSample:hh\\:mm\\:ss}");
|
||||
});
|
||||
|
||||
column.Spacing(3);
|
||||
|
||||
// Survey Findings =======================================================================================
|
||||
column.Item()
|
||||
.PaddingTop(10)
|
||||
.PaddingBottom(10)
|
||||
.Text("SURVEY FINDING:")
|
||||
.Bold().FontSize(12);
|
||||
|
||||
column.Item()
|
||||
.PaddingBottom(10)
|
||||
.Text(text =>
|
||||
{
|
||||
text.Span("Tar Ball: ").Style(TextStyle.Default.FontSize(10));
|
||||
text.Span(_classifyID == "NO" ? " ☐ YES ☑ NO" : " ☑ YES ☐ NO").Style(TextStyle.Default.FontSize(10));
|
||||
});
|
||||
|
||||
column.Item()
|
||||
.PaddingBottom(10)
|
||||
.Text("If YES, Tar Ball falls under the Classification of:")
|
||||
.FontSize(10);
|
||||
|
||||
column.Item()
|
||||
.PaddingBottom(10)
|
||||
.Text(text =>
|
||||
{
|
||||
text.Span(_classifyID == "SD" ? "☑ Sand " : "☐ Sand ").Style(TextStyle.Default.FontSize(10));
|
||||
text.Span(_classifyID == "NS" ? " ☑ Non-sandy " : " ☐ Non-sandy ").Style(TextStyle.Default.FontSize(10));
|
||||
text.Span(_classifyID == "CQ" ? "☑ Coquina" : "☐ Coquina").Style(TextStyle.Default.FontSize(10));
|
||||
});
|
||||
|
||||
column.Item()
|
||||
.PaddingBottom(10)
|
||||
.Text("*tick wherever applicable")
|
||||
.Italic();
|
||||
|
||||
// Photos Section Title
|
||||
column.Item()
|
||||
.PaddingBottom(5)
|
||||
.Text("PHOTOGRAPHS OF SAMPLING")
|
||||
.AlignCenter()
|
||||
.Bold()
|
||||
.FontSize(14);
|
||||
|
||||
// Photos Section
|
||||
column.Item().Table(table =>
|
||||
{
|
||||
column.Spacing(0);
|
||||
|
||||
table.ColumnsDefinition(columns =>
|
||||
{
|
||||
columns.RelativeColumn(1);
|
||||
columns.RelativeColumn(1);
|
||||
});
|
||||
|
||||
// Row 1: Photos ====================================
|
||||
table.Cell().Element(CellStyle).Height(150)
|
||||
.Image(LoadImage(_photoPath1) ?? null) // Loads image or uses null. if exist, fill cell. if null, cell stay empty
|
||||
.FitArea();
|
||||
|
||||
table.Cell().Element(CellStyle).Height(150)
|
||||
.Image(LoadImage(_photoPath2) ?? null)
|
||||
.FitArea();
|
||||
|
||||
// Row 2: Captions for figure 1 & 2 =================
|
||||
table.Cell().Element(CellStyle)
|
||||
.Text("Figure 1: Left Side Coastal View")
|
||||
.FontSize(12).AlignLeft();
|
||||
|
||||
table.Cell().Element(CellStyle)
|
||||
.Text("Figure 2: Right Side Coastal View")
|
||||
.FontSize(12).AlignLeft();
|
||||
|
||||
// Row 3 =============================================
|
||||
table.Cell().Element(CellStyle).Height(150)
|
||||
.Image(LoadImage(_photoPath3) ?? null) // Just pass null if no image
|
||||
.FitArea();
|
||||
|
||||
table.Cell().Element(CellStyle).Height(150)
|
||||
.Image(LoadImage(_photoPath4) ?? null) // Just pass null if no image
|
||||
.FitArea();
|
||||
|
||||
// Row 4: Captions for figure 3 & 4 =================
|
||||
table.Cell().Element(CellStyle)
|
||||
.Text("Figure 3: Drawing Vertical Lines")
|
||||
.FontSize(12).AlignLeft();
|
||||
|
||||
table.Cell().Element(CellStyle)
|
||||
.Text("Figure 4: Drawing Horizontal Lines (Racking)")
|
||||
.FontSize(12).AlignLeft();
|
||||
});
|
||||
|
||||
// Page Break
|
||||
column.Item().PageBreak();
|
||||
|
||||
column.Spacing(3);
|
||||
|
||||
// Additional Photos Section
|
||||
column.Item().Table(table =>
|
||||
{
|
||||
column.Spacing(3);
|
||||
|
||||
table.ColumnsDefinition(columns =>
|
||||
{
|
||||
columns.RelativeColumn(1);
|
||||
columns.RelativeColumn(1);
|
||||
});
|
||||
|
||||
// Helper function to safely add image cells
|
||||
void AddOptionalImageCell(string imagePath)
|
||||
{
|
||||
table.Cell().Element(CellStyle).Height(150).Element(cell =>
|
||||
{
|
||||
var image = LoadImage(imagePath);
|
||||
if (image != null)
|
||||
{
|
||||
cell.Image(image).FitArea();
|
||||
}
|
||||
// If null, leaves an empty cell
|
||||
});
|
||||
}
|
||||
|
||||
// Row 1: Optional images 5 & 6
|
||||
AddOptionalImageCell(_photoPath5);
|
||||
AddOptionalImageCell(_photoPath6);
|
||||
|
||||
// Row 2: Captions
|
||||
table.Cell().Element(CellStyle)
|
||||
.Text($"Figure 5: {(_optionalName1 ?? "")}")
|
||||
.FontSize(12).AlignLeft();
|
||||
|
||||
table.Cell().Element(CellStyle)
|
||||
.Text($"Figure 6: {(_optionalName2 ?? "")}")
|
||||
.FontSize(12).AlignLeft();
|
||||
|
||||
// Row 3: Optional images 7 & 8
|
||||
AddOptionalImageCell(_photoPath7);
|
||||
AddOptionalImageCell(_photoPath8);
|
||||
|
||||
// Row 4: Captions
|
||||
table.Cell().Element(CellStyle)
|
||||
.Text($"Figure 7: {(_optionalName3 ?? "")}")
|
||||
.FontSize(12).AlignLeft();
|
||||
|
||||
table.Cell().Element(CellStyle)
|
||||
.Text($"Figure 8: {(_optionalName4 ?? "")}")
|
||||
.FontSize(12).AlignLeft();
|
||||
});
|
||||
|
||||
// Note Section
|
||||
column.Item()
|
||||
.PaddingTop(10)
|
||||
.PaddingBottom(20)
|
||||
.Text("* If there are any event observe at the current station it is compulsory to add optional photo with description (figure 5 to figure 8)")
|
||||
.Bold()
|
||||
.FontSize(10)
|
||||
.AlignLeft();
|
||||
|
||||
// Signature Section
|
||||
column.Item().Table(table =>
|
||||
{
|
||||
table.ColumnsDefinition(columns =>
|
||||
{
|
||||
//the overall layout of the authorization section
|
||||
columns.RelativeColumn(2);
|
||||
columns.RelativeColumn(1);
|
||||
columns.RelativeColumn(2);
|
||||
columns.RelativeColumn(1);
|
||||
columns.RelativeColumn(1);
|
||||
});
|
||||
|
||||
table.Cell().RowSpan(2).Element(CellStyle)
|
||||
.Text(text =>
|
||||
{
|
||||
text.Span("REPORTED BY: ").Bold().FontSize(12);
|
||||
text.Span(_firstSampler).FontSize(12);
|
||||
});
|
||||
table.Cell().Element(CellStyle).Text("Signature").FontSize(12);
|
||||
table.Cell().Element(CellStyle).Text("");
|
||||
table.Cell().Element(CellStyle).Text("Date").FontSize(12);
|
||||
table.Cell().Element(CellStyle).Text($"{_dateSample:dd/MM/yyyy}").FontSize(12);
|
||||
|
||||
table.Cell().Element(CellStyle).Text("Designation").FontSize(12);
|
||||
table.Cell().ColumnSpan(3).Element(CellStyle).Text(_levelName).FontSize(12);
|
||||
|
||||
table.Cell().RowSpan(2).Element(CellStyle)
|
||||
.Text(text =>
|
||||
{
|
||||
text.Span("CHECKED BY: ").Bold().FontSize(12);
|
||||
text.Span("RIFAIE AZHARI").FontSize(12);
|
||||
});
|
||||
table.Cell().Element(CellStyle).Text("Signature").FontSize(12);
|
||||
table.Cell().ColumnSpan(2).Element(CellStyle).Text("");
|
||||
table.Cell().Element(CellStyle).Text("");
|
||||
|
||||
table.Cell().Element(CellStyle).Text("Designation").FontSize(12);
|
||||
table.Cell().ColumnSpan(3).Element(CellStyle).Text("Executive").FontSize(12);
|
||||
|
||||
table.Cell().RowSpan(2).Element(CellStyle)
|
||||
.Text(text =>
|
||||
{
|
||||
text.Span("VERIFIED BY: ").Bold().FontSize(12);
|
||||
text.Span("J SOMU").FontSize(12);
|
||||
});
|
||||
table.Cell().Element(CellStyle).Text("Signature").FontSize(12);
|
||||
table.Cell().ColumnSpan(2).Element(CellStyle).Text("");
|
||||
table.Cell().Element(CellStyle).Text("");
|
||||
|
||||
table.Cell().Element(CellStyle).Text("Designation").FontSize(12);
|
||||
table.Cell().ColumnSpan(3).Element(CellStyle).Text("Technical Manager").FontSize(12);
|
||||
});
|
||||
});
|
||||
|
||||
// Footer Section
|
||||
page.Footer().AlignCenter().Text(text =>
|
||||
{
|
||||
});
|
||||
|
||||
static IContainer CellStyle(IContainer container) => container.Border(0.5f).Padding(5);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
6
Areas/MMS/Views/Marine/Index.cshtml
Normal file
6
Areas/MMS/Views/Marine/Index.cshtml
Normal file
@ -0,0 +1,6 @@
|
||||
@{
|
||||
ViewData["Title"] = "MMS Dashboard";
|
||||
}
|
||||
|
||||
<h1>Welcome to the MMS Dashboard</h1>
|
||||
<p>This is where you’ll add content for MMS later.</p>
|
||||
224
Areas/MMS/Views/Marine/TarBallForm.cshtml
Normal file
224
Areas/MMS/Views/Marine/TarBallForm.cshtml
Normal file
@ -0,0 +1,224 @@
|
||||
@{
|
||||
ViewData["Title"] = "Tarball Report";
|
||||
Layout = "~/Views/Shared/_Layout.cshtml";
|
||||
}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14"></script>
|
||||
<title>Tarball Report</title>
|
||||
<style>
|
||||
.container {
|
||||
width: 1400px; /* Approximate width for A4 aspect ratio */
|
||||
margin: 20px auto;
|
||||
padding: 20px;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
div {
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
h4 {
|
||||
padding-top: 15px;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
th, td {
|
||||
border: 1px solid #ccc;
|
||||
padding: 10px;
|
||||
}
|
||||
</style>
|
||||
<!-- DataTables CSS -->
|
||||
<link href="~/lib/datatables/datatables.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="app" class="container">
|
||||
<div>
|
||||
<h4>Month</h4>
|
||||
<select v-model="selectedMonth" style="width: 100%; padding: 5px;">
|
||||
<option value="" selected>Filter by Month</option>
|
||||
<option v-for="month in months" :value="month">{{ month }}</option>
|
||||
</select>
|
||||
|
||||
<h4>Year</h4>
|
||||
<select v-model="selectedYear" style="width: 100%; padding: 5px;">
|
||||
<option value="" selected>Filter by Year</option>
|
||||
<option v-for="year in years" :value="year">{{ year }}</option>
|
||||
</select>
|
||||
|
||||
<div>
|
||||
<button v-on:click="clearFilters" class="btn btn-default" style="margin-top: 20px; margin-bottom: 25px;">Clear Filter</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="datatable">
|
||||
<table id="tarballTable" class="table table-bordered table-hover table-striped" style="width:100%;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>No.</th>
|
||||
<th>Date</th>
|
||||
<th>Station</th>
|
||||
<th>Time</th>
|
||||
<th>Status</th>
|
||||
<th>PDF</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@section Scripts {
|
||||
<!-- DataTables JS -->
|
||||
<script src="~/lib/datatables/datatables.js"></script>
|
||||
<script>
|
||||
new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
selectedMonth: '',
|
||||
selectedYear: '',
|
||||
months: [
|
||||
'January', 'February', 'March', 'April', 'May',
|
||||
'June', 'July', 'August', 'September',
|
||||
'October', 'November', 'December'
|
||||
],
|
||||
dataFromServer: @Json.Serialize(Model ?? new List<object>()),
|
||||
dataTable: null
|
||||
},
|
||||
mounted() {
|
||||
if (this.dataFromServer && this.dataFromServer.length > 0) {
|
||||
this.$nextTick(() => {
|
||||
this.initializeDataTable();
|
||||
});
|
||||
} else {
|
||||
console.log("No data received from server");
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
years() {
|
||||
if (!this.dataFromServer || this.dataFromServer.length === 0) {
|
||||
return []; // Return an empty array if no data is available
|
||||
}
|
||||
|
||||
// Extract all years from the data
|
||||
const allYears = this.dataFromServer.map(data => new Date(data.date).getFullYear());
|
||||
|
||||
// Find the minimum and maximum years
|
||||
const minYear = Math.min(...allYears);
|
||||
const maxYear = Math.max(...allYears);
|
||||
|
||||
// Generate a range of years from minYear to maxYear
|
||||
return Array.from({ length: maxYear - minYear + 1 }, (_, i) => (minYear + i).toString());
|
||||
},
|
||||
sortedFilteredData() {
|
||||
// If no filters are applied, return all data sorted by descending date
|
||||
if (!this.selectedMonth && !this.selectedYear) {
|
||||
return this.dataFromServer.sort((a, b) => new Date(b.date) - new Date(a.date));
|
||||
}
|
||||
|
||||
// Filter data by selected month and year
|
||||
const filtered = this.dataFromServer.filter(data => {
|
||||
const date = new Date(data.date);
|
||||
const monthMatches = this.selectedMonth
|
||||
? date.toLocaleString('default', { month: 'long' }) === this.selectedMonth
|
||||
: true;
|
||||
const yearMatches = this.selectedYear
|
||||
? date.getFullYear().toString() === this.selectedYear
|
||||
: true;
|
||||
return monthMatches && yearMatches;
|
||||
});
|
||||
|
||||
// Sort data by date in descending order
|
||||
return filtered.sort((a, b) => new Date(b.date) - new Date(a.date));
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
clearFilters() {
|
||||
this.selectedMonth = '';
|
||||
this.selectedYear = '';
|
||||
},
|
||||
initializeDataTable() {
|
||||
// Initialize DataTables
|
||||
const self = this; // Store the Vue instance context
|
||||
this.dataTable = $('#tarballTable').DataTable({
|
||||
"pageLength": 10,
|
||||
"lengthMenu": [5, 10, 15, 20],
|
||||
"responsive": true,
|
||||
"order": [[1, "desc"]], // Default sorting by Date column (descending)
|
||||
"orderMulti": false, // Disable multi-column sorting
|
||||
"columns": [
|
||||
{ "data": null,"render": (data, type, row, meta) => meta.row + 1},
|
||||
{ "data": "date", "render": (data) => new Date(data).toLocaleDateString('en-GB') },
|
||||
{ "data": "station" },
|
||||
{ "data": "time" }, // Removed the incorrect toLocaleDateString()
|
||||
{
|
||||
"data": null,
|
||||
"render": () => `
|
||||
<button class="btn btn-success" disabled>Approve</button>
|
||||
<button class="btn btn-danger" disabled>Reject</button>
|
||||
`
|
||||
},
|
||||
{
|
||||
"data": null,
|
||||
"render": (data) => `
|
||||
<a href="/MMS/Marine/ViewPDF?id=${data.id}" class="btn btn-primary" target="_blank">View PDF</a>
|
||||
<a href="/MMS/Marine/GenerateReport?id=${data.id}" class="btn btn-primary">Download PDF</a>
|
||||
`
|
||||
//uses ViewPDF for view, GenerateReport for downloading
|
||||
}
|
||||
],
|
||||
"rowCallback": function(row, data, index) {
|
||||
// Update the "No." column to start from 1 for the current page
|
||||
const pageInfo = this.api().page.info();
|
||||
$('td:first', row).html(pageInfo.start + index + 1);
|
||||
}
|
||||
});
|
||||
|
||||
// Populate the table with initial data
|
||||
this.updateDataTable(this.sortedFilteredData);
|
||||
|
||||
//auto-filtering
|
||||
$('#tarballTable_filter input').on('keyup', function () {
|
||||
self.dataTable.search(this.value).draw();
|
||||
});
|
||||
},
|
||||
updateDataTable(data) {
|
||||
if (this.dataTable) {
|
||||
this.dataTable.clear();
|
||||
this.dataTable.rows.add(data);
|
||||
this.dataTable.draw();
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// Initialize DataTables after Vue has rendered the table
|
||||
this.$nextTick(() => {
|
||||
this.initializeDataTable();
|
||||
});
|
||||
},
|
||||
watch: {
|
||||
sortedFilteredData(newData) {
|
||||
// Automatically update DataTables whenever the filtered data changes
|
||||
this.updateDataTable(newData);
|
||||
//trigger search function after updating data(?)
|
||||
if(this.dataTable) {
|
||||
this.dataTable.search('').draw();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
}
|
||||
@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.VisualStudio.Web.CodeGenerators.Mvc.Templates.BlazorIdentity.Pages;
|
||||
using Mono.TextTemplating;
|
||||
using Newtonsoft.Json;
|
||||
using PSTW_CentralSystem.Areas.Inventory.Models;
|
||||
@ -265,6 +266,7 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
|
||||
var userRole = await _userManager.GetRolesAsync(user);
|
||||
var isAdmin = userRole.Contains("SystemAdmin") || userRole.Contains("SuperAdmin") || userRole.Contains("Finance");
|
||||
List<ItemModel> itemList = new List<ItemModel>();
|
||||
List<ItemMovementModel> createDate = new List<ItemMovementModel>();
|
||||
// Get the item list
|
||||
if (isAdmin)
|
||||
{
|
||||
@ -296,14 +298,22 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
|
||||
.ThenInclude(m => m!.FromUser)
|
||||
.Where(i => i.DepartmentId == user.departmentId)
|
||||
.ToListAsync();
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Get the departments list (DepartmentId references Departments)
|
||||
var departments = await _centralDbContext.Departments.ToListAsync();
|
||||
|
||||
var createDates = await _centralDbContext.ItemMovements.Where(r => r.Action == "Register").ToListAsync();
|
||||
|
||||
// Buat dictionary agar lebih cepat dicari berdasarkan ItemID
|
||||
var createDateDict = await _centralDbContext.ItemMovements.Where(r => r.Action == "Register").GroupBy(r => r.ItemId).ToDictionaryAsync(g => g.Key, g => g.Min(m => m.Date));
|
||||
|
||||
// Now join items with users and departments manually
|
||||
var itemListWithDetails = itemList.Select(item => new
|
||||
{
|
||||
createDate = createDateDict.ContainsKey(item.ItemID) ? createDateDict[item.ItemID].ToString("dd/MM/yyyy HH:mm:ss") : null,
|
||||
item.ItemID,
|
||||
item.UniqueID,
|
||||
item.CompanyId,
|
||||
@ -324,7 +334,8 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
|
||||
EndWDate = item.EndWDate.ToString("dd/MM/yyyy"),
|
||||
InvoiceDate = item.InvoiceDate?.ToString("dd/MM/yyyy"),
|
||||
item.Department?.DepartmentName,
|
||||
CreatedBy=item.CreatedBy!.UserName,
|
||||
RegisterDate = createDate,
|
||||
CreatedBy =item.CreatedBy!.UserName,
|
||||
item.Product!.ProductName,
|
||||
item.Product!.ProductShortName,
|
||||
item.Product!.Category,
|
||||
@ -465,12 +476,26 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
|
||||
return NotFound(new { success = false, message = "Item not found" });
|
||||
}
|
||||
|
||||
// Get related item movements
|
||||
var itemMovements = await _centralDbContext.ItemMovements
|
||||
.Where(i => i.ItemId == item.ItemID)
|
||||
.ToListAsync();
|
||||
|
||||
// Remove all related item movements
|
||||
if (itemMovements.Any())
|
||||
{
|
||||
_centralDbContext.ItemMovements.RemoveRange(itemMovements);
|
||||
await _centralDbContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
// Remove the item itself
|
||||
_centralDbContext.Items.Remove(item);
|
||||
await _centralDbContext.SaveChangesAsync();
|
||||
|
||||
return Ok(new { success = true, message = "Item deleted successfully" });
|
||||
}
|
||||
|
||||
|
||||
[HttpPost("GetItem/{id}")] // Endpoint to retrieve an item by its ID
|
||||
public async Task<IActionResult> GetItem(string id)
|
||||
{
|
||||
@ -538,6 +563,7 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
|
||||
item.Movement?.ToOther,
|
||||
item.Movement?.LatestStatus,
|
||||
item.Movement?.LastUser,
|
||||
item.Movement?.LastStore,
|
||||
item.Movement?.MovementComplete,
|
||||
remark = item.Movement?.Remark,
|
||||
QRString = $"{HttpContext.Request.Scheme}://{HttpContext.Request.Host.Value}/I/{item.UniqueID}" // Generate QR String
|
||||
@ -565,16 +591,18 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
|
||||
.Include(i => i.NextUser)
|
||||
.ToListAsync();
|
||||
|
||||
int itemrow = 0;
|
||||
var itemMovementListWithQR = itemMovementList.Select(item => new
|
||||
{
|
||||
id = itemrow++,
|
||||
item, // Includes all properties of the original item
|
||||
QRString = $"{HttpContext.Request.Scheme}://{HttpContext.Request.Host.Value}/I/{item.ItemId}", // Generate QR String
|
||||
ProductName = item.Item?.Product?.ProductName,
|
||||
toUserName = item.NextUser?.FullName,
|
||||
lastUserName = item.FromUser?.FullName
|
||||
}).ToList();
|
||||
// if use qr, need to use this. else do simple return json. datatable qr will read dom and replace element with id=qr{qrstring} with qr image.
|
||||
// then need dynamic numbering for qr even if item movement is repeated by the same item
|
||||
//int itemrow = 0;
|
||||
//var itemMovementListWithQR = itemMovementList.Select(item => new
|
||||
//{
|
||||
// id = itemrow++,
|
||||
// item, // Includes all properties of the original item
|
||||
// QRString = $"{HttpContext.Request.Scheme}://{HttpContext.Request.Host.Value}/I/{item.ItemId}", // Generate QR String
|
||||
// ProductName = item.Item?.Product?.ProductName,
|
||||
// toUserName = item.NextUser?.FullName,
|
||||
// lastUserName = item.FromUser?.FullName
|
||||
//}).ToList();
|
||||
//Console.WriteLine(Json(itemMovementListWithQR));
|
||||
|
||||
|
||||
@ -736,6 +764,7 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
|
||||
return NotFound("Item movement record not found.");
|
||||
}
|
||||
|
||||
updatedList.LastUser = receiveMovement.LastUser;
|
||||
updatedList.LatestStatus = receiveMovement.LatestStatus;
|
||||
updatedList.receiveDate = receiveMovement.receiveDate;
|
||||
updatedList.Remark = receiveMovement.Remark;
|
||||
@ -969,6 +998,74 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
|
||||
#endregion
|
||||
|
||||
#region ItemRequestAdmin
|
||||
|
||||
[HttpPost("AddRequestMaster")]
|
||||
public async Task<IActionResult> AddRequestMaster([FromBody] RequestModel request)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
try
|
||||
{
|
||||
var findUniqueCode = _centralDbContext.Products.FirstOrDefault(r => r.ProductId == request.ProductId);
|
||||
var findUniqueUser = _centralDbContext.Users.FirstOrDefault(r => r.Id == request.UserId);
|
||||
|
||||
if (!string.IsNullOrEmpty(request.Document))
|
||||
{
|
||||
|
||||
var bytes = Convert.FromBase64String(request.Document);
|
||||
string filePath = "";
|
||||
|
||||
var uniqueAbjad = new string(Enumerable.Range(0, 8).Select(_ => "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"[new Random().Next(36)]).ToArray());
|
||||
|
||||
|
||||
if (IsImage(bytes))
|
||||
{
|
||||
filePath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot/media/inventory/request", findUniqueUser.FullName + " " + findUniqueCode.ModelNo + "(" + uniqueAbjad + ") Request.jpg");
|
||||
request.Document = "/media/inventory/request/" + findUniqueUser.FullName + " " + findUniqueCode.ModelNo + "(" + uniqueAbjad + ") Request.jpg";
|
||||
}
|
||||
else if (IsPdf(bytes))
|
||||
{
|
||||
filePath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot/media/inventory/request", findUniqueUser.FullName + " " + findUniqueCode.ModelNo + "_Request.pdf");
|
||||
request.Document = "/media/inventory/request/" + findUniqueUser.FullName + " " + findUniqueCode.ModelNo + "(" + uniqueAbjad + ") Request.pdf";
|
||||
}
|
||||
else
|
||||
{
|
||||
return BadRequest("Unsupported file format.");
|
||||
}
|
||||
|
||||
await System.IO.File.WriteAllBytesAsync(filePath, bytes);
|
||||
}
|
||||
|
||||
_centralDbContext.Requests.Add(request);
|
||||
await _centralDbContext.SaveChangesAsync();
|
||||
|
||||
var updatedList = await _centralDbContext.Requests.ToListAsync();
|
||||
|
||||
return Json(updatedList.Select(i => new
|
||||
{
|
||||
i.ProductId,
|
||||
i.UserId,
|
||||
i.status,
|
||||
i.StationId,
|
||||
i.RequestQuantity,
|
||||
i.requestDate,
|
||||
i.ProductCategory,
|
||||
i.Document,
|
||||
i.approvalDate,
|
||||
i.remarkMasterInv,
|
||||
i.remarkUser,
|
||||
i.fromStoreItem,
|
||||
i.assignStoreItem,
|
||||
}));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("ItemRequestList")]
|
||||
public async Task<IActionResult> ItemRequestList()
|
||||
{
|
||||
@ -977,6 +1074,7 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
|
||||
return Json(itemRequestList.Select(i => new
|
||||
{
|
||||
i.requestID,
|
||||
i.UserId,
|
||||
productName = i.Product?.ProductName,
|
||||
i.ProductId,
|
||||
productImage = i.Product?.ImageProduct,
|
||||
@ -991,6 +1089,8 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
|
||||
i.approvalDate,
|
||||
i.remarkMasterInv,
|
||||
i.remarkUser,
|
||||
i.assignStoreItem,
|
||||
i.fromStoreItem,
|
||||
|
||||
}));
|
||||
|
||||
@ -1188,6 +1288,22 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
|
||||
return Json(storeList);
|
||||
}
|
||||
|
||||
[HttpPost("StoreSpecificMasterList/{userId}")]
|
||||
public async Task<IActionResult> StoreSpecificMasterList(int userId)
|
||||
{
|
||||
var storeList = await _centralDbContext.InventoryMasters
|
||||
.Where(i => i.UserId == userId)
|
||||
.Select(i => i.StoreId) // Extract only StoreIds
|
||||
.Distinct() // Avoid duplicate queries
|
||||
.ToListAsync();
|
||||
|
||||
var storeSpecific = await _centralDbContext.Stores
|
||||
.Where(s => storeList.Contains(s.Id)) // Fetch all relevant stores at once
|
||||
.ToListAsync();
|
||||
|
||||
return Json(storeSpecific);
|
||||
}
|
||||
|
||||
#endregion Store
|
||||
|
||||
#region AllUser
|
||||
@ -1222,7 +1338,7 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
|
||||
{
|
||||
return NotFound("Item movement record not found.");
|
||||
}
|
||||
|
||||
updatedList.MovementComplete = receiveMovement.MovementComplete;
|
||||
updatedList.LatestStatus = receiveMovement.LatestStatus;
|
||||
updatedList.receiveDate = receiveMovement.receiveDate;
|
||||
updatedList.MovementComplete = receiveMovement.MovementComplete;
|
||||
|
||||
11
Controllers/API/MarineAPI.cs
Normal file
11
Controllers/API/MarineAPI.cs
Normal file
@ -0,0 +1,11 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace PSTW_CentralSystem.Controllers.API
|
||||
{
|
||||
[Route("api/[controller]")]
|
||||
[ApiController]
|
||||
public class MarineAPI : ControllerBase
|
||||
{
|
||||
}
|
||||
}
|
||||
@ -5,6 +5,8 @@ using PSTW_CentralSystem.Models;
|
||||
using Newtonsoft.Json;
|
||||
using System.Text.Json;
|
||||
using System.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Reflection;
|
||||
|
||||
namespace PSTW_CentralSystem.CustomPolicy
|
||||
{
|
||||
@ -32,7 +34,7 @@ namespace PSTW_CentralSystem.CustomPolicy
|
||||
var userRole = await _userManager.GetRolesAsync(currentUser ?? new UserModel());
|
||||
var moduleName = _httpContextAccessor.HttpContext?.GetRouteData().Values["controller"]?.ToString();
|
||||
var pageName = _httpContextAccessor.HttpContext?.GetRouteData().Values["action"]?.ToString();
|
||||
var registeredModule = _authDBContext.ModuleSettings.FirstOrDefault(x => x.ModuleName == moduleName);
|
||||
var registeredModule = await _authDBContext.ModuleSettings.Where(x => x.ModuleName == moduleName).ToListAsync();
|
||||
|
||||
if (checkIfSuperAdmin())
|
||||
{
|
||||
@ -83,30 +85,46 @@ namespace PSTW_CentralSystem.CustomPolicy
|
||||
|
||||
void checkModuleActiveOrNot()
|
||||
{
|
||||
if (registeredModule.ModuleStatus == 0)
|
||||
foreach (var module in registeredModule)
|
||||
{
|
||||
if (module.ModuleStatus == 0)
|
||||
{
|
||||
context.Fail();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void checkModuleHaveRoleOrNot()
|
||||
{
|
||||
var allowedUserTypes = registeredModule?.AllowedUserType ?? "";
|
||||
bool isModuleHaveRole = false;
|
||||
foreach (var module in registeredModule)
|
||||
{
|
||||
var allowedUserTypes = module?.AllowedUserType ?? "";
|
||||
if (allowedUserTypes == "Public" || userRole.Any(role => allowedUserTypes.Contains(role)))
|
||||
{
|
||||
context.Succeed(requirement);
|
||||
return;
|
||||
}
|
||||
else if (currentUser != null && allowedUserTypes == "Registered User" )
|
||||
else if (currentUser != null && allowedUserTypes == "Registered User")
|
||||
{
|
||||
checkMethodAndRole();
|
||||
isModuleHaveRole = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
isModuleHaveRole = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isModuleHaveRole)
|
||||
{
|
||||
context.Fail();
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
checkMethodAndRole();
|
||||
}
|
||||
}
|
||||
|
||||
void checkMethodAndRole()
|
||||
|
||||
146
CustomPolicy/RoleModulePolicy_Backup.cs
Normal file
146
CustomPolicy/RoleModulePolicy_Backup.cs
Normal file
@ -0,0 +1,146 @@
|
||||
//using Microsoft.AspNetCore.Authorization;
|
||||
//using Microsoft.AspNetCore.Identity;
|
||||
//using PSTW_CentralSystem.DBContext;
|
||||
//using PSTW_CentralSystem.Models;
|
||||
//using Newtonsoft.Json;
|
||||
//using System.Text.Json;
|
||||
//using System.Data;
|
||||
|
||||
//namespace PSTW_CentralSystem.CustomPolicy
|
||||
//{
|
||||
// public class RoleModulePolicy : IAuthorizationRequirement
|
||||
// {
|
||||
|
||||
// }
|
||||
// public class RoleModuleHandler : AuthorizationHandler<RoleModulePolicy>
|
||||
// {
|
||||
// private readonly CentralSystemContext _authDBContext;
|
||||
// private readonly UserManager<UserModel> _userManager;
|
||||
// private readonly RoleManager<RoleModel> _roleManager;
|
||||
// private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
// public RoleModuleHandler( CentralSystemContext authDBContext, UserManager<UserModel> userManager, RoleManager<RoleModel> roleManager, IHttpContextAccessor httpContextAccessor)
|
||||
// {
|
||||
// _authDBContext = authDBContext;
|
||||
// _userManager = userManager;
|
||||
// _roleManager = roleManager;
|
||||
// _httpContextAccessor = httpContextAccessor;
|
||||
// }
|
||||
// protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, RoleModulePolicy requirement)
|
||||
// {
|
||||
// // Get the current user
|
||||
// var currentUser = await _userManager.GetUserAsync(context.User);
|
||||
// var userRole = await _userManager.GetRolesAsync(currentUser ?? new UserModel());
|
||||
// var moduleName = _httpContextAccessor.HttpContext?.GetRouteData().Values["controller"]?.ToString();
|
||||
// var pageName = _httpContextAccessor.HttpContext?.GetRouteData().Values["action"]?.ToString();
|
||||
// var registeredModule = _authDBContext.ModuleSettings.FirstOrDefault(x => x.ModuleName == moduleName);
|
||||
|
||||
// if (checkIfSuperAdmin())
|
||||
// {
|
||||
// context.Succeed(requirement);
|
||||
// return;
|
||||
// }
|
||||
// else {
|
||||
// checkModuleExistOrNot();
|
||||
// checkModuleHaveRoleOrNot();
|
||||
// }
|
||||
|
||||
// bool checkIfSuperAdmin()
|
||||
// {
|
||||
// var superAdminRole = _authDBContext.Roles.Where(r => r.Name == "SuperAdmin").FirstOrDefault();
|
||||
// var sysAdminRole = _authDBContext.Roles.Where(r => r.Name == "SystemAdmin").FirstOrDefault();
|
||||
// if (userRole.ToString() != null && userRole.Contains("SuperAdmin") && superAdminRole?.Id == 1)
|
||||
// {
|
||||
// return true;
|
||||
// }
|
||||
// else if (userRole.ToString() != null && userRole.Contains("SystemAdmin") && sysAdminRole?.Id == 2)
|
||||
// {
|
||||
// return true;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
|
||||
// void checkModuleExistOrNot()
|
||||
// {
|
||||
|
||||
// if ( moduleName == "Admin")
|
||||
// {
|
||||
// context.Fail();
|
||||
// return;
|
||||
// }
|
||||
// else if (registeredModule == null)
|
||||
// {
|
||||
// context.Fail();
|
||||
// return;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// checkModuleActiveOrNot();
|
||||
// }
|
||||
// }
|
||||
|
||||
// void checkModuleActiveOrNot()
|
||||
// {
|
||||
// if (registeredModule.ModuleStatus == 0)
|
||||
// {
|
||||
// context.Fail();
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
|
||||
// void checkModuleHaveRoleOrNot()
|
||||
// {
|
||||
// var allowedUserTypes = registeredModule?.AllowedUserType ?? "";
|
||||
// if (allowedUserTypes == "Public" || userRole.Any(role => allowedUserTypes.Contains(role)))
|
||||
// {
|
||||
// context.Succeed(requirement);
|
||||
// return;
|
||||
// }
|
||||
// else if (currentUser != null && allowedUserTypes == "Registered User" )
|
||||
// {
|
||||
// checkMethodAndRole();
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// context.Fail();
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
|
||||
// void checkMethodAndRole()
|
||||
// {
|
||||
|
||||
// // Load all ModuleSettings and process them in memory
|
||||
// var moduleSettings = _authDBContext.ModuleSettings.AsEnumerable();
|
||||
|
||||
// // Check if the method exists in the module settings
|
||||
// var isMethodExist = moduleSettings.FirstOrDefault(m => m.MethodAllowedUserType?.Any(mt => mt.MethodName == pageName) == true);
|
||||
|
||||
|
||||
// if (isMethodExist != null) // Check if the method exists which means method is registered
|
||||
// {
|
||||
// var registeredMethod = moduleSettings.Where(m => m.MethodAllowedUserType != null && m.MethodAllowedUserType.Any(mt => mt.MethodName == pageName)).FirstOrDefault();
|
||||
// var allowedUserTypes = registeredMethod?.MethodAllowedUserType?.Where(mt => mt.MethodName == pageName).Select(mt => mt.AllowedUserTypesArray).FirstOrDefault() ?? Array.Empty<string>();
|
||||
// if (userRole.Any(role => allowedUserTypes.Contains(role)) || allowedUserTypes.Contains("All")) // Check if the user role is allowed, allowing only registered user to access.
|
||||
// {
|
||||
// context.Succeed(requirement);
|
||||
// return;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// context.Fail();
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
// else // No method is registered to allow all method to be accessed
|
||||
// {
|
||||
// context.Succeed(requirement);
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// }
|
||||
//}
|
||||
@ -110,5 +110,6 @@ namespace PSTW_CentralSystem.DBContext
|
||||
public DbSet<ApprovalFlowModel> Approvalflow { get; set; }
|
||||
public DbSet<StaffSignModel> Staffsign { get; set; }
|
||||
|
||||
//testingvhjbnadgfsbgdngffdfdsdfdgfdfdg
|
||||
}
|
||||
}
|
||||
|
||||
118
DBContext/MMSSystemContext.cs
Normal file
118
DBContext/MMSSystemContext.cs
Normal file
@ -0,0 +1,118 @@
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Diagnostics;
|
||||
using PSTW_CentralSystem.Models; // Add this to use the MarineTarball class
|
||||
|
||||
namespace PSTW_CentralSystem.DBContext
|
||||
{
|
||||
public class MMSSystemContext : DbContext
|
||||
{
|
||||
public MMSSystemContext(DbContextOptions<MMSSystemContext> options) : base(options)
|
||||
{
|
||||
}
|
||||
|
||||
// DbSet for tbl_marine_tarball
|
||||
public DbSet<MarineTarball> MarineTarballs { get; set; }
|
||||
public DbSet<MarineStation> MarineStations { get; set; }
|
||||
public DbSet<State> States { get; set; }
|
||||
public DbSet<User> Users { get; set; }
|
||||
public DbSet<Level> Levels { get; set; }
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
base.OnModelCreating(modelBuilder);
|
||||
|
||||
// Configure properties if needed
|
||||
modelBuilder.Entity<MarineTarball>(entity =>
|
||||
{
|
||||
entity.ToTable("tbl_marine_tarball");
|
||||
|
||||
entity.HasKey(e => e.Id); // Primary key
|
||||
entity.Property(e => e.Id).HasColumnName("id");
|
||||
entity.Property(e => e.ReportID).HasColumnName("reportID").HasMaxLength(50);
|
||||
entity.Property(e => e.FirstSampler).HasColumnName("firstSampler").HasMaxLength(50);
|
||||
entity.Property(e => e.SecondSampler).HasColumnName("secondSampler").HasMaxLength(50);
|
||||
entity.Property(e => e.DateSample).HasColumnName("dateSample");
|
||||
entity.Property(e => e.TimeSample).HasColumnName("timeSample");
|
||||
entity.Property(e => e.StationID).HasColumnName("stationID").HasMaxLength(20);
|
||||
entity.Property(e => e.ClassifyID).HasColumnName("classifyID").HasMaxLength(20);
|
||||
entity.Property(e => e.Latitude).HasColumnName("latitude");
|
||||
entity.Property(e => e.Longitude).HasColumnName("longitude");
|
||||
entity.Property(e => e.GetLatitude).HasColumnName("getLatitude");
|
||||
entity.Property(e => e.GetLongitude).HasColumnName("getLongitude");
|
||||
entity.Property(e => e.OptionalName1).HasColumnName("optionalName1");
|
||||
entity.Property(e => e.OptionalName2).HasColumnName("optionalName2");
|
||||
entity.Property(e => e.OptionalName3).HasColumnName("optionalName3");
|
||||
entity.Property(e => e.OptionalName4).HasColumnName("optionalName4");
|
||||
entity.Property(e => e.Timestamp).HasColumnName("timestamp");
|
||||
|
||||
entity.HasOne(t => t.User)
|
||||
.WithMany()
|
||||
.HasForeignKey(t => t.FirstSampler)
|
||||
.HasPrincipalKey(u => u.FullName); // Assuming User has FullName
|
||||
|
||||
//Configure relationship with TarballStation
|
||||
entity.HasOne(m => m.MarineStation)
|
||||
.WithMany()
|
||||
.HasForeignKey(m => m.StationID)
|
||||
.HasPrincipalKey(t => t.StationID);
|
||||
});
|
||||
|
||||
modelBuilder.Entity<MarineStation>(entity =>
|
||||
{
|
||||
entity.ToTable("tbl_marine_station");
|
||||
entity.HasKey(e => e.Id); // Primary key
|
||||
entity.Property(e => e.Id).HasColumnName("id");
|
||||
entity.Property(e => e.StationID).HasColumnName("stationID").HasMaxLength(20);
|
||||
entity.Property(e => e.StateID).HasColumnName("stateID").HasMaxLength(10);
|
||||
entity.Property(e => e.CategoryID).HasColumnName("categoryID").HasMaxLength(10);
|
||||
entity.Property(e => e.LocationName).HasColumnName("locationName").HasMaxLength(50);
|
||||
entity.Property(e => e.Longitude).HasColumnName("longitude").HasColumnType("decimal(10,5)");
|
||||
entity.Property(e => e.Latitude).HasColumnName("latitude").HasColumnType("decimal(10,5)");
|
||||
|
||||
// Configure relationship with State
|
||||
entity.HasOne(t => t.State)
|
||||
.WithMany()
|
||||
.HasForeignKey(t => t.StateID)
|
||||
.HasPrincipalKey(s => s.StateID);
|
||||
});
|
||||
|
||||
modelBuilder.Entity<State>(entity =>
|
||||
{
|
||||
entity.ToTable("tbl_state");
|
||||
entity.HasKey(e => e.Id); // Primary key
|
||||
entity.Property(e => e.Id).HasColumnName("id");
|
||||
entity.Property(e => e.StateID).HasColumnName("stateID").HasMaxLength(20);
|
||||
entity.Property(e => e.StateName).HasColumnName("stateName").HasMaxLength(50);
|
||||
});
|
||||
|
||||
modelBuilder.Entity<User>(entity =>
|
||||
{
|
||||
entity.ToTable("tbl_user");
|
||||
entity.HasKey(e => e.Id); // Primary key
|
||||
entity.Property(e => e.Id).HasColumnName("id");
|
||||
entity.Property(e => e.UserID).HasColumnName("userID").HasMaxLength(20);
|
||||
entity.Property(e => e.FullName).HasColumnName("fullname").HasMaxLength(50);
|
||||
entity.Property(e => e.Username).HasColumnName("username").HasMaxLength(40);
|
||||
entity.Property(e => e.Password).HasColumnName("pwd").HasMaxLength(100);
|
||||
entity.Property(e => e.LevelID).HasColumnName("levelID").HasMaxLength(10);
|
||||
entity.Property(e => e.DeptID).HasColumnName("departID").HasMaxLength(10);
|
||||
|
||||
entity.HasOne(u => u.Level)
|
||||
.WithMany()
|
||||
.HasForeignKey(u => u.LevelID)
|
||||
.HasPrincipalKey(l => l.LevelID);
|
||||
});
|
||||
|
||||
modelBuilder.Entity<Level>().ToTable("tbl_level");
|
||||
|
||||
modelBuilder.Entity<Level>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id); // Primary key
|
||||
entity.Property(e => e.Id).HasColumnName("id");
|
||||
entity.Property(e => e.LevelID).HasColumnName("levelID").HasMaxLength(10);
|
||||
entity.Property(e => e.LevelName).HasColumnName("levelName").HasMaxLength(10);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
1059
Migrations/20250310054721_UpdateTableRequest.Designer.cs
generated
Normal file
1059
Migrations/20250310054721_UpdateTableRequest.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
68
Migrations/20250310054721_UpdateTableRequest.cs
Normal file
68
Migrations/20250310054721_UpdateTableRequest.cs
Normal file
@ -0,0 +1,68 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace PSTW_CentralSystem.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class UpdateTableRequest : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "assignStoreItem",
|
||||
table: "request",
|
||||
type: "longtext",
|
||||
nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "fromStoreItem",
|
||||
table: "request",
|
||||
type: "longtext",
|
||||
nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "AspNetUsers",
|
||||
keyColumn: "Id",
|
||||
keyValue: 1,
|
||||
columns: new[] { "ConcurrencyStamp", "PasswordHash", "SecurityStamp" },
|
||||
values: new object[] { "407727d8-2266-45f2-9b48-ef3a450f09c6", "AQAAAAIAAYagAAAAEDc91vi8/AJwNGigDpnzFh7Iplvlph0VGj9GfG1zI6tY/jM/4f3P0CWVQZ/0oetzVg==", "2faceaca-f491-455a-9f10-3f641a5a7e0d" });
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "AspNetUsers",
|
||||
keyColumn: "Id",
|
||||
keyValue: 2,
|
||||
columns: new[] { "ConcurrencyStamp", "PasswordHash", "SecurityStamp" },
|
||||
values: new object[] { "8065f043-f8ed-4733-aa42-6ee6a1ebb636", "AQAAAAIAAYagAAAAEOmfi3vsFMnCUitXZqLgUaq5+Jqmigy8HrXwNqd8IELW2yvFQAMrfHLvJM5h0c+lfQ==", "46a8accc-305f-42e6-a4a2-376bfec07e84" });
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "assignStoreItem",
|
||||
table: "request");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "fromStoreItem",
|
||||
table: "request");
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "AspNetUsers",
|
||||
keyColumn: "Id",
|
||||
keyValue: 1,
|
||||
columns: new[] { "ConcurrencyStamp", "PasswordHash", "SecurityStamp" },
|
||||
values: new object[] { "d801514b-2c36-4df7-9bb5-1c7e351ed27e", "AQAAAAIAAYagAAAAEBSoDiGEYlobLgzVcffYwvTtm1WnXpqrBBT1yYP+kruV4OTtizW7Sel94qAfqUjGcw==", "6132b0af-6a7f-4f38-8959-d049ed486e8f" });
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "AspNetUsers",
|
||||
keyColumn: "Id",
|
||||
keyValue: 2,
|
||||
columns: new[] { "ConcurrencyStamp", "PasswordHash", "SecurityStamp" },
|
||||
values: new object[] { "14f11e89-bb92-49dd-a8df-ec5b0d49df2d", "AQAAAAIAAYagAAAAEEvcS1SY+9pxZKH/P1l4TaodgW3SFSRfcZ+PnjB3MiMmEUSyYoo64AQtX0bOxFSX2g==", "6dca2498-5150-4369-9923-6f19a48258d4" });
|
||||
}
|
||||
}
|
||||
}
|
||||
1075
Migrations/20250311003003_UpdateRequestTable2.Designer.cs
generated
Normal file
1075
Migrations/20250311003003_UpdateRequestTable2.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
126
Migrations/20250311003003_UpdateRequestTable2.cs
Normal file
126
Migrations/20250311003003_UpdateRequestTable2.cs
Normal file
@ -0,0 +1,126 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace PSTW_CentralSystem.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class UpdateRequestTable2 : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "fromStoreItem",
|
||||
table: "request",
|
||||
type: "int",
|
||||
nullable: true,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "longtext",
|
||||
oldNullable: true)
|
||||
.OldAnnotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "assignStoreItem",
|
||||
table: "request",
|
||||
type: "int",
|
||||
nullable: true,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "longtext",
|
||||
oldNullable: true)
|
||||
.OldAnnotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "AspNetUsers",
|
||||
keyColumn: "Id",
|
||||
keyValue: 1,
|
||||
columns: new[] { "ConcurrencyStamp", "PasswordHash", "SecurityStamp" },
|
||||
values: new object[] { "853a1cf1-3482-47c4-b4b0-7b6a3af6696c", "AQAAAAIAAYagAAAAEBJPP1cHHZZyaGLaskNvSj8sOEizvDa1W2JgxMlYtK18+uhZWvW2RPlqBOhaKc0loQ==", "c911b03d-918a-482f-9c9e-773dc64cdd5d" });
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "AspNetUsers",
|
||||
keyColumn: "Id",
|
||||
keyValue: 2,
|
||||
columns: new[] { "ConcurrencyStamp", "PasswordHash", "SecurityStamp" },
|
||||
values: new object[] { "9ccd914d-6310-4d4e-88c0-e842892e1831", "AQAAAAIAAYagAAAAELxkKuB4hcfQ7Pqe/XCRgygejUsY7X9ByQuS/3FMl50OSzmz9s0byWxGYWQXbyBpGA==", "5b9a67a3-8186-474b-9499-c9c40457fb54" });
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_request_assignStoreItem",
|
||||
table: "request",
|
||||
column: "assignStoreItem");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_request_fromStoreItem",
|
||||
table: "request",
|
||||
column: "fromStoreItem");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_request_Stores_assignStoreItem",
|
||||
table: "request",
|
||||
column: "assignStoreItem",
|
||||
principalTable: "Stores",
|
||||
principalColumn: "Id");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_request_Stores_fromStoreItem",
|
||||
table: "request",
|
||||
column: "fromStoreItem",
|
||||
principalTable: "Stores",
|
||||
principalColumn: "Id");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_request_Stores_assignStoreItem",
|
||||
table: "request");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_request_Stores_fromStoreItem",
|
||||
table: "request");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_request_assignStoreItem",
|
||||
table: "request");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_request_fromStoreItem",
|
||||
table: "request");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "fromStoreItem",
|
||||
table: "request",
|
||||
type: "longtext",
|
||||
nullable: true,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int",
|
||||
oldNullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "assignStoreItem",
|
||||
table: "request",
|
||||
type: "longtext",
|
||||
nullable: true,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int",
|
||||
oldNullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "AspNetUsers",
|
||||
keyColumn: "Id",
|
||||
keyValue: 1,
|
||||
columns: new[] { "ConcurrencyStamp", "PasswordHash", "SecurityStamp" },
|
||||
values: new object[] { "407727d8-2266-45f2-9b48-ef3a450f09c6", "AQAAAAIAAYagAAAAEDc91vi8/AJwNGigDpnzFh7Iplvlph0VGj9GfG1zI6tY/jM/4f3P0CWVQZ/0oetzVg==", "2faceaca-f491-455a-9f10-3f641a5a7e0d" });
|
||||
|
||||
migrationBuilder.UpdateData(
|
||||
table: "AspNetUsers",
|
||||
keyColumn: "Id",
|
||||
keyValue: 2,
|
||||
columns: new[] { "ConcurrencyStamp", "PasswordHash", "SecurityStamp" },
|
||||
values: new object[] { "8065f043-f8ed-4733-aa42-6ee6a1ebb636", "AQAAAAIAAYagAAAAEOmfi3vsFMnCUitXZqLgUaq5+Jqmigy8HrXwNqd8IELW2yvFQAMrfHLvJM5h0c+lfQ==", "46a8accc-305f-42e6-a4a2-376bfec07e84" });
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -422,6 +422,12 @@ namespace PSTW_CentralSystem.Migrations
|
||||
b.Property<DateTime?>("approvalDate")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<int?>("assignStoreItem")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int?>("fromStoreItem")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("remarkMasterInv")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
@ -442,6 +448,10 @@ namespace PSTW_CentralSystem.Migrations
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.HasIndex("assignStoreItem");
|
||||
|
||||
b.HasIndex("fromStoreItem");
|
||||
|
||||
b.ToTable("request");
|
||||
});
|
||||
|
||||
@ -754,16 +764,16 @@ namespace PSTW_CentralSystem.Migrations
|
||||
{
|
||||
Id = 1,
|
||||
AccessFailedCount = 0,
|
||||
ConcurrencyStamp = "d801514b-2c36-4df7-9bb5-1c7e351ed27e",
|
||||
ConcurrencyStamp = "853a1cf1-3482-47c4-b4b0-7b6a3af6696c",
|
||||
Email = "admin@pstw.com.my",
|
||||
EmailConfirmed = true,
|
||||
FullName = "MAAdmin",
|
||||
LockoutEnabled = false,
|
||||
NormalizedEmail = "ADMIN@PSTW.COM.MY",
|
||||
NormalizedUserName = "ADMIN@PSTW.COM.MY",
|
||||
PasswordHash = "AQAAAAIAAYagAAAAEBSoDiGEYlobLgzVcffYwvTtm1WnXpqrBBT1yYP+kruV4OTtizW7Sel94qAfqUjGcw==",
|
||||
PasswordHash = "AQAAAAIAAYagAAAAEBJPP1cHHZZyaGLaskNvSj8sOEizvDa1W2JgxMlYtK18+uhZWvW2RPlqBOhaKc0loQ==",
|
||||
PhoneNumberConfirmed = false,
|
||||
SecurityStamp = "6132b0af-6a7f-4f38-8959-d049ed486e8f",
|
||||
SecurityStamp = "c911b03d-918a-482f-9c9e-773dc64cdd5d",
|
||||
TwoFactorEnabled = false,
|
||||
UserInfoStatus = 1,
|
||||
UserName = "admin@pstw.com.my"
|
||||
@ -772,16 +782,16 @@ namespace PSTW_CentralSystem.Migrations
|
||||
{
|
||||
Id = 2,
|
||||
AccessFailedCount = 0,
|
||||
ConcurrencyStamp = "14f11e89-bb92-49dd-a8df-ec5b0d49df2d",
|
||||
ConcurrencyStamp = "9ccd914d-6310-4d4e-88c0-e842892e1831",
|
||||
Email = "sysadmin@pstw.com.my",
|
||||
EmailConfirmed = true,
|
||||
FullName = "SysAdmin",
|
||||
LockoutEnabled = false,
|
||||
NormalizedEmail = "SYSADMIN@PSTW.COM.MY",
|
||||
NormalizedUserName = "SYSADMIN@PSTW.COM.MY",
|
||||
PasswordHash = "AQAAAAIAAYagAAAAEEvcS1SY+9pxZKH/P1l4TaodgW3SFSRfcZ+PnjB3MiMmEUSyYoo64AQtX0bOxFSX2g==",
|
||||
PasswordHash = "AQAAAAIAAYagAAAAELxkKuB4hcfQ7Pqe/XCRgygejUsY7X9ByQuS/3FMl50OSzmz9s0byWxGYWQXbyBpGA==",
|
||||
PhoneNumberConfirmed = false,
|
||||
SecurityStamp = "6dca2498-5150-4369-9923-6f19a48258d4",
|
||||
SecurityStamp = "5b9a67a3-8186-474b-9499-c9c40457fb54",
|
||||
TwoFactorEnabled = false,
|
||||
UserInfoStatus = 1,
|
||||
UserName = "sysadmin@pstw.com.my"
|
||||
@ -973,10 +983,22 @@ namespace PSTW_CentralSystem.Migrations
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("PSTW_CentralSystem.Areas.Inventory.Models.StoreModel", "Stores")
|
||||
.WithMany()
|
||||
.HasForeignKey("assignStoreItem");
|
||||
|
||||
b.HasOne("PSTW_CentralSystem.Areas.Inventory.Models.StoreModel", "Store")
|
||||
.WithMany()
|
||||
.HasForeignKey("fromStoreItem");
|
||||
|
||||
b.Navigation("Product");
|
||||
|
||||
b.Navigation("Station");
|
||||
|
||||
b.Navigation("Store");
|
||||
|
||||
b.Navigation("Stores");
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
|
||||
83
Models/MarineTarball.cs
Normal file
83
Models/MarineTarball.cs
Normal file
@ -0,0 +1,83 @@
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace PSTW_CentralSystem.Models
|
||||
{
|
||||
public class MarineTarball
|
||||
{
|
||||
public int Id { get; set; } // Maps to 'id'
|
||||
public required string ReportID { get; set; } // Maps to 'reportID'
|
||||
public required string FirstSampler { get; set; } // Maps to 'firstSampler'
|
||||
public required string SecondSampler { get; set; } // Maps to 'secondSampler'
|
||||
public DateTime DateSample { get; set; } // Maps to 'dateSample'
|
||||
public TimeSpan TimeSample { get; set; } // Maps to 'timeSample'
|
||||
public required string StationID { get; set; } // Maps to 'stationID'
|
||||
public required string ClassifyID { get; set; } // Maps to 'classifyID'
|
||||
public required string Latitude { get; set; } // Maps to 'latitude'
|
||||
public required string Longitude { get; set; } // Maps to 'longitude'
|
||||
public double GetLatitude { get; set; } // Maps to 'getLatitude'
|
||||
public double GetLongitude { get; set; } // Maps to 'getLongitude'
|
||||
public DateTime Timestamp { get; set; } // Maps to 'timestamp'
|
||||
public string? OptionalName1 { get; set; }
|
||||
public string? OptionalName2 { get; set; }
|
||||
public string? OptionalName3 { get; set; }
|
||||
public string? OptionalName4 { get; set; }
|
||||
|
||||
public required string PhotoPath1 { get; set; } // Left Side Coastal View
|
||||
public required string PhotoPath2 { get; set; } // Right Side Coastal View
|
||||
public required string PhotoPath3 { get; set; } // Vertical Lines
|
||||
public required string PhotoPath4 { get; set; } // Horizontal Lines
|
||||
public string? PhotoPath5 { get; set; } // optional
|
||||
public string? PhotoPath6 { get; set; } // optional
|
||||
public string? PhotoPath7 { get; set; } // optional
|
||||
public string? PhotoPath8 { get; set; } // optional
|
||||
|
||||
[ForeignKey("StationID")]
|
||||
public required MarineStation MarineStation { get; set; }
|
||||
[ForeignKey("FirstSampler")]
|
||||
public required User User { get; set; }
|
||||
}
|
||||
|
||||
public class MarineStation
|
||||
{
|
||||
public int Id { get; set; } // Maps to 'id'
|
||||
public required string StationID { get; set; } // Maps to 'stationID'
|
||||
public required string StateID { get; set; } // Maps to 'stateID'
|
||||
public required string CategoryID { get; set; } // Maps to 'categoryID'
|
||||
public required string LocationName { get; set; } // Maps to 'locationName'
|
||||
public decimal Longitude { get; set; } // Maps to 'longitude'
|
||||
public decimal Latitude { get; set; } // Maps to 'latitude'
|
||||
|
||||
[ForeignKey("StateID")]
|
||||
public required State State { get; set; }
|
||||
|
||||
}
|
||||
|
||||
public class State
|
||||
{
|
||||
public int Id { get; set; } // Maps to 'id'
|
||||
public required string StateID { get; set; } // Maps to 'stateID'
|
||||
public required string StateName { get; set; } // Maps to 'stateName'
|
||||
}
|
||||
|
||||
public class User
|
||||
{
|
||||
public int Id { get; set; } // Maps to 'id'
|
||||
public required string UserID { get; set; } // Maps to 'userID'
|
||||
public required string FullName { get; set; } // Maps to 'fullname'
|
||||
public required string Username { get; set; } // Maps to 'username'
|
||||
public required string Password { get; set; } // Maps to 'pwd'
|
||||
public required string LevelID { get; set; } // Maps to 'levelID'
|
||||
public required string DeptID { get; set; } // Maps to 'deptID'
|
||||
|
||||
[ForeignKey("LevelID")]
|
||||
public required Level Level { get; set; } // Maps to 'levelID'
|
||||
}
|
||||
|
||||
public class Level
|
||||
{
|
||||
public int Id { get; set; } // Maps to 'id'
|
||||
public required string LevelID { get; set; } // Maps to 'levelID'
|
||||
public required string LevelName { get; set; } // Maps to 'levelName'
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ClosedXML" Version="0.104.2" />
|
||||
<PackageReference Include="DocumentFormat.OpenXml" Version="3.3.0" />
|
||||
<PackageReference Include="Lemonade" Version="1.0.276" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.11" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="8.0.11" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.11" />
|
||||
@ -28,11 +29,19 @@
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="8.0.7" />
|
||||
<PackageReference Include="MySql.EntityFrameworkCore" Version="9.0.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="PDFsharp" Version="6.1.1" />
|
||||
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="8.0.2" />
|
||||
<PackageReference Include="QuestPDF" Version="2025.4.0" />
|
||||
<PackageReference Include="QuestPDF" Version="2025.1.7" />
|
||||
<PackageReference Include="QuestPDF.HTML" Version="1.4.2" />
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="8.0.3" />
|
||||
<PackageReference Include="System.Drawing.Common" Version="9.0.5" />
|
||||
<PackageReference Include="SkiaSharp" Version="3.116.1" />
|
||||
<PackageReference Include="System.Drawing.Common" Version="9.0.3" />
|
||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="9.0.3" />
|
||||
<PackageReference Include="Verify.QuestPDF" Version="2.3.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
19
Program.cs
19
Program.cs
@ -10,6 +10,8 @@ using QuestPDF.Infrastructure;
|
||||
using PSTW_CentralSystem.Areas.OTcalculate.Services;
|
||||
using DocumentFormat.OpenXml.Office2016.Drawing.ChartDrawing;
|
||||
|
||||
using System.Net;
|
||||
|
||||
internal class Program
|
||||
{
|
||||
private static void Main(string[] args)
|
||||
@ -17,6 +19,7 @@ internal class Program
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
var centralConnectionString = builder.Configuration.GetConnectionString("CentralConnnection");
|
||||
Settings.License = LicenseType.Community;
|
||||
|
||||
//var inventoryConnectionString = builder.Configuration.GetConnectionString("InventoryConnection");
|
||||
|
||||
// Add services to the container.
|
||||
@ -25,6 +28,9 @@ internal class Program
|
||||
builder.Services.AddScoped<OvertimeExcel>();
|
||||
|
||||
|
||||
builder.Services.AddScoped<NetworkShareAccess>(provider =>
|
||||
new NetworkShareAccess(@"\\192.168.12.42\images\marine\manual_tarball", "installer", "mms@pstw"));
|
||||
|
||||
|
||||
Log.Logger = new LoggerConfiguration()
|
||||
.ReadFrom.Configuration(builder.Configuration)
|
||||
@ -56,6 +62,12 @@ internal class Program
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
builder.Services.AddDbContext<MMSSystemContext>(options =>
|
||||
options.UseMySql(builder.Configuration.GetConnectionString("MMSDatabase"),
|
||||
new MySqlServerVersion(new Version(8, 0, 0))));
|
||||
|
||||
|
||||
//builder.Services.AddDbContext<InventoryDBContext>(options =>
|
||||
//{
|
||||
// options.UseMySql(inventoryConnectionString, new MySqlServerVersion(new Version(8, 0, 39)),
|
||||
@ -78,6 +90,7 @@ internal class Program
|
||||
|
||||
// Add scope
|
||||
builder.Services.AddScoped<IAuthorizationHandler, RoleModuleHandler>();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
@ -101,6 +114,12 @@ internal class Program
|
||||
app.MapControllerRoute(
|
||||
name: "root",
|
||||
pattern: "{controller=Home}/{action=Index}/{id?}");
|
||||
app.MapControllerRoute(
|
||||
name: "areas",
|
||||
pattern: "{area:exists}/{controller=Home}/{action=Index}/{id?}");
|
||||
app.MapControllerRoute(
|
||||
name: "default",
|
||||
pattern: "{controller=Home}/{action=Index}/{id?}");
|
||||
|
||||
app.Run();
|
||||
}
|
||||
|
||||
@ -430,6 +430,7 @@
|
||||
<i class="mdi mdi-view-dashboard"></i><span class="hide-menu">Module Administration</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
<li class="sidebar-item">
|
||||
@ -486,7 +487,7 @@
|
||||
<a class="sidebar-link has-arrow waves-effect waves-dark"
|
||||
href="javascript:void(0)"
|
||||
aria-expanded="false">
|
||||
<i class="mdi mdi-receipt"></i><span class="hide-menu">Report </span>
|
||||
<i class="mdi mdi-receipt"></i><span class="hide-menu">Report</span>
|
||||
</a>
|
||||
<ul aria-expanded="false" class="collapse first-level">
|
||||
<li class="sidebar-item">
|
||||
@ -499,6 +500,31 @@
|
||||
<i class="mdi mdi-view-dashboard"></i><span class="hide-menu">Inventory Report</span>
|
||||
</a>
|
||||
</li>
|
||||
<!--MMS-->
|
||||
<li class="sidebar-item">
|
||||
<a class="sidebar-link has-arrow waves-effect waves-dark"
|
||||
href="javascript:void(0)" aria-expanded="false">
|
||||
<i class="mdi mdi-view-dashboard"></i><span class="hide-menu">MMS</span>
|
||||
</a>
|
||||
<ul aria-expanded="false" class="collapse first-level">
|
||||
<!-- Marine subsection -->
|
||||
<li class="sidebar-item">
|
||||
<a class="sidebar-link has-arrow waves-effect waves-dark"
|
||||
href="javascript:void(0)" aria-expanded="false">
|
||||
<i class="mdi mdi-view-dashboard"></i><span class="hide-menu">Marine</span>
|
||||
</a>
|
||||
<ul aria-expanded="false" class="collapse second-level">
|
||||
<!-- Tar Ball Sampling Form link -->
|
||||
<li class="sidebar-item">
|
||||
<a class="sidebar-link waves-effect waves-dark"
|
||||
asp-area="MMS" asp-controller="Marine" asp-action="TarBallForm" aria-expanded="false">
|
||||
<i class="mdi mdi-view-dashboard"></i><span class="hide-menu">Tarball Report</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
|
||||
@ -3,10 +3,19 @@
|
||||
//"DefaultConnection": "Server=localhost;uid=root;Password='';Database=web_interface;"
|
||||
//"DefaultConnection": "server=175.136.244.102;user id=root;password=tw_mysql_root;port=3306;database=web_interface"
|
||||
//"CentralConnnection": "Server=192.168.12.12;Port=3306;uid=installer;password='pstw_mysql_installer';database=pstw_cs;", //DB_dev Local connection
|
||||
"CentralConnnection": "Server=219.92.7.60;Port=3307;uid=installer;password='pstw_mysql_installer';database=pstw_cs;" //DB_dev Public connection
|
||||
"CentralConnnection": "Server=219.92.7.60;Port=3307;uid=installer;password='pstw_mysql_installer';database=pstw_cs_prod;" //DB_dev Public connection
|
||||
//"InventoryConnection": "Server=219.92.7.60;Port=3307;uid=installer;password='pstw_mysql_installer';database=pstw_cs_inventory;" //DB_dev connection
|
||||
//"DefaultConnection": "Server=219.92.7.60;Port=3307;uid=intern;password='intern_mysql_acct';database=web_interface;"//DB_dev connection
|
||||
"MMSDatabase": "Server=192.168.12.42;Port=3306;Uid=mmsuser;password=mms@pstw_mysql_root;database=db_mms;ConvertZeroDateTime=True;"
|
||||
},
|
||||
"NetworkCredentials": {
|
||||
"ImageServer": {
|
||||
"Username": "installer",
|
||||
"Password": "mms@pstw",
|
||||
"Domain": "."
|
||||
}
|
||||
},
|
||||
"PhotoBasePath": "\\192.168.12.42\\mms\\marine\\manual_tarball",
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
|
||||
BIN
document.pdf
Normal file
BIN
document.pdf
Normal file
Binary file not shown.
BIN
wwwroot/assets/images/pstw-logo.jpg
Normal file
BIN
wwwroot/assets/images/pstw-logo.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
Loading…
Reference in New Issue
Block a user