رفتن به محتوا

کاتالوگ، قیمت، هزینه

کاتالوگ پایهٔ ایران در packages/core/src/catalog/data/*.ir.json است (irCatalogData، با version — بازنگریِ داده) و کاتالوگ کاربر روی آن می‌نشیند: project.catalogOverrides. ادغام عمیق است (mergeCatalogData، فاز ۹٫۵): هر فهرستِ شناسه‌دار — ورق، نوار، صفحه، فهرست‌های یراق — با شناسه (قلمِ همنام یک‌جا جایگزین، تازه به انتها)؛ فهرستِ بی شناسه (standardWidths، جدولِ شمارشِ لولا و پایه) یک‌جا جایگزین؛ هر شیء (system32، cutting، dimensions.base، مشخصاتِ هر دستگاه) کلید به کلید تا هر عمق. پس کاتالوگِ کاربری که یک لولا دارد بقیهٔ لولاهای پایه را پنهان نمی‌کند، defaults.cutting.kerf ِ تنها method را پاک نمی‌کند، و قلمِ تازهٔ پایه در پروژهٔ قدیمی دیده می‌شود. تیپِ رونویسی CatalogOverlay<T> است و اسکیمایش (schema/catalog.ts) از روی اسکیمای کامل ساخته می‌شود.

اسکیمای کامل (catalogData، zod، با version): parseCatalogData(raw) اول شکل را می‌سنجد (catalog/schema با مسیرِ مسئله) و بعد ارجاع‌ها را (validateCatalog). loadProject نتیجهٔ ادغامِ کاتالوگِ کاربر را از همین می‌گذراند — رونویسیِ عمیق می‌تواند دستگاهِ تازه‌ای بی kind بیاورد. خودِ irCatalog()/createCatalog zod نمی‌خواهد: در تکهٔ اولیهٔ خانه است و zod بیرون از آن؛ درستیِ شکلِ دادهٔ پایه را core/test/catalog-v2.test.ts با اسکیما می‌سنجد.

پایه‌ها در رجیستری‌اند (baseCatalogData(id)، امروز ir؛ شناسهٔ ناشناخته catalog/unknown-base). نقشِ دستگاه: هر مشخصه در defaults.appliances یک kind دارد (hob، sink، hood، oven، dishwasher، fridge، microwave) و قاعده‌ها، برشِ صفحه و صحنه خانواده را با appliancesOf(cat, kind) می‌گیرند — نه کلیدِ hood60 یا پیشوندِ hob؛ گازی که کاربر با شناسهٔ خودش بیفزاید برای برش پیشنهاد می‌شود. چون بخشی از پروژه است، با .darz جابه‌جا می‌شود. Worker ِ چیدمان کاتالوگ نمی‌سازد: نخ اصلی catalogFor(project).data ِ سنجیده را همراهِ پروژه می‌فرستد و Worker با indexCatalog (بی سنجشِ دوباره) می‌خواندش — رابط و چیدمان یک کاتالوگ می‌بینند و دادهٔ کاتالوگِ ایران در بستهٔ Worker نمی‌آید.

در وب، «کاتالوگ ورق و یراق…» (منوی ویرایش) پنل CatalogPanel را باز می‌کند: تب‌های ورق، ابعاد ورق، نوار لبه، صفحه، لولا، ریل، پایه، دستگیره، پیچ و میخ، سبد و متعلقات؛ هر قلم یک فرم است (مشخصات فیلدها در catalog-fields.ts)، ثبت روی ترک فیلد. ویرایشِ یراق فقط خودِ قلم را در کاتالوگ کاربر می‌گذارد (putHardware). هر تغییر پیش از ثبت با همان اعتبارسنجِ کاتالوگ پایه (validateCatalog) سنجیده می‌شود؛ اگر رد شد، اعلان به زبانِ رابط و هیچ‌چیز عوض نمی‌شود (lib/catalog-edit.ts). قلمِ پایهٔ ویرایش‌شده با همان شناسه در کاتالوگ کاربر می‌نشیند و خلاصهٔ ردیفش «پایه · ویرایش‌شده» است (itemState: base، edited، mine). در فرمِ باز، پایهٔ ویرایش‌شده «برگرداندن به پایه» دارد و قلمِ خودِ کاربر «حذف از کاتالوگ» — در هر ده تب، یراق هم. هر دو رونویسیِ همان شناسه را برمی‌دارند (dropItem، با همان اعتبارسنجِ ثبت؛ فهرست و دستهٔ یراقِ خالی هم می‌روند) و قدمی در تاریخچه می‌گذارند که واگرد می‌شود. اگر دادهٔ کاربر دور ریخته شود ConfirmDialog ِ کیت می‌پرسد: قلمِ خودت همیشه، پایه فقط وقتی رونویسی با پایه فرق دارد. ثبتِ همان مقداری که کاتالوگ دارد (ترکِ فیلدِ بی تغییر) رونویسی نمی‌سازد — رونویسیِ عینِ پایه قلم را روی پایهٔ امروز قفل می‌کرد. تا پیگیریِ موجِ ۶ پایهٔ ویرایش‌شده در رابط راهِ برگشت نداشت و دکمه فقط کنارِ قلمِ تازهٔ کاربر در چهار تبِ فهرستی بود. قلم پایه حذف نمی‌شود (ادغام فقط افزودن و جایگزینی است).

اتصال‌ها (defaults.joinery، نسل ششم ۱.۵ — R4، R5): هندسهٔ هر نوعِ اتصال و یراقی که برایش خریده می‌شود از همین‌جاست، نه ثابتِ کد: edgeMargin/spacing/minPerJoint (پخشِ اتصال‌ها روی هر خط)، confirmat (سوراخ عبوری روی بدنه، راهنما در لبه)، minifix (کام روی رویهٔ قطعهٔ افقی در camSetback، پیچِ مینی‌فیکس با راهنمای boltPilotDia×boltPilotDepth روی بدنه و سوراخِ لبهٔ boltHoleDia که باید به کام برسد: camSetback + camDia/2dowel (سوراخِ کورِ faceDepth در رویهٔ بدنه و edgeDepth در لبه) و pocketScrew (جیب روی رویهٔ قطعهٔ افقی، inset از سر؛ بدنه سوراخ نمی‌خورد). hardwareId ِ هر نوع باید قلمی از کاتالوگ باشد (catalog/unknown-hardware). کارگاهی که کام را در ۴۲ می‌زند catalogOverrides.defaults.joinery.minifix.camSetback = 42 را می‌نویسد و نقشهٔ سوراخ و عمقِ سوراخِ لبه با آن می‌آیند. تا نسل ششم مینی‌فیکس «دوبل» ِ Ø۸×۳۰ روی رویهٔ ورقِ ۱۶ می‌زد و سوراخِ لبهٔ ۳۰ به کامِ ۳۴ نمی‌رسید، و dowel/pocketScrew بی‌صدا کانفرمات سوراخ و خریداری می‌شدند. insidePart عمقِ سوراخِ کور را با ضخامتِ ورق می‌سنجد.

عددهای کارگاه (نسل ششم ۱.۱۰): هر فاصله‌ای که ماشین‌کاری می‌زند، هر شماری که صورت‌حساب در آن ضرب می‌کند و هر قلمی که راه‌حلِ دستیار یا «این دیوار را پر کن» بی‌پرسش روی یونیت می‌گذارد از کاتالوگ می‌آید، نه از ثابتِ کد — با همان رونویسیِ عمیق (catalogOverrides.defaults.…) و همان اعتبارسنجی:

  • defaults.density — board density per material kind, kg/m³ (melamine MDF 750, raw MDF 740, high gloss 800, membrane 760, chipboard 650, plywood 600, backer 900 — orders of magnitude from board makers’ sheets): lift-weight weighs a lift-up door as area × thickness × density of its front board.
  • defaults.machining.jPull (width 25, depth 10, minThickness 18) and fingerGroove (width 30, depth 8, inset 15, minThickness 16): the router’s routed grips (plan-v6 phase 2), ⚠️ a starting point — set them to your router bit.
  • defaults.machiningplateSetback (ردیفِ پیچِ صفحهٔ لولا از لبهٔ جلوی بدنه؛ ۳۷، همان ردیفِ سیستم ۳۲)، slideFront/slideRear/slideLift (پیچِ ریل: اولی از جلو، آخری مانده به تهِ ریل، ریل بالاتر از کفِ کشو)، handleEdge/handleEnd/handleHoleDia/handleMinEnd (سوراخِ دستگیره از لبهٔ آزاد و سرِ درب، قطر، کمترین فاصله تا سرِ قطعه)، hingeEnd (اولین و آخرین لانهٔ لولا از سرِ درب). کارگاهی که صفحهٔ لولا را در ۴۲ می‌زند defaults.machining.plateSetback = 42 را می‌نویسد و نقشهٔ سوراخِ بدنه با آن می‌آید.
  • defaults.hardwareCountsshelfPinsPerShelf، screwsPerHinge، screwsPerSlidePair، nailPitch (یک میخ هر این طول دورِ پشت‌بندِ روکار)، fastenerWastePercent و edgeBandWastePercent (پرتِ پیچ و میخ، و سرِ رولِ نوار): یادداشتِ «+ ۱۰٪» ِ سطر و ضریبِ شمارش از همان یک عدد.
  • defaults.ids — قلمی که وقتی یونیت یا دیوار چیزی نگفته برداشته می‌شود: hinge، slide، lift، leg، handle، railHandle (دستگیرهٔ درِ بالابر)، pushLatch (the latch a front switched to push-to-open gets), tandemBoxSlide (تاندم‌باکسِ بی‌ریل)، countertop (صفحهٔ دیوارِ بی‌صفحه)، و پیچ‌ومیخِ شمارش: shelfPin، hingeScrew، slideScrew، backNail. راه‌حلِ «لولا انتخاب نشده» و «پایه انتخاب نشده» همین را می‌گذارد و نامِ همان قلم را روی دکمه می‌نویسد («انتخابِ «…»»)؛ «این دیوار را پر کن» لولا، پایه، دستگیره و جک را از همین می‌گیرد و ریل را بلندترین ریلِ ساچمه‌ایِ کاتالوگ که در عمقِ بدنه جا می‌شود (slideForUnit؛ همان آزمونِ part/drawer-too-deep). هر شناسه باید در فهرستِ نقشِ خودش باشد — ریل به جای لولا پذیرفته نمی‌شود (catalog/unknown-hardware، validate-ids.ts).
  • افزوده‌های defaults.dimensionscorniceHeight/valanceHeight/endPanelOverhang (تاج، قرنیز، پیش‌آمدگیِ پهلو)، backInset/backGrooveDepth (شیارِ پشت‌بند در پیش‌تنظیم‌ها)، frame (width/groove/panelPlay/minPanel ِ درب کلاسیک)، pulloutHeight (بیرون‌کشِ بی‌ارتفاع)، drawerBoxDrop (جعبه زیرِ سرِ نمای خودش؛ قاعدهٔ hob-drawer-clearanceswapMinWidth (کفِ عرض در پیشنهادِ «یک ورق کمتر»)، base.counterOverhangEnds، و cutout (minSideClearance/minEdgeMargin/minHoleMargin/frontSetback/cornerRadius/tapSetback ِ برشِ صفحه — قاعدهٔ cutout-fits و پانوشتِ برگهٔ سنگ‌کار همین را می‌خوانند). +60 ِ نسل چهارم که ارتفاعِ بیرون‌کش را از فرمولِ نما پس می‌گرفت مرده بود و رفت.
  • defaults.rules — آستانه‌های دستیار، نوزده عدد که هر کدام ثابتِ یک پروندهٔ قاعده با ⚠️ کنارش بود: ductOffsetMax، hoodHeight (بازهٔ نصبِ هود وقتی مشخصهٔ هود mountHeight ندارد)، wallCabinetReach، workSurfaceNearHob، sinkHobGapMin، waterReach، fillerMin، narrowUnitInside (unit-too-narrow: دو بدنه به‌علاوهٔ این)، lowUtilization، counterHeight (min/max/target ِ راه‌حل)، blindNeedsBasket، blindAccessMin و blindWidthReduction (پیش‌فرضِ پهنای پنهانِ کنج کور در cornerUnit و قاعدهٔ blind-corner-too-narrowdoorWidthMax، drawerWidthMax، heavyDoor، hobClearanceDefault (وقتی هیچ گازی در کاتالوگ فاصلهٔ زیر ندارد) و lastSheetBelow (پیشنهادِ «یک ورق کمتر»)؛ phase 2 of plan-v6 adds cornerHingeAngleMin (155: a corner unit’s hinge, hinge-angle-corner) and frontNeighbourGap (50: fronts farther apart are not neighbours, handle-collision). کارگاهی که درِ ۷۰۰ را می‌پذیرد defaults.rules.doorWidthMax = 700 می‌نویسد و unit-too-wide با آن می‌آید. نگهبان: core/test/domain-constants.test.ts — در core/src هیچ ثابتِ عددیِ قاعده نمی‌ماند و ⚠️ فقط در توضیحِ دامنه (≤ ۱۰).

یراقِ صورت‌حساب از کاتالوگ (C15)

Section titled “یراقِ صورت‌حساب از کاتالوگ (C15)”

هر سطرِ یراق شناسهٔ کاتالوگ دارد و نامش از کاتالوگ می‌آید (needOfcat.hardwareItem(id)) — plain، چون نامِ محصول ترجمه نمی‌شود. واحدش کلید است (piece، pair، meter) و یادداشتش پیام با عددِ خام (i18n.md)؛ شناسهٔ ناموجود خطای روشنِ کاتالوگ است. شناسهٔ یراق در همهٔ فهرست‌ها یکتاست (catalog/duplicate-hardware). قیمتِ هر سطر از دفترِ قیمت، وگرنه از price ِ همان قلم در کاتالوگ — همان ترتیبِ ورق و نوار.

  • fasteners: کانفرمات، مینی‌فیکس، نگهدار طبقه، پیچ لولا (screw-3.5x16)، پیچ ریل (screw-4x16)، میخ پشت‌بند (nail-back).
  • fittings: خریدنی‌های دیگر با kind (pulloutBasket، cornerAccessory، toeKickStrip، hangingRail، ledStrip، pushLatch). A push-to-open unit buys its latch (front.opening.fittingId) once per front that opens: every door, lift door and drawer. انتخاب با اندازه است، نه با ساختنِ شناسه: سبد پهن‌ترینی که در عرضِ یونیت جا می‌شود (width)، پاخورِ PVC کوتاه‌ترین پروفیلی که کم نمی‌آورد (height) — اگر هیچ‌کدام نرسد، نزدیک‌ترین با یادداشتِ سطر. سبد بی width و پاخور بی height در اعتبارسنجی رد می‌شوند. صفحهٔ کنج شناسهٔ corner-<نوع> دارد.
  • ریل از هر خانهٔ کشو شمرده می‌شود و پیچِ ریل هم برای کشویی که ریلش را خودش دارد.
  • دستگیرهٔ پروفیلی (بی holeSpacing) به متر است، جمعِ پهنای نماهایی که رویشان می‌نشیند؛ دستگیرهٔ سوراخ‌دار به عدد (R17). طبقهٔ کنجِ ال دو قطعه است و هر قطعه نگهدارِ خودش را می‌گیرد.
  • دستمزدِ برش «به ازای هر برش» در CNC خالی نمی‌ماند: هر قطعهٔ چیده‌شده یک کانتور است و cutCount همان را می‌شمارد (R18)؛ روی ارهٔ پانل‌بر شمارِ برش‌های گیوتینی است.

Hardware fields of format 4 (plan-v6 phase 2): a hinge says softClose and a slide says softClose and extension (1 = full extension; the drawer’s reach in the 3-D view); a handle says its projection from the face of the front; a lift says its kind (gasStrut, stay, parallel, upAndOver, biFold), the door weight one lift carries (minWeight/maxWeight in kg, both or neither — null when the seller did not say; catalog/bad-lift-weight) and its openAngle (0 for a parallel lift). The catalogue panel edits all of them, lifts in their own tab. A user catalogue written before format 4 comes in without these fields — a format-3 project (migrateV3toV4) or a catalogue JSON exported by an older build, which has no version — and upgradeCatalogOverrides fills them: from the base item with the same id when the user had only edited a base item, else a hinge is soft-close (every base hinge was, and a wrong true is flagged beside a push latch while a wrong false would be silent), a ball-bearing slide is not and an undermount or metal-box slide is, extension 1, projection 30, a gas strut with an unknown weight range and 90° of swing. The JSON import then parses the result as a project would (parseCatalogOverrides: its own strict shape, then the merged catalogue); until generation 6 it checked only references, and a wrong shape failed at the project’s next save.

Handle channel profiles (hardware.profiles, «دستگیرهٔ مخفی»): shape L (along the top of a base unit) or C (between two rows), heightLoss (how much shorter the front beside the channel is), notchHeight and notchDepth (the notch cut into each carcass side to seat it), and a price per metre. defaults.ids.channelEdge must be an L and channelMid a C. The base items (profile-l: loss 35, notch 37 × 26; profile-c: loss 60, notch 73 × 26) are ⚠️ orders of magnitude from one maker’s sections — the profile seller’s drawing decides.

The Iranian base (revision 4) adds, all ⚠️ with price: null: plain (not soft-close) hinges for each overlay — what a push-to-open front needs; 155° and 165° full-overlay hinges for a door beside a corner; a 95° hinge for thick doors; a magnetic push latch; and stay (HK), parallel (HL) and up-and-over (HS) lifts beside the gas strut and the bi-fold lift. The weight ranges (gas strut 2–7 kg, bi-fold 4–12, stay 2–9, parallel 2–12, up-and-over 2–10) are orders of magnitude from makers’ tables, not a promise for a particular strut: ask the seller.

Glass and its frames (glass, hardware.glassFrames): a pane is thickness and a price per square metre; a frame is faceWidth (what it covers of the door, seen from the front), rebate (how far the pane sits inside it), the glassThicknesses its groove takes, and a price per metre. defaults.ids.glass and glassFrame are what a glass door takes when the unit names none, and glass-fit is the rule that says a pane the groove does not take. The base items (glass-frame-20: face 20, rebate 8, for 4 and 5 mm; glass-frame-25: face 25, rebate 9, for 4, 5 and 6; clear and satin glass of 4 and 6) are ⚠️ orders of magnitude from one profile maker’s sections — the seller’s drawing decides.

Sliding door tracks (hardware.slidingTracks): topLoss and bottomLoss (what the two rails take from the opening’s height), overlap (how much two doors cover each other where they pass) and a price per metre; defaults.ids.slidingTrack is what a unit takes when it names none. The base items (sliding-light: 40 / 20 / 30 for a wardrobe, sliding-heavy: 55 / 25 / 40 for wide doors) are ⚠️ orders of magnitude from one maker’s sections.

مشخصهٔ دستگاه (defaults.appliances): height برای هر نوع بلندیِ خودِ دستگاه است؛ بازهٔ نصبِ هود mountHeight (min/max، از برگهٔ سازنده) است و در نبودش defaults.rules.hoodHeight — تا نسل ششم height ِ هود «کمترین ارتفاعِ نصب» خوانده می‌شد و ویرایشِ بلندیِ هود آستانهٔ قاعده را جابه‌جا می‌کرد (R16).

برون‌ریزی و درون‌ریزی (C3)

Section titled “برون‌ریزی و درون‌ریزی (C3)”

کاتالوگ کاربر و دفتر قیمت هر کدام یک JSON ساده‌اند: دکمه‌های «برون‌ریزی JSON» و «درون‌ریزی JSON» در همان پنل‌ها. درون‌ریزی کاتالوگ از همان اعتبارسنج می‌گذرد؛ دفتر قیمت با اسکیمای priceBook (zod) سنجیده می‌شود.

project.prices قیمت‌های خودِ پروژه است. دفترهای ذخیره‌شده جدا از پروژه در Dexie (جدول priceBooks، نسخهٔ ۲ پایگاه) می‌نشینند: نام، فروشنده، شهر، تاریخ برداشت، خودِ دفتر. «ذخیرهٔ قیمت‌های پروژه» دفتر می‌سازد؛ «به کار ببر» قیمت‌ها را در پروژه می‌گذارد و project.priceBookRef (شناسه، فروشنده، شهر، تاریخ) می‌گوید از کجا آمده‌اند. اطمینان هر قلم از دفتر مرجع می‌آید (confidenceOf).

قیمتِ ورق، واحدِ پول، تاریخِ برداشت (۹.۶)

Section titled “قیمتِ ورق، واحدِ پول، تاریخِ برداشت (۹.۶)”

حسابِ قیمت در packages/core/src/costing/price.ts (price، sheetPriceKey/sheetUnitPrice، convertMoney) و costing/reference.ts (priceBookFromReference، SERVICE_PRICE_KEY، confidenceOf) است:

  • یک price(unitPrice, qty) برای هر سطر (ورق، نوار، صفحه، یراق، خدمت): قیمتِ نامعلوم null می‌ماند و سطر حذف نمی‌شود. تا ۹.۶ دو نسخه با دو امضا بود.
  • قیمتِ ورق مالِ جنس و ابعاد است: PriceBook.sheets با نشانیِ sheetPriceKey(جنس، ابعاد)mel-white-16@mel-2800x2100. صورت‌حساب سطرِ ورق را از نقشه‌های چیدمان به تفکیکِ جنس و ابعاد می‌سازد (ورقِ انبار سطر نمی‌گیرد) و یادداشتش ابعاد و بازده است («ورق ۲۸۰۰ × ۲۱۰۰، بازده ۶۸٫۷٪»). priceBookFromReference هر ابعادِ جنس را از قیمتِ متر مربع قیمت می‌کند؛ تا ۹.۶ فقط ابعادِ نخست و چیدمانی که ورقِ دیگر برمی‌داشت قیمتِ ورقِ نخست را می‌خورد.
  • قیمتِ بی ابعاد — کلیدِ تنهای جنس در دفترِ پیش از ۹.۶ و pricePerSheet ِ کاتالوگ — مالِ ابعادِ نخستِ جنس است و روی ابعادِ دیگر «؟» (sheetUnitPrice). شناسهٔ سطر نشانیِ همان قیمت است (کلیدِ کهنه یا نشانیِ تازه)، چون پنلِ هزینه قیمت را در sheets[line.id] می‌نویسد و پاک می‌کند.
  • واحدِ پول فقط در نمایش: دفتر و صورت‌حساب به واحدِ خودِ دفتر (currency) می‌مانند و نمایش به واحدِ پروژه convertMoney(مبلغ، از، به) است (تومان × ۱۰ = ریال). darzsaz prices --write دفترِ تومانی را تومان می‌نویسد. رابط هم (موجِ ۲): CostPanel، CostSummary، گونه‌ها و دفترهای قیمت دفتر را با واحدِ خودش به کار می‌برند، ذخیره و درون‌ریزی می‌کنند و مبلغ را با moneyView (components/cost/money.ts) به واحدِ پروژه نشان می‌دهند؛ قیمتِ تایپ‌شده به واحدِ دفتر برمی‌گردد (دفترِ تومانی در پروژهٔ ریالی ×۱۰ نشان داده و همان عدد ذخیره می‌شود — test/cost.dom.test.tsx). سطرِ خدمت ← کلیدِ services یک نقشه دارد: SERVICE_PRICE_KEY ِ هسته، هم برای confidenceOf هم برای پنل.
  • تاریخِ برداشت: PriceBook.capturedAtCostReport.capturedAt) تاریخِ برداشتِ مرجع است و با ضریبِ دلار عوض نمی‌شود؛ updatedAt همان است مگر ضریب عددها را امروز جلو برده باشد. «ذخیرهٔ قیمت‌های پروژه» در دفترهای قیمت همین تاریخ را برمی‌دارد (دفترِ دستی: updatedAt، و بی آن امروز).

#/p/:id/cost صفحهٔ مستقل است (CostPage): جمعِ قیمت‌دار و شمار قلم‌های بی‌قیمت (که در جمع نیستند)، نوار اطمینان — سهم «بازار»، «نسبی» و «برآورد» از جمع (confidenceShares) تا عددِ حدسی و عددِ واقعی یک شکل جمع نشوند — تفکیک گروه‌ها، «یک ورق کمتر»، دفترهای قیمت، گونه‌ها؛ «ویرایش قیمت‌ها» همان پنل قیمت قبلی را باز می‌کند.

«یک ورق کمتر» همان sensitivity هسته است، به‌درخواست و در Worker (useSensitivity؛ درخواست kind: 'sensitivity' به derive.worker). پیشنهادها بی‌تابع برمی‌گردند (SuggestionData) و با applySuggestion در نخ اصلی اعمال می‌شوند — با واگرد. اگر هیچ پیشنهادی نبود، صادقانه می‌گوید.

گونه = تفاوت با پروژه، به‌صورت JSON Patch کمینه (packages/core/src/variants/patch.ts: add/replace/remove با JSON Pointer؛ آرایهٔ هم‌طول عضو به عضو، ناهم‌طول یک‌جا). variantOf(base, changed, id, name) تفاوت را می‌گیرد، variantProject(base, v) اعمال می‌کند (مسیر نامعتبر نادیده گرفته می‌شود تا گونهٔ کهنه پروژه را نشکند). در داشبورد، گونه با «نام گونه»، «جنس نما» و/یا «جنس بدنه» و دکمهٔ «گونهٔ تازه» ساخته می‌شود؛ «مقایسه» پروژه و هر گونه را با derive(…, { iterations: 120 }) کنار هم می‌گذارد (ورق، هزینهٔ قیمت‌دار، بی‌قیمت، خطا، ورق به تفکیک)؛ «پذیرفتن» گونه را روی پروژه می‌نشاند. رندر کوچک هر گونه نیامده (رندر صحنهٔ سه‌بعدی می‌خواهد).

REFERENCE_IR از packages/core/src/costing/reference.ir.json می‌آید (اسکیمای referenceBook). در CLI: darzsaz prices --from مرجع.json مرجع دیگری را می‌پذیرد و darzsaz prices --update --out مرجع-تازه.json نرخ دلار روز را می‌گیرد و همهٔ عددها را با ضریب «نرخ امروز ÷ نرخ برداشت» جلو می‌برد — تقریب، و همین کلمه در note پرونده نوشته می‌شود؛ منبع هر قلم دست‌نخورده می‌ماند.