Output:
Chunk ID: cff3c9
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 8153
Output:
   300	        "warnings": [
   301	            "Calculated cost basis remains partial where no documented acquisition exists in this source bundle"
   302	        ],
   303	    }
   304
   305
   306	def preview_postfinance_import(conn: Connection, request: dict[str, Any]) -> dict[str, Any]:
   307	    accounts = _resolve_accounts(conn, request)
   308	    zip_raw, overview_raw = _source_bytes(request)
   309	    bundle = parse_postfinance_bundle(zip_raw, overview_raw)
   310	    mapping = _match_instruments(conn, bundle, accounts["etrading_depot"])
   311	    payload = _preview_payload(bundle, accounts, mapping)
   312	    revision = _revision(conn, accounts)
   313	    fingerprint = _hash(payload, revision)
   314	    duplicate = conn.execute(
   315	        "SELECT batch_id FROM postfinance_import_batches WHERE bundle_sha256=?",
   316	        (bundle.bundle_hash,),
   317	    ).fetchone()
   318	    conflict = conn.execute(
   319	        "SELECT batch_id FROM postfinance_import_batches WHERE substr(snapshot_at,1,10)=? AND bundle_sha256<>?",
   320	        (bundle.snapshot.valuation_at[:10], bundle.bundle_hash),
   321	    ).fetchone()
   322	    position_collision = conn.execute(
   323	        "SELECT COUNT(*) FROM positions_snapshot WHERE account_id=? AND snapshot_date=?",
   324	        (accounts["etrading_depot"], bundle.snapshot.valuation_at[:10]),
   325	    ).fetchone()[0]
   326	    cash_collision = conn.execute(
   327	        """SELECT COUNT(*) FROM cash_balances
   328	           WHERE account_id=? AND balance_date=? AND source_type='postfinance_official_import'""",
   329	        (accounts["etrading_cash"], bundle.snapshot.valuation_at[:10]),
   330	    ).fetchone()[0]
   331	    if not duplicate and (position_collision or cash_collision):
   332	        conflict = conflict or {"batch_id": "canonical_projection_date_conflict"}
   333	    semantic_conflicts = conn.execute(
   334	        """SELECT COUNT(*) FROM postfinance_documents d
   335	           WHERE EXISTS (SELECT 1 FROM json_each(?) j WHERE j.value=d.semantic_identity)
   336	             AND d.document_hash NOT IN (SELECT value FROM json_each(?))""",
   337	        (
   338	            json.dumps([d.semantic_identity for d in bundle.documents]),
   339	            json.dumps([d.document_hash for d in bundle.documents]),
   340	        ),
   341	    ).fetchone()[0]
   342	    if semantic_conflicts:
   343	        conflict = conflict or {"batch_id": "semantic_document_conflict"}
   344	    if conflict:
   345	        payload["warnings"].append(
   346	            "A different immutable PostFinance source has the same business identity"
   347	        )
   348	    payload.update(
   349	        {
   350	            "preview_id": f"pfpreview_{fingerprint[:24]}",
   351	            "confirmation_id": f"pfconfirm_{fingerprint[24:48]}",
   352	            "db_revision": revision,
   353	            "duplicate": bool(duplicate),
   354	            "existing_batch_id": str(duplicate["batch_id"]) if duplicate else None,
   355	            "conflict": bool(conflict),
   356	            "conflicting_batch_id": str(conflict["batch_id"]) if conflict else None,
   357	        }
   358	    )
   359	    return payload
   360
   361
   362	def _archive(raw: bytes, digest: str, suffix: str) -> tuple[str, bool]:
   363	    root = Path(os.environ.get("JARVIS_FINANCE_RUNTIME_DIR", str(DEFAULT_RUNTIME_DIR))).expanduser()
   364	    folder = root / "imports" / "postfinance" / "archive"
   365	    folder.mkdir(mode=0o700, parents=True, exist_ok=True)
   366	    folder.chmod(0o700)
   367	    target = folder / f"{digest}.{suffix}"
   368	    if target.exists():
   369	        if sha256(target.read_bytes()).hexdigest() != digest:
   370	            raise ValueError("Archived PostFinance source hash mismatch")
   371	        return f"postfinance/archive/{target.name}", False
   372	    tmp = folder / f".{digest}.{uuid4().hex}.tmp"
   373	    fd = os.open(tmp, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
   374	    try:
   375	        with os.fdopen(fd, "wb") as handle:
   376	            handle.write(raw)
   377	            handle.flush()
   378	            os.fsync(handle.fileno())
   379	        os.replace(tmp, target)
   380	        target.chmod(0o600)
   381	    finally:
   382	        tmp.unlink(missing_ok=True)
   383	    return f"postfinance/archive/{target.name}", True
   384
   385
   386	def _remove_archive(reference: str) -> None:
   387	    root = Path(os.environ.get("JARVIS_FINANCE_RUNTIME_DIR", str(DEFAULT_RUNTIME_DIR))).expanduser()
   388	    (root / "imports" / reference).unlink(missing_ok=True)
   389
   390
   391	def _audit(conn: Connection, batch_id: str, bundle: PFBundle, now: str) -> str:
   392	    audit_id = _id("audit")
   393	    details = {
   394	        "bundle_sha256": bundle.bundle_hash,
   395	        "parser_id": PARSER_ID,
   396	        "parser_version": PARSER_VERSION,
   397	        "document_count": len(bundle.documents) + 1,
   398	        "event_count": len(bundle.events),
   399	        "snapshot_at": bundle.snapshot.valuation_at,
   400	    }
   401	    conn.execute(
   402	        """INSERT INTO audit_log(audit_id,timestamp,source,action,entity_type,entity_id,old_values_json,new_values_json,user_text_note,confirmed,confirmation_timestamp,auto_parsed,parse_confidence,created_by,quality_status,created_at)
   403	           VALUES(?,?,'postfinance_sprint13','postfinance_import_confirm','postfinance_import_batch',?,NULL,?,'Verified official source bundle',1,?,0,NULL,'user','ok',?)""",
   404	        (audit_id, now, batch_id, json.dumps(details, sort_keys=True), now, now),
   405	    )
   406	    return audit_id
   407
   408
   409	def _canonical_transaction(
   410	    conn: Connection,
   411	    event_id: str,
   412	    event: PFEvent,
   413	    account_id: str,
   414	    instrument_id: str | None,
   415	    now: str,
   416	) -> None:
   417	    if event.event_type in {"split", "corporate_action"}:
   418	        return
   419	    tx_type = {
   420	        "buy": "buy",
   421	        "sell": "sell",
   422	        "dividend": "dividend",
   423	        "interest": "interest",
   424	        "fee": "fee",
   425	        "internal_transfer": "transfer",
   426	        "fx": "fx",
   427	    }[event.event_type]
   428	    activity = (
   429	        "internal_transfer"
   430	        if event.event_type == "internal_transfer"
   431	        else "external_cashflow"
   432	        if event.event_type in {"dividend", "interest", "fee"}
   433	        else "trade"
   434	    )
   435	    columns = (
   436	        "transaction_id,transaction_type,account_id,instrument_id,trade_date,settlement_date,quantity,"
   437	        "price_original,gross_amount_original,fee_original,tax_original,net_amount_original,currency_original,"
   438	        "fx_rate_to_chf,fx_source,fx_status,gross_amount_chf,fee_chf,tax_chf,net_amount_chf,source_type,source_id,"
   439	        "external_transaction_id,row_hash,is_confirmed,quality_status,notes,created_at,updated_at,is_voided,"
   440	        "activity_kind,booking_date,base_currency,internal_transfer_group_id,source_reference"
   441	    )
   442	    values = (
   443	        event_id,
   444	        tx_type,
   445	        account_id,
   446	        instrument_id,
   447	        event.occurred_on,
   448	        event.settlement_on,
   449	        _text(event.quantity),
   450	        _text(event.price_original),
   451	        _text(event.gross_original),
   452	        _text(event.fee_original) or "0",
   453	        _text(event.tax_original) or "0",
   454	        _text(event.net_original),
   455	        event.currency,
   456	        _text(event.fx_rate_to_chf),
   457	        None,
   458	        "unavailable",
   459	        None,
   460	        None,
   461	        None,
   462	        None,
   463	        "postfinance_official_import",
   464	        event_id,
   465	        event.event_fingerprint,
   466	        event.event_fingerprint,
   467	        1,
   468	        event.quality_status,
   469	        "Verified PostFinance document event",
   470	        now,
   471	        None,
   472	        0,
   473	        activity,
   474	        event.settlement_on or event.occurred_on,
   475	        "CHF",
   476	        event.internal_transfer_group,
   477	        _hash(event.semantic_reference),
   478	    )
   479	    conn.execute(
   480	        f"INSERT INTO transactions({columns}) VALUES({','.join('?' for _ in values)})", values
   481	    )
   482
   483
   484	def confirm_postfinance_import(conn: Connection, request: dict[str, Any]) -> dict[str, Any]:
   485	    if request.get("confirm") is not True:
   486	        raise ValueError("Explicit PostFinance import confirmation is required")
   487	    preview = preview_postfinance_import(conn, request)
   488	    if (
   489	        request.get("preview_id") != preview["preview_id"]
   490	        or request.get("confirmation_id") != preview["confirmation_id"]
   491	    ):
   492	        raise ValueError("PostFinance import preview is stale or changed")
   493	    if preview["conflict"]:
   494	        raise ValueError("PostFinance source conflicts with existing immutable business identity")
   495	    if preview["duplicate"]:
   496	        row = conn.execute(
   497	            "SELECT batch_id,audit_id FROM postfinance_import_batches WHERE bundle_sha256=?",
   498	            (preview["bundle_sha256"],),
   499	        ).fetchone()
   500	        return {
   501	            "status": "confirmed",
   502	            "batch_id": row["batch_id"],
   503	            "audit_id": row["audit_id"],
   504	            "idempotent": True,
   505	        }
   506	    accounts = _resolve_accounts(conn, request)
   507	    zip_raw, overview_raw = _source_bytes(request)
   508	    bundle = parse_postfinance_bundle(zip_raw, overview_raw)  # mandatory byte reparse on confirm
   509	    if (
   510	        bundle.bundle_hash != preview["bundle_sha256"]
   511	        or _revision(conn, accounts) != preview["db_revision"]
   512	    ):
   513	        raise ValueError("PostFinance source bytes or database revision changed after preview")
   514	    position_map = _match_instruments(conn, bundle, accounts["etrading_depot"])
   515	    event_instruments = {
   516	        event.event_fingerprint: _event_instrument(event, bundle, position_map, conn)
   517	        for event in bundle.events
   518	    }
   519	    archive_refs: list[tuple[str, bool]] = []
   520	    zip_ref, created = _archive(zip_raw, bundle.zip_hash, "zip")
   521	    archive_refs.append((zip_ref, created))
   522	    overview_ref, created = _archive(overview_raw, bundle.overview_hash, "pdf")
   523	    archive_refs.append((overview_ref, created))
   524	    for document in bundle.documents:
   525	        reference, created = _archive(document.raw, document.document_hash, "pdf")
   526	        archive_refs.append((reference, created))
   527	    now = _now()
   528	    batch_id = _id("pfbatch")
   529	    snapshot_id = _id("pfsnapshot")
   530	    try:
   531	        conn.execute("BEGIN IMMEDIATE")
   532	        if _revision(conn, accounts) != preview["db_revision"]:
   533	            raise ValueError("PostFinance database revision changed before persistence")
   534	        _ensure_efinance_account(conn, accounts, now)
   535	        for role, account_id in accounts.items():
   536	            conn.execute(
   537	                "INSERT OR IGNORE INTO postfinance_account_roles(role,account_id,source_reference_hash,created_at) VALUES(?,?,NULL,?)",
   538	                (role, account_id, now),
   539	            )
   540	            mapped = conn.execute(
   541	                "SELECT account_id FROM postfinance_account_roles WHERE role=?", (role,)
   542	            ).fetchone()
   543	            if not mapped or mapped["account_id"] != account_id:
   544	                raise ValueError(
   545	                    "PostFinance account-role mapping conflicts with confirmed mapping"
   546	                )
   547	        audit_id = _audit(conn, batch_id, bundle, now)
   548	        conn.execute(
   549	            """INSERT INTO postfinance_import_batches(batch_id,bundle_sha256,zip_sha256,overview_sha256,parser_id,parser_version,db_revision,archive_reference,snapshot_at,status,audit_id,confirmed_at,confirmed_by) VALUES(?,?,?,?,?,?,?,?,?,'confirmed',?,?,'user')""",
   550	            (
   551	                batch_id,
   552	                bundle.bundle_hash,
   553	                bundle.zip_hash,
   554	                bundle.overview_hash,
   555	                PARSER_ID,
   556	                PARSER_VERSION,
   557	                preview["db_revision"],
   558	                zip_ref,
   559	                bundle.snapshot.valuation_at,
   560	                audit_id,
   561	                now,
   562	            ),
   563	        )
   564	        for document in bundle.documents:
   565	            reference = f"postfinance/archive/{document.document_hash}.pdf"
   566	            ref_hash = _hash(document.semantic_reference)
   567	            existing = conn.execute(
   568	                "SELECT document_hash FROM postfinance_documents WHERE semantic_identity=?",
   569	                (document.semantic_identity,),
   570	            ).fetchone()
   571	            if existing and existing["document_hash"] != document.document_hash:
   572	                raise ValueError("PostFinance semantic document conflict")
   573	            conn.execute(
   574	                """INSERT OR IGNORE INTO postfinance_documents(document_hash,filename_hash,semantic_identity,semantic_reference_hash,document_type,document_date,account_reference_hash,account_role,page_count,archive_reference,parser_version,created_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?)""",
   575	                (
   576	                    document.document_hash,
   577	                    document.filename_hash,
   578	                    document.semantic_identity,
   579	                    ref_hash,
   580	                    document.document_type,
   581	                    document.document_date,
   582	                    document.account_reference_hash,
   583	                    document.account_role,
   584	                    document.page_count,
   585	                    reference,
   586	                    PARSER_VERSION,
   587	                    now,
   588	                ),
   589	            )
   590	            conn.execute(
   591	                "INSERT INTO postfinance_batch_documents(batch_id,document_hash) VALUES(?,?)",
   592	                (batch_id, document.document_hash),
   593	            )
   594	        snapshot = bundle.snapshot
   595	        component_total = snapshot.securities_total_chf + snapshot.cash_total_chf
   596	        difference = snapshot.total_value_chf - component_total
   597	        conn.execute(
   598	            """INSERT INTO postfinance_snapshots(snapshot_id,batch_id,account_id,snapshot_at,total_chf,securities_chf,cash_chf,stocks_chf,etfs_chf,component_total_chf,difference_chf,tolerance_chf,position_count,cash_count,open_orders,reconciliation_status,created_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,? ,?,?,?,?,?)""",
   599	            (
   600	                snapshot_id,
   601	                batch_id,
   602	                accounts["etrading_depot"],
   603	                snapshot.valuation_at,
   604	                _money(snapshot.total_value_chf),
   605	                _money(snapshot.securities_total_chf),
   606	                _money(snapshot.cash_total_chf),
   607	                _money(snapshot.stock_total_chf),
   608	                _money(snapshot.etf_total_chf),
   609	                _money(component_total),
   610	                _money(difference),
   611	                _money(TOLERANCE),
   612	                len(snapshot.positions),
   613	                len(snapshot.cash),
   614	                snapshot.open_orders,
   615	                "matched" if difference == 0 else "within_tolerance",
   616	                now,
   617	            ),
   618	        )
   619	        for event in bundle.events:
   620	            instrument_id = event_instruments[event.event_fingerprint]
   621	            event_id = f"pfevent_{event.event_fingerprint[:24]}"
   622	            account_id = accounts[event.account_role]
   623	            conn.execute(
   624	                """INSERT INTO postfinance_ledger_events(event_id,event_fingerprint,document_hash,semantic_reference_hash,event_type,account_role,account_id,occurred_on,settlement_on,direction,instrument_id,quantity,price_original,gross_original,fee_original,tax_original,net_original,currency,fx_rate_to_chf,internal_transfer_group,quality_status,reason_codes_json,created_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)""",
   625	                (
   626	                    event_id,
   627	                    event.event_fingerprint,
   628	                    event.document_hash,
   629	                    _hash(event.semantic_reference),
   630	                    event.event_type,
   631	                    event.account_role,
   632	                    account_id,
   633	                    event.occurred_on,
   634	                    event.settlement_on,
   635	                    event.direction or "neutral",
   636	                    instrument_id,
   637	                    _text(event.quantity),
   638	                    _text(event.price_original),
   639	                    _text(event.gross_original),
   640	                    _text(event.fee_original) or "0",
   641	                    _text(event.tax_original) or "0",
   642	                    _text(event.net_original),
   643	                    event.currency,
   644	                    _text(event.fx_rate_to_chf),
   645	                    event.internal_transfer_group,
   646	                    event.quality_status,
   647	                    json.dumps(event.reason_codes),
   648	                    now,
   649	                ),
   650	            )
   651	            for kind, amount in (
   652	                ("gross", event.gross_original),
   653	                ("fee", event.fee_original),
   654	                ("tax", event.tax_original),
   655	                ("net", event.net_original),
   656	            ):
   657	                if amount is not None and event.currency:
   658	                    conn.execute(
   659	                        "INSERT INTO postfinance_event_components(component_id,event_id,component_type,amount_original,currency,provenance_document_hash,created_at) VALUES(?,?,?,?,?,?,?)",
   660	                        (
   661	                            _id("pfcomponent"),
   662	                            event_id,
   663	                            kind,
   664	                            _text(amount),
   665	                            event.currency,
   666	                            event.document_hash,
   667	                            now,
   668	                        ),
   669	                    )
   670	            _canonical_transaction(conn, event_id, event, account_id, instrument_id, now)
   671	        lots: dict[str, list[dict[str, Any]]] = defaultdict(list)
   672	        for event in sorted(
   673	            bundle.events,
   674	            key=lambda item: (
   675	                item.occurred_on,
   676	                0 if item.event_type == "buy" else 1,
   677	                item.event_fingerprint,
   678	            ),
   679	        ):
   680	            instrument_id = event_instruments[event.event_fingerprint]
   681	            if (
   682	                event.event_type == "buy"
   683	                and instrument_id
   684	                and event.quantity
   685	                and event.price_original
   686	                and event.currency
   687	            ):
   688	                lots[instrument_id].append({"event": event, "remaining": event.quantity})
   689	            elif event.event_type == "sell" and instrument_id and event.quantity:
   690	                to_consume = event.quantity
   691	                for lot in lots[instrument_id]:
   692	                    if to_consume <= 0:
   693	                        break
   694	                    consumed = min(lot["remaining"], to_consume)
   695	                    lot["remaining"] -= consumed
   696	                    to_consume -= consumed
   697	        for instrument_id, instrument_lots in lots.items():
   698	            for lot in instrument_lots:
   699	                event = lot["event"]
   700	                conn.execute(
   701	                    """INSERT INTO postfinance_cost_basis_lots(lot_id,event_id,instrument_id,acquired_on,quantity_acquired,quantity_remaining,unit_cost_original,fees_original,currency,method,provenance_document_hash,created_at) VALUES(?,?,?,?,?,?,?,?,?,'documented_trade_fifo',?,?)""",
   702	                    (
   703	                        _id("pflot"),
   704	                        f"pfevent_{event.event_fingerprint[:24]}",
   705	                        instrument_id,
   706	                        event.occurred_on,
   707	                        _text(event.quantity),
   708	                        _text(lot["remaining"]),
   709	                        _text(event.price_original),
   710	                        _text(event.fee_original) or "0",
   711	                        event.currency,
   712	                        event.document_hash,
   713	                        now,
   714	                    ),
   715	                )
   716	        for position in snapshot.positions:
   717	            instrument_id = position_map[position.row_reference]
   718	            matched_lots = lots.get(instrument_id, [])
   719	            quantity = sum((lot["remaining"] for lot in matched_lots), Decimal("0"))
   720	            complete = (
   721	                bool(matched_lots)
   722	                and quantity == position.quantity
   723	                and len({lot["event"].currency for lot in matched_lots}) == 1
   724	            )
   725	            calculated = (
   726	                sum(
   727	                    (
   728	                        lot["remaining"] * (lot["event"].price_original or 0)
   729	                        + (lot["event"].fee_original + lot["event"].tax_original)
   730	                        * (lot["remaining"] / lot["event"].quantity)
   731	                        for lot in matched_lots
   732	                        if lot["event"].quantity
   733	                    ),
   734	                    Decimal("0"),
   735	                )
   736	                if complete
   737	                else None
   738	            )
   739	            status = "complete" if complete else "partial" if matched_lots else "unavailable"
   740	            conn.execute(
   741	                """INSERT INTO postfinance_snapshot_positions(snapshot_position_id,snapshot_id,source_row_reference,instrument_id,asset_class,quantity,provider_average_cost_original,provider_cost_total_original,price_original,price_currency,value_chf,weight_pct,computed_cost_basis_original,computed_cost_basis_status,provenance_json,created_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)""",
   742	                (
   743	                    _id("pfposition"),
   744	                    snapshot_id,
   745	                    position.row_reference,
   746	                    instrument_id,
   747	                    position.asset_class,
   748	                    _text(position.quantity),
   749	                    _text(position.provider_average_cost_original),
   750	                    _text(position.provider_cost_total_original),
   751	                    _text(position.market_price_original),
   752	                    position.price_currency,
   753	                    _money(position.market_value_chf),
   754	                    _text(position.weight_pct),
   755	                    _text(calculated),
   756	                    status,
   757	                    json.dumps(
   758	                        {
   759	                            "provider": "official_account_overview",
   760	                            "calculated": "documented_trade_fifo",
   761	                            "estimated": False,
   762	                        },
   763	                        sort_keys=True,
   764	                    ),
   765	                    now,
   766	                ),
   767	            )
   768	            conn.execute(
   769	                """INSERT INTO positions_snapshot(position_snapshot_id,snapshot_date,account_id,platform_id,instrument_id,quantity,average_cost_original,cost_basis_original,market_price_original,market_value_original,market_fx_rate_to_chf,market_value_chf,portfolio_weight_pct,category,data_quality_status,source_type,source_reference,created_at) SELECT ?,?,?,a.platform_id,?,?,?,?,?,?,?,?,?,?,?,?,?,? FROM accounts a WHERE a.account_id=?""",
   770	                (
   771	                    _id("position"),
   772	                    snapshot.valuation_at[:10],
   773	                    accounts["etrading_depot"],
   774	                    instrument_id,
   775	                    _text(position.quantity),
   776	                    _text(position.provider_average_cost_original),
   777	                    _text(position.provider_cost_total_original),
   778	                    _text(position.market_price_original),
   779	                    _text(position.quantity * position.market_price_original),
   780	                    None,
   781	                    _money(position.market_value_chf),
   782	                    _text(position.weight_pct),
   783	                    position.asset_class,
   784	                    "verified",
   785	                    "postfinance_official_import",
   786	                    snapshot_id,
   787	                    now,
   788	                    accounts["etrading_depot"],
   789	                ),
   790	            )
   791	        for cash in snapshot.cash:
   792	            conn.execute(
   793	                "INSERT INTO postfinance_snapshot_cash(snapshot_cash_id,snapshot_id,currency,amount_original,fx_rate_to_chf,value_chf,created_at) VALUES(?,?,?,?,?,?,?)",
   794	                (
   795	                    _id("pfcash"),
   796	                    snapshot_id,
   797	                    cash.currency,
   798	                    _text(cash.amount_original),
   799	                    _text(cash.fx_rate_to_chf),
   800	                    _money(cash.source_value_chf),
   801	                    now,
   802	                ),
   803	            )
   804	            conn.execute(
   805	                "INSERT INTO cash_balances(cash_balance_id,account_id,balance_date,currency,amount_original,fx_rate_to_chf,amount_chf,source_type,quality_status,notes,created_at) VALUES(?,?,?,?,?,?,?,?,?,?,?)",
   806	                (
   807	                    _id("cash"),
   808	                    accounts["etrading_cash"],
   809	                    snapshot.valuation_at[:10],
   810	                    cash.currency,
   811	                    _text(cash.amount_original),
   812	                    _text(cash.fx_rate_to_chf),
   813	                    _money(cash.source_value_chf),
   814	                    "postfinance_official_import",
   815	                    "verified",
   816	                    "Official PostFinance account overview",
   817	                    now,
   818	                ),
   819	            )
   820	        conn.execute(
   821	            "INSERT INTO account_value_snapshots(snapshot_id,account_id,valuation_date,total_value_chf,currency,source_type,quality_status,notes,created_at,valuation_at,source_reference,is_active) VALUES(?,?,?,?,'CHF','postfinance_official_import','ok','Official PostFinance account overview',?,?,?,1)",
   822	            (
   823	                _id("acctval"),
   824	                accounts["etrading_depot"],
   825	                snapshot.valuation_at[:10],
   826	                _money(snapshot.total_value_chf),
   827	                now,
   828	                snapshot.valuation_at,
   829	                snapshot_id,
   830	            ),
   831	        )
   832	        conn.commit()
   833	    except IntegrityError as exc:
   834	        conn.rollback()
   835	        duplicate = conn.execute(
   836	            "SELECT batch_id,audit_id FROM postfinance_import_batches WHERE bundle_sha256=?",
   837	            (bundle.bundle_hash,),
   838	        ).fetchone()
   839	        if duplicate:
   840	            return {
   841	                "status": "confirmed",
   842	                "batch_id": duplicate["batch_id"],
   843	                "audit_id": duplicate["audit_id"],
   844	                "idempotent": True,
   845	            }
   846	        for ref, created in archive_refs:
   847	            if created:
   848	                _remove_archive(ref)
   849	        raise ValueError("PostFinance import conflicted with immutable data") from exc
   850	    except Exception:
   851	        conn.rollback()
   852	        for ref, created in archive_refs:
   853	            if created:
   854	                _remove_archive(ref)
   855	        raise
   856	    return {
   857	        "status": "confirmed",
   858	        "batch_id": batch_id,
   859	        "snapshot_id": snapshot_id,
   860	        "audit_id": audit_id,
   861	        "idempotent": False,
   862	    }
   863
   864
   865	def get_postfinance_summary(conn: Connection) -> dict[str, Any]:
   866	    latest = conn.execute(
   867	        """SELECT snapshot_id,snapshot_at,total_chf,securities_chf,cash_chf,stocks_chf,etfs_chf,
   868	                  position_count,cash_count,open_orders,difference_chf,reconciliation_status,created_at
   869	           FROM postfinance_snapshots ORDER BY snapshot_at DESC,created_at DESC LIMIT 1"""
   870	    ).fetchone()
   871	    if not latest:
   872	        return {
   873	            "latest_snapshot": None,
   874	            "positions": [],
   875	            "cash": [],
   876	            "event_counts": {},
   877	            "imports": [],
   878	        }
   879	    positions = [
   880	        {
   881	            "row_key": _hash("position", row["instrument_name"], row["quantity"])[:16],
   882	            "instrument_name": row["instrument_name"],
   883	            "ticker": row["ticker"],
   884	            "asset_class": row["asset_class"],
   885	            "quantity": row["quantity"],
   886	            "provider_average_cost_original": row["provider_average_cost_original"],
   887	            "provider_cost_total_original": row["provider_cost_total_original"],
   888	            "price_original": row["price_original"],
   889	            "price_currency": row["price_currency"],
   890	            "value_chf": row["value_chf"],
   891	            "weight_pct": row["weight_pct"],
   892	            "computed_cost_basis_original": row["computed_cost_basis_original"],
   893	            "computed_cost_basis_status": row["computed_cost_basis_status"],
   894	        }
   895	        for row in conn.execute(
   896	            """SELECT p.asset_class,p.quantity,p.provider_average_cost_original,
   897	                      p.provider_cost_total_original,p.price_original,p.price_currency,p.value_chf,
   898	                      p.weight_pct,p.computed_cost_basis_original,p.computed_cost_basis_status,
   899	                      i.name AS instrument_name,i.ticker
   900	               FROM postfinance_snapshot_positions p
   901	               JOIN instruments i ON i.instrument_id=p.instrument_id
   902	               WHERE p.snapshot_id=? ORDER BY CAST(p.value_chf AS REAL) DESC""",
   903	            (latest["snapshot_id"],),
   904	        ).fetchall()
   905	    ]
   906	    cash = [
   907	        {
   908	            "row_key": _hash("cash", row["currency"])[:16],
   909	            "currency": row["currency"],
   910	            "amount_original": row["amount_original"],
   911	            "fx_rate_to_chf": row["fx_rate_to_chf"],
   912	            "value_chf": row["value_chf"],
   913	        }
   914	        for row in conn.execute(
   915	            """SELECT currency,amount_original,fx_rate_to_chf,value_chf
   916	               FROM postfinance_snapshot_cash WHERE snapshot_id=? ORDER BY currency""",
   917	            (latest["snapshot_id"],),
   918	        ).fetchall()
   919	    ]
   920	    events = {


Output:
Chunk ID: 5bc711
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 4111
Output:
     1	from __future__ import annotations
     2
     3	from sqlite3 import Connection
     4
     5
     6	def create_postfinance_ledger_import_v1(conn: Connection) -> None:
     7	    """Create immutable PostFinance source, ledger, snapshot and cost-basis tables."""
     8	    position_columns = {row[1] for row in conn.execute("PRAGMA table_info(positions_snapshot)")}
     9	    if "source_type" not in position_columns:
    10	        conn.execute("ALTER TABLE positions_snapshot ADD COLUMN source_type TEXT")
    11	    if "source_reference" not in position_columns:
    12	        conn.execute("ALTER TABLE positions_snapshot ADD COLUMN source_reference TEXT")
    13	    conn.executescript(
    14	        """
    15	        CREATE TABLE IF NOT EXISTS postfinance_account_roles (
    16	            role TEXT PRIMARY KEY CHECK(role IN ('efinance','etrading_depot','etrading_cash')),
    17	            account_id TEXT NOT NULL REFERENCES accounts(account_id),
    18	            source_reference_hash TEXT,
    19	            created_at TEXT NOT NULL,
    20	            UNIQUE(account_id, role)
    21	        );
    22	        CREATE TABLE IF NOT EXISTS postfinance_import_batches (
    23	            batch_id TEXT PRIMARY KEY,
    24	            bundle_sha256 TEXT NOT NULL UNIQUE CHECK(length(bundle_sha256)=64),
    25	            zip_sha256 TEXT NOT NULL CHECK(length(zip_sha256)=64),
    26	            overview_sha256 TEXT NOT NULL CHECK(length(overview_sha256)=64),
    27	            parser_id TEXT NOT NULL,
    28	            parser_version TEXT NOT NULL,
    29	            db_revision TEXT NOT NULL,
    30	            archive_reference TEXT NOT NULL,
    31	            snapshot_at TEXT NOT NULL,
    32	            status TEXT NOT NULL CHECK(status='confirmed'),
    33	            audit_id TEXT NOT NULL,
    34	            confirmed_at TEXT NOT NULL,
    35	            confirmed_by TEXT NOT NULL DEFAULT 'user'
    36	        );
    37	        CREATE TABLE IF NOT EXISTS postfinance_documents (
    38	            document_hash TEXT PRIMARY KEY CHECK(length(document_hash)=64),
    39	            filename_hash TEXT NOT NULL CHECK(length(filename_hash)=64),
    40	            semantic_identity TEXT NOT NULL UNIQUE CHECK(length(semantic_identity)=64),
    41	            semantic_reference_hash TEXT NOT NULL CHECK(length(semantic_reference_hash)=64),
    42	            document_type TEXT NOT NULL,
    43	            document_date TEXT,
    44	            account_reference_hash TEXT,
    45	            account_role TEXT NOT NULL CHECK(account_role IN ('efinance','etrading_depot','etrading_cash')),
    46	            page_count INTEGER NOT NULL CHECK(page_count > 0),
    47	            archive_reference TEXT NOT NULL,
    48	            parser_version TEXT NOT NULL,
    49	            created_at TEXT NOT NULL
    50	        );
    51	        CREATE TABLE IF NOT EXISTS postfinance_batch_documents (
    52	            batch_id TEXT NOT NULL REFERENCES postfinance_import_batches(batch_id),
    53	            document_hash TEXT NOT NULL REFERENCES postfinance_documents(document_hash),
    54	            PRIMARY KEY(batch_id, document_hash)
    55	        );
    56	        CREATE TABLE IF NOT EXISTS postfinance_snapshots (
    57	            snapshot_id TEXT PRIMARY KEY,
    58	            batch_id TEXT NOT NULL UNIQUE REFERENCES postfinance_import_batches(batch_id),
    59	            account_id TEXT NOT NULL REFERENCES accounts(account_id),
    60	            snapshot_at TEXT NOT NULL,
    61	            total_chf TEXT NOT NULL,
    62	            securities_chf TEXT NOT NULL,
    63	            cash_chf TEXT NOT NULL,
    64	            stocks_chf TEXT NOT NULL,
    65	            etfs_chf TEXT NOT NULL,
    66	            component_total_chf TEXT NOT NULL,
    67	            difference_chf TEXT NOT NULL,
    68	            tolerance_chf TEXT NOT NULL,
    69	            position_count INTEGER NOT NULL,
    70	            cash_count INTEGER NOT NULL,
    71	            open_orders INTEGER NOT NULL,
    72	            reconciliation_status TEXT NOT NULL CHECK(reconciliation_status IN ('matched','within_tolerance')),
    73	            created_at TEXT NOT NULL
    74	        );
    75	        CREATE TABLE IF NOT EXISTS postfinance_snapshot_positions (
    76	            snapshot_position_id TEXT PRIMARY KEY,
    77	            snapshot_id TEXT NOT NULL REFERENCES postfinance_snapshots(snapshot_id),
    78	            source_row_reference TEXT NOT NULL,
    79	            instrument_id TEXT NOT NULL REFERENCES instruments(instrument_id),
    80	            asset_class TEXT NOT NULL CHECK(asset_class IN ('stock','etf')),
    81	            quantity TEXT NOT NULL,
    82	            provider_average_cost_original TEXT,
    83	            provider_cost_total_original TEXT,
    84	            price_original TEXT NOT NULL,
    85	            price_currency TEXT NOT NULL,
    86	            value_chf TEXT NOT NULL,
    87	            weight_pct TEXT NOT NULL,
    88	            computed_cost_basis_original TEXT,
    89	            computed_cost_basis_status TEXT NOT NULL CHECK(computed_cost_basis_status IN ('complete','partial','unavailable')),
    90	            provenance_json TEXT NOT NULL CHECK(json_valid(provenance_json)),
    91	            created_at TEXT NOT NULL,
    92	            UNIQUE(snapshot_id, source_row_reference),
    93	            UNIQUE(snapshot_id, instrument_id)
    94	        );
    95	        CREATE TABLE IF NOT EXISTS postfinance_snapshot_cash (
    96	            snapshot_cash_id TEXT PRIMARY KEY,
    97	            snapshot_id TEXT NOT NULL REFERENCES postfinance_snapshots(snapshot_id),
    98	            currency TEXT NOT NULL,
    99	            amount_original TEXT NOT NULL,
   100	            fx_rate_to_chf TEXT NOT NULL,
   101	            value_chf TEXT NOT NULL,
   102	            created_at TEXT NOT NULL,
   103	            UNIQUE(snapshot_id, currency)
   104	        );
   105	        CREATE TABLE IF NOT EXISTS postfinance_ledger_events (
   106	            event_id TEXT PRIMARY KEY,
   107	            event_fingerprint TEXT NOT NULL UNIQUE CHECK(length(event_fingerprint)=64),
   108	            document_hash TEXT NOT NULL REFERENCES postfinance_documents(document_hash),
   109	            semantic_reference_hash TEXT NOT NULL CHECK(length(semantic_reference_hash)=64),
   110	            event_type TEXT NOT NULL CHECK(event_type IN ('buy','sell','dividend','interest','fee','internal_transfer','fx','split','corporate_action')),
   111	            account_role TEXT NOT NULL CHECK(account_role IN ('efinance','etrading_depot','etrading_cash')),
   112	            account_id TEXT NOT NULL REFERENCES accounts(account_id),
   113	            occurred_on TEXT NOT NULL,
   114	            settlement_on TEXT,
   115	            direction TEXT NOT NULL CHECK(direction IN ('in','out','neutral')),
   116	            instrument_id TEXT REFERENCES instruments(instrument_id),
   117	            quantity TEXT,
   118	            price_original TEXT,
   119	            gross_original TEXT,
   120	            fee_original TEXT NOT NULL DEFAULT '0',
   121	            tax_original TEXT NOT NULL DEFAULT '0',
   122	            net_original TEXT,
   123	            currency TEXT,
   124	            fx_rate_to_chf TEXT,
   125	            internal_transfer_group TEXT,
   126	            quality_status TEXT NOT NULL CHECK(quality_status IN ('complete','partial')),
   127	            reason_codes_json TEXT NOT NULL CHECK(json_valid(reason_codes_json)),
   128	            created_at TEXT NOT NULL
   129	        );
   130	        CREATE INDEX IF NOT EXISTS idx_postfinance_events_time ON postfinance_ledger_events(occurred_on, event_type);
   131	        CREATE INDEX IF NOT EXISTS idx_postfinance_events_transfer ON postfinance_ledger_events(internal_transfer_group) WHERE internal_transfer_group IS NOT NULL;
   132	        CREATE TABLE IF NOT EXISTS postfinance_event_components (
   133	            component_id TEXT PRIMARY KEY,
   134	            event_id TEXT NOT NULL REFERENCES postfinance_ledger_events(event_id),
   135	            component_type TEXT NOT NULL CHECK(component_type IN ('gross','fee','tax','net','fx_amount')),
   136	            amount_original TEXT NOT NULL,
   137	            currency TEXT NOT NULL,
   138	            provenance_document_hash TEXT NOT NULL REFERENCES postfinance_documents(document_hash),
   139	            created_at TEXT NOT NULL,
   140	            UNIQUE(event_id, component_type, currency)
   141	        );
   142	        CREATE TABLE IF NOT EXISTS postfinance_cost_basis_lots (
   143	            lot_id TEXT PRIMARY KEY,
   144	            event_id TEXT NOT NULL UNIQUE REFERENCES postfinance_ledger_events(event_id),
   145	            instrument_id TEXT NOT NULL REFERENCES instruments(instrument_id),
   146	            acquired_on TEXT NOT NULL,
   147	            quantity_acquired TEXT NOT NULL,
   148	            quantity_remaining TEXT NOT NULL,
   149	            unit_cost_original TEXT NOT NULL,
   150	            fees_original TEXT NOT NULL,
   151	            currency TEXT NOT NULL,
   152	            method TEXT NOT NULL CHECK(method='documented_trade_fifo'),
   153	            provenance_document_hash TEXT NOT NULL REFERENCES postfinance_documents(document_hash),
   154	            created_at TEXT NOT NULL
   155	        );
   156	        CREATE UNIQUE INDEX IF NOT EXISTS ux_account_value_postfinance_official_reference
   157	            ON account_value_snapshots(source_reference)
   158	            WHERE source_type='postfinance_official_import' AND source_reference IS NOT NULL;
   159
   160	        CREATE TRIGGER IF NOT EXISTS postfinance_batches_immutable BEFORE UPDATE ON postfinance_import_batches
   161	        BEGIN SELECT RAISE(ABORT, 'postfinance import batches are immutable'); END;
   162	        CREATE TRIGGER IF NOT EXISTS postfinance_batches_no_delete BEFORE DELETE ON postfinance_import_batches
   163	        BEGIN SELECT RAISE(ABORT, 'postfinance import batches cannot be deleted'); END;
   164	        CREATE TRIGGER IF NOT EXISTS postfinance_documents_immutable BEFORE UPDATE ON postfinance_documents
   165	        BEGIN SELECT RAISE(ABORT, 'postfinance documents are immutable'); END;
   166	        CREATE TRIGGER IF NOT EXISTS postfinance_documents_no_delete BEFORE DELETE ON postfinance_documents
   167	        BEGIN SELECT RAISE(ABORT, 'postfinance documents cannot be deleted'); END;
   168	        CREATE TRIGGER IF NOT EXISTS postfinance_batch_documents_immutable BEFORE UPDATE ON postfinance_batch_documents
   169	        BEGIN SELECT RAISE(ABORT, 'postfinance batch document links are immutable'); END;
   170	        CREATE TRIGGER IF NOT EXISTS postfinance_batch_documents_no_delete BEFORE DELETE ON postfinance_batch_documents
   171	        BEGIN SELECT RAISE(ABORT, 'postfinance batch document links cannot be deleted'); END;
   172	        CREATE TRIGGER IF NOT EXISTS postfinance_snapshots_immutable BEFORE UPDATE ON postfinance_snapshots
   173	        BEGIN SELECT RAISE(ABORT, 'postfinance snapshots are immutable'); END;
   174	        CREATE TRIGGER IF NOT EXISTS postfinance_snapshots_no_delete BEFORE DELETE ON postfinance_snapshots
   175	        BEGIN SELECT RAISE(ABORT, 'postfinance snapshots cannot be deleted'); END;
   176	        CREATE TRIGGER IF NOT EXISTS postfinance_snapshot_positions_immutable BEFORE UPDATE ON postfinance_snapshot_positions
   177	        BEGIN SELECT RAISE(ABORT, 'postfinance snapshot positions are immutable'); END;
   178	        CREATE TRIGGER IF NOT EXISTS postfinance_snapshot_positions_no_delete BEFORE DELETE ON postfinance_snapshot_positions
   179	        BEGIN SELECT RAISE(ABORT, 'postfinance snapshot positions cannot be deleted'); END;
   180	        CREATE TRIGGER IF NOT EXISTS postfinance_snapshot_cash_immutable BEFORE UPDATE ON postfinance_snapshot_cash
   181	        BEGIN SELECT RAISE(ABORT, 'postfinance snapshot cash is immutable'); END;
   182	        CREATE TRIGGER IF NOT EXISTS postfinance_snapshot_cash_no_delete BEFORE DELETE ON postfinance_snapshot_cash
   183	        BEGIN SELECT RAISE(ABORT, 'postfinance snapshot cash cannot be deleted'); END;
   184	        CREATE TRIGGER IF NOT EXISTS postfinance_events_immutable BEFORE UPDATE ON postfinance_ledger_events
   185	        BEGIN SELECT RAISE(ABORT, 'postfinance ledger events are immutable'); END;
   186	        CREATE TRIGGER IF NOT EXISTS postfinance_events_no_delete BEFORE DELETE ON postfinance_ledger_events
   187	        BEGIN SELECT RAISE(ABORT, 'postfinance ledger events cannot be deleted'); END;
   188	        CREATE TRIGGER IF NOT EXISTS postfinance_event_components_immutable BEFORE UPDATE ON postfinance_event_components
   189	        BEGIN SELECT RAISE(ABORT, 'postfinance event components are immutable'); END;
   190	        CREATE TRIGGER IF NOT EXISTS postfinance_event_components_no_delete BEFORE DELETE ON postfinance_event_components
   191	        BEGIN SELECT RAISE(ABORT, 'postfinance event components cannot be deleted'); END;
   192	        CREATE TRIGGER IF NOT EXISTS postfinance_cost_basis_immutable BEFORE UPDATE ON postfinance_cost_basis_lots
   193	        BEGIN SELECT RAISE(ABORT, 'postfinance cost basis is immutable'); END;
   194	        CREATE TRIGGER IF NOT EXISTS postfinance_cost_basis_no_delete BEFORE DELETE ON postfinance_cost_basis_lots
   195	        BEGIN SELECT RAISE(ABORT, 'postfinance cost basis cannot be deleted'); END;
   196	        CREATE TRIGGER IF NOT EXISTS postfinance_official_account_values_immutable BEFORE UPDATE ON account_value_snapshots
   197	        WHEN OLD.source_type='postfinance_official_import'
   198	        BEGIN SELECT RAISE(ABORT, 'official postfinance account valuations are immutable'); END;
   199	        CREATE TRIGGER IF NOT EXISTS postfinance_official_account_values_no_delete BEFORE DELETE ON account_value_snapshots
   200	        WHEN OLD.source_type='postfinance_official_import'
   201	        BEGIN SELECT RAISE(ABORT, 'official postfinance account valuations cannot be deleted'); END;
   202	        CREATE TRIGGER IF NOT EXISTS postfinance_official_positions_immutable BEFORE UPDATE ON positions_snapshot
   203	        WHEN OLD.source_type='postfinance_official_import'
   204	        BEGIN SELECT RAISE(ABORT, 'official postfinance position projections are immutable'); END;
   205	        CREATE TRIGGER IF NOT EXISTS postfinance_official_positions_no_delete BEFORE DELETE ON positions_snapshot
   206	        WHEN OLD.source_type='postfinance_official_import'
   207	        BEGIN SELECT RAISE(ABORT, 'official postfinance position projections cannot be deleted'); END;
   208	        CREATE TRIGGER IF NOT EXISTS postfinance_official_cash_immutable BEFORE UPDATE ON cash_balances
   209	        WHEN OLD.source_type='postfinance_official_import'
   210	        BEGIN SELECT RAISE(ABORT, 'official postfinance cash projections are immutable'); END;
   211	        CREATE TRIGGER IF NOT EXISTS postfinance_official_cash_no_delete BEFORE DELETE ON cash_balances
   212	        WHEN OLD.source_type='postfinance_official_import'
   213	        BEGIN SELECT RAISE(ABORT, 'official postfinance cash projections cannot be deleted'); END;
   214	        CREATE TRIGGER IF NOT EXISTS postfinance_official_transactions_immutable BEFORE UPDATE ON transactions
   215	        WHEN OLD.source_type='postfinance_official_import'
   216	        BEGIN SELECT RAISE(ABORT, 'official postfinance transactions are immutable'); END;
   217	        CREATE TRIGGER IF NOT EXISTS postfinance_official_transactions_no_delete BEFORE DELETE ON transactions
   218	        WHEN OLD.source_type='postfinance_official_import'
   219	        BEGIN SELECT RAISE(ABORT, 'official postfinance transactions cannot be deleted'); END;
   220	        CREATE TRIGGER IF NOT EXISTS postfinance_audit_immutable_update
   221	        BEFORE UPDATE ON audit_log WHEN OLD.entity_type='postfinance_import_batch'
   222	        BEGIN SELECT RAISE(ABORT, 'postfinance import audit is immutable'); END;
   223	        CREATE TRIGGER IF NOT EXISTS postfinance_audit_no_delete
   224	        BEFORE DELETE ON audit_log WHEN OLD.entity_type='postfinance_import_batch'
   225	        BEGIN SELECT RAISE(ABORT, 'postfinance import audit cannot be deleted'); END;
   226	        """
   227	    )


Output:
Chunk ID: dc9bb0
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 448
Output:
     1	from __future__ import annotations
     2
     3	from fastapi import APIRouter, Depends, HTTPException
     4
     5	from jarvis_finance.api.dependencies import get_db
     6	from jarvis_finance.api.schemas.postfinance import (
     7	    PostFinanceConfirmRequest,
     8	    PostFinanceImportRequest,
     9	)
    10	from jarvis_finance.services.postfinance_service import (
    11	    confirm_postfinance_import,
    12	    get_postfinance_summary,
    13	    preview_postfinance_import,
    14	)
    15
    16	router = APIRouter(prefix="/postfinance", tags=["postfinance"])
    17
    18
    19	def _run(callable_, *args):
    20	    try:
    21	        return callable_(*args)
    22	    except ValueError as exc:
    23	        raise HTTPException(status_code=422, detail=str(exc)) from exc
    24
    25
    26	def _safe_preview(payload: dict) -> dict:
    27	    hidden = {
    28	        "bundle_sha256",
    29	        "zip_sha256",
    30	        "overview_sha256",
    31	        "db_revision",
    32	        "existing_batch_id",
    33	        "conflicting_batch_id",
    34	    }
    35	    return {key: value for key, value in payload.items() if key not in hidden}
    36
    37
    38	@router.get("/summary")
    39	def summary(conn=Depends(get_db)):
    40	    return _run(get_postfinance_summary, conn)
    41
    42
    43	@router.post("/imports/preview")
    44	def import_preview(request: PostFinanceImportRequest, conn=Depends(get_db)):
    45	    return _safe_preview(_run(preview_postfinance_import, conn, request.model_dump()))
    46
    47
    48	@router.post("/imports/confirm")
    49	def import_confirm(request: PostFinanceConfirmRequest, conn=Depends(get_db)):
    50	    return _run(confirm_postfinance_import, conn, request.model_dump())


nl -ba src/jarvis_finance/services/postfinance_service.py | sed -n '920,980p'


sed -n '1,220p' src/jarvis_finance/api/main.py


git diff -- src/jarvis_finance/api/main.py src/jarvis_finance/storage/migrations.py scripts/ci_portfolio_phase3_gate.py frontend/src/router/index.ts frontend/src/pages/PortfolioPage.vue


Output:
Chunk ID: 94b1fb
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 325
Output:
   920	    events = {
   921	        str(row["event_type"]): int(row["c"])
   922	        for row in conn.execute(
   923	            "SELECT event_type,COUNT(*) c FROM postfinance_ledger_events GROUP BY event_type ORDER BY event_type"
   924	        ).fetchall()
   925	    }
   926	    imports = [
   927	        dict(row)
   928	        for row in conn.execute(
   929	            """SELECT b.snapshot_at,b.parser_id,b.parser_version,b.confirmed_at,b.status,
   930	                      s.total_chf,s.securities_chf,s.cash_chf,s.position_count,s.cash_count,
   931	                      s.difference_chf,s.reconciliation_status,
   932	                      (SELECT COUNT(*) FROM postfinance_batch_documents d WHERE d.batch_id=b.batch_id) AS document_count
   933	               FROM postfinance_import_batches b JOIN postfinance_snapshots s ON s.batch_id=b.batch_id
   934	               ORDER BY b.snapshot_at DESC,b.confirmed_at DESC"""
   935	        ).fetchall()
   936	    ]
   937	    safe_latest = {key: latest[key] for key in latest.keys() if key != "snapshot_id"}
   938	    return {
   939	        "latest_snapshot": safe_latest,
   940	        "positions": positions,
   941	        "cash": cash,
   942	        "event_counts": events,
   943	        "imports": imports,
   944	    }


Output:
Chunk ID: 56ebc5
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 782
Output:
from __future__ import annotations

import os
from urllib.parse import urlparse

from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse

from jarvis_finance.api.routers import budget, cash, crypto, crypto_trader, equity, health, market, overview, positions, postfinance, reports, system, truewealth
from jarvis_finance.api.security import WRITE_METHODS, is_write_request_allowed, resolve_write_mode

LOCAL_ORIGINS = [
    "http://localhost:5173",
    "http://127.0.0.1:5173",
    "http://100.85.29.67:5173",
    "http://agent.tailbd371b.ts.net:5173",
    "http://localhost:8503",
    "http://127.0.0.1:8503",
]
READ_ONLY_POST_PATHS = {
    "/api/portfolio/ingestion/preview",
    "/api/portfolio/policy/preview",
    "/api/postfinance/imports/preview",
    "/api/truewealth/imports/preview",
    "/api/truewealth/manual-values/preview",
}

def _is_safe_local_origin(origin: str) -> bool:
    parsed = urlparse(origin)
    if parsed.scheme != "http" or not parsed.hostname or not parsed.port:
        return False
    hostname = parsed.hostname.lower()
    if hostname in {"localhost", "127.0.0.1"}:
        return True
    if hostname.startswith("100."):
        return True
    if hostname.endswith(".ts.net"):
        return True
    return False

def build_local_origins(environ: dict[str, str] | None = None) -> list[str]:
    env = environ or os.environ
    origins = list(LOCAL_ORIGINS)
    for raw_origin in env.get("JARVIS_FINANCE_CORS_ORIGINS", "").split(","):
        origin = raw_origin.strip().rstrip("/")
        if origin and _is_safe_local_origin(origin) and origin not in origins:
            origins.append(origin)
    return origins

def create_app(*, write_mode: str | None = None) -> FastAPI:
    active_write_mode = resolve_write_mode(write_mode)
    app = FastAPI(
        title="JARVIS Finance API",
        version="0.1.0",
        description="Read-only FastAPI v0 skeleton for the future Vue User Dashboard.",
    )
    app.add_middleware(
        CORSMiddleware,
        allow_origins=build_local_origins(),
        allow_credentials=False,
        allow_methods=["GET", "POST"] if active_write_mode == "disabled" else ["GET", "POST", "PUT", "PATCH", "DELETE"],
        allow_headers=["*"],
    )

    @app.middleware("http")
    async def block_untrusted_writes(request: Request, call_next):
        read_only_post = request.method == "POST" and request.url.path in READ_ONLY_POST_PATHS
        if request.method in WRITE_METHODS and not read_only_post and not is_write_request_allowed(request, active_write_mode):
            return JSONResponse(status_code=403, content={"detail": "write_operations_disabled"})
        return await call_next(request)

    app.state.write_mode = active_write_mode
    for router in (health.router, overview.router, crypto.router, crypto_trader.router, equity.router, cash.router, reports.router, positions.router, budget.router, system.router, market.router, truewealth.router, postfinance.router):
        app.include_router(router, prefix="/api")
    return app

app = create_app()


Output:
Chunk ID: 811a9f
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 0
Output:


NO FINDINGS