Parser and Turkish-aware ranked search for Turkey's official construction unit-price lists ("poz" / birim fiyat listeleri) published by public institutions such as ÇŞB (Ministry of Environment and Urbanization), DSİ, and MSB.
Anyone building construction software for the Turkish market — procurement, tender preparation (ihale), progress payments (hakediş), quantity surveying (metraj) — eventually needs these lists in a database with a fast autocomplete on top. Getting from the officially published PDFs to that point involves a handful of non-obvious pitfalls. This library is the distilled, dependency-free core that solves them, extracted from a production procurement system that manages 22,000+ poz records.
-
Turkish price formatting. ÇŞB prices arrive as
"310,00"and"1.712,50"(thousands dot + decimal comma).parseTurkishDecimalconverts them to dot-decimal strings — safe to feed into Postgresnumeric/ PrismaDecimalwithout float round-trips. -
Units glued to descriptions. In the ÇŞB price book the unit is not a separate column — it is the last word of the description:
"Taşcı ustası Sa".splitTrailingUnitseparates it fail-closed: only allowlisted tokens are treated as units, so delivery-place suffixes ("İşbaşında") or technical tokens ("kA","V") never get chopped off a description. -
Broken JS lowercasing for Turkish.
"ISITMA".toLowerCase()is"isitma", but Turkish lowercasesIto dotlessı.toLowerTurkishand the double-key matcher (matchesQuery) get this right and tolerate missing diacritics in both query and data —"tas yunu"finds"Taş yünü","taş"finds"TASYUNU". -
Duplicate item numbers in official PDFs.
dedupeByPozNo(last-record-wins) also protects bulk upserts from Postgres'ON CONFLICT cannot affect row a second time. -
Autocomplete ranking.
searchPozorders candidates the way a data entry form wants them: exact poz number → poz number prefix → description tokens (AND semantics, most specific record first).
npm install resmi-poz
import { parseCsbRecord, parseUnifiedRecord, dedupeByPozNo, searchPoz, } from "resmi-poz"; // 1. Normalize raw extracted data (two common shapes supported) const csb = parseCsbRecord({ poz: "10.100.1001", tanim: "Taşcı ustası Sa", fiyat: "310,00", }); // → { pozNo: "10.100.1001", description: "Taşcı ustası", unit: "Sa", // unitPrice: "310.00", year: 2026, source: "CSB" } const dsi = parseUnifiedRecord({ pozNo: "56.520.1000", tanim: "Temel Sondaj Deliğinin Açılması", birim: "m", birimFiyat: 5335.48, kaynak: "DSI", }); // 2. Deduplicate before a bulk upsert const rows = dedupeByPozNo([csb!, dsi!]); // 3. Ranked autocomplete over any candidate set searchPoz("taşcı", rows); // Turkish-aware, diacritic-tolerant searchPoz("10.100", rows); // poz-number prefix
The search layer is pure: fetch a candidate set however you like (full
in-memory list, an ILIKE-prefiltered DB slice) and let searchPoz
order it.
This library ships parsers, not price data. The official lists are
published by the respective institutions (e.g. ÇŞB's yearly
İnşaat ve Tesisat Birim Fiyatları book); obtain them from the official
sources and extract to JSON in either supported shape. Small anonymized
samples for testing live in data/.
| Function | Purpose |
|---|---|
parseTurkishDecimal(raw) |
"1.712,50" → "1712.50", invalid → null |
splitTrailingUnit(text, unitMap?) |
Fail-closed unit extraction from ÇŞB descriptions |
parseCsbRecord(raw, options?) |
ÇŞB-shape record → PozRecord | null |
parseUnifiedRecord(raw, options?) |
Unified-shape record → PozRecord | null |
dedupeByPozNo(records) |
Last-wins dedupe on poz number |
searchPoz(query, records, limit?) |
Ranked autocomplete search |
toLowerTurkish / foldDiacritics / matchesQuery |
Turkish-aware text matching primitives |
DEFAULT_UNIT_MAP |
Extendable unit allowlist |
Resmi birim fiyat (poz) listelerini veritabanına almak ve üzerine hızlı
bir otomatik tamamlama koymak için gereken çekirdek: TR fiyat biçimi
("1.712,50"), tanımın sonuna gömülü birim ("Taşcı ustası Sa",
fail-closed ayıklama), Türkçe küçük harf / diakritik sorunları
(İ/ı, "tas yunu" → "taş yünü"), PDF'lerdeki mükerrer poz numaraları
ve poz-no → tanım öncelikli sıralama. Bağımlılıksız, saf fonksiyonlar;
üretimde 22.000+ poz kaydıyla çalışan bir satın alma sisteminden
çıkarılmıştır.
MIT