Files
karczma-aplikacja-stoliki/public/assets/js/modules/bill.js
T

341 lines
10 KiB
JavaScript

import { endpoints } from "./config.js";
import { t } from "./i18n.js";
import { trackEvent } from "./analytics.js";
import { requireFullAccess } from "./access.js";
import {
buildBillRequestQueueMessage,
buildWaiterCallQueueMessage,
cacheOpenBills,
ensureGuestActionAllowed,
guestActionBlockedMessage,
queueGuestAction,
refreshGuestPendingActions,
} from "./queue.js";
import { showToast } from "./toast.js";
import {
getBillState,
getHashParam,
setBillState,
} from "./state.js";
function escapeHtml(value) {
return String(value ?? "")
.replace(/&/g, "&")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#39;");
}
export async function callWaiter(type) {
if (type !== "order") return;
if (!(await ensureGuestActionAllowed("waiter_call"))) {
return;
}
const queued = await queueGuestAction("waiter_call", buildWaiterCallQueueMessage(), {
waiterType: "order",
});
if (!queued.ok) {
if (queued.reason === "pending") {
showToast(guestActionBlockedMessage("waiter_call"));
} else {
showToast(t("waiter.toast_fail"));
}
return;
}
trackEvent("waiter_call_requested", { waiterType: "order" });
showToast(t("waiter.toast_ok"));
}
export function openWaiterDialog() {
requireFullAccess("waiter", () => {
openWaiterDialogInternal();
});
}
export async function openWaiterDialogInternal() {
if (!(await ensureGuestActionAllowed("waiter_call"))) {
return;
}
document.getElementById("waiterModal").classList.add("active");
document.body.style.overflow = "hidden";
}
export function closeWaiterDialog() {
document.getElementById("waiterModal").classList.remove("active");
document.body.style.overflow = "";
}
export async function confirmCallWaiter() {
closeWaiterDialog();
await callWaiter("order");
}
export async function proceedToBillPayment() {
if (!(await ensureGuestActionAllowed("bill_request"))) {
return;
}
goToStep("stepPayment");
}
export function openBillDialog() {
requireFullAccess("bill", () => {
openBillDialogInternal();
});
}
export async function openBillDialogInternal() {
await refreshGuestPendingActions();
trackEvent("bill_dialog_opened");
setBillState({ payment: "", doc: "", nip: "", company: null, selectedBillId: null });
document.getElementById("billModal").classList.add("active");
document.body.style.overflow = "hidden"; // Zablokuj scroll tła
document.getElementById("billLoading").classList.remove("hidden");
document.getElementById("billLoading").textContent = t("bill.loading");
document.getElementById("billListContainer").classList.add("hidden");
goToStep("stepBillList");
try {
const res = await fetch(`${endpoints.bills}?h=${encodeURIComponent(getHashParam())}`);
const result = await res.json();
if (result.status === "success" && result.data.length > 0) {
const bills = result.data;
cacheOpenBills(bills);
if (bills.length === 1) {
showBillReview(bills[0]);
document.getElementById("btnBackToBills").style.display = "none";
} else {
renderBillList(bills);
document.getElementById("btnBackToBills").style.display = "block";
}
} else {
document.getElementById("billLoading").innerHTML = t("bill.loading_empty");
}
} catch (err) {
document.getElementById("billLoading").innerHTML = t("bill.loading_error");
}
}
function renderBillList(bills) {
document.getElementById("billLoading").classList.add("hidden");
document.getElementById("billListContainer").classList.remove("hidden");
const container = document.getElementById("billListItems");
container.innerHTML = "";
bills.forEach((b) => {
const div = document.createElement("div");
div.className = "option-card";
div.style.flexDirection = "row";
div.style.justifyContent = "space-between";
div.style.padding = "15px";
div.onclick = () => showBillReview(b);
const numerFormat = b.numer ? `#${b.numer}` : t("bill.bill_fallback");
div.innerHTML = `
<div>
<div style="font-weight:bold;">${escapeHtml(numerFormat)}</div>
<div style="font-size:12px; color:var(--text-muted);">${escapeHtml(b.opis)}</div>
</div>
<div style="font-weight:bold; color:var(--primary);">${Number(b.suma).toFixed(2)} PLN</div>
`;
container.appendChild(div);
});
}
export function goBackToBillList() {
goToStep("stepBillList");
}
export function showBillReview(bill) {
const billState = getBillState();
billState.selectedBillId = bill.id;
const content = document.getElementById("billReviewContent");
content.innerHTML = "";
bill.pozycje.forEach((p) => {
const div = document.createElement("div");
div.style.display = "flex";
div.style.justifyContent = "space-between";
div.style.marginBottom = "8px";
div.style.borderBottom = "1px solid rgba(255,255,255,0.05)";
div.style.paddingBottom = "8px";
div.innerHTML = `
<div style="flex:1;">
<div style="font-weight:600; font-size: 14px;">${escapeHtml(p.nazwa)}</div>
<div style="font-size:12px; color:var(--text-muted);">${escapeHtml(p.ilosc)} x ${Number(p.cena).toFixed(2)} PLN</div>
</div>
<div style="font-weight:600;">${Number(p.wartosc).toFixed(2)} PLN</div>
`;
content.appendChild(div);
});
document.getElementById("billTotalAmount").textContent = bill.suma.toFixed(2) + " PLN";
goToStep("stepBillReview");
}
export function closeBillDialog() {
document.getElementById("billModal").classList.remove("active");
document.body.style.overflow = ""; // Odblokuj scroll tła
}
export function goToStep(stepId) {
document.querySelectorAll(".step").forEach((el) => el.classList.remove("active"));
document.getElementById(stepId).classList.add("active");
}
export function selectPayment(method) {
getBillState().payment = method;
goToStep("stepDocument");
}
export async function selectDocument(docType) {
const billState = getBillState();
billState.doc = docType;
if (docType === "paragon") {
if (!(await ensureGuestActionAllowed("bill_request"))) {
return;
}
const queued = await queueGuestAction("bill_request", buildBillRequestQueueMessage("paragon"), {
payment: billState.payment || null,
docType: "paragon",
});
if (!queued.ok) {
if (queued.reason === "pending") {
showToast(guestActionBlockedMessage("bill_request"));
} else {
showToast(t("bill.toast_fail"));
}
return;
}
trackEvent("bill_request_sent", { docType: "paragon" });
closeBillDialog();
showToast(t("bill.toast_receipt"));
} else {
goToStep("stepNIP");
document.getElementById("nipInput").value = "";
setTimeout(() => document.getElementById("nipInput").focus(), 100);
}
}
export async function fetchGUS() {
const nip = document.getElementById("nipInput").value.replace(/[\s-]/g, "");
if (nip.length < 10) {
alert(t("bill.nip_invalid"));
return;
}
const btn = document.getElementById("btnGUS");
btn.textContent = t("bill.gus_searching");
btn.disabled = true;
try {
const response = await fetch(`${endpoints.gusLookup}?nip=${encodeURIComponent(nip)}`);
const result = await response.json();
if (result.status && result.data) {
const data = result.data;
let fullStreet = data.street || "";
if (data.propertyNumber) fullStreet += " " + data.propertyNumber;
if (data.apartmentNumber) fullStreet += "/" + data.apartmentNumber;
const billState = getBillState();
billState.nip = nip;
billState.company = {
name: data.name,
street: fullStreet.trim(),
zip: data.zipCode,
city: data.city,
nip: data.nip,
};
document.getElementById("cmpName").value = billState.company.name;
document.getElementById("cmpStreet").value = billState.company.street;
document.getElementById("cmpZip").value = billState.company.zip;
document.getElementById("cmpCity").value = billState.company.city;
document.getElementById("cmpNip").value = `${t("bill.queue_nip")} ${billState.company.nip}`;
// reset do readonly
document.getElementById("cmpName").readOnly = true;
document.getElementById("cmpStreet").readOnly = true;
document.getElementById("cmpZip").readOnly = true;
document.getElementById("cmpCity").readOnly = true;
document.getElementById("btnEditCompany").textContent = t("bill.edit_company");
goToStep("stepVerify");
} else {
alert(t("bill.gus_fail"));
}
} catch (error) {
console.error("Błąd pobierania danych z GUS:", error);
alert(t("bill.gus_error"));
} finally {
btn.textContent = t("bill.gus");
btn.disabled = false;
}
}
export function editCompanyData() {
const n = document.getElementById("cmpName");
const s = document.getElementById("cmpStreet");
const z = document.getElementById("cmpZip");
const c = document.getElementById("cmpCity");
const btn = document.getElementById("btnEditCompany");
if (n.readOnly) {
n.readOnly = false;
s.readOnly = false;
z.readOnly = false;
c.readOnly = false;
n.focus();
btn.textContent = t("bill.edit_done");
} else {
n.readOnly = true;
s.readOnly = true;
z.readOnly = true;
c.readOnly = true;
btn.textContent = t("bill.edit_company");
}
}
export async function confirmInvoice() {
const billState = getBillState();
billState.company.name = document.getElementById("cmpName").value;
billState.company.street = document.getElementById("cmpStreet").value;
billState.company.zip = document.getElementById("cmpZip").value;
billState.company.city = document.getElementById("cmpCity").value;
if (!(await ensureGuestActionAllowed("bill_request"))) {
return;
}
const queued = await queueGuestAction("bill_request", buildBillRequestQueueMessage("faktura"), {
payment: billState.payment || null,
docType: "faktura",
nip: billState.nip || null,
company: billState.company || null,
});
if (!queued.ok) {
if (queued.reason === "pending") {
showToast(guestActionBlockedMessage("bill_request"));
} else {
showToast(t("bill.toast_fail"));
}
return;
}
closeBillDialog();
trackEvent("bill_request_sent", { docType: "faktura" });
showToast(t("bill.toast_invoice"));
}