Restructure generated contract as a full-format rental agreement
Rewrites contractDocumentGenerator.ts from a short summary into a proper Wohnraummietvertrag layout: numbered §§ for Vertragsparteien, Mieträume, Mietzeit, Miete/Nebenkosten, Kaution, Instandhaltung/ Schönheitsreparaturen, Hausordnung, Tierhaltung/Untervermietung, Mietanpassung, sonstige Vereinbarungen, salvatorische Klausel, plus signature lines for both landlord and tenant (with the drawn signature embedded once signed) and a page-footer disclaimer instead of a top-of-document warning box. Adds Contract.landlordName/landlordAddress/propertyAddress so the document can show a real letterhead — addresses default to a "please fill in" placeholder rather than a fabricated address, since this is a real WG's data. Landlord fills them once via new fields in the contract generator form; persisted per contract like the other template options.
This commit is contained in:
parent
bba4fc318b
commit
410db93c30
@ -98,6 +98,9 @@ const CONTRACT_DOCUMENTS_SELECT = {
|
||||
wifiIncluded: true,
|
||||
furnished: true,
|
||||
utilityBillingModel: true,
|
||||
landlordName: true,
|
||||
landlordAddress: true,
|
||||
propertyAddress: true,
|
||||
signedAt: true,
|
||||
user: { select: { id: true, fullName: true } },
|
||||
room: { select: { id: true, roomNumber: true, sizeSqm: true } },
|
||||
@ -193,6 +196,9 @@ contractsRouter.post(
|
||||
depositAmount,
|
||||
rentAdjustmentType,
|
||||
rentAdjustmentClause,
|
||||
landlordName,
|
||||
landlordAddress,
|
||||
propertyAddress,
|
||||
} = req.body || {};
|
||||
|
||||
if (utilityBillingModel !== undefined && !UTILITY_MODELS.includes(utilityBillingModel)) {
|
||||
@ -212,6 +218,9 @@ contractsRouter.post(
|
||||
depositAmount: Number.isFinite(Number(depositAmount)) ? Number(depositAmount) : undefined,
|
||||
rentAdjustmentType: rentAdjustmentType || undefined,
|
||||
rentAdjustmentClause: typeof rentAdjustmentClause === 'string' ? rentAdjustmentClause.trim() || null : undefined,
|
||||
landlordName: typeof landlordName === 'string' && landlordName.trim() ? landlordName.trim() : undefined,
|
||||
landlordAddress: typeof landlordAddress === 'string' ? landlordAddress.trim() || null : undefined,
|
||||
propertyAddress: typeof propertyAddress === 'string' ? propertyAddress.trim() || null : undefined,
|
||||
},
|
||||
include: { room: true, user: true },
|
||||
}).catch(() => null);
|
||||
|
||||
@ -3,15 +3,24 @@ import { Contract, Room, User, UtilityBillingModel, RentAdjustmentType } from '@
|
||||
|
||||
type ContractForPdf = Contract & { room: Room; user: User };
|
||||
|
||||
const ADJUSTMENT_LABEL: Record<RentAdjustmentType, string> = {
|
||||
NONE: 'Keine Mietanpassungsklausel.',
|
||||
INDEX_MIETE: 'Indexmietklausel: Die Miete kann sich gemäß der Entwicklung des Verbraucherpreisindexes ändern.',
|
||||
STAFFEL_MIETE: 'Staffelmietklausel: Die Miete steigt zu vorab vereinbarten Zeitpunkten in vereinbarten Schritten.',
|
||||
const UTILITY_LABEL: Record<UtilityBillingModel, string> = {
|
||||
PAUSCHALE:
|
||||
'Die Nebenkosten werden als Pauschale erhoben und sind in der in § 4 genannten Warmmiete enthalten. ' +
|
||||
'Eine gesonderte Abrechnung findet nicht statt, auch nicht bei Nachzahlung oder Guthaben.',
|
||||
ABRECHNUNG:
|
||||
'Die Nebenkosten werden als Vorauszahlung erhoben und jährlich auf Basis der tatsächlich angefallenen ' +
|
||||
'Kosten anteilig nach Zimmergröße (§ 556a BGB) abgerechnet. Nachzahlungen und Guthaben werden im ' +
|
||||
'Rahmen der jeweiligen Abrechnung ausgeglichen.',
|
||||
};
|
||||
|
||||
const UTILITY_LABEL: Record<UtilityBillingModel, string> = {
|
||||
PAUSCHALE: 'Die Nebenkosten sind als Pauschale in der Warmmiete enthalten und werden nicht separat abgerechnet.',
|
||||
ABRECHNUNG: 'Die Nebenkosten werden jährlich auf Basis der tatsächlichen Kosten anteilig nach Zimmergröße abgerechnet.',
|
||||
const ADJUSTMENT_LABEL: Record<RentAdjustmentType, string> = {
|
||||
NONE: 'Es ist keine Mietanpassungsklausel vereinbart. Mieterhöhungen richten sich nach den gesetzlichen Vorschriften (§§ 557 ff. BGB).',
|
||||
INDEX_MIETE:
|
||||
'Es wird eine Indexmiete gemäß § 557b BGB vereinbart. Die Miete verändert sich entsprechend der ' +
|
||||
'Entwicklung des vom Statistischen Bundesamt ermittelten Verbraucherpreisindexes für Deutschland.',
|
||||
STAFFEL_MIETE:
|
||||
'Es wird eine Staffelmiete gemäß § 557a BGB vereinbart. Die Miete erhöht sich zu den nachfolgend ' +
|
||||
'genannten Zeitpunkten um die jeweils vereinbarten Beträge.',
|
||||
};
|
||||
|
||||
function formatEuro(value: unknown): string {
|
||||
@ -19,82 +28,199 @@ function formatEuro(value: unknown): string {
|
||||
}
|
||||
|
||||
function formatDate(date: Date | null | undefined): string {
|
||||
return date ? date.toLocaleDateString('de-DE') : 'unbefristet';
|
||||
return date ? date.toLocaleDateString('de-DE') : null as unknown as string;
|
||||
}
|
||||
|
||||
function addressOrPlaceholder(value: string | null | undefined): string {
|
||||
return value && value.trim() ? value.trim() : '[Anschrift bitte im Vertragsformular ergänzen]';
|
||||
}
|
||||
|
||||
/**
|
||||
* Erzeugt aus den Vertrags-Baukasten-Feldern (siehe Contract-Model:
|
||||
* wifiIncluded, furnished, utilityBillingModel, noticePeriodMonths,
|
||||
* rentAdjustmentType, depositAmount, ...) ein PDF-Dokument als Buffer.
|
||||
* Erzeugt aus den Vertrags-Baukasten-Feldern ein vollständig strukturiertes
|
||||
* PDF im Format eines gewöhnlichen Wohnraum-/WG-Zimmer-Mietvertrags
|
||||
* (Vertragsparteien, Mieträume, Mietzeit, Miete/Nebenkosten, Kaution,
|
||||
* Instandhaltung, Hausordnung, Tierhaltung, Untervermietung, Kündigung,
|
||||
* Mietanpassung, salvatorische Klausel, Unterschriften).
|
||||
*
|
||||
* Ausdrücklich eine Vorlage/Dokumentation auf Basis der App-Daten, keine
|
||||
* rechtsverbindliche oder anwaltlich geprüfte Vertragsvorlage.
|
||||
* Ausdrücklich eine aus den App-Daten generierte Vorlage, keine
|
||||
* anwaltlich geprüfte oder rechtsverbindlich garantierte Vertragsurkunde
|
||||
* — siehe Hinweis am Dokumentende.
|
||||
*/
|
||||
export function generateContractPdf(contract: ContractForPdf): Promise<Buffer> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const doc = new PDFDocument({ margin: 56 });
|
||||
const doc = new PDFDocument({ margin: 56, bufferPages: true, size: 'A4' });
|
||||
const chunks: Buffer[] = [];
|
||||
doc.on('data', (chunk) => chunks.push(chunk));
|
||||
doc.on('end', () => resolve(Buffer.concat(chunks)));
|
||||
doc.on('error', reject);
|
||||
|
||||
doc.fontSize(18).text('Mietvertrag über Wohnraum (Vorlage)', { underline: true });
|
||||
doc.moveDown(0.3);
|
||||
doc.fontSize(9).fillColor('#666666').text(
|
||||
'Automatisch erstellte Vorlage auf Basis der Angaben in der WG-Verwaltungs-App. ' +
|
||||
'Keine Rechtsberatung, keine Gewähr auf rechtliche Vollständigkeit.',
|
||||
const H1 = () => doc.font('Helvetica-Bold').fontSize(11);
|
||||
const BODY = () => doc.font('Helvetica').fontSize(10.5);
|
||||
const section = (title: string, bodyFn: () => void) => {
|
||||
doc.moveDown(0.9);
|
||||
H1().text(title);
|
||||
doc.moveDown(0.25);
|
||||
BODY();
|
||||
bodyFn();
|
||||
};
|
||||
|
||||
// --- Titel ---
|
||||
doc.font('Helvetica-Bold').fontSize(17).text('Mietvertrag über Wohnraum', { align: 'center' });
|
||||
doc.moveDown(0.15);
|
||||
doc.font('Helvetica').fontSize(10).fillColor('#555555').text(
|
||||
`Zimmer ${contract.room.roomNumber} · ${addressOrPlaceholder(contract.propertyAddress)}`,
|
||||
{ align: 'center' },
|
||||
);
|
||||
doc.fillColor('#000000');
|
||||
doc.moveDown(1);
|
||||
doc.moveDown(1.2);
|
||||
|
||||
doc.fontSize(13).text('§ 1 Mietparteien und Mietobjekt', { underline: true });
|
||||
doc.fontSize(11).moveDown(0.3);
|
||||
doc.text(`Vermieter: Familie Lombardo, WG Nackenheim`);
|
||||
doc.text(`Mieter: ${contract.user.fullName}`);
|
||||
doc.text(`Mietobjekt: ${contract.room.roomNumber}, ${Number(contract.room.sizeSqm)} qm, WG Nackenheim`);
|
||||
doc.moveDown(1);
|
||||
// --- § 1 Vertragsparteien ---
|
||||
section('§ 1 Vertragsparteien', () => {
|
||||
doc.text(`Vermieter: ${contract.landlordName}`);
|
||||
doc.text(`Anschrift des Vermieters: ${addressOrPlaceholder(contract.landlordAddress)}`);
|
||||
doc.moveDown(0.4);
|
||||
doc.text(`Mieter: ${contract.user.fullName}`);
|
||||
doc.text(`E-Mail: ${contract.user.email}`);
|
||||
doc.moveDown(0.4);
|
||||
doc.text(
|
||||
'Vermieter und Mieter schließen den nachfolgenden Mietvertrag über den in § 2 bezeichneten Wohnraum ' +
|
||||
'innerhalb einer gemeinschaftlich bewohnten Wohnung (Wohngemeinschaft).',
|
||||
);
|
||||
});
|
||||
|
||||
doc.fontSize(13).text('§ 2 Mietzeit', { underline: true });
|
||||
doc.fontSize(11).moveDown(0.3);
|
||||
doc.text(`Mietbeginn: ${formatDate(contract.startDate)}`);
|
||||
doc.text(`Mietende: ${formatDate(contract.endDate)}`);
|
||||
doc.text(`Kündigungsfrist: ${contract.noticePeriodMonths} Monate zum Monatsende.`);
|
||||
doc.moveDown(1);
|
||||
// --- § 2 Mieträume ---
|
||||
section('§ 2 Mieträume', () => {
|
||||
doc.text(`Anschrift der Wohnung: ${addressOrPlaceholder(contract.propertyAddress)}`);
|
||||
doc.text(`Vermietetes Zimmer: ${contract.room.roomNumber}, ca. ${Number(contract.room.sizeSqm)} m².`);
|
||||
doc.text(
|
||||
'Mitbenutzung: Küche, Bad/WC sowie ggf. weitere Gemeinschaftsflächen der Wohnung stehen dem Mieter ' +
|
||||
'gemeinsam mit den übrigen Bewohnern zur Mitbenutzung zur Verfügung.',
|
||||
);
|
||||
doc.text(`Das Zimmer wird ${contract.furnished ? 'möbliert' : 'unmöbliert'} übergeben.`);
|
||||
doc.text(`Internet/WLAN: ${contract.wifiIncluded ? 'im Mietpreis enthalten.' : 'nicht im Mietpreis enthalten.'}`);
|
||||
});
|
||||
|
||||
doc.fontSize(13).text('§ 3 Miete und Nebenkosten', { underline: true });
|
||||
doc.fontSize(11).moveDown(0.3);
|
||||
doc.text(`Warmmiete gesamt: ${formatEuro(contract.totalWarmRent)} pro Monat, fällig am ${contract.paymentDueDay}. Werktag.`);
|
||||
doc.text(UTILITY_LABEL[contract.utilityBillingModel]);
|
||||
doc.text(`Kaution: ${formatEuro(contract.depositAmount)}.`);
|
||||
doc.moveDown(1);
|
||||
// --- § 3 Mietzeit ---
|
||||
section('§ 3 Mietzeit', () => {
|
||||
doc.text(`Das Mietverhältnis beginnt am ${formatDate(contract.startDate)}.`);
|
||||
doc.text(
|
||||
contract.endDate
|
||||
? `Es endet, ohne dass es einer Kündigung bedarf, am ${formatDate(contract.endDate)}.`
|
||||
: 'Es läuft auf unbestimmte Zeit.',
|
||||
);
|
||||
doc.text(
|
||||
`Die ordentliche Kündigungsfrist beträgt ${contract.noticePeriodMonths} Monate zum Ende eines ` +
|
||||
'Kalendermonats. Die Kündigung bedarf der Schriftform. Das Recht zur außerordentlichen ' +
|
||||
'Kündigung aus wichtigem Grund bleibt unberührt.',
|
||||
);
|
||||
});
|
||||
|
||||
doc.fontSize(13).text('§ 4 Ausstattung', { underline: true });
|
||||
doc.fontSize(11).moveDown(0.3);
|
||||
doc.text(`WLAN: ${contract.wifiIncluded ? 'im Mietpreis inbegriffen.' : 'nicht im Mietpreis enthalten.'}`);
|
||||
doc.text(`Zimmer: ${contract.furnished ? 'möbliert übergeben.' : 'unmöbliert übergeben.'}`);
|
||||
doc.moveDown(1);
|
||||
// --- § 4 Miete und Nebenkosten ---
|
||||
section('§ 4 Miete und Nebenkosten', () => {
|
||||
doc.text(`Die Warmmiete beträgt ${formatEuro(contract.totalWarmRent)} monatlich.`);
|
||||
doc.text(
|
||||
`Sie ist im Voraus, spätestens am ${contract.paymentDueDay}. Werktag eines jeden Monats, auf das ` +
|
||||
'vom Vermieter benannte Konto zu entrichten.',
|
||||
);
|
||||
doc.text(UTILITY_LABEL[contract.utilityBillingModel]);
|
||||
});
|
||||
|
||||
doc.fontSize(13).text('§ 5 Mietanpassung', { underline: true });
|
||||
doc.fontSize(11).moveDown(0.3);
|
||||
doc.text(ADJUSTMENT_LABEL[contract.rentAdjustmentType]);
|
||||
if (contract.rentAdjustmentClause) doc.text(contract.rentAdjustmentClause);
|
||||
doc.moveDown(2);
|
||||
// --- § 5 Kaution ---
|
||||
section('§ 5 Kaution', () => {
|
||||
doc.text(
|
||||
`Der Mieter leistet eine Mietsicherheit (Kaution) in Höhe von ${formatEuro(contract.depositAmount)}. ` +
|
||||
'Die Kaution wird getrennt vom Vermögen des Vermieters angelegt und nach Beendigung des ' +
|
||||
'Mietverhältnisses und ordnungsgemäßer Rückgabe des Mietobjekts abzüglich etwaiger berechtigter ' +
|
||||
'Forderungen des Vermieters zurückerstattet.',
|
||||
);
|
||||
});
|
||||
|
||||
// --- § 6 Instandhaltung und Schönheitsreparaturen ---
|
||||
section('§ 6 Instandhaltung und Schönheitsreparaturen', () => {
|
||||
doc.text(
|
||||
'Der Mieter hat das Mietobjekt pfleglich zu behandeln und Schäden, die über die vertragsgemäße ' +
|
||||
'Abnutzung hinausgehen, dem Vermieter unverzüglich anzuzeigen. Schäden, die durch den Mieter, ' +
|
||||
'seine Besucher oder von ihm veranlasste Dritte schuldhaft verursacht wurden, sind vom Mieter zu ' +
|
||||
'ersetzen. Für die Ausführung von Schönheitsreparaturen gelten die gesetzlichen Regelungen, ' +
|
||||
'soweit hierzu keine gesonderte, wirksame Vereinbarung getroffen wurde.',
|
||||
);
|
||||
});
|
||||
|
||||
// --- § 7 Hausordnung und Gemeinschaftsleben ---
|
||||
section('§ 7 Hausordnung und Gemeinschaftsleben', () => {
|
||||
doc.text(
|
||||
'Der Mieter verpflichtet sich, die geltende Hausordnung sowie die innerhalb der Wohngemeinschaft ' +
|
||||
'einvernehmlich abgestimmten Regelungen (u. a. Putzplan) einzuhalten und auf die übrigen ' +
|
||||
'Bewohner Rücksicht zu nehmen. Die aktuelle Hausordnung ist im Dokumenten-Safe der ' +
|
||||
'Verwaltungs-App hinterlegt.',
|
||||
);
|
||||
});
|
||||
|
||||
// --- § 8 Tierhaltung und Untervermietung ---
|
||||
section('§ 8 Tierhaltung und Untervermietung', () => {
|
||||
doc.text(
|
||||
'Die Haltung von Haustieren sowie die vollständige oder teilweise Untervermietung/Gebrauchsüberlassung ' +
|
||||
'des Mietobjekts an Dritte bedürfen der vorherigen schriftlichen Zustimmung des Vermieters. Ein ' +
|
||||
'gesetzlicher Anspruch auf Erteilung der Erlaubnis bleibt hiervon unberührt.',
|
||||
);
|
||||
});
|
||||
|
||||
// --- § 9 Mietanpassung ---
|
||||
section('§ 9 Mietanpassung', () => {
|
||||
doc.text(ADJUSTMENT_LABEL[contract.rentAdjustmentType]);
|
||||
if (contract.rentAdjustmentClause) doc.text(contract.rentAdjustmentClause);
|
||||
});
|
||||
|
||||
// --- § 10 Sonstige Vereinbarungen ---
|
||||
section('§ 10 Sonstige Vereinbarungen', () => {
|
||||
doc.text(
|
||||
'Änderungen und Ergänzungen dieses Vertrags bedürfen der Schriftform. Dies gilt auch für die ' +
|
||||
'Aufhebung des Schriftformerfordernisses selbst. Sollte eine Bestimmung dieses Vertrags unwirksam ' +
|
||||
'sein oder werden, bleibt die Wirksamkeit der übrigen Bestimmungen hiervon unberührt.',
|
||||
);
|
||||
});
|
||||
|
||||
// --- Unterschriften ---
|
||||
doc.moveDown(1.4);
|
||||
H1().text('Ort, Datum');
|
||||
BODY().moveDown(0.2).text('Nackenheim, ' + formatDate(contract.signedAt || new Date()));
|
||||
doc.moveDown(1.2);
|
||||
|
||||
const sigTop = doc.y;
|
||||
doc.moveTo(56, sigTop + 40).lineTo(260, sigTop + 40).stroke();
|
||||
doc.moveTo(320, sigTop + 40).lineTo(524, sigTop + 40).stroke();
|
||||
doc.fontSize(9).text('Vermieter', 56, sigTop + 44, { width: 204 });
|
||||
doc.text('Mieter', 320, sigTop + 44, { width: 204 });
|
||||
|
||||
if (contract.signedAt && contract.tenantSignatureUrl) {
|
||||
doc.fontSize(13).text('§ 6 Unterschrift', { underline: true });
|
||||
doc.fontSize(11).moveDown(0.3);
|
||||
doc.text(`Digital unterschrieben von ${contract.user.fullName} am ${formatDate(contract.signedAt)}.`);
|
||||
doc.moveDown(0.5);
|
||||
const match = /^data:image\/(png|jpeg);base64,(.+)$/.exec(contract.tenantSignatureUrl);
|
||||
if (match) {
|
||||
try {
|
||||
doc.image(Buffer.from(match[2], 'base64'), { width: 200 });
|
||||
doc.image(Buffer.from(match[2], 'base64'), 320, sigTop - 36, { width: 180 });
|
||||
} catch {
|
||||
doc.text('(Unterschrift-Bild konnte nicht eingebettet werden)');
|
||||
/* Unterschrift-Bild ungültig — Zeile bleibt leer, Signatur-Vermerk unten reicht als Beleg. */
|
||||
}
|
||||
}
|
||||
} else {
|
||||
doc.fontSize(11).text('Noch nicht unterschrieben.');
|
||||
doc.fontSize(9).fillColor('#555555').text(
|
||||
`Digital unterschrieben von ${contract.user.fullName} am ${formatDate(contract.signedAt)}.`,
|
||||
56,
|
||||
sigTop + 60,
|
||||
);
|
||||
doc.fillColor('#000000');
|
||||
}
|
||||
|
||||
// --- Fußzeile mit Hinweis auf allen Seiten ---
|
||||
const pageCount = doc.bufferedPageRange().count;
|
||||
for (let i = 0; i < pageCount; i++) {
|
||||
doc.switchToPage(i);
|
||||
doc.fontSize(7.5).fillColor('#888888').text(
|
||||
'Automatisiert aus den Angaben in der WG-Verwaltungs-App erstellte Vertragsvorlage — keine Rechtsberatung, ' +
|
||||
`keine Gewähr für rechtliche Vollständigkeit. Seite ${i + 1} von ${pageCount}.`,
|
||||
56,
|
||||
doc.page.height - 40,
|
||||
{ width: doc.page.width - 112, align: 'center' },
|
||||
);
|
||||
doc.fillColor('#000000');
|
||||
}
|
||||
|
||||
doc.end();
|
||||
|
||||
@ -281,6 +281,9 @@ model Contract {
|
||||
wifiIncluded Boolean @default(false) @map("wifi_included")
|
||||
furnished Boolean @default(false)
|
||||
utilityBillingModel UtilityBillingModel @default(PAUSCHALE) @map("utility_billing_model")
|
||||
landlordName String @default("Familie Lombardo") @map("landlord_name")
|
||||
landlordAddress String? @map("landlord_address") // vom Vermieter selbst einzutragen, keine erfundene Anschrift
|
||||
propertyAddress String? @map("property_address") // Anschrift der Wohnung/des Hauses
|
||||
|
||||
// Digitale Unterschrift des Mieters zum generierten Vertragsdokument
|
||||
tenantSignatureUrl String? @map("tenant_signature_url") // Data-URL des gezeichneten Unterschrift-Bilds
|
||||
|
||||
@ -2849,6 +2849,35 @@
|
||||
function buildContractTemplateForm(c) {
|
||||
const form = el('div', 'contract-template-form');
|
||||
|
||||
const landlordNameField = el('div', 'field');
|
||||
landlordNameField.style.minWidth = '180px';
|
||||
landlordNameField.appendChild(el('label', null, 'Vermieter (Name)'));
|
||||
const landlordNameInput = document.createElement('input');
|
||||
landlordNameInput.type = 'text';
|
||||
landlordNameInput.value = c.landlordName || '';
|
||||
landlordNameField.appendChild(landlordNameInput);
|
||||
form.appendChild(landlordNameField);
|
||||
|
||||
const landlordAddressField = el('div', 'field');
|
||||
landlordAddressField.style.minWidth = '220px';
|
||||
landlordAddressField.appendChild(el('label', null, 'Anschrift des Vermieters'));
|
||||
const landlordAddressInput = document.createElement('input');
|
||||
landlordAddressInput.type = 'text';
|
||||
landlordAddressInput.placeholder = 'Straße Hausnr., PLZ Ort';
|
||||
landlordAddressInput.value = c.landlordAddress || '';
|
||||
landlordAddressField.appendChild(landlordAddressInput);
|
||||
form.appendChild(landlordAddressField);
|
||||
|
||||
const propertyAddressField = el('div', 'field');
|
||||
propertyAddressField.style.minWidth = '220px';
|
||||
propertyAddressField.appendChild(el('label', null, 'Anschrift der Wohnung'));
|
||||
const propertyAddressInput = document.createElement('input');
|
||||
propertyAddressInput.type = 'text';
|
||||
propertyAddressInput.placeholder = 'Straße Hausnr., PLZ Nackenheim';
|
||||
propertyAddressInput.value = c.propertyAddress || '';
|
||||
propertyAddressField.appendChild(propertyAddressInput);
|
||||
form.appendChild(propertyAddressField);
|
||||
|
||||
const wifiField = el('label', 'field-checkbox');
|
||||
const wifiCheckbox = document.createElement('input');
|
||||
wifiCheckbox.type = 'checkbox';
|
||||
@ -2926,6 +2955,9 @@
|
||||
rentAdjustmentType: adjustmentSelect.value,
|
||||
noticePeriodMonths: noticeInput.value,
|
||||
depositAmount: depositInput.value,
|
||||
landlordName: landlordNameInput.value,
|
||||
landlordAddress: landlordAddressInput.value,
|
||||
propertyAddress: propertyAddressInput.value,
|
||||
}, generateBtn);
|
||||
form.appendChild(generateBtn);
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user