Putzplan von Mietern selbst pflegbar, Belegupload bei Ausgaben, Warmmiete in Kaltmiete/Nebenkosten/WLAN-Kosten aufgesplittet
This commit is contained in:
parent
117c0b66d9
commit
dd7077a214
@ -70,7 +70,8 @@ async function main() {
|
||||
userId: tenant.id,
|
||||
roomId: room.id,
|
||||
startDate: new Date('2025-10-01'),
|
||||
totalWarmRent,
|
||||
coldRent: def.baseRent,
|
||||
utilityCosts: def.utilityPauschal,
|
||||
depositAmount: totalWarmRent * 3,
|
||||
isActive: true,
|
||||
},
|
||||
|
||||
@ -108,7 +108,7 @@ export class PaymentService {
|
||||
tenantName: c.user.fullName,
|
||||
status,
|
||||
dueDate: dueDate.toISOString(),
|
||||
amountDue: Number(currentPayment?.amount ?? c.totalWarmRent),
|
||||
amountDue: Number(currentPayment?.amount ?? Number(c.coldRent) + Number(c.utilityCosts) + Number(c.wifiCost)),
|
||||
amountPaid: paid ? Number(currentPayment.amount) : 0,
|
||||
};
|
||||
});
|
||||
|
||||
@ -7,9 +7,12 @@ const prisma = new PrismaClient();
|
||||
/**
|
||||
* Router für den Putzplan (rotierende Reinigungsaufgaben).
|
||||
*
|
||||
* Zugriffsregel: ein Mieter sieht (und kann abhaken) nur seine eigenen
|
||||
* zugewiesenen Putzaufgaben. Vermieter/Admin sehen und verwalten den
|
||||
* kompletten Putzplan aller Mieter.
|
||||
* Zugriffsregel: der Putzplan wird von den Mietern selbst gepflegt — jeder
|
||||
* eingeloggte Nutzer (Mieter wie Vermieter/Admin) kann Putzbereiche anlegen
|
||||
* und Aufgaben eintragen/zuweisen. Ein Mieter sieht (und kann abhaken) nur
|
||||
* seine eigenen zugewiesenen Aufgaben; Vermieter/Admin sehen den kompletten
|
||||
* Plan. Die Bestätigung/Verifizierung erledigter Aufgaben bleibt Vermieter/
|
||||
* Admin vorbehalten (Qualitätskontrolle).
|
||||
*/
|
||||
export const cleaningTasksRouter = Router();
|
||||
|
||||
@ -20,11 +23,11 @@ cleaningTasksRouter.get('/cleaning-areas', requireAuth, async (_req: AuthedReque
|
||||
res.status(200).json({ areas });
|
||||
});
|
||||
|
||||
// POST /cleaning-areas { name } — nur Vermieter/Admin legen neue Bereiche an.
|
||||
// POST /cleaning-areas { name } — jeder eingeloggte Nutzer kann neue
|
||||
// Bereiche anlegen (Putzplan wird von den Mietern selbst gepflegt).
|
||||
cleaningTasksRouter.post(
|
||||
'/cleaning-areas',
|
||||
requireAuth,
|
||||
requireRole('LANDLORD', 'ADMIN'),
|
||||
async (req: AuthedRequest, res: Response) => {
|
||||
const { name } = req.body || {};
|
||||
if (!name || !String(name).trim()) {
|
||||
@ -52,11 +55,12 @@ const TASK_SELECT = {
|
||||
assignedUser: { select: { id: true, fullName: true } },
|
||||
};
|
||||
|
||||
// Vermieter/Admin: Liste der Mieter (für das Zuweisen-Formular im Web-Dashboard).
|
||||
// Liste der Mieter (für das Putzplan-Formular) — für alle eingeloggten
|
||||
// Nutzer sichtbar, da der Putzplan von den Mietern selbst gepflegt wird und
|
||||
// sie Aufgaben auch anderen Mitbewohnern zuweisen können.
|
||||
cleaningTasksRouter.get(
|
||||
'/tenants',
|
||||
requireAuth,
|
||||
requireRole('LANDLORD', 'ADMIN'),
|
||||
async (_req: AuthedRequest, res: Response) => {
|
||||
const tenants = await prisma.user.findMany({
|
||||
where: { role: 'TENANT' },
|
||||
@ -67,11 +71,12 @@ cleaningTasksRouter.get(
|
||||
},
|
||||
);
|
||||
|
||||
// Vermieter/Admin legen eine neue Putzaufgabe an (z. B. für die kommende Rotationswoche).
|
||||
// Jeder eingeloggte Nutzer (Mieter wie Vermieter/Admin) legt eine neue
|
||||
// Putzaufgabe an (z. B. für die kommende Rotationswoche) — der Putzplan wird
|
||||
// von den Mietern selbst gepflegt/ausgefüllt.
|
||||
cleaningTasksRouter.post(
|
||||
'/cleaning-tasks',
|
||||
requireAuth,
|
||||
requireRole('LANDLORD', 'ADMIN'),
|
||||
async (req: AuthedRequest, res: Response) => {
|
||||
const { assignedUserId, area, weekOf, dueDate } = req.body || {};
|
||||
|
||||
|
||||
@ -115,7 +115,9 @@ const CONTRACT_DOCUMENTS_SELECT = {
|
||||
endDate: true,
|
||||
isActive: true,
|
||||
contractDocumentUrls: true,
|
||||
totalWarmRent: true,
|
||||
coldRent: true,
|
||||
utilityCosts: true,
|
||||
wifiCost: true,
|
||||
depositAmount: true,
|
||||
paymentDueDay: true,
|
||||
noticePeriodMonths: true,
|
||||
@ -177,7 +179,8 @@ contractsRouter.patch(
|
||||
requireAuth,
|
||||
requireRole('LANDLORD', 'ADMIN'),
|
||||
async (req: AuthedRequest, res: Response) => {
|
||||
const { startDate, endDate, totalWarmRent, depositAmount, paymentDueDay, noticePeriodMonths } = req.body || {};
|
||||
const { startDate, endDate, coldRent, utilityCosts, wifiCost, depositAmount, paymentDueDay, noticePeriodMonths } =
|
||||
req.body || {};
|
||||
|
||||
const existing = await prisma.contract.findUnique({ where: { id: req.params.id } });
|
||||
if (!existing) return res.status(404).json({ error: 'Vertrag nicht gefunden' });
|
||||
@ -188,8 +191,14 @@ contractsRouter.patch(
|
||||
if (endDate !== undefined && endDate !== null && Number.isNaN(Date.parse(endDate))) {
|
||||
return res.status(400).json({ error: 'endDate ist ungültig' });
|
||||
}
|
||||
if (totalWarmRent !== undefined && (!Number.isFinite(Number(totalWarmRent)) || Number(totalWarmRent) <= 0)) {
|
||||
return res.status(400).json({ error: 'totalWarmRent muss eine positive Zahl sein' });
|
||||
if (coldRent !== undefined && (!Number.isFinite(Number(coldRent)) || Number(coldRent) <= 0)) {
|
||||
return res.status(400).json({ error: 'coldRent muss eine positive Zahl sein' });
|
||||
}
|
||||
if (utilityCosts !== undefined && (!Number.isFinite(Number(utilityCosts)) || Number(utilityCosts) < 0)) {
|
||||
return res.status(400).json({ error: 'utilityCosts muss eine Zahl sein' });
|
||||
}
|
||||
if (wifiCost !== undefined && (!Number.isFinite(Number(wifiCost)) || Number(wifiCost) < 0)) {
|
||||
return res.status(400).json({ error: 'wifiCost muss eine Zahl sein' });
|
||||
}
|
||||
if (depositAmount !== undefined && (!Number.isFinite(Number(depositAmount)) || Number(depositAmount) < 0)) {
|
||||
return res.status(400).json({ error: 'depositAmount muss eine Zahl sein' });
|
||||
@ -222,7 +231,9 @@ contractsRouter.patch(
|
||||
data: {
|
||||
startDate: startDate !== undefined ? parsedStart : undefined,
|
||||
endDate: endDate !== undefined ? parsedEnd : undefined,
|
||||
totalWarmRent: totalWarmRent !== undefined ? Number(totalWarmRent) : undefined,
|
||||
coldRent: coldRent !== undefined ? Number(coldRent) : undefined,
|
||||
utilityCosts: utilityCosts !== undefined ? Number(utilityCosts) : undefined,
|
||||
wifiCost: wifiCost !== undefined ? Number(wifiCost) : undefined,
|
||||
depositAmount: depositAmount !== undefined ? Number(depositAmount) : undefined,
|
||||
paymentDueDay: paymentDueDay !== undefined ? Number(paymentDueDay) : undefined,
|
||||
noticePeriodMonths: noticePeriodMonths !== undefined ? Number(noticePeriodMonths) : undefined,
|
||||
@ -507,7 +518,8 @@ contractsRouter.post(
|
||||
requireAuth,
|
||||
requireRole('LANDLORD', 'ADMIN'),
|
||||
async (req: AuthedRequest, res: Response) => {
|
||||
const { userId, roomId, startDate, endDate, totalWarmRent, depositAmount, paymentDueDay, noticePeriodMonths } = req.body || {};
|
||||
const { userId, roomId, startDate, endDate, coldRent, utilityCosts, wifiCost, depositAmount, paymentDueDay, noticePeriodMonths } =
|
||||
req.body || {};
|
||||
|
||||
if (!userId || typeof userId !== 'string') return res.status(400).json({ error: 'userId ist erforderlich' });
|
||||
if (!roomId || typeof roomId !== 'string') return res.status(400).json({ error: 'roomId ist erforderlich' });
|
||||
@ -515,8 +527,14 @@ contractsRouter.post(
|
||||
if (endDate !== undefined && endDate !== null && Number.isNaN(Date.parse(endDate))) {
|
||||
return res.status(400).json({ error: 'endDate ist ungueltig' });
|
||||
}
|
||||
if (!Number.isFinite(Number(totalWarmRent)) || Number(totalWarmRent) <= 0) {
|
||||
return res.status(400).json({ error: 'totalWarmRent muss eine positive Zahl sein' });
|
||||
if (!Number.isFinite(Number(coldRent)) || Number(coldRent) <= 0) {
|
||||
return res.status(400).json({ error: 'coldRent muss eine positive Zahl sein' });
|
||||
}
|
||||
if (utilityCosts !== undefined && (!Number.isFinite(Number(utilityCosts)) || Number(utilityCosts) < 0)) {
|
||||
return res.status(400).json({ error: 'utilityCosts muss eine Zahl sein' });
|
||||
}
|
||||
if (wifiCost !== undefined && (!Number.isFinite(Number(wifiCost)) || Number(wifiCost) < 0)) {
|
||||
return res.status(400).json({ error: 'wifiCost muss eine Zahl sein' });
|
||||
}
|
||||
if (!Number.isFinite(Number(depositAmount)) || Number(depositAmount) < 0) {
|
||||
return res.status(400).json({ error: 'depositAmount muss eine Zahl sein' });
|
||||
@ -555,7 +573,9 @@ contractsRouter.post(
|
||||
roomId,
|
||||
startDate: parsedStart,
|
||||
endDate: parsedEnd,
|
||||
totalWarmRent: Number(totalWarmRent),
|
||||
coldRent: Number(coldRent),
|
||||
utilityCosts: Number.isFinite(Number(utilityCosts)) ? Number(utilityCosts) : 0,
|
||||
wifiCost: Number.isFinite(Number(wifiCost)) ? Number(wifiCost) : 0,
|
||||
depositAmount: Number(depositAmount),
|
||||
paymentDueDay: Number.isFinite(Number(paymentDueDay)) ? Number(paymentDueDay) : 1,
|
||||
noticePeriodMonths: Number.isFinite(Number(noticePeriodMonths)) ? Number(noticePeriodMonths) : 3,
|
||||
|
||||
@ -297,7 +297,9 @@ invitationsRouter.post('/invitations/:token/accept', async (req: Request, res: R
|
||||
startDate: invitation.contractStartDate || new Date(),
|
||||
endDate: invitation.contractEndDate,
|
||||
paymentDueDay: invitation.paymentDueDay || 1,
|
||||
totalWarmRent: Number(room.baseRent) + Number(room.utilityPauschal),
|
||||
coldRent: Number(room.baseRent),
|
||||
utilityCosts: Number(room.utilityPauschal),
|
||||
wifiCost: 0,
|
||||
depositAmount: Number(room.baseRent) * 3,
|
||||
isActive: true,
|
||||
},
|
||||
|
||||
@ -117,7 +117,13 @@ export function generateContractPdf(contract: ContractForPdf): Promise<Buffer> {
|
||||
|
||||
// --- § 4 Miete und Nebenkosten ---
|
||||
section('§ 4 Miete und Nebenkosten', () => {
|
||||
doc.text(`Die Warmmiete beträgt ${formatEuro(contract.totalWarmRent)} monatlich.`);
|
||||
const totalRent = Number(contract.coldRent) + Number(contract.utilityCosts) + Number(contract.wifiCost);
|
||||
doc.text(`Die Kaltmiete beträgt ${formatEuro(contract.coldRent)} monatlich.`);
|
||||
doc.text(`Die Nebenkosten betragen ${formatEuro(contract.utilityCosts)} monatlich.`);
|
||||
if (Number(contract.wifiCost) > 0) {
|
||||
doc.text(`Die WLAN-Kosten betragen ${formatEuro(contract.wifiCost)} monatlich.`);
|
||||
}
|
||||
doc.text(`Die monatliche Gesamtmiete (Warmmiete) beträgt somit ${formatEuro(totalRent)}.`);
|
||||
doc.text(
|
||||
`Sie ist im Voraus, spätestens am ${contract.paymentDueDay}. eines jeden Monats, auf das ` +
|
||||
'vom Vermieter benannte Konto zu entrichten.',
|
||||
|
||||
@ -272,7 +272,9 @@ model Contract {
|
||||
startDate DateTime @map("start_date")
|
||||
endDate DateTime? @map("end_date") // NULL = unbefristet
|
||||
|
||||
totalWarmRent Decimal @map("total_warm_rent") @db.Decimal(8, 2)
|
||||
coldRent Decimal @map("cold_rent") @db.Decimal(8, 2) // Kaltmiete
|
||||
utilityCosts Decimal @default(0) @map("utility_costs") @db.Decimal(8, 2) // Nebenkosten
|
||||
wifiCost Decimal @default(0) @map("wifi_cost") @db.Decimal(8, 2) // WLAN-Kosten
|
||||
depositAmount Decimal @map("deposit_amount") @db.Decimal(8, 2)
|
||||
paymentDueDay Int @default(1) @map("payment_due_day") // Fälligkeit: Kalendertag im Monat (z.B. 1 oder 15)
|
||||
|
||||
|
||||
@ -827,8 +827,8 @@
|
||||
|
||||
<div class="page" id="page-cleaning">
|
||||
|
||||
<section id="cleaningAssignSection" style="display:none">
|
||||
<h2>Putzaufgabe zuweisen</h2>
|
||||
<section id="cleaningAssignSection">
|
||||
<h2>Putzaufgabe eintragen</h2>
|
||||
<div class="invite-card">
|
||||
<div id="cleaningFormError" style="display:none" class="form-error"></div>
|
||||
<form id="cleaningForm" class="invite-form-row" style="flex-wrap:wrap;">
|
||||
@ -937,6 +937,10 @@
|
||||
<label for="expenseCategory">Kategorie</label>
|
||||
<select id="expenseCategory"></select>
|
||||
</div>
|
||||
<div class="field" style="flex:1 1 200px;">
|
||||
<label for="expenseReceipt">Beleg (Foto/PDF, optional)</label>
|
||||
<input type="file" id="expenseReceipt" accept="image/*,application/pdf" />
|
||||
</div>
|
||||
<button type="submit" class="btn-primary" style="width:auto" id="expenseSubmitBtn">Eintragen</button>
|
||||
</form>
|
||||
</div>
|
||||
@ -1473,10 +1477,9 @@
|
||||
document.getElementById('inviteSection').style.display = isLandlord ? 'block' : 'none';
|
||||
document.getElementById('tenantDataSection').style.display = isLandlord ? 'block' : 'none';
|
||||
document.getElementById('rentSectionTitle').textContent = isLandlord ? 'Miet-Ampel je Zimmer' : 'Meine Miete';
|
||||
document.getElementById('cleaningAssignSection').style.display = isLandlord ? 'block' : 'none';
|
||||
document.getElementById('cleaningSectionTitle').textContent = isLandlord ? 'Putzplan (alle Mieter)' : 'Mein Putzplan';
|
||||
if (isLandlord) initInvitePanel();
|
||||
if (isLandlord) initCleaningAssignForm();
|
||||
initCleaningAssignForm();
|
||||
|
||||
document.getElementById('absenceUserField').style.display = isLandlord ? 'flex' : 'none';
|
||||
|
||||
@ -2386,7 +2389,9 @@
|
||||
const amount = document.getElementById('expenseAmount').value;
|
||||
const description = document.getElementById('expenseDescription').value.trim();
|
||||
const category = document.getElementById('expenseCategory').value;
|
||||
await apiFetch('/expenses', { method: 'POST', body: JSON.stringify({ amount, description, category }) });
|
||||
const receiptFile = document.getElementById('expenseReceipt').files[0];
|
||||
const receiptUrl = receiptFile ? await readFileAsCompressedDataUrl(receiptFile, 1600, 0.82) : undefined;
|
||||
await apiFetch('/expenses', { method: 'POST', body: JSON.stringify({ amount, description, category, receiptUrl }) });
|
||||
form.reset();
|
||||
populateExpenseCategorySelect();
|
||||
loadExpenses();
|
||||
@ -2457,6 +2462,12 @@
|
||||
const shareText = openShares.map(s => `${s.user.fullName}: ${formatEuro(s.shareAmount)}`).join(', ');
|
||||
left.appendChild(el('div', 'meta-text', 'Offen: ' + shareText));
|
||||
}
|
||||
if (x.receiptUrl) {
|
||||
const chipWrap = el('div');
|
||||
chipWrap.style.marginTop = '4px';
|
||||
chipWrap.appendChild(buildDocumentChip('Beleg', x.receiptUrl));
|
||||
left.appendChild(chipWrap);
|
||||
}
|
||||
row.appendChild(left);
|
||||
|
||||
const right = el('div');
|
||||
@ -3640,7 +3651,7 @@
|
||||
}
|
||||
|
||||
// Editierbare Vertrags-Eckdaten (dieselben Felder wie bei der
|
||||
// Vertragserstellung: Mietbeginn/-ende, Warmmiete, Kaution, Fälligkeit,
|
||||
// Vertragserstellung: Mietbeginn/-ende, Kaltmiete/Nebenkosten/WLAN-Kosten, Kaution, Fälligkeit,
|
||||
// Kündigungsfrist) — jederzeit vom Vermieter über PATCH /contracts/:id
|
||||
// änderbar, inkl. serverseitiger Plausibilitätsprüfung bei Datumsänderung.
|
||||
function buildContractEckdatenForm(c) {
|
||||
@ -3680,16 +3691,38 @@
|
||||
if (unbefristetCheckbox.checked) endInput.value = '';
|
||||
};
|
||||
|
||||
const rentField = el('div', 'field');
|
||||
rentField.style.cssText = 'width:95px; flex:0 0 auto;';
|
||||
rentField.appendChild(el('label', null, 'Warmmiete (€)'));
|
||||
const rentInput = document.createElement('input');
|
||||
rentInput.type = 'number';
|
||||
rentInput.min = '0';
|
||||
rentInput.step = '0.01';
|
||||
rentInput.value = c.totalWarmRent;
|
||||
rentField.appendChild(rentInput);
|
||||
form.appendChild(rentField);
|
||||
const coldRentField = el('div', 'field');
|
||||
coldRentField.style.cssText = 'width:95px; flex:0 0 auto;';
|
||||
coldRentField.appendChild(el('label', null, 'Kaltmiete (€)'));
|
||||
const coldRentInput = document.createElement('input');
|
||||
coldRentInput.type = 'number';
|
||||
coldRentInput.min = '0';
|
||||
coldRentInput.step = '0.01';
|
||||
coldRentInput.value = c.coldRent;
|
||||
coldRentField.appendChild(coldRentInput);
|
||||
form.appendChild(coldRentField);
|
||||
|
||||
const utilityCostsField = el('div', 'field');
|
||||
utilityCostsField.style.cssText = 'width:95px; flex:0 0 auto;';
|
||||
utilityCostsField.appendChild(el('label', null, 'Nebenkosten (€)'));
|
||||
const utilityCostsInput = document.createElement('input');
|
||||
utilityCostsInput.type = 'number';
|
||||
utilityCostsInput.min = '0';
|
||||
utilityCostsInput.step = '0.01';
|
||||
utilityCostsInput.value = c.utilityCosts;
|
||||
utilityCostsField.appendChild(utilityCostsInput);
|
||||
form.appendChild(utilityCostsField);
|
||||
|
||||
const wifiCostField = el('div', 'field');
|
||||
wifiCostField.style.cssText = 'width:95px; flex:0 0 auto;';
|
||||
wifiCostField.appendChild(el('label', null, 'WLAN-Kosten (€)'));
|
||||
const wifiCostInput = document.createElement('input');
|
||||
wifiCostInput.type = 'number';
|
||||
wifiCostInput.min = '0';
|
||||
wifiCostInput.step = '0.01';
|
||||
wifiCostInput.value = c.wifiCost;
|
||||
wifiCostField.appendChild(wifiCostInput);
|
||||
form.appendChild(wifiCostField);
|
||||
|
||||
const depositField = el('div', 'field');
|
||||
depositField.style.cssText = 'width:95px; flex:0 0 auto;';
|
||||
@ -3726,7 +3759,10 @@
|
||||
noticeField.appendChild(noticeInput);
|
||||
form.appendChild(noticeField);
|
||||
|
||||
const allInputs = [startInput, endInput, unbefristetCheckbox, rentInput, depositInput, dueDaySelect, noticeInput];
|
||||
const allInputs = [
|
||||
startInput, endInput, unbefristetCheckbox, coldRentInput, utilityCostsInput, wifiCostInput,
|
||||
depositInput, dueDaySelect, noticeInput,
|
||||
];
|
||||
const setEditable = (editable) => {
|
||||
allInputs.forEach(input => { input.disabled = !editable; });
|
||||
if (editable) endInput.disabled = unbefristetCheckbox.checked;
|
||||
@ -3766,7 +3802,9 @@
|
||||
body: JSON.stringify({
|
||||
startDate: startInput.value,
|
||||
endDate: unbefristetCheckbox.checked ? null : (endInput.value || null),
|
||||
totalWarmRent: rentInput.value,
|
||||
coldRent: coldRentInput.value,
|
||||
utilityCosts: utilityCostsInput.value,
|
||||
wifiCost: wifiCostInput.value,
|
||||
depositAmount: depositInput.value,
|
||||
paymentDueDay: Number(dueDaySelect.value),
|
||||
noticePeriodMonths: noticeInput.value,
|
||||
@ -3996,15 +4034,36 @@
|
||||
endField.appendChild(unbefristetLabel);
|
||||
formArea.appendChild(endField);
|
||||
|
||||
const rentField = el('div', 'field');
|
||||
rentField.style.width = '130px';
|
||||
rentField.appendChild(el('label', null, 'Warmmiete (€)'));
|
||||
const rentInput = document.createElement('input');
|
||||
rentInput.type = 'number';
|
||||
rentInput.min = '0';
|
||||
rentInput.step = '0.01';
|
||||
rentField.appendChild(rentInput);
|
||||
formArea.appendChild(rentField);
|
||||
const coldRentField = el('div', 'field');
|
||||
coldRentField.style.width = '110px';
|
||||
coldRentField.appendChild(el('label', null, 'Kaltmiete (€)'));
|
||||
const coldRentInput = document.createElement('input');
|
||||
coldRentInput.type = 'number';
|
||||
coldRentInput.min = '0';
|
||||
coldRentInput.step = '0.01';
|
||||
coldRentField.appendChild(coldRentInput);
|
||||
formArea.appendChild(coldRentField);
|
||||
|
||||
const utilityCostsField = el('div', 'field');
|
||||
utilityCostsField.style.width = '110px';
|
||||
utilityCostsField.appendChild(el('label', null, 'Nebenkosten (€)'));
|
||||
const utilityCostsInput = document.createElement('input');
|
||||
utilityCostsInput.type = 'number';
|
||||
utilityCostsInput.min = '0';
|
||||
utilityCostsInput.step = '0.01';
|
||||
utilityCostsField.appendChild(utilityCostsInput);
|
||||
formArea.appendChild(utilityCostsField);
|
||||
|
||||
const wifiCostField = el('div', 'field');
|
||||
wifiCostField.style.width = '110px';
|
||||
wifiCostField.appendChild(el('label', null, 'WLAN-Kosten (€)'));
|
||||
const wifiCostInput = document.createElement('input');
|
||||
wifiCostInput.type = 'number';
|
||||
wifiCostInput.min = '0';
|
||||
wifiCostInput.step = '0.01';
|
||||
wifiCostInput.value = '0';
|
||||
wifiCostField.appendChild(wifiCostInput);
|
||||
formArea.appendChild(wifiCostField);
|
||||
|
||||
const depositField = el('div', 'field');
|
||||
depositField.style.width = '130px';
|
||||
@ -4065,7 +4124,8 @@
|
||||
function applyRoomDefaults() {
|
||||
const info = roomInfoById[roomSelect.value];
|
||||
if (!info) return;
|
||||
rentInput.value = (Number(info.baseRent) + Number(info.utilityPauschal)).toFixed(2);
|
||||
coldRentInput.value = Number(info.baseRent).toFixed(2);
|
||||
utilityCostsInput.value = Number(info.utilityPauschal).toFixed(2);
|
||||
depositInput.value = (Number(info.baseRent) * 3).toFixed(2);
|
||||
}
|
||||
|
||||
@ -4088,7 +4148,9 @@
|
||||
roomId: roomSelect.value,
|
||||
startDate: startInput.value,
|
||||
endDate: unbefristetCheckbox.checked ? undefined : (endInput.value || undefined),
|
||||
totalWarmRent: rentInput.value,
|
||||
coldRent: coldRentInput.value,
|
||||
utilityCosts: utilityCostsInput.value,
|
||||
wifiCost: wifiCostInput.value,
|
||||
depositAmount: depositInput.value,
|
||||
paymentDueDay: Number(dueDaySelect.value),
|
||||
}, submitBtn);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user