341 lines
10 KiB
JavaScript
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, "<")
|
|
.replace(/>/g, ">")
|
|
.replace(/"/g, """)
|
|
.replace(/'/g, "'");
|
|
}
|
|
|
|
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"));
|
|
}
|