{"id":2266,"date":"2026-09-02T00:35:31","date_gmt":"2026-09-01T17:35:31","guid":{"rendered":"https:\/\/lazarewww.ru\/cases\/vizion-2\/"},"modified":"2026-09-02T00:35:31","modified_gmt":"2026-09-01T17:35:31","slug":"vizion","status":"publish","type":"isotope","link":"https:\/\/lazarewww.ru\/en\/cases\/vizion\/","title":{"rendered":"Vizion"},"content":{"rendered":"<div class=\"wp-bootstrap-blocks-row row\">\n\t\n\n<div class=\"col-12 col-lg-3\">\n\t\t\t\n\n<h2 class=\"wp-block-heading with_decor\" id=\"vvedenie\">Introduction:<\/h2>\n\n\t<\/div>\n\n\n\n<div class=\"col-12 col-lg-5\">\n\t\t\t<div class=\"p-2\">\n\t\t\t\n\n<p class=\"wp-block-paragraph\">A property developer lives inside a CRM: leads, developments, deals, payments. It runs the day-to-day well and answers management&#8217;s questions badly. How much was sold this quarter, who owes money, which apartments have been sitting unsold for a third month \u2014 for that you go to Excel, and from Excel to Power BI. By the time a report is assembled and checked, it has gone stale.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Vizion sits alongside the CRM, reads its database and hands management finished reporting. Not a data dump but a working tool: filters, groupings, drill-down to an individual deal.<\/p>\n\n\t\t<\/div>\n\t<\/div>\n\n<\/div>\n\n\n<div class=\"wp-bootstrap-blocks-row row case-text\">\n\t\n\n<div class=\"col-12 col-lg-6\">\n\t\t\t<div class=\"p-2\">\n\t\t\t\n\n<p class=\"wp-block-paragraph\"><em>The screenshots show the system&#8217;s interface. Company names, personal names and the figures in the tables have been replaced with fictional ones: no client data is shown.<\/em><\/p>\n\n\t\t<\/div>\n\t<\/div>\n\n<\/div>\n\n\n\n\n<div class=\"wp-bootstrap-blocks-row row case-media\">\n\t\n\n<div class=\"col-12\">\n\t\t\t\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1440\" height=\"900\" src=\"\/wp-content\/uploads\/2026\/09\/vizion-reports-list.png\" alt=\"The report list screen with ready-made reports and the AI report generation tile\" class=\"wp-image-1993\" srcset=\"https:\/\/lazarewww.ru\/wp-content\/uploads\/2026\/09\/vizion-reports-list.png 1440w, https:\/\/lazarewww.ru\/wp-content\/uploads\/2026\/09\/vizion-reports-list-300x188.png 300w, https:\/\/lazarewww.ru\/wp-content\/uploads\/2026\/09\/vizion-reports-list-1024x640.png 1024w, https:\/\/lazarewww.ru\/wp-content\/uploads\/2026\/09\/vizion-reports-list-768x480.png 768w, https:\/\/lazarewww.ru\/wp-content\/uploads\/2026\/09\/vizion-reports-list-900x563.png 900w, https:\/\/lazarewww.ru\/wp-content\/uploads\/2026\/09\/vizion-reports-list-600x375.png 600w\" sizes=\"auto, (max-width: 1440px) 100vw, 1440px\" \/><figcaption class=\"wp-element-caption\">The report list: ready-made forms and a separate &#171;generate a report with AI&#187; tile.<\/figcaption><\/figure>\n\n\t<\/div>\n\n<\/div>\n\n\n\n<hr class=\"hr-dashed opacity-50 my-5\">\n\n\n<div class=\"wp-bootstrap-blocks-row row\">\n\t\n\n<div class=\"col-12 col-lg-3\">\n\t\t\t\n\n<h2 class=\"wp-block-heading with_decor\" id=\"chto-sdelano\">How it went:<\/h2>\n\n\t<\/div>\n\n\n\n<div class=\"col-12 col-lg-5\">\n\t\t\t<div class=\"p-2\">\n\t\t\t\n\n<p class=\"wp-block-paragraph\"><strong>A report is a config, not code.<\/strong> The decision everything else grew out of. A report is described as a structure: columns and their types, filters, expressions, groupings with aggregates, derived metrics, overdue badges, totals. Adding a new report means describing it. That is where edits on the fly come from, and moving a report from one client to another.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Reports are put together by a model.<\/strong> Since a config is a structure, a human is not the only one who can write it. The system runs a chat on top of the company&#8217;s data: ask it &#171;how many deals in January&#187; and it answers in words; ask it &#171;make a report on quarterly sales&#187; and it creates a report. Before generating, the model has to look at the company&#8217;s real data; afterwards the config is run as a dry pass. Got an entity or a relation name wrong \u2014 it gets a structured error with hints and rewrites it itself. From there the report is edited by conversation: &#171;add a column&#187;, &#171;group by residential development&#187;. The model runs on a server of our own: reports hold the sales, debts and payments of someone else&#8217;s business, and data like that does not go outside.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Every client is on their own database.<\/strong> Each has their own copy of the CRM and their own reference lists. Connections are stored in the database rather than in the application config, passwords are encrypted, and the right one is substituted on the fly for the active company. To make a single system report work for everyone, I separated the config from any particular database: the report holds a reference to a concept, and what that concept turns out to be for this client is decided at query time.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Import from Power BI.<\/strong> A separate service unpacks a Power BI file and returns its model as a structure. The model is then matched against the CRM data, and the same report is assembled inside the system. A route for anyone who has been on Power BI for years and does not want to start from scratch.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Documents.<\/strong> Sales proposals for a development \u2014 with the company&#8217;s logo, palette and company details, with discounts calculated, PDF on the way out. Plus working with other people&#8217;s templates: the client uploads their own Word file with placeholders, the system suggests which data field goes where, a human confirms, and the document is filled in.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Dashboards<\/strong> are assembled from widgets by dragging, and every user has one of their own.<\/p>\n\n\t\t<\/div>\n\t<\/div>\n\n<\/div>\n\n\n<div class=\"wp-bootstrap-blocks-row row case-gallery\">\n\t\n\n<div class=\"col-12 col-lg-6\">\n\t\t\t\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1440\" height=\"900\" src=\"\/wp-content\/uploads\/2026\/09\/vizion-svod-po-proektam.png\" alt=\"A summary table by project with a totals row\" class=\"wp-image-1994\" srcset=\"https:\/\/lazarewww.ru\/wp-content\/uploads\/2026\/09\/vizion-svod-po-proektam.png 1440w, https:\/\/lazarewww.ru\/wp-content\/uploads\/2026\/09\/vizion-svod-po-proektam-300x188.png 300w, https:\/\/lazarewww.ru\/wp-content\/uploads\/2026\/09\/vizion-svod-po-proektam-1024x640.png 1024w, https:\/\/lazarewww.ru\/wp-content\/uploads\/2026\/09\/vizion-svod-po-proektam-768x480.png 768w, https:\/\/lazarewww.ru\/wp-content\/uploads\/2026\/09\/vizion-svod-po-proektam-900x563.png 900w, https:\/\/lazarewww.ru\/wp-content\/uploads\/2026\/09\/vizion-svod-po-proektam-600x375.png 600w\" sizes=\"auto, (max-width: 1440px) 100vw, 1440px\" \/><figcaption class=\"wp-element-caption\">Summary by project: figures per building and a totals row for the selection.<\/figcaption><\/figure>\n\n\t<\/div>\n\n\n\n<div class=\"col-12 col-lg-6\">\n\t\t\t\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1440\" height=\"900\" src=\"\/wp-content\/uploads\/2026\/09\/vizion-debitorka.png\" alt=\"The accounts receivable report with overdue badges\" class=\"wp-image-1995\" srcset=\"https:\/\/lazarewww.ru\/wp-content\/uploads\/2026\/09\/vizion-debitorka.png 1440w, https:\/\/lazarewww.ru\/wp-content\/uploads\/2026\/09\/vizion-debitorka-300x188.png 300w, https:\/\/lazarewww.ru\/wp-content\/uploads\/2026\/09\/vizion-debitorka-1024x640.png 1024w, https:\/\/lazarewww.ru\/wp-content\/uploads\/2026\/09\/vizion-debitorka-768x480.png 768w, https:\/\/lazarewww.ru\/wp-content\/uploads\/2026\/09\/vizion-debitorka-900x563.png 900w, https:\/\/lazarewww.ru\/wp-content\/uploads\/2026\/09\/vizion-debitorka-600x375.png 600w\" sizes=\"auto, (max-width: 1440px) 100vw, 1440px\" \/><figcaption class=\"wp-element-caption\">Accounts receivable: how overdue a payment is, shown as a badge right in the row.<\/figcaption><\/figure>\n\n\t<\/div>\n\n<\/div>\n\n\n\n<hr class=\"hr-dashed opacity-50 my-5\">\n\n\n<div class=\"wp-bootstrap-blocks-row row\">\n\t\n\n<div class=\"col-12 col-lg-3\">\n\t\t\t\n\n<h2 class=\"wp-block-heading with_decor\" id=\"chto-bylo-slozhnym\">What was hard:<\/h2>\n\n\t<\/div>\n\n\n\n<div class=\"col-12 col-lg-5\">\n\t\t\t<div class=\"p-2\">\n\t\t\t\n\n<p class=\"wp-block-paragraph\">The main constraint on the project is someone else&#8217;s data schema, which cannot be changed. Sixty-odd CRM tables, read only, writing forbidden by the architecture. Everything the product needs is pulled out of what is already there, and it has to be done fast at the volumes of a real property developer.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Memory was a separate ordeal. Grouping large reports used to push the process into its ceiling; I had to rewrite the query as chunked reading with the sort order restored afterwards. Peak consumption dropped by more than half.<\/p>\n\n\t\t<\/div>\n\t<\/div>\n\n<\/div>\n\n\n\n<hr class=\"hr-dashed opacity-50 my-5\">\n\n\n<div class=\"wp-bootstrap-blocks-row row\">\n\t\n\n<div class=\"col-12 col-lg-3\">\n\t\t\t\n\n<h2 class=\"wp-block-heading with_decor\" id=\"rezultat\">Result:<\/h2>\n\n\t<\/div>\n\n\n\n<div class=\"col-12 col-lg-5\">\n\t\t\t<div class=\"p-2\">\n\t\t\t\n\n<p class=\"wp-block-paragraph\">The system is used by property developers in Kazakhstan, Uzbekistan and the UAE. Each has its own currency, time zone and interface language. Inside there are ready-made reports on contracts, receivables, incoming payments, unsold units and client balances, summary tables with drill-down, and an export that matches exactly what a person sees on screen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The project is alive; the latest changes went in this week.<\/p>\n\n\t\t<\/div>\n\t<\/div>\n\n<\/div>\n\n\n<div class=\"wp-bootstrap-blocks-row row case-media case-media--full\">\n\t\n\n<div class=\"col-12\">\n\t\t\t\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1440\" height=\"1900\" src=\"\/wp-content\/uploads\/2026\/09\/vizion-dashboard-prodazhi.png\" alt=\"The Sales dashboard in Vizion: sales dynamics, deals and revenue by sales manager, sales by residential development\" class=\"wp-image-2077\" srcset=\"https:\/\/lazarewww.ru\/wp-content\/uploads\/2026\/09\/vizion-dashboard-prodazhi.png 1440w, https:\/\/lazarewww.ru\/wp-content\/uploads\/2026\/09\/vizion-dashboard-prodazhi-227x300.png 227w, https:\/\/lazarewww.ru\/wp-content\/uploads\/2026\/09\/vizion-dashboard-prodazhi-776x1024.png 776w, https:\/\/lazarewww.ru\/wp-content\/uploads\/2026\/09\/vizion-dashboard-prodazhi-768x1013.png 768w, https:\/\/lazarewww.ru\/wp-content\/uploads\/2026\/09\/vizion-dashboard-prodazhi-1164x1536.png 1164w, https:\/\/lazarewww.ru\/wp-content\/uploads\/2026\/09\/vizion-dashboard-prodazhi-900x1188.png 900w, https:\/\/lazarewww.ru\/wp-content\/uploads\/2026\/09\/vizion-dashboard-prodazhi-600x792.png 600w\" sizes=\"auto, (max-width: 1440px) 100vw, 1440px\" \/><figcaption class=\"wp-element-caption\">A dashboard made of widgets: the period is switched at the top, and the layout is each user&#8217;s own.<\/figcaption><\/figure>\n\n\t<\/div>\n\n<\/div>\n\n\n<div class=\"wp-bootstrap-blocks-row row case-media case-media--full\">\n\t\n\n<div class=\"col-12\">\n\t\t\t\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1440\" height=\"900\" src=\"\/wp-content\/uploads\/2026\/09\/vizion-akty-sverki.png\" alt=\"The Reconciliation statements report with the filter panel and the payment schedule\" class=\"wp-image-1997\" srcset=\"https:\/\/lazarewww.ru\/wp-content\/uploads\/2026\/09\/vizion-akty-sverki.png 1440w, https:\/\/lazarewww.ru\/wp-content\/uploads\/2026\/09\/vizion-akty-sverki-300x188.png 300w, https:\/\/lazarewww.ru\/wp-content\/uploads\/2026\/09\/vizion-akty-sverki-1024x640.png 1024w, https:\/\/lazarewww.ru\/wp-content\/uploads\/2026\/09\/vizion-akty-sverki-768x480.png 768w, https:\/\/lazarewww.ru\/wp-content\/uploads\/2026\/09\/vizion-akty-sverki-900x563.png 900w, https:\/\/lazarewww.ru\/wp-content\/uploads\/2026\/09\/vizion-akty-sverki-600x375.png 600w\" sizes=\"auto, (max-width: 1440px) 100vw, 1440px\" \/><figcaption class=\"wp-element-caption\">Reconciliation statements: filters by contract parameters and an expandable payment schedule.<\/figcaption><\/figure>\n\n\t<\/div>\n\n<\/div>\n\n\n\n<hr class=\"hr-dashed opacity-50 my-5\">\n\n\n<div class=\"wp-bootstrap-blocks-row row\">\n\t\n\n<div class=\"col-12 col-lg-3\">\n\t\t\t\n\n<h2 class=\"wp-block-heading with_decor\" id=\"moya-rol\">My role:<\/h2>\n\n\t<\/div>\n\n\n\n<div class=\"col-12 col-lg-5\">\n\t\t\t<div class=\"p-2\">\n\t\t\t\n\n<p class=\"wp-block-paragraph\">The back end, the report engine, the layer that talks to the CRM database, the model integration, migrations and tests. The front-end skeleton was done at the start by a second developer; from there on the project has been mine.<\/p>\n\n\t\t<\/div>\n\t<\/div>\n\n<\/div>\n\n\n\n<hr class=\"hr-dashed opacity-50 my-5\">\n\n\n<div class=\"wp-bootstrap-blocks-row row\">\n\t\n\n<div class=\"col-12 col-lg-3\">\n\t\t\t\n\n<h2 class=\"wp-block-heading with_decor\" id=\"stek\">Stack:<\/h2>\n\n\t<\/div>\n\n\n\n<div class=\"col-12 col-lg-5\">\n\t\t\t<div class=\"p-2\">\n\t\t\t\n\n<p class=\"wp-block-paragraph\">Laravel \u00b7 PHP \u00b7 PostgreSQL \u00b7 Vue \u00b7 TypeScript \u00b7 ECharts \u00b7 Docker \u00b7 GitHub Actions<\/p>\n\n\t\t<\/div>\n\t<\/div>\n\n<\/div>\n\n\n<div class=\"wp-bootstrap-blocks-row row case-cta\">\n\t\n\n<div class=\"col-12\">\n\t\t\t\n\n<a class=\"btn btn-outline-primary btn-lg\" href=\"https:\/\/vizion.macroglobal.tech\/\" target=\"_blank\" rel=\"noopener\">Visit the site!<\/a>\n\n\n\n<p class=\"wp-block-paragraph\">For anyone curious about Vizion.<\/p>\n\n\t<\/div>\n\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Management reporting for property developers on top of a CRM: a report engine driven by configs, reports generated by a model, and multi-tenancy over someone else&#8217;s database. In production at property developers in Kazakhstan, Uzbekistan and the UAE.<\/p>\n","protected":false},"featured_media":2091,"template":"","isotope_tag":[96,94,95],"class_list":["post-2266","isotope","type-isotope","status-publish","has-post-thumbnail","hentry","isotope_category-web-apps-en"],"acf":[],"_links":{"self":[{"href":"https:\/\/lazarewww.ru\/?rest_route=\/wp\/v2\/isotope\/2266","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/lazarewww.ru\/?rest_route=\/wp\/v2\/isotope"}],"about":[{"href":"https:\/\/lazarewww.ru\/?rest_route=\/wp\/v2\/types\/isotope"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/lazarewww.ru\/?rest_route=\/wp\/v2\/media\/2091"}],"wp:attachment":[{"href":"https:\/\/lazarewww.ru\/?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2266"}],"wp:term":[{"taxonomy":"isotope_tag","embeddable":true,"href":"https:\/\/lazarewww.ru\/?rest_route=%2Fwp%2Fv2%2Fisotope_tag&post=2266"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}