diff --git a/Areas/Report/Views/Reporting/InventoryReport.cshtml b/Areas/Report/Views/Reporting/InventoryReport.cshtml index d513fc8..e72d6d9 100644 --- a/Areas/Report/Views/Reporting/InventoryReport.cshtml +++ b/Areas/Report/Views/Reporting/InventoryReport.cshtml @@ -2,7 +2,7 @@ ViewData["Title"] = "Dashboard"; Layout = "~/Views/Shared/_Layout.cshtml"; } -
+

Reporting Dashboard

@@ -64,70 +64,69 @@
-

User Report

+

Report Inquiry

-
-
-
-
-
-

Department

- - -
-
+
+
+
+
+
+
+

Department

+ + +
-
-
-

Date filter

- -
-
+
+
+
+

Date filter

+ +
-
-
-

Position Filter

- -
-
-
-
- +
+
+
+

Position Filter

+ +
+
+ +
-
-
-
-
-
-
-

User List

- -
-
-
- - - - - - - - - - - - - - - - - -
Item NameItem Price
{{item.itemName}}{{item.itemPrice}}
Total{{selectedUser.totalItemPrice}}
-
+
+
+ +
+
+
+
+
+
+

User List

+ +
+
+
+
+
+
+
+

Store

+
+
+
@@ -171,15 +170,17 @@ filteredProduct: [], selectedMonth: null, selectedDepartment: [], - selectedSingleDepartment: null, + selectedSingleDepartment: [], selectedItem: [], selectedCategory: [], selectedPosition: [], usersInfo: [], submitBody: null, formResponse: null, - selectedUser: {}, - userItemsTable:null, + selectedUser: [], + userItemsTable: null, + itemsTable: null, + activeTab: "userTab" } }, mounted() { @@ -188,32 +189,54 @@ this.fetchDepartmentsCompaniesList(); }, watch: { - //watch selectedDepartment. when selectedDepartment is changed, if selectedCategory is null filter productList based on selectedDepartment only. otherwise filter the productList based on selectedDepartment and selectedCategory selectedDepartment() { this.filterProducts(); }, selectedCategory() { this.filterProducts(); }, - }, - computed: { - userItemsTableCompt() { - if (!this.selectedUser || !this.$refs.userItemsTable) { - return null; - } - return $(this.$refs.userItemsTable).DataTable(); - } + selectedUser(newVal) { + if (this.userItemsTable) { + this.userItemsTable.clear().destroy(); + this.userItemsTable = null; + } + // if (newVal && newVal.items) { + const tableData = newVal?.items ? newVal.items : []; + this.$nextTick(() => { + this.userItemsTable = $('#userItemsTable').DataTable({ + data: tableData , + columns: [ + { data: 'uniqueID', title: 'Unique ID' }, + { data: 'itemName', title: 'Item Name' }, + { data: 'itemPrice', title: 'Item Price' }, + + ], + destroy: true, + drawCallback: function () { + if(!newVal) {return}; + const total = newVal.totalItemPrice; + $(this.api().table().node()).find('tbody tr.total-row').remove(); + $(this.api().table().node()).find('tbody').append( + ` + Total + ${total} + ` + ); + }, + }); + }); + } }, methods: { async generateReport(){ - if(this.selectedMonth == null || this.selectedSingleDepartment.length == 0 || this.selectedPosition.length == 0) + if(this.selectedMonth == null || this.selectedSingleDepartment.length == 0 || this.selectedPosition.length == 0) { var msg = []; if(this.selectedMonth == null) { msg.push("Date"); } - if(this.selectedSingleDepartment.length == 0) + if(this.selectedSingleDepartment.length == 0) { msg.push("Department"); } @@ -223,7 +246,7 @@ } alert(msg.length > 1 ? msg.join(" & ") + " cannot be empty" : msg[0] + " cannot be empty"); } - else{ + else{ this.submitBody = { formDate: new Date(this.selectedMonth.year, this.selectedMonth.month + 1, 0, 23, 59, 59).toISOString().slice(0, 19), // selectedCategory: this.selectedCategory, @@ -247,7 +270,17 @@ if (response.ok) { const data = await response.json(); this.formResponse = data - console.log(this.formResponse); + this.initTable(); + if (this.userItemsTable) { + this.userItemsTable.clear().destroy(); + this.userItemsTable = null; + this.selectedUser = null; + } + if (this.itemsTable) { + this.itemsTable.clear().destroy(); + this.itemsTable = null; + } + console.log(data); } else { console.error(`Failed to fetch user: ${response.statusText}`); @@ -363,12 +396,19 @@ ); } }, - initUserItemsTable() { + initTable() { + this.userItemsTable = $('#userItemsTable').DataTable(); this.$nextTick(() => { - if (this.$refs.userItemsTable) { - $(this.$refs.userItemsTable).DataTable().clear().destroy(); - } - this.userItemsTable = $('#userItemsTable').DataTable(); + this.itemsTable = $('#itemsTable').DataTable({ + data: this.formResponse.allProductInStore , + columns: [ + { data: 'productName', title: 'Item Name' }, + { data: 'quantity', title: 'Quantity' }, + { data: 'totalPrice', title: 'Total Item Price' }, + + ], + destroy: true, + }); }); }, // selectedUserFunc() { diff --git a/Controllers/API/ReportingAPI.cs b/Controllers/API/ReportingAPI.cs index 72a6b7f..2f0d1a2 100644 --- a/Controllers/API/ReportingAPI.cs +++ b/Controllers/API/ReportingAPI.cs @@ -31,6 +31,7 @@ namespace PSTW_CentralSystem.Controllers.API { public string? ProductName { get; set; } = default!; public int Quantity { get; set; } + public float TotalPrice { get; set; } } public class ReportQuery @@ -146,13 +147,14 @@ namespace PSTW_CentralSystem.Controllers.API int ToStore = reportQuery.ToStore; var currentProductBalance = new List(); var result = await _centralDbContext.Products - .Select(p => new { p.ProductName, p.QuantityJSON }) + .Include(p => p.Items) + .Select(p => new { p.ProductName, p.QuantityJSON, p.Items}) .Where(p => p.QuantityJSON != null) .ToListAsync(); var quantityJsonDict = new Dictionary>(); foreach (var item in result) { - quantityJsonDict[item.ProductName] = JsonConvert.DeserializeObject>(item.QuantityJSON)!; + quantityJsonDict[item.ProductName] = JsonConvert.DeserializeObject>(item.QuantityJSON!)!; if (!quantityJsonDict.TryGetValue(item.ProductName, out var deptDict)) { continue; @@ -163,7 +165,8 @@ namespace PSTW_CentralSystem.Controllers.API currentProductBalance.Add(new ProductReport { ProductName = item.ProductName, - Quantity = quantity + Quantity = quantity, + TotalPrice = item.Items!.Sum(i => i.ConvertPrice * i.Quantity) }); } } @@ -177,7 +180,8 @@ namespace PSTW_CentralSystem.Controllers.API .Select(m => new { ProductName = m.Item!.Product!.ProductName, - QuantityOut = m.Quantity??0, + QuantityIn = m.Quantity??0, + PriceIn = m.Quantity * m.Item!.ConvertPrice }) .ToListAsync(); @@ -190,6 +194,7 @@ namespace PSTW_CentralSystem.Controllers.API { ProductName = m.Item!.Product!.ProductName, QuantityOut = m.Quantity??0, + PriceOut = m.Quantity * m.Item!.ConvertPrice }) .ToListAsync(); @@ -199,12 +204,14 @@ namespace PSTW_CentralSystem.Controllers.API var movementIn = thisMonthMovementIn.FirstOrDefault(m => m.ProductName == item.ProductName); if (movementIn != null && item.Quantity > 0) { - item.Quantity += movementIn.QuantityOut; + item.Quantity += movementIn.QuantityIn; + item.TotalPrice += (float)movementIn.PriceIn!; } var movementOut = thisMonthMovementOut.FirstOrDefault(m => m.ProductName == item.ProductName); if (movementOut != null && item.Quantity > 0) { item.Quantity -= movementOut.QuantityOut; + item.TotalPrice -= (float)movementOut.PriceOut!; } } @@ -239,6 +246,10 @@ namespace PSTW_CentralSystem.Controllers.API 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 = latestItemMovements.Where(m => m.ToUser == u.Id).Sum(m => m!.Item!.ConvertPrice * m.Quantity), diff --git a/Views/Shared/_Layout.cshtml b/Views/Shared/_Layout.cshtml index 6814de1..bc98cae 100644 --- a/Views/Shared/_Layout.cshtml +++ b/Views/Shared/_Layout.cshtml @@ -922,6 +922,9 @@ + + @* + *@ diff --git a/wwwroot/lib/Vue-Datatables/datatables.net-vue3.d.ts b/wwwroot/lib/Vue-Datatables/datatables.net-vue3.d.ts new file mode 100644 index 0000000..927fa59 --- /dev/null +++ b/wwwroot/lib/Vue-Datatables/datatables.net-vue3.d.ts @@ -0,0 +1,83 @@ +import { AllowedComponentProps } from 'vue'; +import type { Api } from 'datatables.net'; +import { ComponentCustomProps } from 'vue'; +import { ComponentOptionsMixin } from 'vue'; +import type { Config } from 'datatables.net'; +import type { ConfigColumns } from 'datatables.net'; +import { DefineComponent } from 'vue'; +import { ExtractPropTypes } from 'vue'; +import { Plugin as Plugin_2 } from 'vue'; +import { PropType } from 'vue'; +import { Ref } from 'vue'; +import { VNodeProps } from 'vue'; + +declare type __VLS_NonUndefinedable = T extends undefined ? never : T; + +declare type __VLS_TypePropsToRuntimeProps = { + [K in keyof T]-?: {} extends Pick ? { + type: PropType<__VLS_NonUndefinedable>; + } : { + type: PropType; + required: true; + }; +}; + +declare type __VLS_WithTemplateSlots = T & { + new (): { + $slots: S; + }; +}; + +export declare const DataTable: __VLS_WithTemplateSlots, { +/** +* DataTable instance +*/ +dt: Ref | undefined>; +}, unknown, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, string[], string, VNodeProps & AllowedComponentProps & ComponentCustomProps, Readonly>> & { +[x: `on${Capitalize}`]: ((...args: any[]) => any) | undefined; +}, {}>, { + default: (_: {}) => any; +}>; + +declare const _default: InstallableComponent; +export default _default; + +export declare type InstallableComponent = typeof DataTable & { + install: Exclude; +}; + +export { } diff --git a/wwwroot/lib/Vue-Datatables/datatables.net-vue3.js b/wwwroot/lib/Vue-Datatables/datatables.net-vue3.js new file mode 100644 index 0000000..891ac6a --- /dev/null +++ b/wwwroot/lib/Vue-Datatables/datatables.net-vue3.js @@ -0,0 +1 @@ +"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const n=require("vue"),g=["childRow","column-sizing","column-visibility","destroy","draw","error","init","length","order","page","preDraw","preInit","preXhr","processing","requestChild","search","stateLoadParams","stateLoaded","stateSaveParams","xhr","autoFill","preAutoFill","buttons-action","buttons-processing","column-reorder","key","key-blur","key-focus","key-refocus","key-return-submit","responsive-display","responsive-resize","rowgroup-datasrc","pre-row-reorder","row-reorder","row-reordered","dtsb-inserted","deselect","select","select-blur","selectItems","selectStyle","user-select","stateRestore-change"];let m;const D={name:"Datatables.netVue",inheritAttrs:!1,use(u){m=u}},h=n.defineComponent({...D,props:{ajax:null,columns:null,data:null,options:null},emits:g,setup(u,{expose:f}){const i=u,v=n.ref(null),c={},s=n.ref(),w=n.ref([]);n.watch(()=>i.data,t=>{let e=s.value;e&&(p(e),e.clear(),e.rows.add(t).draw(!1))},{deep:!0}),n.onMounted(()=>{var a;const t=n.getCurrentInstance();let e=Object.assign({},i.options)||{};if(i.data&&(e.data=i.data,k(e.data)),i.columns&&(e.columns=i.columns),e.columns&&y(e.columns,t),i.ajax&&(e.ajax=i.ajax),e.columnDefs||(e.columnDefs=[]),t){let l=Object.keys(t.slots);for(let r=0;r{var t;p(s.value),(t=s.value)==null||t.destroy(!0)});function k(t){w.value=t.value?t.value.slice():t.slice()}function b(t){return function(e,a,l,r){let o=r.settings.sTableId+","+r.row+","+r.col;if(!c[o]){let d=n.h("div",t({cellData:e,colIndex:r.col,rowData:l,rowIndex:r.row,type:a}));c[o]=document.createElement("div"),n.render(d,c[o])}return c[o]}}function y(t,e){if(e)for(let l=0;le[0]||(n.setBlockTracking(-1),e[0]=n.createElementVNode("div",{class:"datatable"},[n.createElementVNode("table",n.mergeProps({ref_key:"table",ref:v},t.$attrs,{style:{width:"100%"}}),[n.renderSlot(t.$slots,"default")],16)]),n.setBlockTracking(1),e[0])}}),j=(()=>{const u=h;return u.install=f=>{f.component("Datatables.netVue",u)},u})();exports.DataTable=h;exports.default=j; diff --git a/wwwroot/lib/Vue-Datatables/datatables.net-vue3.mjs b/wwwroot/lib/Vue-Datatables/datatables.net-vue3.mjs new file mode 100644 index 0000000..1be7e92 --- /dev/null +++ b/wwwroot/lib/Vue-Datatables/datatables.net-vue3.mjs @@ -0,0 +1,166 @@ +import { defineComponent as x, ref as p, watch as j, onMounted as I, getCurrentInstance as A, unref as E, onBeforeUnmount as O, setBlockTracking as h, createElementVNode as g, mergeProps as T, renderSlot as R, h as S, render as _ } from "vue"; +const w = [ + "childRow", + "column-sizing", + "column-visibility", + "destroy", + "draw", + "error", + "init", + "length", + "order", + "page", + "preDraw", + "preInit", + "preXhr", + "processing", + "requestChild", + "search", + "stateLoadParams", + "stateLoaded", + "stateSaveParams", + "xhr", + "autoFill", + "preAutoFill", + "buttons-action", + "buttons-processing", + "column-reorder", + "key", + "key-blur", + "key-focus", + "key-refocus", + "key-return-submit", + "responsive-display", + "responsive-resize", + "rowgroup-datasrc", + "pre-row-reorder", + "row-reorder", + "row-reordered", + "dtsb-inserted", + "deselect", + "select", + "select-blur", + "selectItems", + "selectStyle", + "user-select", + "stateRestore-change" +]; +let m; +const C = { + name: "Datatables.netVue", + inheritAttrs: !1, + use(i) { + m = i; + } +}, L = /* @__PURE__ */ x({ + ...C, + props: { + ajax: null, + columns: null, + data: null, + options: null + }, + emits: w, + setup(i, { expose: c }) { + const o = i, y = p(null), u = {}, l = p(), D = p([]); + j( + () => o.data, + (t) => { + let e = l.value; + e && (f(e), e.clear(), e.rows.add(t).draw(!1)); + }, + { + deep: !0 + } + ), I(() => { + var a; + const t = A(); + let e = Object.assign({}, o.options) || {}; + if (o.data && (e.data = o.data, k(e.data)), o.columns && (e.columns = o.columns), e.columns && v(e.columns, t), o.ajax && (e.ajax = o.ajax), e.columnDefs || (e.columnDefs = []), t) { + let n = Object.keys(t.slots); + for (let r = 0; r < n.length; r++) { + let s = n[r]; + if (s.match(/^column\-/)) { + let d = s.replace("column-", ""); + e.columnDefs.push({ + target: d.match(/^\d+$/) ? parseInt(d) : d + ":name", + render: "#" + s + }); + } + } + v(e.columnDefs, t); + } + if (!m) + throw new Error( + "DataTables library not set. See https://datatables.net/tn/19 for details." + ); + l.value = new m(E(y), e), (a = l.value) == null || a.on("preXhr", function() { + f(l.value); + }); + for (let n of w) + l.value && t && l.value.on(n, function() { + var r = Array.from(arguments), s = r.shift(); + r.unshift({ event: s, dt: l }), r.unshift(n), t.emit.apply(t, r); + }); + }), O(() => { + var t; + f(l.value), (t = l.value) == null || t.destroy(!0); + }); + function k(t) { + D.value = t.value ? t.value.slice() : t.slice(); + } + function b(t) { + return function(e, a, n, r) { + let s = r.settings.sTableId + "," + r.row + "," + r.col; + if (!u[s]) { + let d = S("div", t({ + cellData: e, + colIndex: r.col, + rowData: n, + rowIndex: r.row, + type: a + })); + u[s] = document.createElement("div"), _(d, u[s]); + } + return u[s]; + }; + } + function v(t, e) { + if (e) + for (let n = 0; n < t.length; n++) { + let r = t[n]; + if (typeof r.render == "string" && r.render.charAt(0) === "#") { + var a = r.render.replace("#", ""); + e.slots[a] && (r.render = b(e.slots[a])); + } else if (typeof r.render == "object" && typeof r.render.display == "string" && r.render.display.charAt(0) === "#") { + var a = r.render.display.replace("#", ""); + e.slots[a] && (r.render.display = b(e.slots[a])); + } + } + } + function f(t) { + let e = Object.keys(u), a = t.table().node().id; + for (var n = 0; n < e.length; n++) + e[n].indexOf(a + ",") === 0 && delete u[e[n]]; + } + return c({ + dt: l + }), (t, e) => e[0] || (h(-1), e[0] = g("div", { class: "datatable" }, [ + g("table", T({ + ref_key: "table", + ref: y + }, t.$attrs, { style: { width: "100%" } }), [ + R(t.$slots, "default") + ], 16) + ]), h(1), e[0]); + } +}), V = /* @__PURE__ */ (() => { + const i = L; + return i.install = (c) => { + c.component("Datatables.netVue", i); + }, i; +})(); +export { + L as DataTable, + V as default +}; diff --git a/wwwroot/lib/Vue-Datatables/datatables.net-vue3.umd.js b/wwwroot/lib/Vue-Datatables/datatables.net-vue3.umd.js new file mode 100644 index 0000000..7c098a1 --- /dev/null +++ b/wwwroot/lib/Vue-Datatables/datatables.net-vue3.umd.js @@ -0,0 +1 @@ +(function(l,n){typeof exports=="object"&&typeof module<"u"?n(exports,require("vue")):typeof define=="function"&&define.amd?define(["exports","vue"],n):(l=typeof globalThis<"u"?globalThis:l||self,n((l.datatables=l.datatables||{},l.datatables["net-vue3"]={}),l.Vue))})(this,function(l,n){"use strict";const b=["childRow","column-sizing","column-visibility","destroy","draw","error","init","length","order","page","preDraw","preInit","preXhr","processing","requestChild","search","stateLoadParams","stateLoaded","stateSaveParams","xhr","autoFill","preAutoFill","buttons-action","buttons-processing","column-reorder","key","key-blur","key-focus","key-refocus","key-return-submit","responsive-display","responsive-resize","rowgroup-datasrc","pre-row-reorder","row-reorder","row-reordered","dtsb-inserted","deselect","select","select-blur","selectItems","selectStyle","user-select","stateRestore-change"];let p;const k={name:"Datatables.netVue",inheritAttrs:!1,use(c){p=c}},h=n.defineComponent({...k,props:{ajax:null,columns:null,data:null,options:null},emits:b,setup(c,{expose:m}){const d=c,g=n.ref(null),u={},o=n.ref(),j=n.ref([]);n.watch(()=>d.data,t=>{let e=o.value;e&&(y(e),e.clear(),e.rows.add(t).draw(!1))},{deep:!0}),n.onMounted(()=>{var a;const t=n.getCurrentInstance();let e=Object.assign({},d.options)||{};if(d.data&&(e.data=d.data,x(e.data)),d.columns&&(e.columns=d.columns),e.columns&&v(e.columns,t),d.ajax&&(e.ajax=d.ajax),e.columnDefs||(e.columnDefs=[]),t){let s=Object.keys(t.slots);for(let r=0;r{var t;y(o.value),(t=o.value)==null||t.destroy(!0)});function x(t){j.value=t.value?t.value.slice():t.slice()}function w(t){return function(e,a,s,r){let i=r.settings.sTableId+","+r.row+","+r.col;if(!u[i]){let f=n.h("div",t({cellData:e,colIndex:r.col,rowData:s,rowIndex:r.row,type:a}));u[i]=document.createElement("div"),n.render(f,u[i])}return u[i]}}function v(t,e){if(e)for(let s=0;se[0]||(n.setBlockTracking(-1),e[0]=n.createElementVNode("div",{class:"datatable"},[n.createElementVNode("table",n.mergeProps({ref_key:"table",ref:g},t.$attrs,{style:{width:"100%"}}),[n.renderSlot(t.$slots,"default")],16)]),n.setBlockTracking(1),e[0])}}),D=(()=>{const c=h;return c.install=m=>{m.component("Datatables.netVue",c)},c})();l.DataTable=h,l.default=D,Object.defineProperties(l,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});