Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## Unreleased

- Áttekintő/Számlák: a Mai fókusz lejárt és közeli számlakártyái ugyanarra a számlalistára visznek, amelyből a darabszám és összeg készül; a számla modul külön Határidő szűrőt kapott.
- Áttekintő: a készpénzes számlák nem számítanak nyitott lejárt tételnek akkor sem, ha régi/importált adatban még fizetésre váró státusz maradt.

## 0.3.6 - 2026-07-02

- Számla modul: új kimutatás panel a látható/szűrt lista bruttó, nyitott, fizetett, ÁFA, lejárt, kategória és fizetési mód bontásával.
Expand Down
44 changes: 30 additions & 14 deletions src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@
}
return tasks.map(function (task) {
return [
'<button class="dashboard-task ' + h(task.level || "info") + '" type="button" data-launch-module="' + h(task.moduleId) + '">',
'<button class="dashboard-task ' + h(task.level || "info") + '" type="button" data-launch-module="' + h(task.moduleId) + '"' + (task.action ? ' data-launch-action="' + h(task.action) + '"' : "") + '>',
'<span>' + h(task.label) + '</span>',
'<strong>' + h(task.title) + '</strong>',
'<small>' + h(task.detail) + '</small>',
Expand Down Expand Up @@ -267,6 +267,7 @@
var overdueTotal = openOverdue.reduce(sumInvoiceGross, 0);
tasks.push({
moduleId: "invoices",
action: "invoice-overdue",
level: "danger",
label: "Lejárt számla",
title: openOverdue.length + " fizetésre váró számla",
Expand All @@ -280,6 +281,7 @@
}, INVOICE_DUE_SOON_DAYS);
tasks.push({
moduleId: "invoices",
action: "invoice-due-soon",
level: "warning",
label: "Közeli számlalejárat",
title: dueSoon.length + " számla " + INVOICE_DUE_SOON_DAYS + " napon belül",
Expand Down Expand Up @@ -402,41 +404,50 @@

function isInvoicePaid(invoice) {
var status = normalizeInvoiceStatus(invoice && invoice.status);
return status === "utalva" || status === "kiegyenlitve" || status === "fizetve" || status === "paid";
return status === "utalva" || status === "kiegyenlitve" || status === "fizetve" || status === "paid" || status.includes("befizet") || isAutoSettledInvoice(invoice);
}

function isAutoSettledInvoice(invoice) {
var paymentMethod = normalizeInvoiceStatus(invoice && invoice.paymentMethod);
return paymentMethod.includes("kesz") || paymentMethod === "kp";
}

function sumInvoiceGross(sum, invoice) {
return sum + Number(invoice.grossAmount || 0);
}

function isPastDate(value) {
if (!value) {
return false;
}
var date = new Date(value);
var date = parseLocalDate(value);
if (Number.isNaN(date.getTime())) {
return false;
}
var today = new Date();
today.setHours(0, 0, 0, 0);
date.setHours(0, 0, 0, 0);
return date < today;
}

function daysUntil(value) {
if (!value) {
return 9999;
}
var target = new Date(value);
var target = parseLocalDate(value);
if (Number.isNaN(target.getTime())) {
return 9999;
}
var today = new Date();
today.setHours(0, 0, 0, 0);
target.setHours(0, 0, 0, 0);
return Math.ceil((target - today) / 86400000);
}

function parseLocalDate(value) {
var match = String(value || "").match(/^(\d{4})-(\d{2})-(\d{2})$/);
if (match) {
return new Date(Number(match[1]), Number(match[2]) - 1, Number(match[3]));
}
var date = new Date(value);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve missing due dates as non-overdue

When an imported or older record has dueDate: null, this fallback now calls new Date(null), which is a valid Jan 1, 1970 date; isPastDate(null) therefore returns true and undated open invoices or utility settlements are shown as overdue dashboard/focus items. The previous falsy guard skipped these records, so keep a falsy check here or parse the normalized string instead of the original value.

Useful? React with 👍 / 👎.

if (!Number.isNaN(date.getTime())) {
date.setHours(0, 0, 0, 0);
}
return date;
}

function deadlineText(days) {
if (days < 0) {
return "Lejárt " + Math.abs(days) + " napja";
Expand Down Expand Up @@ -579,8 +590,13 @@
});
root.querySelectorAll("[data-launch-module]").forEach(function (card) {
card.addEventListener("click", function () {
platform.state.activeModuleId = card.dataset.launchModule;
location.hash = card.dataset.launchModule;
var moduleId = card.dataset.launchModule;
var moduleDefinition = platform.getModuleById(moduleId);
if (moduleDefinition && card.dataset.launchAction && typeof moduleDefinition.applyLaunchAction === "function") {
moduleDefinition.applyLaunchAction(card.dataset.launchAction, getContext());
}
platform.state.activeModuleId = moduleId;
location.hash = moduleId;
render();
});
});
Expand Down
71 changes: 69 additions & 2 deletions src/modules/invoices/invoices.module.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
month: "",
status: "all",
paymentMethod: "all",
dueState: "all",
localSearch: "",
compactMode: false,
sortKey: "date",
Expand Down Expand Up @@ -290,6 +291,7 @@
' <label class="field compact-field invoice-search-field"><span>Számla keresés</span><input id="invoiceLocalSearch" type="search" placeholder="Partner, sorszám, projekt..." value="' + hAttr(uiState.localSearch) + '" /></label>',
' <div class="field compact-field"><label>Státusz</label>' + filterSelect("invoiceStatusFilter", "status", ["all"].concat(statuses), uiState.status, "Összes státusz") + "</div>",
' <div class="field compact-field"><label>Fizetési mód</label>' + filterSelect("invoicePaymentFilter", "paymentMethod", ["all"].concat(paymentMethods), uiState.paymentMethod, "Összes mód") + "</div>",
' <div class="field compact-field"><label>Határidő</label>' + dueStateSelect() + "</div>",
' <div class="toolbar-summary" data-invoice-toolbar-summary><strong>' + visibleInvoices.length + " / " + toolState.totalBase + '</strong><span>' + toolState.helper + '</span></div>',
' <div class="bulk-actions" data-invoice-bulk-actions><span>' + toolState.selectedCount + " kijelölve</span>" + '<button class="settle-button" type="button" id="invoiceBulkSettle" ' + (toolState.selectedCount ? "" : "disabled") + '>Kiegyenlítés</button></div>',
'<button class="secondary-button" type="button" id="invoiceSelectVisible" ' + (toolState.selectableCount ? "" : "disabled") + '>Láthatók kijelölése</button>',
Expand Down Expand Up @@ -360,7 +362,7 @@
var activeFilterCount = countActiveInvoiceFilters(context);
var hasGlobalSearch = hasInvoiceGlobalSearch(context);
var hasLocalSearch = hasInvoiceLocalSearch();
var usesAllInvoices = hasGlobalSearch || hasLocalSearch || hasProjectQuickFilter() || hasPartnerQuickFilter();
var usesAllInvoices = hasGlobalSearch || hasLocalSearch || hasProjectQuickFilter() || hasPartnerQuickFilter() || hasInvoiceDueFilter();
var totalBase = usesAllInvoices ? allInvoices.length : monthInvoices.length;
var selectedCount = getSelectedInvoiceIds().length;
var selectableCount = visibleInvoices.filter(function (invoice) {
Expand Down Expand Up @@ -626,6 +628,12 @@
window.HRPlatform.notify();
});

root.querySelector("#invoiceDueFilter").addEventListener("change", function (event) {
uiState.dueState = event.target.value;
uiState.selectedIds = {};
window.HRPlatform.notify();
});

root.querySelectorAll("[data-column-filter]").forEach(function (input) {
input.addEventListener("input", function () {
uiState.filters[input.getAttribute("data-column-filter")] = input.value;
Expand Down Expand Up @@ -1082,7 +1090,7 @@
}

function getVisibleInvoices(context, invoices) {
var visible = hasInvoiceGlobalSearch(context) || hasInvoiceLocalSearch() || hasProjectQuickFilter() || hasPartnerQuickFilter()
var visible = hasInvoiceGlobalSearch(context) || hasInvoiceLocalSearch() || hasProjectQuickFilter() || hasPartnerQuickFilter() || hasInvoiceDueFilter()
? invoices.slice()
: invoices.filter(function (invoice) {
return getInvoiceMonth(invoice) === uiState.month;
Expand Down Expand Up @@ -1110,6 +1118,10 @@
return Boolean(String(uiState.localSearch || "").trim());
}

function hasInvoiceDueFilter() {
return uiState.dueState !== "all";
}

function countActiveInvoiceFilters(context) {
var count = 0;
if (context && String(context.searchTerm || "").trim()) {
Expand All @@ -1124,6 +1136,9 @@
if (uiState.paymentMethod !== "all") {
count += 1;
}
if (uiState.dueState !== "all") {
count += 1;
}
Object.keys(uiState.filters).forEach(function (key) {
if (String(uiState.filters[key] || "").trim()) {
count += 1;
Expand All @@ -1135,6 +1150,7 @@
function clearInvoiceFilters() {
uiState.status = "all";
uiState.paymentMethod = "all";
uiState.dueState = "all";
uiState.localSearch = "";
uiState.filters = {
date: "",
Expand All @@ -1157,6 +1173,14 @@
return false;
}

if (uiState.dueState === "overdue" && !isOverdue(invoice)) {
return false;
}

if (uiState.dueState === "dueSoon" && !isDueSoon(invoice)) {
return false;
}

if (uiState.filters.date && !String(invoice.date || "").includes(uiState.filters.date)) {
return false;
}
Expand Down Expand Up @@ -2006,6 +2030,11 @@
return dueDate < today;
}

function isDueSoon(invoice) {
var days = daysUntilInvoiceDueDate(invoice);
return !isPaidInvoice(invoice) && days != null && days >= 0 && days <= 7;
}

function daysUntilInvoiceDueDate(invoice) {
if (!invoice || !invoice.dueDate) {
return null;
Expand All @@ -2020,6 +2049,27 @@
return Math.ceil((dueDate - today) / 86400000);
}

function applyLaunchAction(action) {
if (action !== "invoice-overdue" && action !== "invoice-due-soon") {
return;
}

clearInvoiceFilters();
uiState.dueState = action === "invoice-overdue" ? "overdue" : "dueSoon";
uiState.compactMode = true;
uiState.editingId = "";
uiState.selectedIds = {};
uiState.sortKey = "dueDate";
uiState.sortDirection = "asc";
if (window.HRPlatform && window.HRPlatform.state) {
window.HRPlatform.state.searchTerm = "";
var searchInput = document.getElementById("globalSearch");
if (searchInput) {
searchInput.value = "";
}
}
}

function statCard(label, value, detail) {
return '<article class="module-card"><h4>' + label + "</h4><strong>" + value + "</strong><p>" + detail + "</p></article>";
}
Expand Down Expand Up @@ -2068,6 +2118,22 @@
);
}

function dueStateSelect() {
var items = [
{ value: "all", label: "Összes határidő" },
{ value: "overdue", label: "Lejárt" },
{ value: "dueSoon", label: "7 napon belül" }
];

return (
'<select id="invoiceDueFilter" name="dueState">' +
items.map(function (item) {
return '<option value="' + item.value + '" ' + (item.value === uiState.dueState ? "selected" : "") + ">" + item.label + "</option>";
}).join("") +
"</select>"
);
}

function hAttr(value) {
return window.HRPlatform.utils.escapeHtml(value);
}
Expand Down Expand Up @@ -2120,6 +2186,7 @@
render: render,
afterRender: afterRender,
exportRows: exportRows,
applyLaunchAction: applyLaunchAction,
isCompact: function () {
return uiState.compactMode;
}
Expand Down
Loading