This commit is contained in:
MOHD ARIFF 2026-03-12 14:03:59 +08:00
parent 8890cba87e
commit 45afdec610
3 changed files with 216 additions and 82 deletions

View File

@ -66,67 +66,82 @@
<div class="card-header"> <div class="card-header">
<h3 class="card-title">Report Inquiry</h3> <h3 class="card-title">Report Inquiry</h3>
</div> </div>
</div> <div class="card-body">
<div class="card-body"> <div v-if="reportData">
<div v-if="reportData"> <div class="row justify-content-center">
<div class="row justify-content-center"> <div class="col-3">
<div class="col-3"> <div class="row col-10">
<div class="row col-10"> <h4>Department</h4>
<h4>Department</h4> <multiselect v-model="selectedSingleDepartment" :options="compDeptList" :multiple="false" group-values="departments" group-label="companyName"
<multiselect v-model="selectedSingleDepartment" :options="compDeptList" :multiple="false" group-values="departments" group-label="companyName" :group-select="false" placeholder="Search Department" track-by="departmentId" label="departmentName">
:group-select="false" placeholder="Search Department" track-by="departmentId" label="departmentName"> </multiselect>
</multiselect> <div class=""><button class="btn btn-danger" v-on:click="selectedSingleDepartment = []">Clear</button></div>
<div class=""><button class="btn btn-danger" v-on:click="selectedSingleDepartment = []">Clear</button></div> </div>
</div> </div>
</div> <div class="col-3">
<div class="col-3"> <div class="row col-10">
<div class="row col-10"> <h4>Date filter</h4>
<h4>Date filter</h4> <vue-date-picker v-model="selectedMonth" month-picker auto-apply></vue-date-picker>
<vue-date-picker v-model="selectedMonth" month-picker auto-apply></vue-date-picker> <div class=""><button class="btn btn-danger" v-on:click="selectedMonth = null">Clear</button></div>
<div class=""><button class="btn btn-danger" v-on:click="selectedMonth = null">Clear</button></div> </div>
</div> </div>
</div> <div class="col-12">
<div class="col-3"> <button class="btn btn-primary" v-on:click="generateReport">Generate Report</button>
<div class="row col-10">
<h4>Position Filter</h4>
<multiselect v-model="selectedPosition" :options="positionList" :multiple="false" placeholder="Search Position" track-by="positionId" label="positionName"></multiselect>
<div class=""><button class="btn btn-danger" v-on:click="selectedPosition = []">Clear</button></div>
</div> </div>
</div> </div>
<div class="col-12">
<button class="btn btn-primary" v-on:click="generateReport">Generate Report</button>
</div>
</div> </div>
</div> </div>
</div> <hr />
<hr /> <ul class="nav nav-tabs nav-tabs-outline" id="reportTabs" role="tablist">
<ul class="nav nav-tabs nav-tabs-outline" id="reportTabs" role="tablist"> <li class="nav-item">
<li class="nav-item"> <button :class="['nav-link', {active: activeTab === 'userTab'}]" v-on:click="activeTab = 'userTab'">User</button>
<button :class="['nav-link', {active: activeTab === 'userTab'}]" v-on:click="activeTab = 'userTab'">User</button> </li>
</li> <li class="nav-item">
<li class="nav-item"> <button :class="['nav-link', {active: activeTab === 'stationTab'}]" v-on:click="activeTab = 'stationTab'">Station</button>
<button :class="['nav-link', {active: activeTab === 'storeTab'}]" v-on:click="activeTab = 'storeTab'">Store</button> </li>
</li> <li class="nav-item">
</ul> <button :class="['nav-link', {active: activeTab === 'storeTab'}]" v-on:click="activeTab = 'storeTab'">Store</button>
<hr /> </li>
<div class="card-body"> <li class="nav-item">
<div v-if="reportData"> <button :class="['nav-link', {active: activeTab === 'allStoreTab'}]" v-on:click="activeTab = 'allStoreTab'">All Item</button>
<div class="row"> </li>
<div v-if="formResponse" v-show="activeTab === 'userTab'"> </ul>
<div class="col-3"> <hr />
<h4>User List</h4> <div class="card-body">
<multiselect v-model="selectedUser" :options="formResponse.userItemBalance" :multiple="false" placeholder="Select User" track-by="userId" label="userFullName"></multiselect> <div v-if="reportData">
<div class="row">
<div v-if="formResponse" v-show="activeTab === 'userTab'">
<div class="col-3">
<h4>User List</h4>
<multiselect v-model="selectedUser" :options="formResponse.userItemBalance" :multiple="false" placeholder="Select User" track-by="userId" label="userFullName"></multiselect>
</div>
<div class="col-9">
<table id="userItemsTable" class="display table table-bordered table-hover"></table>
</div>
</div> </div>
<div class="col-9"> <div v-if="formResponse" v-show="activeTab === 'stationTab'">
<table id="userItemsTable" class="display table table-bordered"></table> <div class="col-3">
<h4>Store</h4>
</div>
<div class="col-9">
<table id="stationItemsTable" class="display table table-bordered table-hover"></table>
</div>
</div> </div>
</div> <div v-if="formResponse" v-show="activeTab === 'storeTab'">
<div v-if="formResponse" v-show="activeTab === 'storeTab'"> <div class="col-3">
<div class="col-3"> <h4>Store</h4>
<h4>Store</h4> </div>
<div class="col-9">
<table id="storeItemsTable" class="display table table-bordered table-hover"></table>
</div>
</div> </div>
<div class="col-9"> <div v-if="formResponse" v-show="activeTab === 'allStoreTab'">
<table id="itemsTable" class="display table table-bordered"></table> <div class="col-3">
<h4>Store</h4>
</div>
<div class="col-9">
<table id="storeAllItemsTable" class="display table table-bordered table-hover"></table>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -173,13 +188,14 @@
selectedSingleDepartment: [], selectedSingleDepartment: [],
selectedItem: [], selectedItem: [],
selectedCategory: [], selectedCategory: [],
selectedPosition: [],
usersInfo: [], usersInfo: [],
submitBody: null, submitBody: null,
formResponse: null, formResponse: null,
selectedUser: [], selectedUser: [],
userItemsTable: null, userItemsTable: null,
itemsTable: null, stationItemsTable: null,
storeItemsTable: null,
storeAllItemsTable: null,
activeTab: "userTab" activeTab: "userTab"
} }
}, },
@ -202,6 +218,7 @@
} }
// if (newVal && newVal.items) { // if (newVal && newVal.items) {
const tableData = newVal?.items ? newVal.items : []; const tableData = newVal?.items ? newVal.items : [];
var app = this;
this.$nextTick(() => { this.$nextTick(() => {
this.userItemsTable = $('#userItemsTable').DataTable({ this.userItemsTable = $('#userItemsTable').DataTable({
data: tableData , data: tableData ,
@ -223,13 +240,18 @@
</tr>` </tr>`
); );
}, },
"createdRow": function (row, data, dataIndex) {
$(row).on('click', function () {
app.fetchItemMovement(data.uniqueID);
});
}
}); });
}); });
} }
}, },
methods: { methods: {
async generateReport(){ async generateReport(){
if(this.selectedMonth == null || this.selectedSingleDepartment.length == 0 || this.selectedPosition.length == 0) if(this.selectedMonth == null || this.selectedSingleDepartment.length == 0)
{ {
var msg = []; var msg = [];
if(this.selectedMonth == null) if(this.selectedMonth == null)
@ -240,10 +262,6 @@
{ {
msg.push("Department"); msg.push("Department");
} }
if(this.selectedPosition.length == 0)
{
msg.push("Position");
}
alert(msg.length > 1 ? msg.join(" & ") + " cannot be empty" : msg[0] + " cannot be empty"); alert(msg.length > 1 ? msg.join(" & ") + " cannot be empty" : msg[0] + " cannot be empty");
} }
else{ else{
@ -251,9 +269,6 @@
formDate: new Date(this.selectedMonth.year, this.selectedMonth.month + 1, 0, 23, 59, 59).toISOString().slice(0, 19), formDate: new Date(this.selectedMonth.year, this.selectedMonth.month + 1, 0, 23, 59, 59).toISOString().slice(0, 19),
// selectedCategory: this.selectedCategory, // selectedCategory: this.selectedCategory,
deptId: this.selectedSingleDepartment.departmentCode, deptId: this.selectedSingleDepartment.departmentCode,
toUser: this.selectedPosition.positionId == 1 ? 1 : 0,
toStation: this.selectedPosition.positionId == 2 ? 1 : 0,
toStore: this.selectedPosition.positionId == 3 ? 1 : 0,
} }
this.fetchCurrentReport(); this.fetchCurrentReport();
} }
@ -276,11 +291,14 @@
this.userItemsTable = null; this.userItemsTable = null;
this.selectedUser = null; this.selectedUser = null;
} }
if (this.itemsTable) { if (this.stationItemsTable) {
this.itemsTable.clear().destroy(); this.stationItemsTable.clear().destroy();
this.itemsTable = null; this.stationItemsTable = null;
}
if (this.storeItemsTable) {
this.storeItemsTable.clear().destroy();
this.storeItemsTable = null;
} }
console.log(data);
} }
else { else {
console.error(`Failed to fetch user: ${response.statusText}`); console.error(`Failed to fetch user: ${response.statusText}`);
@ -397,10 +415,54 @@
} }
}, },
initTable() { initTable() {
this.userItemsTable = $('#userItemsTable').DataTable();
this.$nextTick(() => { this.$nextTick(() => {
this.itemsTable = $('#itemsTable').DataTable({ this.userItemsTable = $('#userItemsTable').DataTable();
data: this.formResponse.allProductInStore , const stationTableData = this.formResponse.stationItemBalance ? this.formResponse.stationItemBalance : [];
this.stationItemsTable = $('#stationItemsTable').DataTable({
data: stationTableData.items,
columns: [
{ data: 'itemName', title: 'Item Name' },
{ data: 'uniqueID', title: 'Unique ID' },
{ data: 'quantity', title: 'Quantity' },
{ data: 'itemPrice', title: 'Item Price' },
],
destroy: true,
drawCallback: function () {
const total = stationTableData.totalItemPrice;
$(this.api().table().node()).find('tbody tr.total-row').remove();
$(this.api().table().node()).find('tbody').append(
`<tr class="total-row">
<td colspan="3"><strong>Total</strong></td>
<td class="text-end"><strong>${total}</strong></td>
</tr>`
);
},
});
const storeTableData = this.formResponse.storeItemBalance ? this.formResponse.storeItemBalance : [];
this.storeItemsTable = $('#storeItemsTable').DataTable({
data: storeTableData.items,
columns: [
{ data: 'itemName', title: 'Item Name' },
{ data: 'uniqueID', title: 'Unique ID' },
{ data: 'quantity', title: 'Quantity' },
{ data: 'itemPrice', title: 'Item Price' },
],
destroy: true,
drawCallback: function () {
const total = storeTableData.totalItemPrice;
$(this.api().table().node()).find('tbody tr.total-row').remove();
$(this.api().table().node()).find('tbody').append(
`<tr class="total-row">
<td colspan="3"><strong>Total</strong></td>
<td class="text-end"><strong>${total}</strong></td>
</tr>`
);
},
});
const storeAllTableData = this.formResponse.allProductInStore ? this.formResponse.allProductInStore : [];
this.storeAllItemsTable = $('#storeAllItemsTable').DataTable({
data: storeAllTableData ,
columns: [ columns: [
{ data: 'productName', title: 'Item Name' }, { data: 'productName', title: 'Item Name' },
{ data: 'quantity', title: 'Quantity' }, { data: 'quantity', title: 'Quantity' },
@ -411,6 +473,27 @@
}); });
}); });
}, },
async fetchItemMovement(data){
try {
const response = await fetch(`/ReportingAPI/GetItemMovement/`, {
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data),
method: 'POST',
});
if (response.ok) {
const data = await response.json();
this.itemMovement = data;
}
else {
console.error(`Failed to fetch item movement: ${response.statusText}`);
}
}
catch (error) {
console.error('There was a problem with the fetch operation:', error);
}
}
// selectedUserFunc() { // selectedUserFunc() {
// if (this.userItemsTable) { // if (this.userItemsTable) {
// this.userItemsTable.destroy(); // this.userItemsTable.destroy();

View File

@ -1920,8 +1920,10 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
[HttpPost("UserList")] [HttpPost("UserList")]
public async Task<IActionResult> UserList() public async Task<IActionResult> UserList()
{ {
//get current user
var userList = await _centralDbContext.Users.Include(i => i.Department).ToListAsync(); var thisUser = await _userManager.GetUserAsync(User);
var thisUserDept = thisUser!.departmentId;
var userList = await _centralDbContext.Users.Include(i => i.Department).Where(i => i.departmentId == thisUserDept).ToListAsync();
return Json(userList.Select(i => new return Json(userList.Select(i => new
{ {
i.Id, i.Id,

View File

@ -38,9 +38,6 @@ namespace PSTW_CentralSystem.Controllers.API
{ {
public string? FormDate { get; set; } public string? FormDate { get; set; }
public string? DeptId { get; set; } public string? DeptId { get; set; }
public int ToUser { get; set; }
public int ToStation { get; set; }
public int ToStore { get; set; }
} }
#region ItemReport #region ItemReport
@ -142,9 +139,6 @@ namespace PSTW_CentralSystem.Controllers.API
{ {
string FormDate = reportQuery.FormDate!; string FormDate = reportQuery.FormDate!;
string DeptId = reportQuery.DeptId!; string DeptId = reportQuery.DeptId!;
int ToUser = reportQuery.ToUser;
int ToStation = reportQuery.ToStation;
int ToStore = reportQuery.ToStore;
var currentProductBalance = new List<ProductReport>(); var currentProductBalance = new List<ProductReport>();
var result = await _centralDbContext.Products var result = await _centralDbContext.Products
.Include(p => p.Items) .Include(p => p.Items)
@ -226,9 +220,9 @@ namespace PSTW_CentralSystem.Controllers.API
.Where(s => s.Department!.DepartmentCode == DeptId) .Where(s => s.Department!.DepartmentCode == DeptId)
.ToListAsync(); .ToListAsync();
var departmentStores = await _centralDbContext.Stores var departmentStores = await _centralDbContext.Departments
.Include(s => s.Company) .Include(s => s.Company)
.Where(s => s.Department!.DepartmentCode == DeptId) .Where(s => s.DepartmentCode == DeptId)
.ToListAsync(); .ToListAsync();
var latestItemMovements = await _centralDbContext.ItemMovements var latestItemMovements = await _centralDbContext.ItemMovements
@ -237,6 +231,7 @@ namespace PSTW_CentralSystem.Controllers.API
.Include(m => m.NextUser) .Include(m => m.NextUser)
.Include(m => m.NextStation) .Include(m => m.NextStation)
.Include(m => m.NextStore) .Include(m => m.NextStore)
.Where(m => m.MovementComplete == true)
.GroupBy(m => m.ItemId) .GroupBy(m => m.ItemId)
.Select(g => g .Select(g => g
.Where(m => m.Date <= parsedDate) .Where(m => m.Date <= parsedDate)
@ -269,7 +264,7 @@ namespace PSTW_CentralSystem.Controllers.API
UniqueID = m.Item.UniqueID, UniqueID = m.Item.UniqueID,
}) })
.ToList(), .ToList(),
TotalItemPrice = latestItemMovements.Where(m => m != null && m.ToUser == u.Id).Sum(m => m!.Item!.ConvertPrice * m.Quantity), TotalItemPrice = latestItemMovements.Where(m => m != null && m.ToUser == u.Id).Select(m => m!.Item!.ConvertPrice).Sum(),
}) })
.Where(u => u.Items.Count > 0).ToList(); .Where(u => u.Items.Count > 0).ToList();
@ -292,15 +287,39 @@ namespace PSTW_CentralSystem.Controllers.API
UniqueID = m.Item.UniqueID, UniqueID = m.Item.UniqueID,
}) })
.ToList(), .ToList(),
TotalItemPrice = latestItemMovements.Where(m => m != null && m.ToStation == u.StationId).Sum(m => m!.Item!.ConvertPrice * m.Quantity), TotalItemPrice = latestItemMovements.Where(m => m != null && m.ToStation == u.StationId).Select(m => m!.Item!.ConvertPrice).Sum(),
}) })
.Where(u => u.Items.Count > 0).ToList(); .Where(u => u.Items.Count > 0).FirstOrDefault();
var storeItemMovements = departmentStores
.Select(u => new
{
Department = u.DepartmentName,
Items = latestStationItemMovements
.Where(m => m != null && m.ToStore > 0 && m.NextStore!.StoreName.Contains(u.DepartmentName))
.Select(m => new
{
ItemId = m!.ItemId,
ItemName = m.Item!.Product!.ProductName,
Quantity = m.Quantity,
ItemPrice = m.Item!.ConvertPrice,
PO = m.Item!.PONo,
DO = m.Item!.DONo,
SerialNumber = m.Item.SerialNumber,
UniqueID = m.Item.UniqueID,
})
.ToList(),
TotalItemPrice = latestStationItemMovements.Where(m => m != null && m.ToStore > 0 && m.NextStore!.StoreName.Contains(u.DepartmentName)).Select(m => m!.Item!.ConvertPrice).Sum(),
})
.Where(u => u.Items.Count > 0).FirstOrDefault();
var report = new var report = new
{ {
allProductInStore = currentProductBalance, allProductInStore = currentProductBalance,
userItemBalance = usersItemMovements, userItemBalance = usersItemMovements,
stationItemBalance = stationItemMovements, stationItemBalance = stationItemMovements,
storeItemBalance = storeItemMovements,
}; };
return Json(report); return Json(report);
@ -311,7 +330,37 @@ namespace PSTW_CentralSystem.Controllers.API
} }
} }
#endregion #endregion
#region Item Movement report
[HttpPost("GetItemMovement")]
public async Task<IActionResult> GetItemMovement([FromBody] String data)
{
try
{
if (string.IsNullOrEmpty(data))
{
return BadRequest("Invalid Unique ID");
}
var itemMovement = await _centralDbContext.ItemMovements
.Include(im => im.Item)
.Where(im => im.Item!.UniqueID == data)
.OrderByDescending(im => im.Id)
.Select(im => new
{
From = im.LastUser != null ? im.FromUser!.FullName : im.LastStation != null ? im.FromStation!.StationName : im.LastStore != null ? im.FromStore!.StoreName : null,
To = im.ToUser != null ? im.NextUser!.FullName : im.ToStation != null ? im.NextStation!.StationName : im.ToStore != null ? im.NextStore!.StoreName : null,
Date = im.Date,
MovementStatus = im.MovementComplete
})
.ToListAsync();
return Json(itemMovement);
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
#endregion
#region Report Inventory ii #region Report Inventory ii
[HttpPost("GetInventoryReport")] [HttpPost("GetInventoryReport")]
public async Task<IActionResult> GetInventoryReport([FromBody] ReportFilterDTO filter) public async Task<IActionResult> GetInventoryReport([FromBody] ReportFilterDTO filter)