Add show/hide cost price & profit feature - in reports #4130 (#4350)

* Add show/hide cost price & profit feature

* .env should be ignored.

* js code formatted. .vscode folder ignore for vscode user settings.json

* style is replaced with bootstrap class, formatted and .env.example

* toggle button on table to like in other

* comment corrected.

* class re-factored

* minor refactor

* formatted with 4 space

---------

Co-authored-by: Lotussoft Youngtech <lotussoftyoungtech@gmail.com>
This commit is contained in:
BhojKamal
2025-12-21 15:23:39 +05:45
committed by GitHub
parent 643b0ac499
commit aee5f31cf5
12 changed files with 202 additions and 104 deletions

87
.env
View File

@@ -1,87 +0,0 @@
#--------------------------------------------------------------------
# ENVIRONMENT
#--------------------------------------------------------------------
CI_ENVIRONMENT = production
CI_DEBUG = false
#--------------------------------------------------------------------
# APP
#--------------------------------------------------------------------
app.appTimezone = 'UTC'
#--------------------------------------------------------------------
# DATABASE
#--------------------------------------------------------------------
database.default.hostname = 'localhost'
database.default.database = 'ospos'
database.default.username = 'admin'
database.default.password = 'pointofsale'
database.default.DBDriver = 'MySQLi'
database.default.DBPrefix = 'ospos_'
database.default.port = 3306
database.development.hostname = 'localhost'
database.development.database = 'ospos'
database.development.username = 'admin'
database.development.password = 'pointofsale'
database.development.DBDriver = 'MySQLi'
database.development.DBPrefix = 'ospos_'
database.development.port = 3306
database.tests.hostname = 'localhost'
database.tests.database = 'ospos'
database.tests.username = 'admin'
database.tests.password = 'pointofsale'
database.tests.DBDriver = 'MySQLi'
database.tests.DBPrefix = 'ospos_'
database.tests.charset = utf8mb4
database.tests.DBCollat = utf8mb4_general_ci
database.tests.port = 3306
#--------------------------------------------------------------------
# EMAIL
#--------------------------------------------------------------------
email.SMTPHost = ''
email.SMTPUser = ''
email.SMTPPass = ''
email.SMTPPort =
email.SMTPTimeout = 5
email.SMTPCrypto = 'tls'
#--------------------------------------------------------------------
# ENCRYPTION
#--------------------------------------------------------------------
encryption.key = ''
#--------------------------------------------------------------------
# HONEYPOT
#--------------------------------------------------------------------
honeypot.hidden = true
honeypot.label = 'Fill This Field'
honeypot.name = 'honeypot'
honeypot.template = '<label>{label}</label><input type="text" name="{name}" value="">'
honeypot.container = '<div style="display:none">{template}</div>'
#--------------------------------------------------------------------
# LOGGER
# - 0 = Disables logging, Error logging TURNED OFF
# - 1 = Emergency Messages - System is unusable
# - 2 = Alert Messages - Action Must Be Taken Immediately
# - 3 = Critical Messages - Application component unavailable, unexpected exception.
# - 4 = Runtime Errors - Don't need immediate action, but should be monitored.
# - 5 = Warnings - Exceptional occurrences that are not errors.
# - 6 = Notices - Normal but significant events.
# - 7 = Info - Interesting events, like user logging in, etc.
# - 8 = Debug - Detailed debug information.
# - 9 = All Messages
#--------------------------------------------------------------------
logger.threshold = 0
app.db_log_enabled = false
app.db_log_only_long = false

View File

1
.gitignore vendored
View File

@@ -8,6 +8,7 @@ public/license/*
!public/license/.gitkeep
app/Config/email.php
npm-debug.log*
.vscode
# Docker
!docker/.env

View File

@@ -14,7 +14,7 @@ First of all, if you're seeing the message `system folder missing` after launchi
2. Create/locate a new MySQL database to install Open Source Point of Sale into.
3. Execute the file `app/Database/database.sql` to create the tables needed.
4. Unzip and upload Open Source Point of Sale files to the web-server.
5. Open `.env` file and modify credentials to connect to your database if needed.
5. Open `.env` file and modify credentials to connect to your database if needed. (First copy .env.example to .env and update)
7. Go to your install `public` dir via the browser.
8. Log in using
- Username: admin

View File

@@ -2,11 +2,12 @@
namespace Config;
use CodeIgniter\Config\BaseService;
use CodeIgniter\HTTP\IncomingRequest;
use Config\Services as AppServices;
use Locale;
use HTMLPurifier;
use HTMLPurifier_Config;
use CodeIgniter\Config\BaseService;
use Config\Services as AppServices;
use CodeIgniter\HTTP\IncomingRequest;
/**
* Services Configuration file.

View File

@@ -146,4 +146,5 @@ return [
"used" => "Points Used",
"work_orders" => "Work Orders",
"zero_and_less" => "Zero and Less",
"toggle_cost_and_profit" => "Toggle Cost & Profit",
];

View File

@@ -146,4 +146,5 @@ return [
"used" => "Points Used",
"work_orders" => "Work Orders",
"zero_and_less" => "Zero and less",
"toggle_cost_and_profit" => "Toggle Cost & Profit",
];

View File

@@ -0,0 +1,98 @@
<?php // used in reports ?>
// Utility functions for safe localStorage access
function safeSetItem(key, value) {
try {
localStorage.setItem(key, value);
} catch (e) {
console.error(`Failed to set item in localStorage: ${e.message}`);
}
}
function safeGetItem(key) {
try {
return localStorage.getItem(key);
} catch (e) {
console.error(`Failed to get item from localStorage: ${e.message}`);
return null; // Default fallback
}
}
function safeRemoveItem(key) {
try {
localStorage.removeItem(key);
} catch (e) {
console.error(`Failed to remove item from localStorage: ${e.message}`);
}
}
// Load saved column visibility from localStorage
var savedVisibility = JSON.parse(safeGetItem('columnVisibility')) || { cost: false, profit: false };
var visibleColumns = savedVisibility;
// Function to save column visibility to localStorage
function saveColumnVisibility(visibility) {
safeSetItem('columnVisibility', JSON.stringify(visibility));
}
// Apply column visibility on table initialization
function applyColumnVisibility(columns) {
return columns.map(function (col) {
if (visibleColumns[col.field] !== undefined) {
col.visible = visibleColumns[col.field]; // Apply visibility from localStorage
}
return col;
});
}
// Event listener for column visibility toggle
$('#table').on('column-switch.bs.table', function (e, field, checked) {
visibleColumns[field] = checked; // Save the visibility of this column
saveColumnVisibility(visibleColumns); // Store it in localStorage
});
// Ensure that saved column visibility is applied immediately after table load
$('#table').bootstrapTable('refreshOptions', {
columns: $('#table').bootstrapTable('getOptions').columns // Force refresh to apply column visibility
});
// Initialize visibility settings from localStorage
var summaryVisibility = JSON.parse(safeGetItem('summaryVisibility')) || { cost: false, profit: false };
// Function to apply visibility for cost and profit rows
function applySummaryVisibility() {
var rows = $('#report_summary .summary_row');
var costRow = rows.eq(rows.length - 2); // Second-to-last row
var profitRow = rows.eq(rows.length - 1); // Last row
if (summaryVisibility.cost === false) {
costRow.hide(); // Hide the cost row
} else {
costRow.show(); // Show the cost row
}
if (summaryVisibility.profit === false) {
profitRow.hide(); // Hide the profit row
} else {
profitRow.show(); // Show the profit row
}
}
// Toggle visibility when the button is clicked
$('#toggleCostProfitButton').click(function () {
summaryVisibility.cost = !summaryVisibility.cost;
summaryVisibility.profit = !summaryVisibility.profit;
safeSetItem('summaryVisibility', JSON.stringify(summaryVisibility));
applySummaryVisibility();
});
// Apply saved visibility state on page load
applySummaryVisibility();
// Initialize dialog (if editable)
var init_dialog = function () {
<?php if (isset($editable)): ?>
table_support.submit_handler('<?php echo site_url("reports/get_detailed_{$editable}_row") ?>');
dialog_support.init("a.modal-dlg");
<?php endif; ?>
};

View File

@@ -19,6 +19,15 @@
<div class="ct-chart ct-golden-section" id="chart1"></div>
<div id="toolbar">
<div class="pull-left form-inline" role="toolbar">
<!-- Toggle Button -->
<button id="toggleCostProfitButton" class="btn btn-default btn-sm print_hide">
<?php echo lang('Reports.toggle_cost_and_profit'); ?>
</button>
</div>
</div>
<?= view($chart_type) ?>
<div id="chart_report_summary">
@@ -27,4 +36,6 @@
<?php } ?>
</div>
<script src="<?= base_url('js/hide_cost_profit.js') ?>"></script>
<?= view('partial/footer') ?>

View File

@@ -19,6 +19,14 @@
<div id="page_subtitle"><?= esc($subtitle) ?></div>
<div id="toolbar">
<div class="pull-left form-inline" role="toolbar">
<button id="toggleCostProfitButton" class="btn btn-default btn-sm print_hide">
<?php echo lang('Reports.toggle_cost_and_profit'); ?>
</button>
</div>
</div>
<div id="table_holder">
<table id="table"></table>
</div>
@@ -27,26 +35,29 @@
<?php
foreach ($summary_data as $name => $value) {
if ($name == "total_quantity") {
?>
?>
<div class="summary_row"><?= lang("Reports.$name") . ": $value" ?></div>
<?php } else { ?>
<div class="summary_row"><?= lang("Reports.$name") . ': ' . to_currency($value) ?></div>
<?php
<?php
}
}
?>
</div>
<script type="text/javascript">
$(document).ready(function() {
$(document).ready(function () {
<?= view('partial/bootstrap_tables_locale') ?>
<?= view('partial/visibility_js') ?>
$('#table')
.addClass("table-striped")
.addClass("table-bordered")
.bootstrapTable({
columns: <?= transform_headers(esc($headers), true, false) ?>,
columns: applyColumnVisibility(<?= transform_headers(esc($headers), true, false) ?>),
stickyHeader: true,
stickyHeaderOffsetLeft: $('#table').offset().left + 'px',
stickyHeaderOffsetRight: $('#table').offset().right + 'px',
pageSize: <?= $config['lines_per_page'] ?>,
sortable: true,
showExport: true,
@@ -57,7 +68,7 @@
data: <?= json_encode($data) ?>,
iconSize: 'sm',
paginationVAlign: 'bottom',
escape: true,
escape: false,
search: true
});
});

View File

@@ -16,6 +16,15 @@
<div id="page_subtitle"><?= esc($subtitle) ?></div>
<div id="toolbar">
<div class="pull-left form-inline" role="toolbar">
<!-- Toggle Button -->
<button id="toggleCostProfitButton" class="btn btn-default btn-sm print_hide">
<?php echo lang('Reports.toggle_cost_and_profit'); ?>
</button>
</div>
</div>
<div id="table_holder">
<table id="table"></table>
</div>
@@ -27,14 +36,16 @@
</div>
<script type="text/javascript">
$(document).ready(function() {
$(document).ready(function () {
<?= view('partial/bootstrap_tables_locale') ?>
var details_data = <?= json_encode(esc($details_data)) ?>;
<?php if ($config['customer_reward_enable'] && !empty($details_data_rewards)) { ?>
var details_data_rewards = <?= json_encode(esc($details_data_rewards)) ?>;
<?php } ?>
var init_dialog = function() {
<?= view('partial/visibility_js') ?>
var init_dialog = function () {
<?php if (isset($editable)) { ?>
table_support.submit_handler('<?= esc(site_url("reports/get_detailed_$editable" . '_row')) ?>');
dialog_support.init("a.modal-dlg");
@@ -45,8 +56,10 @@
.addClass("table-striped")
.addClass("table-bordered")
.bootstrapTable({
columns: <?= transform_headers(esc($headers['summary']), true) ?>,
columns: applyColumnVisibility(<?= transform_headers(esc($headers['summary']), true) ?>),
stickyHeader: true,
stickyHeaderOffsetLeft: $('#table').offset().left + 'px',
stickyHeaderOffsetRight: $('#table').offset().right + 'px',
pageSize: <?= $config['lines_per_page'] ?>,
pagination: true,
sortable: true,
@@ -59,22 +72,24 @@
iconSize: 'sm',
paginationVAlign: 'bottom',
detailView: true,
escape: true,
escape: false,
search: true,
onPageChange: init_dialog,
onPostBody: function() {
onPostBody: function () {
dialog_support.init("a.modal-dlg");
},
onExpandRow: function(index, row, $detail) {
onExpandRow: function (index, row, $detail) {
$detail.html('<table></table>').find("table").bootstrapTable({
columns: <?= transform_headers_readonly(esc($headers['details'])) ?>,
data: details_data[(!isNaN(row.id) && row.id) || $(row[0] || row.id).text().replace(/(POS|RECV)\s*/g, '')]
data: details_data[(!isNaN(row.id) && row.id) || $(row[0] || row.id).text().replace(
/(POS|RECV)\s*/g, '')]
});
<?php if ($config['customer_reward_enable'] && !empty($details_data_rewards)) { ?>
$detail.append('<table></table>').find("table").bootstrapTable({
columns: <?= transform_headers_readonly(esc($headers['details_rewards'])) ?>,
data: details_data_rewards[(!isNaN(row.id) && row.id) || $(row[0] || row.id).text().replace(/(POS|RECV)\s*/g, '')]
data: details_data_rewards[(!isNaN(row.id) && row.id) || $(row[0] || row.id).text().replace(
/(POS|RECV)\s*/g, '')]
});
<?php } ?>
}

View File

@@ -0,0 +1,46 @@
/**
* public/js/hide_cost_profit.js
* toggle cost and profit in graphical report.
*/
$(function () {
const safeSetItem = (key, value) => {
try {
localStorage.setItem(key, value);
} catch (e) {
console.error("Storage error", e);
}
};
const safeGetItem = (key) => {
try {
return localStorage.getItem(key);
} catch (e) {
return null;
}
};
let summaryVisibility = JSON.parse(safeGetItem("summaryVisibility")) || {
cost: false,
profit: false,
};
function applySummaryVisibility() {
const rows = $("#chart_report_summary .summary_row");
if (rows.length < 2) return; // Prevent errors if data is missing
const costRow = rows.eq(rows.length - 2);
const profitRow = rows.eq(rows.length - 1);
summaryVisibility.cost ? costRow.show() : costRow.hide();
summaryVisibility.profit ? profitRow.show() : profitRow.hide();
}
$("#toggleCostProfitButton").on("click", function () {
summaryVisibility.cost = !summaryVisibility.cost;
summaryVisibility.profit = !summaryVisibility.profit;
safeSetItem("summaryVisibility", JSON.stringify(summaryVisibility));
applySummaryVisibility();
});
applySummaryVisibility();
});