diff --git a/custom_addons/dashboard/models/stock_dashboard.py b/custom_addons/dashboard/models/stock_dashboard.py index 7d3fbc397..764bde9b2 100644 --- a/custom_addons/dashboard/models/stock_dashboard.py +++ b/custom_addons/dashboard/models/stock_dashboard.py @@ -140,6 +140,12 @@ class SamashtiDashboard(models.AbstractModel): else: return [] + @api.model + def get_products_data(self): + all_prod = self.env['product.product'].search([('type', '=', 'consu')]) + all_category = all_prod.category_id + + @api.model def get_sale_margin_data(self, from_date, to_date): # Get user's timezone diff --git a/custom_addons/dashboard/static/src/components/pqgrid_dashboard/pqgrid_consumption_dashboard.js b/custom_addons/dashboard/static/src/components/pqgrid_dashboard/pqgrid_consumption_dashboard.js index 08fe37692..5103b95ab 100644 --- a/custom_addons/dashboard/static/src/components/pqgrid_dashboard/pqgrid_consumption_dashboard.js +++ b/custom_addons/dashboard/static/src/components/pqgrid_dashboard/pqgrid_consumption_dashboard.js @@ -4,6 +4,18 @@ import { useService } from "@web/core/utils/hooks"; import { registry } from "@web/core/registry"; import { standardActionServiceProps } from "@web/webclient/actions/action_service"; +function formatFloat(value) { + if (value === null || value === undefined || isNaN(value)) { + return "0.00"; + } + return parseFloat(value).toLocaleString('en-US', { + minimumFractionDigits: 2, + maximumFractionDigits: 2 + }); +} + + + export class ConsumptionDashboard extends Component { static props = { ...standardActionServiceProps, @@ -102,7 +114,7 @@ export class ConsumptionDashboard extends Component { } // Check for 'Outer bag' in product name (case insensitive) if (row.product_name && row.product_name.toLowerCase().includes('outer bag')) { - return 'Outer bag'; + return 'Outer Bags'; } // Default to first 2 characters of product code if (row.product_code && row.product_code.length >= 2) { @@ -170,12 +182,12 @@ export class ConsumptionDashboard extends Component { width: 120, filter: { type: 'textbox', condition: 'contain', listeners: ['keyup'] } }, - { title: "Date", dataIndx: "date", width: 150 }, + { title: "Date", dataIndx: "date", width: 100 }, { title: "Product Name", dataIndx: "product_name", - width: 250, + width: 300, filter: { type: 'textbox', condition: 'contain', listeners: ['keyup'] } }, { @@ -205,7 +217,7 @@ export class ConsumptionDashboard extends Component { width: 80 }, { - title: "Value", + title: "Value (INR)", dataIndx: "value", width: 120, dataType: "float", @@ -218,7 +230,7 @@ export class ConsumptionDashboard extends Component { const getColumnPriority = (prefix) => { const priorityMap = { 'Laminate': 1, - 'Outer bag': 2, + 'Outer Bags': 2, 'RM':3, 'PM':4, 'ST':5, @@ -250,19 +262,22 @@ export class ConsumptionDashboard extends Component { // Create dynamic columns const dynamicColumns = sortedPrefixes.map(prefix => ({ dataIndx: prefix, - title: prefix+" ₹" , + title: (() => { + let label; + + if (prefix === 'FU') label = 'Fuel'; + else if (prefix === 'MA') label = 'Maintenance'; + else if (prefix === 'MP') label = 'Manpower'; + else if (prefix === 'PW') label = 'Power'; + else label = prefix; + + return label + " (INR)"; + })(), width: 120, align: "right", dataType: "float", format: "#,###.00", summary: { type: "sum" }, - render: function(ui) { - const value = ui.cellData; -// if (value > 0) { -// return `${value}`; -// } - return value ? value.toFixed(2) : "0"; - } })); return [...baseColumns, ...dynamicColumns]; diff --git a/custom_addons/dashboard/static/src/components/pqgrid_dashboard/pqgrid_sale_dashboard.js b/custom_addons/dashboard/static/src/components/pqgrid_dashboard/pqgrid_sale_dashboard.js index 1e8871797..3c204af43 100644 --- a/custom_addons/dashboard/static/src/components/pqgrid_dashboard/pqgrid_sale_dashboard.js +++ b/custom_addons/dashboard/static/src/components/pqgrid_dashboard/pqgrid_sale_dashboard.js @@ -4,6 +4,15 @@ import { useService } from "@web/core/utils/hooks"; import { registry } from "@web/core/registry"; import { standardActionServiceProps } from "@web/webclient/actions/action_service"; +function formatFloat(value) { + if (value === null || value === undefined || isNaN(value)) { + return "0.00"; + } + return parseFloat(value).toLocaleString('en-US', { + minimumFractionDigits: 2, + maximumFractionDigits: 2 + }); +} export class SaleDashboard extends Component { @@ -49,6 +58,27 @@ export class SaleDashboard extends Component { } } + getTotalProductionValue() { + return formatFloat(this.state.sale_rows.reduce((sum, row) => sum + (row.cost || 0), 0).toFixed(2)) + " ₹"; + } + getTotalSaleValue() { + return formatFloat(this.state.sale_rows.reduce((sum, row) => sum + (row.sale_price || 0), 0).toFixed(2)) + " ₹"; + } + getTotalMarginValue() { + return formatFloat(this.state.sale_rows.reduce((sum, row) => sum + (row.margin || 0), 0).toFixed(2)) + " ₹"; + } + getMarginPercentage() { + const totalMarginValue = this.state.sale_rows.reduce((sum, row) => sum + (row.margin || 0), 0).toFixed(2) + const totalSaleValue = this.state.sale_rows.reduce((sum, row) => sum + (row.sale_price || 0), 0).toFixed(2) + + if (totalSaleValue === 0) return 0; + + const percentage = (totalMarginValue / totalSaleValue) * 100; + return formatFloat(percentage.toFixed(2)); // Returns with 2 decimal places +} + + + initializeDates() { const today = new Date(); const firstDay = new Date(today.getFullYear(), today.getMonth(), 1); @@ -103,12 +133,12 @@ export class SaleDashboard extends Component { { title: "Invoice", dataIndx: "invoice", width: 180, filter: { type: 'textbox', condition: 'contain', listeners: ['keyup'] } }, { title: "Customer", dataIndx: "customer", width: 280, filter: { type: 'textbox', condition: 'contain', listeners: ['keyup'] } }, { title: "Tags", dataIndx: "tags", width: 100, }, - { title: "Quantity (Bags/No's)", dataIndx: "quantity", width: 150, dataType: "float", format: "#,###.00", summary: { type: "sum" }, align: "right" }, + { title: "Quantity (Bags/No's)", dataIndx: "quantity", width: 150, dataType: "float", format: "#,###.00", summary: { type: "sum_" }, align: "right" }, { title: "Weight (kgs)", dataIndx: "weight", width: 120 }, - { title: "Production Cost (INR)", dataIndx: "cost", width: 150, dataType: "float", format: "#,###.00", summary: { type: "sum" }, align: "right" }, - { title: "Sale Price (INR)", dataIndx: "sale_price", width: 120, dataType: "float", format: "#,###.00", summary: { type: "sum" }, align: "right" }, - { title: "Margin (INR)", dataIndx: "margin", width: 120, dataType: "float", format: "#,###.00", summary: { type: "sum" }, align: "right" }, - { title: "Margin %", dataIndx: "margin_percent", width: 120, dataType: "float", format: "#,###.00", summary: { type: "sum" }, align: "right" }, + { title: "Production Cost (INR)", dataIndx: "cost", width: 150, dataType: "float", format: "#,###.00", summary: { type: "sum_" }, align: "right" }, + { title: "Sale Price (INR)", dataIndx: "sale_price", width: 120, dataType: "float", format: "#,###.00", summary: { type: "sum_" }, align: "right" }, + { title: "Margin (INR)", dataIndx: "margin", width: 120, dataType: "float", format: "#,###.00", summary: { type: "sum_" }, align: "right" }, + { title: "Margin %", dataIndx: "margin_percent", width: 120, dataType: "float", format: "#,###.00", summary: { type: "sum_" }, align: "right" }, { title: "View", width: 120, @@ -135,6 +165,10 @@ export class SaleDashboard extends Component { async renderSaleGrid() { const columns = await this.getSaleColumns() const agg = pq.aggregate; + agg.sum_ = function(arr, col) { + return " " + agg.sum(arr, col).toFixed(2).toString(); + }; + const gridOptions = { selectionModel: { type: "row" }, diff --git a/custom_addons/dashboard/static/src/components/pqgrid_dashboard/pqgrid_sale_dashboard.xml b/custom_addons/dashboard/static/src/components/pqgrid_dashboard/pqgrid_sale_dashboard.xml index cf55a2d44..d69bb9634 100644 --- a/custom_addons/dashboard/static/src/components/pqgrid_dashboard/pqgrid_sale_dashboard.xml +++ b/custom_addons/dashboard/static/src/components/pqgrid_dashboard/pqgrid_sale_dashboard.xml @@ -3,60 +3,120 @@
-
-
- -
-
-
- -
-
-

Sale Margin Dashboard

-

- - - - -

+
+
+ +
+
+
+ +
+
+

Sale Margin Dashboard

+

+ + + + +

+
+
+
+ + +
+
+
+
+ + + + +
+
+ +
+
+ + + + +
+
+ +
+ +
+
+
- - -
-
-
-
- - - - +
+
+
+
+
+ +
+
Total Invoices
+

+
-
-
- - - - +
+
+
+
+ +
+
Total Production
+

+
-
- +
+
+
+
+ +
+
Total Sale
+

+

+
+
+ +
+
+
+
+ +
+
Total Margin
+

+

+
+
+ +
+
+
+
+ +
+
Margin %
+

+

-
-
-
-
diff --git a/custom_addons/dashboard/static/src/components/pqgrid_dashboard/pqgrid_stock_dashboard.js b/custom_addons/dashboard/static/src/components/pqgrid_dashboard/pqgrid_stock_dashboard.js index 4d9287e7e..9b2027b81 100644 --- a/custom_addons/dashboard/static/src/components/pqgrid_dashboard/pqgrid_stock_dashboard.js +++ b/custom_addons/dashboard/static/src/components/pqgrid_dashboard/pqgrid_stock_dashboard.js @@ -4,6 +4,17 @@ import { useService } from "@web/core/utils/hooks"; import { registry } from "@web/core/registry"; import { standardActionServiceProps } from "@web/webclient/actions/action_service"; +function formatFloat(value) { + if (value === null || value === undefined || isNaN(value)) { + return "0.00"; + } + return parseFloat(value).toLocaleString('en-US', { + minimumFractionDigits: 2, + maximumFractionDigits: 2 + }); +} + + export class StockDashboard extends Component { static props = { ...standardActionServiceProps, @@ -17,6 +28,7 @@ export class StockDashboard extends Component { this.state = useState({ rows: [], + category:[], fromDate: "", toDate: "" }); @@ -92,7 +104,71 @@ export class StockDashboard extends Component { console.error("Error loading data:", error); } } + getTotalStockValue() { + return formatFloat(this.state.rows.reduce((sum, row) => sum + (row.value || 0), 0).toFixed(2)) + " ₹"; + } +parseFloatValue(value) { + if (value === null || value === undefined || value === "") { + return 0; + } + if (typeof value === 'number') { + return value; + } + if (typeof value === 'string') { + let cleaned = value.replace(/[^\d.-]/g, ''); + let result = parseFloat(cleaned); + return isNaN(result) ? 0 : result; + } + return 0; +} +// Stock dashboard calculation methods +getTotalProductsCount() { + // Count unique products based on product_code + const uniqueProducts = new Set( + this.state.rows + .filter(row => row.product_code) + .map(row => row.product_code) + ); + return uniqueProducts.size; +} + +getCategoryCount() { + // Count unique categories + const uniqueCategories = new Set( + this.state.rows + .filter(row => row.category) + .map(row => row.category) + ); + return uniqueCategories.size; +} + + + +getOutOfStockCount() { + return this.state.rows.filter(row => { + const currentStock = this.parseFloatValue(row.closing_stock || row.quantity || 0); + return currentStock <= 0; + }).length; +} + +getLowStockCount() { + return this.state.rows.filter(row => { + const currentStock = this.parseFloatValue(row.closing_stock || row.quantity || 0); + const minStock = this.parseFloatValue(row.min_stock) || 50; // Default minimum stock + return currentStock > 0 && currentStock < minStock; + }).length; +} + +getLowStockPercentage() { + const totalProducts = this.getTotalProductsCount(); + const lowStockCount = this.getLowStockCount(); + + if (totalProducts === 0) return "0.00"; + + const percentage = (lowStockCount / totalProducts) * 100; + return formatFloat(percentage); +} async getColumns() { return [ { title: "Product Code", dataIndx: "product_code", width: 100, filter: { type: 'textbox', condition: 'contain', listeners: ['keyup'] } }, @@ -118,8 +194,13 @@ export class StockDashboard extends Component { width: "100%", height: "100%", editable: false, + freezeCols: 2, stripeRows: true, - filterModel: { on: true, mode: "AND", header: true, autoSearch: true, type: 'local', minLength: 1 }, + menuIcon: true, //show header menu icon initially. + menuUI: { + tabs: ['filter'] //display only filter tab. + }, + filterModel: { on: true, mode: "AND", header: true, autoSearch: true, type: 'local', minLength: 1,menuIcon: true }, dataModel: { data: this.state.rows, location: "local", sorting: "local", paging: "local" }, colModel: columns, toolbar: { diff --git a/custom_addons/dashboard/static/src/components/pqgrid_dashboard/pqgrid_stock_dashboard.xml b/custom_addons/dashboard/static/src/components/pqgrid_dashboard/pqgrid_stock_dashboard.xml index 2336e1634..3dec75b40 100644 --- a/custom_addons/dashboard/static/src/components/pqgrid_dashboard/pqgrid_stock_dashboard.xml +++ b/custom_addons/dashboard/static/src/components/pqgrid_dashboard/pqgrid_stock_dashboard.xml @@ -3,7 +3,7 @@
-
+
@@ -55,7 +55,67 @@
-
+
+ +
+ +
+
+
+
+ +
+
Total Products
+

+ +

+ +
+
+
+ + +
+
+
+
+ +
+
Current Stock Value
+

+ Total inventory value +

+
+
+ + +
+
+
+
+ +
+
Out of Stock
+

+ Products need restocking +

+
+
+ + +
+
+
+
+ +
+
Low Stock Alert
+

+ Below minimum levels +

+
+
+
diff --git a/custom_addons/dashboard/views/pqgrid_dashboard.xml b/custom_addons/dashboard/views/pqgrid_dashboard.xml index 4a1c22f9b..860544c8d 100644 --- a/custom_addons/dashboard/views/pqgrid_dashboard.xml +++ b/custom_addons/dashboard/views/pqgrid_dashboard.xml @@ -1,9 +1,6 @@ - + Stock Dashboard @@ -11,12 +8,10 @@ {'user_id':uid} - + + Consumption Dashboard + ConsumptionDashboard + Sale Margin Dashboard @@ -24,6 +19,19 @@ {'user_id':uid} + + + + + - - Consumption Dashboard - ConsumptionDashboard - +