Real Estate Developer Website on 1C-Bitrix
A developer's website is not a property aggregator or a classifieds board. A developer sells specific apartments in specific buildings, and buyers make decisions based on floor plans, window views, and construction progress — not someone else's interior photos. The average deal cycle runs 2-4 months. During that time, a visitor returns to the site 8-15 times: comparing layouts, checking construction updates, recalculating mortgage payments. If the site doesn't serve those tasks — the client moves to a competitor whose interactive floor plan actually works.
The defining technical feature of a developer website is the interactive floor plan (known in the industry as a "chess board" — a visual grid of floors and apartments). It's not just a table of floor vs. section. It's an SVG diagram where every apartment is clickable, color-coded by status, and linked to real data from the info block. The chess board is what separates a developer's site from a generic catalog template.
Data Architecture: Complex → Building → Apartment
The info block structure mirrors the physical hierarchy: a residential complex contains buildings (sections), and a building contains apartments.
Info block "Residential Complexes" (or a Highload block if there are fewer than 50 complexes) — the parent entity. Properties:
| Property | Type | Purpose |
|---|---|---|
| NAME | Complex name | Title and SEO |
| ADDRESS | S (string) | Address, geocoding |
| COORDINATES | S ("lat,lng") | Map pin |
| STAGE | L (list) | Phase: design, excavation, construction, completed |
| COMPLETION_DATE | S (date) | Planned completion |
| INFRASTRUCTURE | S (HTML/text) | Nearby infrastructure description |
| DEVELOPER_ID | E (link) | Reference to the developer company |
| GENPLAN_SVG | F (file) | SVG site plan of the territory |
Info block sections represent buildings and sections (entrances). Each complex is a top-level section. Buildings are second-level sections under the complex. If a building has multiple entrances — third-level sections. This nesting lets you use the built-in bitrix:catalog.section.list navigation without custom queries.
Info block elements are apartments. Each apartment belongs to a building (or entrance) section. Minimum property set:
| Property | Type | Index | Notes |
|---|---|---|---|
| ROOMS | L (list) | Faceted | Studio, 1, 2, 3, 4+ |
| AREA_TOTAL | N (number) | Faceted | Total area, m² |
| AREA_LIVING | N | None | Living area |
| AREA_KITCHEN | N | None | Kitchen area |
| FLOOR | N | Faceted | Floor number |
| PRICE | N | Faceted | Price |
| PRICE_PER_M2 | N | Faceted | Price per m² |
| STATUS | L | Faceted | Available / Reserved / Sold |
| LAYOUT_IMG | F (file) | None | Floor plan image |
| LAYOUT_SVG_ID | S (string) | None | Apartment ID in the SVG chess board |
| FINISHING | L | Faceted | Shell / Rough / Turnkey |
| WINDOW_VIEW | L | None | Courtyard / Street / Panoramic |
| DECORATION_IMG | F (multiple) | None | Finishing photos if applicable |
The LAYOUT_SVG_ID property is the bridge between database records and the SVG file. More on this below.
The Interactive Chess Board: A Deep Dive
The chess board is a visual cross-section of a building — floors and apartments rendered as an SVG diagram. The user sees a building facade or a floor plan, clicks on an apartment, and gets a card with price, area, and layout. Apartments are color-coded by status: green for available, yellow for reserved, gray for sold. This is the primary conversion tool: developers with a working chess board see 30-40% more inquiries than those with a plain table catalog.
Preparing the SVG file. A designer draws the facade or floor plan in Adobe Illustrator or Figma and exports to SVG. Each apartment is a separate <path>, <polygon>, or <rect> element with a data-apartment-id attribute matching the LAYOUT_SVG_ID property value in the info block. Example SVG fragment:
<svg viewBox="0 0 1200 800" xmlns="http://www.w3.org/2000/svg">
<!-- Floor 5, Section 1 -->
<g data-floor="5" data-section="1">
<path data-apartment-id="B1-5-01"
d="M100,200 L250,200 L250,350 L100,350 Z"
class="apartment" />
<path data-apartment-id="B1-5-02"
d="M260,200 L450,200 L450,350 L260,350 Z"
class="apartment" />
<path data-apartment-id="B1-5-03"
d="M460,200 L600,200 L600,350 L460,350 Z"
class="apartment" />
</g>
<!-- Stairwell, elevator — no data-apartment-id -->
<rect x="610" y="200" width="40" height="150" class="stairs" />
</svg>
Naming convention for data-apartment-id: building-floor-number (B1-5-01). The format is locked in the technical specification before development starts. If the designer delivers SVG files without these attributes, a developer will spend 2-3 days adding markup manually. That's why an SVG template with sample attributes goes to the designer before they start drawing.
Inline SVG, not <img>. The SVG is embedded directly in the page HTML — not loaded via <img> or <object>. The reason: JavaScript cannot access the contents of <img src="plan.svg"> (cross-origin restrictions and no DOM access). Inline SVG becomes part of the page DOM, and every <path data-apartment-id="..."> is reachable via document.querySelector.
In practice, Bitrix reads the SVG file from the section (building) property and outputs its contents via file_get_contents() directly into the component template:
// Chess board component template.php
$svgPath = CFile::GetPath($arResult['SECTION']['UF_FLOOR_PLAN_SVG']);
$svgContent = file_get_contents($_SERVER['DOCUMENT_ROOT'] . $svgPath);
$svgContent = preg_replace('/<\?xml[^?]*\?>/', '', $svgContent);
echo '<div class="chess-board">' . $svgContent . '</div>';
JavaScript: binding SVG to apartment data. On page load, the frontend receives a JSON array of apartments for the current building — either via an AJAX call or an inline <script> tag populated from CIBlockElement::GetList. JSON structure:
const apartments = [
{
svgId: "B1-5-01",
id: 4521,
rooms: 2,
area: 58.3,
floor: 5,
price: 7200000,
pricePerM2: 123500,
status: "available", // available | reserved | sold
layoutImg: "/upload/layouts/b1-5-01.jpg",
url: "/sunrise-complex/building-1/apartment-4521/"
},
// ...
];
Initialization:
function initChessBoard(apartments) {
const svgContainer = document.querySelector('.chess-board svg');
if (!svgContainer) return;
const statusColors = {
available: '#4CAF50',
reserved: '#FFC107',
sold: '#9E9E9E'
};
apartments.forEach(apt => {
const el = svgContainer.querySelector(
`[data-apartment-id="${apt.svgId}"]`
);
if (!el) return;
el.style.fill = statusColors[apt.status];
el.style.cursor = apt.status === 'sold' ? 'default' : 'pointer';
el.style.transition = 'fill 0.2s, opacity 0.2s';
el.addEventListener('mouseenter', () => {
if (apt.status === 'sold') return;
el.style.opacity = '0.7';
showTooltip(el, apt);
});
el.addEventListener('mouseleave', () => {
el.style.opacity = '1';
hideTooltip();
});
el.addEventListener('click', () => {
if (apt.status === 'sold') return;
showApartmentCard(apt);
});
});
}
Hover tooltips. When the user hovers over an apartment, a tooltip appears showing rooms, area, and price. Position is calculated from the SVG element's getBoundingClientRect():
function showTooltip(svgElement, apt) {
const tooltip = document.getElementById('chess-tooltip');
const rect = svgElement.getBoundingClientRect();
tooltip.innerHTML = `
<div class="chess-tooltip__rooms">${apt.rooms}-bed</div>
<div class="chess-tooltip__area">${apt.area} m²</div>
<div class="chess-tooltip__price">
${apt.price.toLocaleString('en-US')}
</div>
`;
tooltip.style.left = `${rect.left + rect.width / 2}px`;
tooltip.style.top = `${rect.top - 10}px`;
tooltip.style.display = 'block';
}
Click opens a detail card. Clicking an available apartment opens a side panel or modal with full details: floor plan image, room-by-room areas, floor, window view, finishing, and buttons for "Reserve" and "Download PDF." The data is already loaded in the apartments array — no additional AJAX call needed.
Filtering directly on the chess board. Above the SVG — a filter panel: room count (checkboxes), area range, price range. When filters change, JavaScript hides non-matching apartments by setting el.style.opacity = '0.1' and pointerEvents = 'none'. Matching apartments stay bright. This runs instantly with zero server calls — all data is client-side.
Mobile responsiveness. On desktop, the SVG fills 100% of its container width, and viewBox handles scaling. On mobile (screen < 768px), a facade view is unreadable — apartments are too small to tap. Two solutions: 1) switch to a per-floor view where one floor spans the full screen width and the user swipes between floors; 2) pinch-to-zoom via panzoom or hammer.js. The per-floor approach is more reliable.
Real-time status updates. When a manager reserves an apartment in CRM or 1C, the status on the website should change without a page reload. Options: WebSocket (overkill for most developers), Server-Sent Events (simpler), or polling every 30 seconds. Polling is the simplest: an AJAX request returns [{svgId, status}], and JavaScript updates the colors. For 200 apartments per building, the JSON response is under 5 KB.
Construction Progress: Photo Reports and Cameras
The "Construction Progress" section is mandatory for buildings under construction. A separate info block "Photo Reports": each element is one report (date, description, multiple file property for photos). Sections map to buildings. Display as a timeline, sorted by DATE_ACTIVE_FROM DESC.
Drone video — a string property with a YouTube/Vimeo URL. Embedded via <iframe> with loading="lazy".
Webcam — an <iframe> streaming from a provider (Ivideon, Trassir, or similar). Embedded in the complex section template. Template caching is disabled for the camera block, while the rest of the page caches normally.
Mortgage Calculator with Bank Programs
Pure JavaScript. A Highload block "Mortgage Programs" stores fields: BANK_NAME, PROGRAM_NAME, RATE, MIN_DOWNPAYMENT, MAX_TERM, IS_ACTIVE. On apartment page load — an AJAX call or inline JSON with active programs.
Annuity payment formula:
P = S × (r × (1 + r)^n) / ((1 + r)^n − 1)
where S = property price − down payment
r = annual rate / 12 / 100
n = term in months
Interface: bank selector (dropdown) → rate and minimum down payment auto-fill → three sliders (property price auto-filled from the apartment card, down payment, term) → result: monthly payment, total overpayment, total amount. Recalculation on every slider input event, no debounce needed — the math is instant.
Apartment Comparison and Favorites
Favorites for unauthenticated users — localStorage. An array of {id, complexId} objects, capped at 30. Heart icon on apartment cards and on the chess board. Counter in the site header. On login — sync localStorage with the UserFavorites Highload block.
Comparison supports up to 4 apartments. A horizontally scrollable table on mobile: rooms, total/living/kitchen area, floor, price, price per m², finishing, status. Layout images displayed side by side for visual comparison.
PDF Floor Plan Generation
The user clicks "Download PDF" on an apartment card and receives a PDF with the floor plan, specifications, and developer contacts. Generated server-side via mPDF or Dompdf.
The PDF template is an HTML file rendered to PDF. Contents: developer logo, complex name, building, floor, apartment number, floor plan image, specifications table, QR code linking to the apartment page. The floor plan image uses an absolute server path (mPDF doesn't reliably fetch images by URL).
$mpdf = new \Mpdf\Mpdf(['tempDir' => '/tmp/mpdf']);
$html = $this->renderPdfTemplate($apartment);
$mpdf->WriteHTML($html);
$mpdf->Output('apartment-' . $apartment['ID'] . '.pdf', 'D');
PDFs are generated on demand, not pre-built. Caching is keyed on ELEMENT_ID + TIMESTAMP_X hash: if the apartment data hasn't changed, serve the cached PDF from /upload/pdf_cache/.
CRM and 1C Integration
Inquiries → Bitrix24. The "Reserve" / "Get Consultation" form triggers REST API crm.deal.add (deals, not leads — developers work with deals). Fields: TITLE = "Inquiry for apt. #{number}, {complex name}", CATEGORY_ID = real estate sales pipeline, UF_CRM_APARTMENT_ID = info block element ID, UF_CRM_COMPLEX = complex name, SOURCE_ID = "WEB". Secured via webhook with a secret key over HTTPS.
1C synchronization. Developers track apartment inventory in 1C:Enterprise. Prices and statuses update in 1C, and the website must reflect current data. Integration options:
-
Built-in 1C-Bitrix exchange (the
catalogmodule) — works via CommerceML (XML). 1C exports XML with products, Bitrix imports them. Handles prices well, but apartment statuses (available/reserved/sold) require custom property mapping. -
REST API — 1C calls a website endpoint (
/api/apartments/update-status/), sending JSON{apartment_code: "B1-5-01", status: "reserved", price: 7500000}. The Bitrix-side controller finds the element byLAYOUT_SVG_IDand updates properties viaCIBlockElement::SetPropertyValues. - Periodic CSV export — 1C drops a CSV to FTP, a Bitrix agent picks it up every 15 minutes and parses it. Crude but effective for developers running legacy 1C without REST API.
SEO and Structured Data
Meta templates via info block settings:
- Title:
Buy an apartment in {complex name} — #ELEMENT_NAME#, from #PROPERTY_PRICE# - Description:
#PROPERTY_ROOMS#-bed apartment, #PROPERTY_AREA_TOTAL# m², floor #PROPERTY_FLOOR# in {complex name}. Developer: #PROPERTY_DEVELOPER#.
Structured data — Schema.org Residence for the complex and Offer for each apartment:
{
"@context": "https://schema.org",
"@type": "Residence",
"name": "Sunrise Complex",
"address": "15 Builder St., Moscow",
"geo": {"@type": "GeoCoordinates", "latitude": 55.75, "longitude": 37.61},
"makesOffer": [
{
"@type": "Offer",
"name": "2-bed apartment, 58.3 m², floor 5",
"price": "7200000",
"priceCurrency": "RUB",
"availability": "https://schema.org/InStock"
}
]
}
For ranking on "buy apartment in [complex name]" queries: unique text content on the complex page (not just apartment cards), breadcrumbs (BreadcrumbList), and an XML sitemap with individual URLs for every apartment.
Stages and Timelines
| Project Scale | Timeline |
|---|---|
| Single complex, 1-2 buildings, up to 200 apartments, basic chess board | 1-4 weeks |
| 2-5 complexes, chess board + construction progress + mortgage calculator + CRM | 5-8 weeks |
| Developer portal, 10+ complexes, 1C integration, PDF, user account | 8-12 weeks |
Timelines assume the designer delivers SVG files with correct data-apartment-id markup. If SVGs need to be created from scratch using architectural drawings — add 1-2 weeks per building.







