| from __future__ import annotations |
|
|
| from datetime import datetime, time |
| from decimal import Decimal |
| from pathlib import Path |
|
|
| import xlsxwriter |
|
|
| from .models import FundPortfolioReport, Severity, ValidationResult |
|
|
|
|
| NAVY = "#11233F" |
| BLUE = "#246BFE" |
| PALE_BLUE = "#EAF1FF" |
| TEAL = "#0F9D8A" |
| PALE_TEAL = "#E5F7F3" |
| AMBER = "#D98E04" |
| PALE_AMBER = "#FFF4D6" |
| RED = "#C83C3C" |
| PALE_RED = "#FDECEC" |
| SLATE = "#526173" |
| LIGHT = "#F5F7FA" |
| WHITE = "#FFFFFF" |
| BORDER = "#D9E0E8" |
|
|
|
|
| def _number(value: Decimal) -> float: |
| return float(value) |
|
|
|
|
| def _as_datetime(value): |
| return datetime.combine(value, time.min) |
|
|
|
|
| def _formats(workbook: xlsxwriter.Workbook) -> dict[str, xlsxwriter.format.Format]: |
| return { |
| "title": workbook.add_format( |
| { |
| "bold": True, |
| "font_size": 18, |
| "font_color": WHITE, |
| "bg_color": NAVY, |
| "align": "left", |
| "valign": "vcenter", |
| } |
| ), |
| "subtitle": workbook.add_format( |
| { |
| "font_size": 10, |
| "font_color": SLATE, |
| "bg_color": LIGHT, |
| "align": "left", |
| "valign": "vcenter", |
| } |
| ), |
| "section": workbook.add_format( |
| { |
| "bold": True, |
| "font_size": 11, |
| "font_color": WHITE, |
| "bg_color": BLUE, |
| "align": "left", |
| "valign": "vcenter", |
| } |
| ), |
| "table_header": workbook.add_format( |
| { |
| "bold": True, |
| "font_color": WHITE, |
| "bg_color": NAVY, |
| "border": 1, |
| "border_color": NAVY, |
| "align": "center", |
| "valign": "vcenter", |
| "text_wrap": True, |
| } |
| ), |
| "label": workbook.add_format( |
| {"bold": True, "font_color": NAVY, "bg_color": LIGHT} |
| ), |
| "text": workbook.add_format({"font_color": NAVY}), |
| "text_border": workbook.add_format( |
| {"font_color": NAVY, "border": 1, "border_color": BORDER} |
| ), |
| "wrapped_text": workbook.add_format( |
| { |
| "font_color": NAVY, |
| "border": 1, |
| "border_color": BORDER, |
| "text_wrap": True, |
| "valign": "top", |
| } |
| ), |
| "integer": workbook.add_format( |
| {"num_format": "#,##0", "border": 1, "border_color": BORDER} |
| ), |
| "decimal": workbook.add_format( |
| {"num_format": "#,##0.00", "border": 1, "border_color": BORDER} |
| ), |
| "price": workbook.add_format( |
| {"num_format": "#,##0.000000", "border": 1, "border_color": BORDER} |
| ), |
| "money": workbook.add_format( |
| { |
| "num_format": '#,##0.00;[Red]-#,##0.00', |
| "border": 1, |
| "border_color": BORDER, |
| } |
| ), |
| "percent": workbook.add_format( |
| { |
| "num_format": "0.00%;[Red]-0.00%", |
| "border": 1, |
| "border_color": BORDER, |
| } |
| ), |
| "date": workbook.add_format( |
| {"num_format": "dd/mm/yyyy", "border": 1, "border_color": BORDER} |
| ), |
| "total_label": workbook.add_format( |
| { |
| "bold": True, |
| "font_color": WHITE, |
| "bg_color": NAVY, |
| "border": 1, |
| "border_color": NAVY, |
| } |
| ), |
| "total_number": workbook.add_format( |
| { |
| "bold": True, |
| "font_color": WHITE, |
| "bg_color": NAVY, |
| "num_format": "#,##0.00", |
| "border": 1, |
| "border_color": NAVY, |
| } |
| ), |
| "total_percent": workbook.add_format( |
| { |
| "bold": True, |
| "font_color": WHITE, |
| "bg_color": NAVY, |
| "num_format": "0.00%", |
| "border": 1, |
| "border_color": NAVY, |
| } |
| ), |
| "declared_label": workbook.add_format( |
| { |
| "bold": True, |
| "font_color": NAVY, |
| "bg_color": PALE_BLUE, |
| "border": 1, |
| "border_color": BORDER, |
| } |
| ), |
| "declared_number": workbook.add_format( |
| { |
| "bold": True, |
| "font_color": NAVY, |
| "bg_color": PALE_BLUE, |
| "num_format": "#,##0.00", |
| "border": 1, |
| "border_color": BORDER, |
| } |
| ), |
| "declared_percent": workbook.add_format( |
| { |
| "bold": True, |
| "font_color": NAVY, |
| "bg_color": PALE_BLUE, |
| "num_format": "0.00%", |
| "border": 1, |
| "border_color": BORDER, |
| } |
| ), |
| "status_ok": workbook.add_format( |
| {"bold": True, "font_color": TEAL, "bg_color": PALE_TEAL} |
| ), |
| "status_warning": workbook.add_format( |
| {"bold": True, "font_color": AMBER, "bg_color": PALE_AMBER} |
| ), |
| "status_error": workbook.add_format( |
| {"bold": True, "font_color": RED, "bg_color": PALE_RED} |
| ), |
| "small": workbook.add_format({"font_size": 8, "font_color": SLATE}), |
| "hash": workbook.add_format( |
| {"font_name": "Consolas", "font_size": 8, "font_color": SLATE} |
| ), |
| } |
|
|
|
|
| def _status_format(formats, validation: ValidationResult): |
| if validation.error_count: |
| return formats["status_error"] |
| if validation.warning_count: |
| return formats["status_warning"] |
| return formats["status_ok"] |
|
|
|
|
| def _add_title( |
| sheet, |
| title: str, |
| subtitle: str, |
| last_column: int, |
| formats, |
| ) -> None: |
| sheet.merge_range(0, 0, 0, last_column, title, formats["title"]) |
| sheet.merge_range(1, 0, 1, last_column, subtitle, formats["subtitle"]) |
| sheet.set_row(0, 30) |
| sheet.set_row(1, 22) |
| sheet.hide_gridlines(2) |
|
|
|
|
| def _configure_print(sheet, *, repeat_header_row: int | None = None) -> None: |
| sheet.set_landscape() |
| sheet.set_paper(9) |
| sheet.fit_to_pages(1, 0) |
| sheet.center_horizontally() |
| sheet.set_margins(left=0.25, right=0.25, top=0.45, bottom=0.45) |
| sheet.set_header("&LFinReport Agent&C&F&R&P / &N", {"margin": 0.2}) |
| sheet.set_footer( |
| "&LKaynak belgeye göre otomatik oluşturulmuştur.&R&D &T", |
| {"margin": 0.2}, |
| ) |
| if repeat_header_row is not None: |
| sheet.repeat_rows(repeat_header_row, repeat_header_row) |
|
|
|
|
| def _write_summary(workbook, report, validation, formats) -> None: |
| sheet = workbook.add_worksheet("Özet") |
| _add_title( |
| sheet, |
| "FinReport Agent · Doğrulanmış Portföy Özeti", |
| f"{report.metadata.fund_name} · {report.metadata.period}", |
| 5, |
| formats, |
| ) |
| sheet.set_column("A:A", 25) |
| sheet.set_column("B:B", 24) |
| sheet.set_column("C:E", 20) |
| sheet.set_column("F:F", 15) |
| sheet.freeze_panes(8, 0) |
|
|
| sheet.write("A3", "Doğrulama durumu", formats["label"]) |
| sheet.write("B3", validation.status, _status_format(formats, validation)) |
| sheet.write("A4", "Kaynak dosya", formats["label"]) |
| sheet.write("B4", report.metadata.source_filename, formats["text"]) |
| sheet.write("D3", "Fon kodu", formats["label"]) |
| sheet.write("E3", report.metadata.fund_code or "-", formats["text"]) |
| sheet.write("D4", "Dönem", formats["label"]) |
| sheet.write("E4", report.metadata.period, formats["text"]) |
| sheet.write("A6", "Portföy uzlaştırması", formats["section"]) |
| sheet.merge_range("B6:F6", "", formats["section"]) |
|
|
| headers = [ |
| "Kalem", |
| "Muhasebesel Sınıf", |
| "PDF Beyanı (TL)", |
| "Detay Hesabı (TL)", |
| "Fark (TL)", |
| "FPD (%)", |
| ] |
| for column, header in enumerate(headers): |
| sheet.write(7, column, header) |
|
|
| totals = report.declared_totals |
| equity_calc = sum(row.total_value for row in report.equities) |
| repo_calc = sum(row.total_value for row in report.repos) |
| collateral_calc = sum(row.total_value for row in report.cash_collaterals) |
| rows = [ |
| ( |
| "Hisse Senetleri", |
| "Portföye dahil", |
| totals.equity_value, |
| equity_calc, |
| totals.equity_fpd_pct, |
| ), |
| ( |
| "T. Repo", |
| "Portföye dahil", |
| totals.repo_value, |
| repo_calc, |
| totals.repo_fpd_pct, |
| ), |
| ( |
| "VİOP Nakit Teminatı", |
| "Portföye dahil", |
| totals.cash_collateral_value, |
| collateral_calc, |
| totals.cash_collateral_fpd_pct, |
| ), |
| ( |
| "FON PORTFÖY DEĞERİ", |
| "Uzlaştırılmış toplam", |
| totals.fund_portfolio_value, |
| equity_calc + repo_calc + collateral_calc, |
| Decimal("1"), |
| ), |
| ( |
| "Futures Pozisyon Değeri", |
| "Bilgi / maruziyet", |
| totals.derivative_exposure, |
| sum(row.total_value for row in report.derivatives), |
| Decimal("0"), |
| ), |
| ] |
| for row_index, (name, classification, declared, calculated, percentage) in enumerate( |
| rows, start=8 |
| ): |
| sheet.write(row_index, 0, name, formats["text_border"]) |
| sheet.write(row_index, 1, classification, formats["text_border"]) |
| sheet.write_number(row_index, 2, _number(declared), formats["money"]) |
| sheet.write_number(row_index, 3, _number(calculated), formats["money"]) |
| sheet.write_formula( |
| row_index, |
| 4, |
| f"=D{row_index + 1}-C{row_index + 1}", |
| formats["money"], |
| _number(calculated - declared), |
| ) |
| sheet.write_number(row_index, 5, _number(percentage), formats["percent"]) |
|
|
| sheet.add_table( |
| 7, |
| 0, |
| 12, |
| 5, |
| { |
| "name": "PortfolioSummary", |
| "style": "Table Style Medium 2", |
| "columns": [ |
| {"header": header, "header_format": formats["table_header"]} |
| for header in headers |
| ], |
| }, |
| ) |
| sheet.conditional_format( |
| "E9:E13", |
| { |
| "type": "cell", |
| "criteria": "not between", |
| "minimum": -0.01, |
| "maximum": 0.01, |
| "format": formats["status_error"], |
| }, |
| ) |
| sheet.write("A15", "Not", formats["label"]) |
| sheet.merge_range( |
| "B15:F15", |
| ( |
| "Futures pozisyon değeri maruziyettir; fon portföy değeri toplamına " |
| "eklenmez. VİOP nakit teminatı portföy uzlaştırmasına dahildir." |
| ), |
| formats["subtitle"], |
| ) |
| sheet.set_row(7, 28) |
| _configure_print(sheet) |
|
|
|
|
| def _write_equities(workbook, report, formats) -> None: |
| sheet = workbook.add_worksheet("Hisse Senetleri") |
| _add_title( |
| sheet, |
| "Hisse Senetleri", |
| f"{len(report.equities)} pozisyon · Kaynak koordinatları korunmuştur", |
| 13, |
| formats, |
| ) |
| headers = [ |
| "Kod", |
| "İhraççı Kurum", |
| "Döviz", |
| "ISIN", |
| "Nominal Değer", |
| "Birim Alış Fiyatı", |
| "Satın Alış Tarihi", |
| "Borsa Sözleşme No", |
| "Güncel Birim Fiyat", |
| "Toplam Değer (TL)", |
| "Grup (%)", |
| "FPD (%)", |
| "FTD (%)", |
| "Kaynak Sayfa", |
| ] |
| header_row = 3 |
| for column, header in enumerate(headers): |
| sheet.write(header_row, column, header) |
|
|
| for row_index, row in enumerate(report.equities, start=header_row + 1): |
| sheet.write(row_index, 0, row.code, formats["text_border"]) |
| issuer_format = ( |
| formats["wrapped_text"] if len(row.issuer) > 42 else formats["text_border"] |
| ) |
| sheet.write(row_index, 1, row.issuer, issuer_format) |
| if len(row.issuer) > 42: |
| sheet.set_row(row_index, 30) |
| sheet.write(row_index, 2, row.currency, formats["text_border"]) |
| sheet.write(row_index, 3, row.isin, formats["text_border"]) |
| sheet.write_number(row_index, 4, _number(row.nominal), formats["decimal"]) |
| sheet.write_number(row_index, 5, _number(row.purchase_price), formats["price"]) |
| sheet.write_datetime( |
| row_index, 6, _as_datetime(row.purchase_date), formats["date"] |
| ) |
| sheet.write(row_index, 7, row.contract_no or "", formats["text_border"]) |
| sheet.write_number(row_index, 8, _number(row.current_price), formats["price"]) |
| sheet.write_number(row_index, 9, _number(row.total_value), formats["money"]) |
| sheet.write_number(row_index, 10, _number(row.group_pct), formats["percent"]) |
| sheet.write_number(row_index, 11, _number(row.fpd_pct), formats["percent"]) |
| sheet.write_number(row_index, 12, _number(row.ftd_pct), formats["percent"]) |
| sheet.write_number(row_index, 13, row.source.page, formats["integer"]) |
|
|
| last_data_row = header_row + len(report.equities) |
| sheet.add_table( |
| header_row, |
| 0, |
| last_data_row, |
| len(headers) - 1, |
| { |
| "name": "EquityPositions", |
| "style": "Table Style Medium 2", |
| "columns": [ |
| {"header": header, "header_format": formats["table_header"]} |
| for header in headers |
| ], |
| }, |
| ) |
| calculated_row = last_data_row + 1 |
| declared_row = last_data_row + 2 |
| sheet.write(calculated_row, 0, "HESAPLANAN TOPLAM", formats["total_label"]) |
| sheet.write_formula( |
| calculated_row, |
| 4, |
| f"=SUM(E{header_row + 2}:E{last_data_row + 1})", |
| formats["total_number"], |
| _number(sum(row.nominal for row in report.equities)), |
| ) |
| sheet.write_formula( |
| calculated_row, |
| 9, |
| f"=SUM(J{header_row + 2}:J{last_data_row + 1})", |
| formats["total_number"], |
| _number(sum(row.total_value for row in report.equities)), |
| ) |
| for column, attr in ((10, "group_pct"), (11, "fpd_pct"), (12, "ftd_pct")): |
| sheet.write_formula( |
| calculated_row, |
| column, |
| f"=SUM({xlsxwriter.utility.xl_col_to_name(column)}" |
| f"{header_row + 2}:{xlsxwriter.utility.xl_col_to_name(column)}" |
| f"{last_data_row + 1})", |
| formats["total_percent"], |
| _number(sum(getattr(row, attr) for row in report.equities)), |
| ) |
| for column in range(1, len(headers)): |
| if column not in {4, 9, 10, 11, 12}: |
| sheet.write_blank(calculated_row, column, None, formats["total_label"]) |
|
|
| totals = report.declared_totals |
| sheet.write(declared_row, 0, "PDF BEYAN TOPLAMI", formats["declared_label"]) |
| sheet.write_number( |
| declared_row, 4, _number(totals.equity_nominal), formats["declared_number"] |
| ) |
| sheet.write_number( |
| declared_row, 9, _number(totals.equity_value), formats["declared_number"] |
| ) |
| sheet.write_number(declared_row, 10, 1, formats["declared_percent"]) |
| sheet.write_number( |
| declared_row, 11, _number(totals.equity_fpd_pct), formats["declared_percent"] |
| ) |
| sheet.write_number( |
| declared_row, 12, _number(totals.equity_ftd_pct), formats["declared_percent"] |
| ) |
| for column in range(1, len(headers)): |
| if column not in {4, 9, 10, 11, 12}: |
| sheet.write_blank(declared_row, column, None, formats["declared_label"]) |
|
|
| sheet.freeze_panes(header_row + 1, 2) |
| sheet.set_column("A:A", 12) |
| sheet.set_column("B:B", 38) |
| sheet.set_column("C:C", 9) |
| sheet.set_column("D:D", 17) |
| sheet.set_column("E:F", 17) |
| sheet.set_column("G:G", 15) |
| sheet.set_column("H:H", 20) |
| sheet.set_column("I:J", 18) |
| sheet.set_column("K:M", 12) |
| sheet.set_column("N:N", 13) |
| sheet.set_row(header_row, 34) |
| _configure_print(sheet, repeat_header_row=header_row) |
|
|
|
|
| def _write_repos(workbook, report, formats) -> None: |
| sheet = workbook.add_worksheet("T. Repo") |
| _add_title( |
| sheet, |
| "Ters Repo Pozisyonları", |
| f"{len(report.repos)} pozisyon", |
| 19, |
| formats, |
| ) |
| headers = [ |
| "Kod", |
| "Döviz", |
| "İhraççı", |
| "Vade Tarihi", |
| "Kalan Gün", |
| "ISIN", |
| "İç İskonto (%)", |
| "Faiz Ödeme Sayısı", |
| "Nominal Değer", |
| "Birim Alış Fiyatı", |
| "Satın Alış Tarihi", |
| "Repo Teminatı", |
| "Borsa Sözleşme No", |
| "Net Dönüş", |
| "Günlük Br Değer", |
| "Toplam Değer (TL)", |
| "Grup (%)", |
| "FPD (%)", |
| "FTD (%)", |
| "Kaynak Sayfa", |
| ] |
| header_row = 3 |
| for column, header in enumerate(headers): |
| sheet.write(header_row, column, header) |
| for row_index, row in enumerate(report.repos, start=header_row + 1): |
| values = [row.code, row.currency, row.issuer] |
| for column, value in enumerate(values): |
| sheet.write(row_index, column, value, formats["text_border"]) |
| sheet.write_datetime( |
| row_index, 3, _as_datetime(row.maturity_date), formats["date"] |
| ) |
| sheet.write_number(row_index, 4, row.days_to_maturity, formats["integer"]) |
| sheet.write(row_index, 5, row.isin, formats["text_border"]) |
| sheet.write_number( |
| row_index, 6, _number(row.discount_rate / 100), formats["percent"] |
| ) |
| sheet.write_number( |
| row_index, 7, row.interest_payment_count, formats["integer"] |
| ) |
| sheet.write_number(row_index, 8, _number(row.nominal), formats["money"]) |
| sheet.write_number(row_index, 9, _number(row.purchase_price), formats["price"]) |
| sheet.write_datetime( |
| row_index, 10, _as_datetime(row.purchase_date), formats["date"] |
| ) |
| sheet.write_number( |
| row_index, 11, _number(row.collateral_amount), formats["money"] |
| ) |
| sheet.write(row_index, 12, row.contract_no, formats["text_border"]) |
| sheet.write_number(row_index, 13, _number(row.net_return), formats["money"]) |
| sheet.write_number(row_index, 14, _number(row.daily_value), formats["price"]) |
| sheet.write_number(row_index, 15, _number(row.total_value), formats["money"]) |
| sheet.write_number(row_index, 16, _number(row.group_pct), formats["percent"]) |
| sheet.write_number(row_index, 17, _number(row.fpd_pct), formats["percent"]) |
| sheet.write_number(row_index, 18, _number(row.ftd_pct), formats["percent"]) |
| sheet.write_number(row_index, 19, row.source.page, formats["integer"]) |
|
|
| last_data_row = header_row + len(report.repos) |
| sheet.add_table( |
| header_row, |
| 0, |
| last_data_row, |
| len(headers) - 1, |
| { |
| "name": "RepoPositions", |
| "style": "Table Style Medium 2", |
| "columns": [ |
| {"header": header, "header_format": formats["table_header"]} |
| for header in headers |
| ], |
| }, |
| ) |
| total_row = last_data_row + 1 |
| sheet.write(total_row, 0, "HESAPLANAN TOPLAM", formats["total_label"]) |
| sheet.write_formula( |
| total_row, |
| 15, |
| f"=SUM(P{header_row + 2}:P{last_data_row + 1})", |
| formats["total_number"], |
| _number(sum(row.total_value for row in report.repos)), |
| ) |
| for column, attr in ((16, "group_pct"), (17, "fpd_pct"), (18, "ftd_pct")): |
| letter = xlsxwriter.utility.xl_col_to_name(column) |
| sheet.write_formula( |
| total_row, |
| column, |
| f"=SUM({letter}{header_row + 2}:{letter}{last_data_row + 1})", |
| formats["total_percent"], |
| _number(sum(getattr(row, attr) for row in report.repos)), |
| ) |
| for column in range(1, len(headers)): |
| if column not in {15, 16, 17, 18}: |
| sheet.write_blank(total_row, column, None, formats["total_label"]) |
| sheet.freeze_panes(header_row + 1, 2) |
| sheet.set_column(0, 19, 15) |
| sheet.set_column("C:C", 18) |
| sheet.set_column("F:F", 17) |
| sheet.set_column("M:M", 20) |
| sheet.set_row(header_row, 34) |
| _configure_print(sheet, repeat_header_row=header_row) |
|
|
|
|
| def _write_derivatives(workbook, report, formats) -> None: |
| sheet = workbook.add_worksheet("Türev") |
| _add_title( |
| sheet, |
| "Türev Pozisyonlar ve Nakit Teminatlar", |
| "Maruziyet ile portföye dahil nakit teminatı ayrı sunulur", |
| 13, |
| formats, |
| ) |
| headers = [ |
| "Tür", |
| "Kod", |
| "Döviz", |
| "Vade Tarihi", |
| "Nominal", |
| "Birim Alış Fiyatı", |
| "Satın Alış Tarihi", |
| "Borsa Sözleşme No", |
| "Güncel Fiyat", |
| "Pozisyon Değeri (TL)", |
| "Grup (%)", |
| "FPD (%)", |
| "FTD (%)", |
| "Kaynak Sayfa", |
| ] |
| header_row = 3 |
| for column, header in enumerate(headers): |
| sheet.write(header_row, column, header) |
| for row_index, row in enumerate(report.derivatives, start=header_row + 1): |
| sheet.write(row_index, 0, row.position_type, formats["text_border"]) |
| sheet.write(row_index, 1, row.code, formats["text_border"]) |
| sheet.write(row_index, 2, row.currency, formats["text_border"]) |
| if row.maturity_date: |
| sheet.write_datetime( |
| row_index, 3, _as_datetime(row.maturity_date), formats["date"] |
| ) |
| else: |
| sheet.write(row_index, 3, "-", formats["text_border"]) |
| sheet.write_number(row_index, 4, _number(row.nominal), formats["decimal"]) |
| sheet.write_number(row_index, 5, _number(row.purchase_price), formats["price"]) |
| sheet.write_datetime( |
| row_index, 6, _as_datetime(row.purchase_date), formats["date"] |
| ) |
| sheet.write(row_index, 7, row.contract_no or "", formats["text_border"]) |
| sheet.write_number(row_index, 8, _number(row.current_price), formats["price"]) |
| sheet.write_number(row_index, 9, _number(row.total_value), formats["money"]) |
| sheet.write_number(row_index, 10, _number(row.group_pct), formats["percent"]) |
| sheet.write_number(row_index, 11, _number(row.fpd_pct), formats["percent"]) |
| sheet.write_number(row_index, 12, _number(row.ftd_pct), formats["percent"]) |
| sheet.write_number(row_index, 13, row.source.page, formats["integer"]) |
|
|
| last_derivative_row = header_row + len(report.derivatives) |
| sheet.add_table( |
| header_row, |
| 0, |
| last_derivative_row, |
| len(headers) - 1, |
| { |
| "name": "DerivativePositions", |
| "style": "Table Style Medium 2", |
| "columns": [ |
| {"header": header, "header_format": formats["table_header"]} |
| for header in headers |
| ], |
| }, |
| ) |
| cash_header = last_derivative_row + 3 |
| sheet.merge_range( |
| cash_header - 1, |
| 0, |
| cash_header - 1, |
| 5, |
| "Portföye Dahil Nakit Teminatlar", |
| formats["section"], |
| ) |
| cash_headers = [ |
| "Kalem", |
| "Döviz", |
| "Nominal", |
| "Toplam Değer (TL)", |
| "Grup (%)", |
| "FPD (%)", |
| "Kaynak Sayfa", |
| ] |
| for column, header in enumerate(cash_headers): |
| sheet.write(cash_header, column, header) |
| for row_index, row in enumerate(report.cash_collaterals, start=cash_header + 1): |
| sheet.write(row_index, 0, row.name, formats["text_border"]) |
| sheet.write(row_index, 1, row.currency, formats["text_border"]) |
| sheet.write_number(row_index, 2, _number(row.nominal), formats["money"]) |
| sheet.write_number(row_index, 3, _number(row.total_value), formats["money"]) |
| sheet.write_number(row_index, 4, _number(row.group_pct), formats["percent"]) |
| sheet.write_number(row_index, 5, _number(row.fpd_pct), formats["percent"]) |
| sheet.write_number(row_index, 6, row.source.page, formats["integer"]) |
| if report.cash_collaterals: |
| sheet.add_table( |
| cash_header, |
| 0, |
| cash_header + len(report.cash_collaterals), |
| len(cash_headers) - 1, |
| { |
| "name": "CashCollaterals", |
| "style": "Table Style Medium 4", |
| "columns": [ |
| {"header": header, "header_format": formats["table_header"]} |
| for header in cash_headers |
| ], |
| }, |
| ) |
| sheet.freeze_panes(header_row + 1, 2) |
| sheet.set_column("A:B", 20) |
| sheet.set_column("C:C", 10) |
| sheet.set_column("D:J", 18) |
| sheet.set_column("K:N", 13) |
| sheet.set_row(header_row, 34) |
| sheet.set_row(cash_header, 30) |
| _configure_print(sheet, repeat_header_row=header_row) |
|
|
|
|
| def _write_findings(workbook, report, validation, formats) -> None: |
| sheet = workbook.add_worksheet("Kontroller") |
| _add_title( |
| sheet, |
| "Finansal Doğrulama ve Denetim İzi", |
| ( |
| f"{validation.error_count} hata · {validation.warning_count} uyarı · " |
| f"{validation.info_count} bilgi" |
| ), |
| 8, |
| formats, |
| ) |
| sheet.write("A3", "Durum", formats["label"]) |
| sheet.write("B3", validation.status, _status_format(formats, validation)) |
| sheet.write("A4", "SHA-256", formats["label"]) |
| sheet.merge_range("B4:I4", report.metadata.source_sha256, formats["hash"]) |
|
|
| headers = [ |
| "Seviye", |
| "Kontrol Kodu", |
| "Açıklama", |
| "Sayfa", |
| "Kayıt", |
| "Beklenen", |
| "Gerçekleşen", |
| "Çalışma Sayfası", |
| "Sonuç", |
| ] |
| header_row = 6 |
| for column, header in enumerate(headers): |
| sheet.write(header_row, column, header) |
| for row_index, finding in enumerate(validation.findings, start=header_row + 1): |
| severity_label = { |
| Severity.ERROR: "Hata", |
| Severity.WARNING: "Uyarı", |
| Severity.INFO: "Bilgi", |
| }[finding.severity] |
| severity_format = { |
| Severity.ERROR: formats["status_error"], |
| Severity.WARNING: formats["status_warning"], |
| Severity.INFO: formats["status_ok"], |
| }[finding.severity] |
| sheet.write(row_index, 0, severity_label, severity_format) |
| sheet.write(row_index, 1, finding.code, formats["text_border"]) |
| sheet.write(row_index, 2, finding.message, formats["text_border"]) |
| sheet.write(row_index, 3, finding.page or "", formats["text_border"]) |
| sheet.write(row_index, 4, finding.record_key or "", formats["text_border"]) |
| sheet.write(row_index, 5, finding.expected or "", formats["text_border"]) |
| sheet.write(row_index, 6, finding.actual or "", formats["text_border"]) |
| sheet.write(row_index, 7, finding.sheet or "", formats["text_border"]) |
| result = "İncele" if finding.severity != Severity.INFO else "Bilgilendirme" |
| sheet.write(row_index, 8, result, severity_format) |
|
|
| last_row = header_row + max(1, len(validation.findings)) |
| if validation.findings: |
| sheet.add_table( |
| header_row, |
| 0, |
| last_row, |
| len(headers) - 1, |
| { |
| "name": "ValidationFindings", |
| "style": "Table Style Medium 2", |
| "columns": [ |
| {"header": header, "header_format": formats["table_header"]} |
| for header in headers |
| ], |
| }, |
| ) |
| sheet.freeze_panes(header_row + 1, 0) |
| sheet.set_column("A:B", 22) |
| sheet.set_column("C:C", 72) |
| sheet.set_column("D:E", 13) |
| sheet.set_column("F:G", 22) |
| sheet.set_column("H:I", 20) |
| sheet.set_row(header_row + 1, 38) |
| sheet.set_row(header_row, 30) |
| _configure_print(sheet, repeat_header_row=header_row) |
|
|
|
|
| def _write_provenance(workbook, report, formats) -> None: |
| sheet = workbook.add_worksheet("Kaynak İzleri") |
| _add_title( |
| sheet, |
| "Hücre Kaynak İzleri", |
| "Her kayıt PDF sayfası ve koordinat kutusuyla eşleştirilir", |
| 8, |
| formats, |
| ) |
| headers = [ |
| "Çalışma Sayfası", |
| "Kayıt Türü", |
| "Kayıt Anahtarı", |
| "PDF Sayfası", |
| "X0", |
| "Y0", |
| "X1", |
| "Y1", |
| "Çıkarım Yöntemi", |
| ] |
| header_row = 3 |
| for column, header in enumerate(headers): |
| sheet.write(header_row, column, header) |
| records = [] |
| records.extend( |
| ("Hisse Senetleri", "Hisse", row.code, row.source) |
| for row in report.equities |
| ) |
| records.extend(("T. Repo", "Repo", row.code, row.source) for row in report.repos) |
| records.extend( |
| ("Türev", "Türev", row.code, row.source) for row in report.derivatives |
| ) |
| records.extend( |
| ("Türev", "Nakit Teminat", row.name, row.source) |
| for row in report.cash_collaterals |
| ) |
| for row_index, (sheet_name, record_type, key, source) in enumerate( |
| records, start=header_row + 1 |
| ): |
| sheet.write(row_index, 0, sheet_name, formats["text_border"]) |
| sheet.write(row_index, 1, record_type, formats["text_border"]) |
| sheet.write(row_index, 2, key, formats["text_border"]) |
| sheet.write_number(row_index, 3, source.page, formats["integer"]) |
| for offset, coordinate in enumerate(source.bbox): |
| sheet.write_number( |
| row_index, 4 + offset, coordinate, formats["decimal"] |
| ) |
| sheet.write(row_index, 8, source.method.value, formats["text_border"]) |
| sheet.add_table( |
| header_row, |
| 0, |
| header_row + len(records), |
| len(headers) - 1, |
| { |
| "name": "SourceProvenance", |
| "style": "Table Style Medium 2", |
| "columns": [ |
| {"header": header, "header_format": formats["table_header"]} |
| for header in headers |
| ], |
| }, |
| ) |
| sheet.freeze_panes(header_row + 1, 0) |
| sheet.set_column("A:C", 22) |
| sheet.set_column("D:I", 15) |
| sheet.set_row(header_row, 30) |
| _configure_print(sheet, repeat_header_row=header_row) |
|
|
|
|
| def write_workbook( |
| report: FundPortfolioReport, |
| validation: ValidationResult, |
| output_path: str | Path, |
| ) -> Path: |
| path = Path(output_path).expanduser().resolve() |
| path.parent.mkdir(parents=True, exist_ok=True) |
| workbook = xlsxwriter.Workbook( |
| path, |
| { |
| "constant_memory": False, |
| "nan_inf_to_errors": True, |
| }, |
| ) |
| workbook.set_properties( |
| { |
| "title": f"{report.metadata.fund_code or ''} Fon Portföy Raporu", |
| "subject": "FinReport Agent tarafından kaynak PDF'den oluşturuldu", |
| "author": "FinReport Agent", |
| "company": "FinReport Agent", |
| "comments": ( |
| f"Kaynak SHA-256: {report.metadata.source_sha256}. " |
| "Kontroller sayfasını inceleyin." |
| ), |
| } |
| ) |
| formats = _formats(workbook) |
| _write_summary(workbook, report, validation, formats) |
| _write_equities(workbook, report, formats) |
| _write_repos(workbook, report, formats) |
| _write_derivatives(workbook, report, formats) |
| _write_findings(workbook, report, validation, formats) |
| _write_provenance(workbook, report, formats) |
| workbook.close() |
| return path |
|
|