SPS V4 · Data provenance

How the data got into SPS V4

The inbound companion to the pbi_v1 read contract. That page is how you read out of SPS; this one is how the data got in — the one-off Excel bootstrap, the Databricks gold sync that now overlays it, and the seam between them. Read this to trust the lineage and to see the exact transforms any replacement pipeline must reproduce.

01 Lifecycle at a glance

SPS V4 was never fed by a live pipeline at launch. It was bootstrapped once from Excel, and financials are now kept current by a gold-layer sync. Three phases:

t0 · once

Excel bootstrap

The whole DB, loaded from the historical Excel workbook + weekly submission files. Everything at launch came from here.

now · ongoing

Gold sync overlay

Databricks sps_table2 overlays monthly Fakt financials + daily inventory onto the Excel baseline. Money is source-of-truth here.

next · your call

Owned pipeline

Replace the manual Excel import with an automated source → SPS ingestion. Sections 04–06 are its spec.

PhaseCadenceOwns which dataMechanism
Excel bootstrapone-off (re-run on rebuilds)Everything at t0 — projects, people, financials, weekly status, submissions, work volumes, materials, criticalsscripts/import-excel.ts
Gold syncdailyFakt financials (rev/costs/hours) + accruals + daily inventory snapshots — overlaid per monthscripts/sync-databricks-financials.ts
In-app entrycontinuousEverything Planas-side going forward — PVs submit weekly via the appapp submit pipeline (not covered here)

The Excel import ran through dev and staging repeatedly during build, and a real production replace was executed on 2026-06-22 (~140 projects, identity preserved).

02 The sources

Two Excel inputs feed the bootstrap, plus one gold source for the ongoing sync.

Central workbook — DUOMENYS_PVT.xlsx

One workbook, eight sheets. The source of truth for projects, people, and the financial / status baseline.

Projektai
projects + PV/PS people
Projektu duomenys
dates, type, stage, contract, AV
Pajamos_sanaudos
monthly revenue + costs
Papildomos_pajamos
monthly additional revenue
Valandos_men
monthly hours + headcount
Projektu_stebesena
weekly fever-chart status
SPS_formai
accruals + inventory
Pirkimai
read-only, reported not written check-only

Weekly submissions — SPS FAILAI 3/

A folder of per-week, per-project macro workbooks. Filename encodes everything the walker needs; three form versions are supported.

2026-05-12_08_30 … forma V3.1 … _2114.xlsm
                   └ version         └ project code
versions: V3 · V3.1 (primary/additional hours split) · V3.2 (adds daily inventory)
sheets read: SPSPF (D3 = project code) · IVESTIS (hours) · SDU (work volume m²) · KRITINES (critical activities)

Gold layer — Databricks sps_table2

Per-Objektas monthly aggregate view in the Databricks Hive gold schema, read via the 1.2 Command Execution API on cluster adf, or from ADLS as an abfss:// export. Feeds the ongoing financials/inventory sync (§06).

03 The import machine

scripts/import-excel.ts orchestrates everything in two batches. Phase A reads the central workbook; Phase B reads the submission folder; then three materialization passes project the submissions into the normalized read tables.

All of Phase A + B + materialization run inside one prisma.$transaction (600 s timeout) — it either fully replaces the imported data or rolls back. The Databricks sync and the FK-orphan sweep run after commit, as separate steps.
StepReadsWrites
A1 Projects + peopleProjektaiprojects, users, project_assignments
A2 Project extrasProjektu duomenysprojects (dates, type, stage, contract), AV assignment
A4 FinancialsPajamos_sanaudos, Papildomos_pajamos, Valandos_menproject_financials (per project/month)
A5 Weekly statusProjektu_stebesena, SPS_formaiproject_status_weekly (fever + inventory cols)
B SubmissionsSPS FAILAI 3/*.xlsmweekly_submissions (immutable, versioned, snapshot JSONB)
Materialize status/volumes/latestsubmitted weekly_submissionswork_volumes, critical_activities, project_material*, status/financials read models

A3 (materials catalog rebuild) was removed — materials_catalog is now app-owned and migration-seeded, so the import no longer touches it.

Idempotency — read this before assuming re-runnable

Entry points

CommandDoes
db:import-excelThe full bootstrap. Flags: --dry-run, --check-only, --replace-operational, --preserve-identity, --min-submissions N (floor gate).
db:replace-prodProd-only wrapper: preflight, JIT firewall, backup, atomic import with floor gate, gold sync, FK sweep, smoke tests.
db:sync-databricks-financialsOverlay sps_table2 gold financials + inventory (§06). --source <abfss|path>, --dry-run.
db:backfill-*Idempotent live-DB fixups: contract-costs, hours, submissions-window, criticals-done, project logos.

04 Sheet → schema mapping

The centerpiece: what each Excel column becomes in Postgres, and what transform is applied on the way. This is the spec a replacement pipeline must reproduce. Exact cell references live in the phase parsers under scripts/import/excel/; the authoritative column-by-column list is docs/spec-gates/field-mapping.csv.

key primary / composite key xform non-trivial transform — see §05

Projektaiprojects · users · project_assignments

SourceTargetTransform
code (A)projects.id / codeText PK — strings allowed (e.g. 2132_ADM). key
name (B)projects.name1:1
active (E)projects.archived_atactive = 2archived_at = now. xform
country (F)projects.countryISO 3166-1 alpha-2
PV name + email (G, H)users + project_assignments kind=pvName → seed user (seed-{emailLocal} id), resolved to Entra OID at first SSO login. xform
PS name + email (I, J)users + project_assignments kind=psSame as PV.

Projektu duomenysprojects

SourceTargetTransform
start / sign / end datesprojects.start_date, contract_end_date, agreed_end_dateRenamed 1:1.
typeprojects.typeEnum A / B / C / D.
stageprojects.stageFK to reference.project_stages; Iniciavimas typo normalized to Inicijavimas. xform
contract revenueprojects.contract_revenueEUR.
margin % (K)projects.contract_costscontract_costs = revenue × (1 − margin). Margin is a ratio < 1, not EUR; margin ≥ 1 → null (bad data). xform
contract hoursprojects.contract_hours1:1.
AV name (O)users + project_assignments kind=avName → Entra UPN via av-email-map.json (best-effort az ad user list); unresolved → placeholder {slug}@av-unresolved.sps.local. xform

Pajamos_sanaudos · Papildomos_pajamos · Valandos_menproject_financials

All three aggregate to one row per (project, month-end). key: project_id, month

SourceTargetTransform
Pajamos_sanaudos · revenuerev_primaryAggregated per project/month; cost-group dimension collapsed (all → primary).
Pajamos_sanaudos · costscosts_primarySame aggregation.
Papildomos_pajamos · additionalrev_additionalAggregated, then upserted onto the primary rows. xform
Valandos_men · hourshours_primaryAggregated per project/month.
Valandos_men · actual / planned workersactual_workers, planned_workers1:1.
month datemonthNormalized to month-end ISO date. xform
Phase A is the source of truth for project_financials. Submissions and the gold sync overlay months onto this baseline (upsert by key) — they never wipe a project. A sparse snapshot can't erase the central-workbook series.

Projektu_stebesena · SPS_formaiproject_status_weekly

Weekly rows keyed by ISO week. key: project_id, year, week_number

SourceTargetTransform
Projektu_stebesena · week dateyear, week_numberConverted to ISO year + week. xform
buffer % / work % / expected end / cum. hoursbuffer_consumed_pct, work_done_pct, expected_end_date, hours_done_cumFever-chart columns. Bypass submissions — Phase A owns them.
SPS_formai · sukaupimai (Q)sukaupimaiAccruals, per week.
SPS_formai · atsargos viso (W) / >90d (X)atsargos_viso, atsargos_90d1:1.
— derived —atsargos_30datsargos_30d = max(0, viso − 90d). xform

Submission .xlsmweekly_submissions.snapshot_data (v1 JSONB) → read models

Each submission parses into an immutable v1 JSON snapshot, then materializes into the normalized tables. Shape contract: docs/spec-gates/snapshot-v1-schema.md.

Source (sheet)Materialized targetTransform
SDU grid (m² by week)work_volumes (planned, actual)Union of all submissions, highest version per week, replayed oldest-first. xform
KRITINEScritical_activitiesLatest snapshot; done flag → ISO date (§05); owner resolved or null.
materials blockproject_material · project_material_monthsResolved to catalog FK; duplicate (type,name) merged, quantities summed; positional monthly array → keyed by month-end. xform
IVESTIS hours / statusproject_financials · project_status_weeklyOverlaid onto Phase A baseline (upsert), never wipe.

05 Transforms that bite

The non-obvious conversions. Any pipeline that replaces the Excel import has to get these right or the numbers drift.

06 The seam — where the gold sync takes over

The Excel import set the financial baseline. From there, the Databricks sps_table2 sync keeps financials current — this is the boundary between "how it started" and "how it runs now."

Net picture: projects, people, weekly status, work volumes, materials and criticals were loaded once from Excel and are now maintained in-app. Financials + inventory were seeded from Excel and are kept live by the gold sync. Nothing else has an automated feed yet — that's §08.

07 Known gaps & caveats

Stated plainly so you can reconcile against them rather than discover them.

08 What could be owned next

The manual Excel import was a bootstrap, not a pipeline. The sustainable version is an automated source → SPS ingestion (Rivile / gold → the same Postgres tables). Sections 04–06 are its functional spec — reproduce those transforms and the boundary rules and the read models stay correct.

Open items to scope if that's the direction:

09 Reference & contact

Everything named on this page, in the SPS V4 repo.

WhatPath
Import runbook (phases, idempotency, reset)docs/runbooks/excel-data-import.md
Authoritative field map (65 rows)docs/spec-gates/field-mapping.csv
Snapshot v1 JSONB contractdocs/spec-gates/snapshot-v1-schema.md
Fakt / Planas semanticsdocs/design/2026-05-21-marza-faktine-semantics.md
Import orchestrator + phase parsersscripts/import-excel.ts · scripts/import/excel/
Materializationscripts/import/materialize.ts
Gold sync (parser + planner + reader)scripts/sync-databricks-financials.ts · src/lib/databricks/
Outbound read contract (the other brief)docs/datalake-access-page/ · sps-datalake-contract.pages.dev
One line: SPS V4 was bootstrapped once from Excel, financials are kept live by the sps_table2 gold sync, and everything else is now entered in-app. Anything you want to reconcile, re-derive, or automate beyond that is a short conversation with Vilius.