Пример #1
0
def team_block():
    fte = aantal_fte()
    # fte_begroot = aantal_fte_begroot()
    fte_color = BLACK  # dependent_color(fte_begroot - fte, 1, -1)

    return VBlock([
        TextBlock("Team", MID_SIZE),
        HBlock([
            VBlock([
                TextBlock("Aantal mensen", DEF_SIZE, padding=5, color=GRAY),
                TextBlock(aantal_mensen(), MID_SIZE, text_format=".5"),
            ]),
            VBlock([
                TextBlock("Aantal FTE", DEF_SIZE, padding=5, color=GRAY),
                TextBlock(fte, MID_SIZE, color=fte_color, text_format=".5"),
                # TextBlock('Begroot', defsize, padding=5, color=GRAY),
                # TextBlock(fte_begroot, midsize, color=GRAY, format='.5'),
            ]),
        ]),
    ])
Пример #2
0
def operations_block():
    return VBlock([
        TextBlock("Operations", HEADER_SIZE),
        TextBlock("KPI's", MID_SIZE),
        HBlock([kpi_block(verbose=False)], link="operations.html", padding=40),
        TextBlock(""),  # Todo: verticale marge mogelijk maken
        operations_chart(),
        TextBlock(""),  # Todo: verticale marge mogelijk maken
        # billable_chart(),
        corrections_block(),
        planning_chart(),
    ])
Пример #3
0
def billable_chart():
    months_back = 3
    return VBlock([
        TextBlock(
            f"Billable, hele team, laatste {months_back} maanden",
            DEF_SIZE,
            color=GRAY,
        ),
        TrendLines().chart("billable_hele_team",
                           250,
                           150,
                           x_start=months_ago(months_back)),
    ])
Пример #4
0
def render_sales_page(output_folder: Path):

    sales_trajecten = VBlock([
        TextBlock('Actieve salestrajecten', MID_SIZE),
        Table(
            sales_waarde_details(),
            TableConfig(
                headers=[
                    'klant', 'project', 'grootte', 'kans', 'fase', 'waarde',
                    'bron'
                ],
                aligns=[
                    'left', 'left', 'right', 'right', 'left', 'right', 'left'
                ],
                formats=['', '', '€', '%', '', '€', ''],
                totals=[0, 0, 1, 0, 0, 1, 0],
            ),
        ),
    ])

    pijplijn = VBlock([
        TextBlock('Werk in de pijplijn', MID_SIZE),
        TextBlock('Moet uit Simplicate komen'),
        # Table(
        #     werk_in_pijplijn_details(),
        #     TableConfig(
        #         headers=['klant', 'project', '% af', 'onderhanden', 'eigenaar'],
        #         aligns=['left', 'left', 'right', 'right', 'left'],
        #         formats=['', '', '%', '€', ''],
        #         totals=[0, 0, 0, 1, 0],
        #     ),
        # ),
    ])

    page = Page(
        [TextBlock('Sales', HEADER_SIZE),
         HBlock([sales_trajecten, pijplijn])])

    page.render(output_folder / 'sales.html')
Пример #5
0
def error_block():
    errs = log.get_errors()
    if not errs:
        return None
    error_lines = [
        TextBlock(
            f'<b>{err["file"]}, {err["function"]}</b><br/>{err["message"]}',
            DEF_SIZE,
            width=260,
            color=RED,
        ) for err in errs
    ]
    return VBlock([TextBlock("Errors", MID_SIZE)] + error_lines)
Пример #6
0
def render_billable_page(output_folder: Path):
    users = sorted(tuple_of_productie_users())
    fromday = Day().plus_months(-6)
    period = Period(fromday)
    cols = 3
    rows = len(users) // cols + 1
    grid = Grid(rows, cols)
    row = 0
    col = 0
    for user in users:
        labels, hours = billable_trend_person_week(user, period)  # {weekno: hours} dict
        hours_data = HoursData(period, [user])
        chart = BarChart(
            hours,
            ChartConfig(
                width=400,
                height=220,
                colors=['#ddeeff'],
                bottom_labels=labels,
                max_y_axis=40,
                y_axis_max_ticks=5,
            ),
        )
        user_block = VBlock(
            [
                TextBlock(
                    f'{user} {hours_data.effectivity():.0f}%  / {hours_data.billable_perc():.0f}%', font_size=MID_SIZE
                ),
                chart,
            ]
        )
        grid.set_cell(row, col, user_block)
        col += 1
        if col == cols:
            col = 0
            row += 1

    page = Page(
        [
            TextBlock('Billable uren', HEADER_SIZE),
            TextBlock(
                'Billable uren per week het afgelopen halfjaar.<br/><br/>'
                + 'Grafiek toont uren gewerkt op billable projecten zonder rekening te houden met correcties.<br/>'
                + 'Percentages zijn effectiviteit en billable.',
                color="gray",
            ),
            grid,
        ]
    )
    page.render(output_folder / 'billable.html')
Пример #7
0
def travelbase_block():
    bookings = get_bookings_per_week(booking_type="bookings",
                                     only_complete_weeks=True)
    if not isinstance(bookings, pd.DataFrame):
        return TextBlock("Kon boekingen niet ophalen", color=RED)
    legend = ", ".join(
        [f"{brand}: {int(bookings[brand].sum())}" for brand in BRANDS])
    return VBlock(
        [
            TextBlock("Travelbase", MID_SIZE),
            TextBlock("Aantal boekingen per week", color=GRAY),
            travelbase_scatterchart(bookings, 250, 180),
            TextBlock(legend),
        ],
        link="travelbase.html",
    )
Пример #8
0
def omzet_chart():
    # Behaalde omzet per week
    return VBlock(
        [
            TextBlock('Omzet'),
            TextBlock("per week, laatste 6 maanden...", DEF_SIZE, color=GRAY),
            TrendLines().chart(
                "omzet_per_week",
                250,
                150,
                x_start=months_ago(6),
                min_y_axis=0,
                max_y_axis=80000,
            ),
        ],
        link="billable.html",
    )
Пример #9
0
def corrections_block():
    weeks_back = 4
    interesting_correction = 8
    period = Period(Day().plus_weeks(-weeks_back))

    def corrections_percentage_coloring(value):
        return dependent_color(value, red_treshold=5, green_treshold=3)

    def project_link(_, fullline):
        return f"https://oberon.simplicate.com/projects/{fullline[0]}/hours"

    result = VBlock(
        [
            TextBlock("Correcties", MID_SIZE),
            HBlock(
                [
                    TextBlock(
                        corrections_percentage(period),
                        MID_SIZE,
                        text_format="%",
                        color=corrections_percentage_coloring,
                    ),
                    TextBlock(
                        f"correcties op uren van<br/> week {period.fromday.week_number()} "
                        +
                        f"tot en met week {period.fromday.week_number() + weeks_back - 1}.",
                        color=GRAY,
                    ),
                ],
                padding=70,
            ),
            Table(
                largest_corrections(interesting_correction, period),
                TableConfig(
                    headers=[],
                    aligns=["left", "left", "right"],
                    hide_columns=[0],
                    row_linking=project_link,
                ),
            ),
        ],
        link="corrections.html",
    )
    return result
Пример #10
0
def render_maandrapportage(output_folder, year, month):
    yuki_result = YukiResult(year, month)
    page = Page([
        VBlock([
            TextBlock(f'Maandrapportage {MAANDEN[month - 1].lower()}, {year}',
                      HEADER_SIZE),
            hours_block(year, month),
            profit_and_loss_block(yuki_result, year, month),
            balance_block(yuki_result, year, month),
            # HBlock([cash_block(), debiteuren_block()]),
            cashflow_analysis_block(yuki_result, year, month),
        ])
    ])
    htmlpath = output_folder / f'{year}_{month:02}.html'
    page.render(htmlpath, template='maandrapportage.html')

    # Generate PDF
    pdfpath = htmlpath.with_suffix('.pdf')
    options = {"enable-local-file-access": None}
    pdfkit.from_file(str(htmlpath), str(pdfpath), options=options)
Пример #11
0
def klanten_block():
    klanten = VBlock(
        [
            TextBlock("Klanten", MID_SIZE),
            TextBlock("Top 3 klanten laatste 6 maanden",
                      DEF_SIZE,
                      padding=10,
                      color=GRAY),
            Table(
                top_x_klanten_laatste_zes_maanden(3),
                TableConfig(
                    headers=[],
                    aligns=["left", "right", "right"],
                    formats=["", "€", "%"],
                    totals=[0, 0, 1],
                ),
            ),
        ],
        link="clients.html",
    )
    return klanten
Пример #12
0
def debiteuren_block():
    # betaaltermijn = gemiddelde_betaaltermijn()
    # betaaltermijn_color = dependent_color(betaaltermijn, 45, 30)
    debiteuren = debiteuren_30_60_90_yuki()
    max_y = ceil(sum(debiteuren) / 100000) * 100000
    return VBlock(
        [
            TextBlock("Debiteuren", MID_SIZE),
            StackedBarChart(
                debiteuren,
                ChartConfig(
                    width=240,
                    height=250,
                    labels=["<30 dg", "30-60 dg", "60-90 dg", "> 90 dg"],
                    colors=[GREEN, YELLOW, ORANGE, RED],
                    max_y_axis=max(450000, max_y),
                ),
            ),
        ],
        link="debiteuren.html",
    )
Пример #13
0
def verzuim_block():
    period = Period(Day().plus_months(-3))
    verzuim = verzuimpercentage(period)
    verzuim_color = dependent_color(verzuim, 3, 1.5)
    return VBlock(
        [
            TextBlock("Verzuim", MID_SIZE),
            TextBlock("Verzuimpercentage de laatste 3 maanden",
                      DEF_SIZE,
                      color=GRAY),
            TextBlock(
                verzuim,
                MID_SIZE,
                text_format="%1",
                color=verzuim_color,
                tooltip=
                "Gemiddeld bij DDA in 2019: 3.0%. Groen bij 1,5%, rood bij 3%",
            ),
        ],
        link="absence.html",
    )
Пример #14
0
def winst_berekening_block():
    grid = Grid(cols=5,
                aligns=['left', 'right', 'right', 'right', 'right'],
                has_header=True)

    laatste_maand = laatste_geboekte_maand()
    naam_laatste_maand = MAANDEN[laatste_maand - 1]
    naam_huidige_maand = MAANDEN[laatste_maand]
    yuki_omzet_url = 'https://oberon.yukiworks.nl/domain/aspx/Finances.aspx?app=FinReports.aspx'
    begroting_url = (
        'https://docs.google.com/spreadsheets/d/1KsVEIBcnlntGR9dHYn_gSREmpoidWUUZoGlCN7ck7Zo/edit#gid=2127576386'
    )

    add_row(grid,
            '',
            'Boekhouding (Yuki)',
            'Correctie',
            'Totaal nu',
            'Begroot',
            bold=True)

    omzet_tm_laatste_maand = omzet_tm_maand(laatste_maand)
    log(f'Yuki omzet tm {naam_laatste_maand}', omzet_tm_laatste_maand)
    add_row(grid, f'Omzet t/m {naam_laatste_maand}',
            (omzet_tm_laatste_maand, yuki_omzet_url))

    projectkosten_tm_laatste_maand = projectkosten_tm_maand(laatste_maand)
    log(f'Yuki projectkosten tm {naam_laatste_maand}',
        projectkosten_tm_laatste_maand)
    add_row(grid, f'Projectkosten t/m {naam_laatste_maand}',
            (-projectkosten_tm_maand(laatste_maand), yuki_omzet_url))

    omzet_nu = omzet_tm_nu()
    log('Yuki omzet tm nu', omzet_nu)
    add_row(
        grid,
        f'Omzet vanaf {naam_huidige_maand}',
        '',
        (omzet_nu - omzet_tm_laatste_maand, yuki_omzet_url),
        '',
        '',
    )

    add_row(
        grid,
        f'Projectkosten vanaf {naam_huidige_maand}',
        '',
        (-projectkosten_tm_nu() + projectkosten_tm_maand(laatste_maand),
         yuki_omzet_url),
    )

    onderhanden = simplicate_onderhanden_werk()
    log('Simplicate onderhanden werk', onderhanden)
    add_row(grid, 'Onderhanden werk nu (Simplicate)', '',
            (onderhanden, 'onderhanden.html'), '', '')

    begroot = omzet_begroot()
    log('Begroot', begroot)
    werkelijk = bruto_marge_werkelijk()
    log('BBI (omz-proj+onderh)', werkelijk)
    add_row(
        grid,
        'Opbrengsten',
        omzet_tm_laatste_maand - projectkosten_tm_laatste_maand,
        '',
        werkelijk,
        begroot,
        bold=True,
    )
    add_row(grid)

    kosten_tm_laatste_maand = kosten_boekhoudkundig_tm_maand(laatste_maand)
    log(f'Kosten tm {naam_laatste_maand}', kosten_tm_laatste_maand)
    add_row(
        grid,
        f'Kosten t/m {naam_laatste_maand}',
        (kosten_tm_laatste_maand, yuki_omzet_url),
        '',
        '',
        '',
    )

    begroot = kosten_begroot_na_maand(laatste_maand)
    log(f'Kosten begroot vanaf {naam_huidige_maand}', begroot)
    add_row(
        grid,
        f'Begrote kosten vanaf {naam_huidige_maand}',
        '',
        (begroot, begroting_url),
        '',
        '',
    )

    add_row(
        grid,
        'Kosten',
        kosten_tm_laatste_maand,
        '',
        kosten_werkelijk(),
        kosten_tm_laatste_maand + begroot,
        bold=True,
    )
    add_row(grid)

    yuki_winst = omzet_tm_laatste_maand - projectkosten_tm_laatste_maand - kosten_tm_laatste_maand
    werkelijk = winst_werkelijk()
    begroot = winst_begroot()
    log('Yuki winst', yuki_winst)
    log('Winst werkelijk', werkelijk)
    log('Winst begroot', begroot)
    add_row(
        grid,
        'Winst',
        yuki_winst,
        '',
        werkelijk,
        begroot,
        bold=True,
    )
    return VBlock([TextBlock('Winstberekening', MID_SIZE), grid],
                  block_id="Winstberekening")
Пример #15
0
def render_productiviteit_page():
    tables = HBlock([
        Table(
            productiviteit_overzicht(),
            block_id='overzicht',
            headers=[
                'persoon',
                'omzet',
                'uren',
                'per uur',
                'geboekt',
                'productief',
                '% productief',
                'billable',
                '% billable',
            ],
            aligns=[
                'left', 'right', 'right', 'right', 'right', 'right', 'right',
                'right', 'right'
            ],
            formats=['', '€', '.', '€', '.', '.', '%', '.', '%'],
            totals=[0, 1, 1, 0, 1, 1, 0, 1, 0],
        )
    ])

    for user in tuple_of_productie_users():
        data = productiviteit_persoon(user)
        target = user_target(user)
        if target:
            total = sum([row[3] for row in data])
            perc = (total / user_target_now(user) - 1) * 100
            percstr = do_format(perc, '+%')
            targetstr = f'target: € {target:,.0f}'.replace(',', '.')
        else:
            targetstr = percstr = ''
        tables.add_block(
            Table(
                data,
                block_id='productiviteit_' + user,
                headers=[user, targetstr, percstr, 'omzet', 'uren', 'per uur'],
                aligns=['left', 'left', 'left', 'right', 'right', 'right'],
                formats=['', '', '', '€', '.', '€'],
                totals=[0, 0, 0, 1, 1, 0],
            ))

    page = Page([
        TextBlock('Productiviteit', HEADER_SIZE),
        VBlock([TextBlock('Under construction', color='red'), tables])
    ])
    page.add_onloadcode('make_users_clickable();')
    page.render('output/productiviteit.html')

    for user in tuple_of_productie_users():
        productiviteit_table = Table(
            productiviteit_persoon(user),
            headers=[user, '', '', 'omzet', 'uren', 'per uur'],
            aligns=['left', 'left', 'left', 'right', 'right', 'right'],
            formats=['', '', '', '€', '.', '€'],
            totals=[0, 0, 0, 1, 1, 0],
        )

        chartdata = [{
            'x': rec['datum'].strftime('%Y-%m-%d'),
            'y': rec['hours']
        } for rec in billable_trend_person(user)]
        chart = ScatterChart(400,
                             400,
                             values=chartdata,
                             color='#6666cc',
                             fill_color='#ddeeff',
                             y_start=0,
                             x_type='date')

        page = Page([
            TextBlock(f'Productiviteit {user}', HEADER_SIZE), chart,
            productiviteit_table
        ])
        page.render(f'output/productiviteit_{user}.html')
Пример #16
0
def profit_and_loss_block(yuki_result: YukiResult, year: int, month: int):
    maand = MAANDEN[month - 1]
    last_date_this_month = last_date_of_month(year, month)
    begroting = HeaderSheet('Begroting 2021', 'Begroting', header_col=2, header_row=2)
    omzetplanning = HeaderSheet('Begroting 2021', 'Omzetplanning')
    toelichtingen = []
    try:
        toelichting_sheet = HeaderSheet('Begroting 2021', str(month))
    except WorksheetNotFound:
        toelichting_sheet = None

    grid = Grid(
        cols=8,
        has_header=False,
        line_height=0,
        aligns=['left', 'right', 'right', 'right', '', 'right', 'right', 'right'],
    )

    def add_normal_row(title, result, budget=None):
        if budget:
            budget_month = TextBlock(budget[0], text_format='.', color=GRAY)
            budget_ytd = TextBlock(budget[1], text_format='.', color=GRAY)
        else:
            budget_month, budget_ytd = 0, 0
        grid.add_row(
            [
                TextBlock(title),
                TextBlock(result[0], text_format='.'),
                '',
                budget_month,
                '',
                TextBlock(result[1], text_format='.'),
                '',
                budget_ytd,
            ]
        )
        if toelichting_sheet:
            toelichting = toelichting_sheet[title, 'Toelichting']
            if toelichting:
                b = toelichtingen  # zeer merkwaardige constructie maar krijg een foutmelding als ik direct iets toevoeg aan toelichtingen
                b += [(title, toelichting)]

    def add_subtotal_row(title, subtotal, budget=None, style=TOPLINE):
        if budget:
            budget_month = TextBlock(budget[0], text_format='.', color=GRAY, style=BOLD)
            budget_ytd = TextBlock(budget[1], text_format='.', color=GRAY, style=BOLD)
        else:
            budget_month, budget_ytd = 0, 0
        grid.add_row(
            [
                TextBlock(title, style=BOLD),
                '',
                TextBlock(subtotal[0], text_format='.', style=BOLD),
                budget_month,
                '',
                '',
                TextBlock(subtotal[1], text_format='.', style=BOLD),
                budget_ytd,
            ],
            styles=['', style, style, '', '', style, style, ''],
        )

    def turnover_planning(begroting_posts):
        def budget_ytd(sheet, post):
            return sum([get_int(sheet[post, MAANDEN[m - 1]]) for m in range(1, month + 1)])

        if type(begroting_posts) != list:
            begroting_posts = [begroting_posts]
        planned_month = sum([budget_column(omzetplanning, post) for post in begroting_posts])
        planned_ytd = sum([budget_ytd(omzetplanning, post) for post in begroting_posts])
        return (planned_month, planned_ytd)

    def budgeted(begroting_posts):
        def budget_month(sheet, post):
            return (
                get_int(sheet[post, maand]) - get_int(sheet[post, MAANDEN[month - 2]]) if month else budget_column(post)
            )

        if type(begroting_posts) != list:
            begroting_posts = [begroting_posts]
        planned_month = sum([budget_month(begroting, post) for post in begroting_posts]) * 1000
        planned_ytd = sum([budget_column(begroting, post) for post in begroting_posts]) * 1000
        return (planned_month, planned_ytd)

    def get_int(str):
        return int(str.replace('.', '')) if str else 0

    def budget_column(sheet, post):
        return get_int(sheet[post, maand])

    # Header
    grid.add_row(
        [
            '',
            '',
            TextBlock(maand, style=BOLD),
            TextBlock('begroot', color=GRAY),
            '',
            '',
            TextBlock('ytd', style=BOLD),
            TextBlock('begroot', color=GRAY),
        ],
        styles=['width:160px;', '', '', '', 'width:80px;'],
    )
    # Omzet
    add_normal_row('Omzet', yuki_result.omzet())
    add_normal_row('Projectkosten', yuki_result.projectkosten())
    add_normal_row('Uitbesteed werk', yuki_result.uitbesteed_werk())
    add_normal_row('Hostingkosten', yuki_result.month_ytd('hosting_expenses'))
    turnover_budgeted = budgeted(['Omzet'])  # turnover_planning('TOTAAL OMZET')
    add_subtotal_row('BBI', yuki_result.bbi(), turnover_budgeted)
    grid.add_row()

    # Overige inkomsten
    other_income = yuki_result.month_ytd('other_income')
    other_budgeted = (0, 0)
    add_subtotal_row('Overige inkomsten', other_income, other_budgeted)
    grid.add_row()

    # TOTAAL INKOMSTEN
    grid.add_row()
    margin_budgeted = tuple_add(turnover_budgeted, other_budgeted)
    total_income = tuple_add(yuki_result.bbi(), yuki_result.other_income())
    add_subtotal_row('Totaal bruto marge', total_income, margin_budgeted, style=DOUBLE_TOPLINE)
    grid.add_row()
    grid.add_row()

    # Personeel
    people_budgeted = budgeted(['Management', 'Medewerkers'])
    add_normal_row('Mensen', yuki_result.people(), people_budgeted)

    wbso_budgeted = tuple(-x for x in budgeted('Subsidie'))
    add_normal_row('WBSO', yuki_result.wbso(), wbso_budgeted)

    add_subtotal_row('Personeelskosten', yuki_result.personnell())
    grid.add_row()

    # Bedrijfskosten
    housing_budgeted = budgeted('Huisvesting')
    add_normal_row('Huisvesting', yuki_result.housing(), housing_budgeted)

    marketing_budgeted = budgeted('Marketing')
    add_normal_row('Sales / Marketing', yuki_result.marketing(), marketing_budgeted)

    other_expenses_budgeted = budgeted('Overige kosten')
    add_normal_row('Overige kosten', yuki_result.other_expenses(), other_expenses_budgeted)

    add_subtotal_row('Bedrijfskosten', yuki_result.company_costs())
    grid.add_row()

    # BEDRIJFSLASTEN
    operating_expenses_budgeted = tuple_add(
        people_budgeted, wbso_budgeted, housing_budgeted, marketing_budgeted, other_expenses_budgeted
    )
    add_subtotal_row(
        'TOTAAL BEDRIJFSLASTEN', yuki_result.operating_expenses(), operating_expenses_budgeted, style=DOUBLE_TOPLINE
    )
    grid.add_row()
    grid.add_row()

    # Deprecation
    depreciation_budgeted = budgeted('Afschrijvingen')
    depreciation_budgeted = (-depreciation_budgeted[0], -depreciation_budgeted[1])
    add_subtotal_row('Afschrijvingen', yuki_result.depreciation(), depreciation_budgeted, style='')

    # Financial
    financial_budgeted = (0, 0)
    add_subtotal_row('Financieel resultaat', yuki_result.financial(), financial_budgeted, style='')
    grid.add_row()

    # Winst
    # total_costs =  [oe+d-f for oe,d,f in zip(operating_expenses, depreciation, financial)]
    total_costs_budgeted = tuple_add(operating_expenses_budgeted, depreciation_budgeted, financial_budgeted)
    profit_budgeted = [m - c for m, c in zip(margin_budgeted, total_costs_budgeted)]
    add_subtotal_row('Winst volgens de boekhouding', yuki_result.profit(), None, style=DOUBLE_TOPLINE)

    add_subtotal_row('Mutatie onderhanden werk', yuki_result.mutation_wip(last_date_this_month), style='')
    # total_profit = tuple_add(profit, mutation_wip)
    # gtotal_profit = total_profit  # save for balance
    total_profit_month, total_profit_ytd = yuki_result.total_profit()
    grid.add_row(
        [
            TextBlock('TOTAAL WINST', style=BOLD),
            '',
            TextBlock(total_profit_month, text_format='.', style=BOLD),
            TextBlock(profit_budgeted[0], text_format='.', style=BOLD, color=GRAY),
            '',
            '',
            TextBlock(total_profit_ytd, text_format='.', style=BOLD),
            TextBlock(profit_budgeted[1], text_format='.', style=BOLD, color=GRAY),
        ],
        styles=['', '', 'border:2px solid gray', '', '', '', 'border:2px solid gray'],
    )

    contents = [TextBlock(f'Winst & verliesrekening', MID_SIZE), grid, toelichting_block(toelichtingen)]
    return VBlock(contents, css_class="page-break-before", style="page-break-before: always;")
Пример #17
0
def balance_block(yuki_result: YukiResult, year: int, month: int):
    maand = MAANDEN[month - 1]
    vorige_maand = MAANDEN[month - 2] if month >= 2 else f'Begin {year}'
    last_date_this_month = last_date_of_month(year, month)
    grid = Grid(cols=6, has_header=False, aligns=['left', 'right', 'right', '', 'right', 'right'])
    toelichtingen = []
    try:
        toelichting_sheet = HeaderSheet('Begroting 2021', str(month))
    except WorksheetNotFound:
        toelichting_sheet = None  # Todo: Same code as in profit_and_loss_block. Refactor.

    def add_normal_row(title, result):
        grid.add_row(
            [
                TextBlock(title),
                TextBlock(result[0], text_format='.'),
                '',
                '',
                TextBlock(result[1], text_format='.', color="GRAY"),
                '',
            ]
        )
        if toelichting_sheet:
            toelichting = toelichting_sheet[title, 'Toelichting']
            if toelichting:
                b = toelichtingen  # zeer merkwaardige constructie maar krijg een foutmelding als ik direct iets toevoeg aan toelichtingen
                b += [(title, toelichting)]

    def add_subtotal_row(title, subtotal, style=TOPLINE):
        grid.add_row(
            [
                TextBlock(title, style=BOLD),
                '',
                TextBlock(subtotal[0], text_format='.', style=BOLD),
                '',
                '',
                TextBlock(subtotal[1], text_format='.', style=BOLD, color="GRAY"),
            ],
            styles=['', style, style, '', style, style],
        )

    # Header
    grid.add_row(
        [
            '',
            '',
            TextBlock(f'{maand.lower()}', style=BOLD),
            '',
            '',
            TextBlock(f'{vorige_maand.lower()}', style=BOLD, color=GRAY),
        ],
        styles=['width:160px;', '', '', 'width:80px;'],
    )

    # Materiele vaste activa
    tangible_fixed_assets = yuki_result.month_prev('tangible_fixed_assets')
    add_normal_row('Materiële vaste activa', tangible_fixed_assets)

    # Financiële vaste activa
    financial_fixed_assets = yuki_result.month_prev('financial_fixed_assets')
    add_normal_row('Financiële vaste activa', financial_fixed_assets)

    # Vaste activa
    fixed_assets = tuple_add(tangible_fixed_assets, financial_fixed_assets)
    add_subtotal_row('Vaste activa', fixed_assets)

    # Debiteuren
    debtors = yuki_result.month_prev(
        'debtors',
    )
    add_normal_row('Debiteuren', debtors)

    # Overige vorderingen
    other_receivables = yuki_result.other_receivables()
    add_normal_row('Overige vorderingen', other_receivables)

    # Onderhanden werk
    work_in_progress = yuki_result.get_work_in_progress(last_date_this_month)
    add_normal_row('Onderhanden werk', work_in_progress)

    # Liquide middelen
    liquid_assets = yuki_result.month_prev('liquid_assets')
    add_normal_row('Liquide middelen', liquid_assets)

    # Vlottende activa
    current_assets = tuple_add(debtors, other_receivables, work_in_progress, liquid_assets)
    add_subtotal_row('Vlottende activa', current_assets)

    # TOTAAL ACTIVA
    total_assets = tuple_add(fixed_assets, current_assets)
    add_subtotal_row('TOTAAL ACTIVA', total_assets, style=DOUBLE_TOPLINE)
    grid.add_row([])

    # Aandelenkapitaal
    share_capital = yuki_result.month_prev('share_capital')
    add_normal_row('Aandelenkapitaal', share_capital)

    # Reserves
    reserves = yuki_result.month_prev('reserves')
    add_normal_row('Reserves', reserves)

    # Onverdeeld resultaat
    undistributed_result_last_year = yuki_result.month_prev('undistributed_result')
    # # undistributed_result = tuple_add(undistributed_result_last_year, gtotal_profit)
    # profit_month, profit_last_month = yuki_result.total_profit()
    # undistributed_result = (
    #     undistributed_result_last_year[0] + profit_month,
    #     undistributed_result_last_year[0] + profit_last_month,
    # )
    # undistributed_result = tuple_add(undistributed_result, yuki_result.profit()) # Add this years profit

    # a = yuki_result.profit()
    result_until_this_month = yuki_result.profit()[1]
    last_date_last_month = last_date_of_month(year, month - 1) if month > 1 else last_date_of_month(year - 1, 12)
    # b = yuki_result.profit(last_date_last_month)
    result_until_last_month = yuki_result.profit(last_date_last_month)[1]

    # work_in_progress_last_month = yuki_result.get_work_in_progress(last_date_last_month)

    undistributed_result = tuple_add(
        undistributed_result_last_year, (result_until_this_month, result_until_last_month), work_in_progress
    )
    add_normal_row('Onverdeeld resultaat', undistributed_result)

    # Eigen vermogen
    equity = tuple_add(share_capital, reserves, undistributed_result)
    add_subtotal_row('Eigen vermogen', equity)

    # Crediteuren
    add_normal_row('Crediteuren', yuki_result.creditors())

    # Medewerkers
    add_normal_row('Medewerkers', yuki_result.debt_to_employees())

    # Belasting
    add_normal_row('Belastingen', yuki_result.taxes())

    # Overige schulden
    add_normal_row('Overige schulden', yuki_result.other_debts())

    # Kortlopende schulden
    add_subtotal_row('Kortlopende schulden', yuki_result.short_term_debt())

    # TOTAAL PASSIVA
    total_liabilities = tuple_add(equity, yuki_result.short_term_debt())
    add_subtotal_row('TOTAAL PASSVA', total_liabilities, style=DOUBLE_TOPLINE)

    # Tijd voor wat checks
    if total_assets[0] != total_liabilities[0]:
        grid.add_row(
            [TextBlock(f"Balansverschil in {maand} van {abs(total_assets[0] - total_liabilities[0])}", color=RED)]
        )
    if total_assets[1] != total_liabilities[1]:
        grid.add_row(
            [
                TextBlock(
                    f"Balansverschil in {vorige_maand} van {abs(total_assets[1] - total_liabilities[1])}", color=RED
                )
            ]
        )

    return VBlock(
        [TextBlock(f'Balans per einde {maand.lower()} {year}', MID_SIZE), grid, toelichting_block(toelichtingen)],
        css_class="page-break-before",
        style="page-break-before: always;",
    )
Пример #18
0
def cashflow_analysis_block(yuki_result, year, month):
    grid = Grid(cols=3, has_header=False, aligns=['left', 'right', 'right'])

    def add_normal_row(title, value, shift=False, value_color=None):
        row = [TextBlock(title)]
        value_text = TextBlock(value, text_format='.', color=value_color)
        if shift:
            row += ['', value_text]
        else:
            row += [value_text, '']
        grid.add_row(row)

    def add_subtotal_row(title, value, style=TOPLINE):
        grid.add_row(
            [
                TextBlock(title, style=BOLD),
                '',
                TextBlock(value, text_format='.', style=BOLD),
            ],
            styles=['', style, style],
        )

    # def yuki_figure(post, year, month, negate=False):
    #     date = last_date_of_month(year, month)
    #     prev_date = last_date_of_month(year, month - 1) if month > 1 else last_date_of_month(year - 1, 12)
    #     negation = -1 if negate else 1
    #     ytd = yuki().post(post, date) * negation
    #     monthly = ytd - yuki().post(post, prev_date) * negation
    #     return monthly

    # Winst
    profit = yuki_result.total_profit()[0]
    add_normal_row('Nettowinst', profit)

    # Afschrijvingen
    deprecation = -yuki_result.depreciation()[0]
    add_normal_row('Afschrijvingen', deprecation)

    # Cashflow
    cashflow = profit + deprecation
    add_subtotal_row('Cashflow', cashflow)
    grid.add_row([])

    # Toename vorderingen
    debtors = yuki_result.month_prev(
        'debtors',
    )
    other_receivables = yuki_result.other_receivables()
    financial_fixed_assets = yuki_result.month_prev('financial_fixed_assets')
    increase_receivables = (
        debtors[0]
        + other_receivables[0]
        + financial_fixed_assets[0]
        - debtors[1]
        - other_receivables[1]
        - financial_fixed_assets[1]
    )
    descr = 'Toegenomen vorderingen' if increase_receivables >= 0 else 'Afgenomen vorderingen'
    add_normal_row(descr, -increase_receivables)

    # Toename onderhanden werk
    in_progress = yuki_result.get_work_in_progress()
    increase_in_progress = in_progress[0] - in_progress[1]
    descr = 'Toegenomen onderhanden werk' if increase_in_progress >= 0 else 'Afgenomen onderhanden werk'
    add_normal_row(descr, -increase_in_progress)

    # Toename crediteuren
    short_term_debt = yuki_result.short_term_debt()
    increase_creditors = short_term_debt[0] - short_term_debt[1]
    descr = 'Toegenomen crediteuren' if increase_creditors >= 0 else 'Afgenomen crediteuren'
    add_normal_row(descr, increase_creditors)

    # Verandering van netto werkkapitaal
    increase_working_capital = -increase_receivables - increase_in_progress + increase_creditors
    add_subtotal_row('Verandering van netto werkkapitaal', increase_working_capital)
    grid.add_row([])

    # Operationele kasstroom
    operating_cash_flow = cashflow + increase_working_capital
    add_subtotal_row('Operationele kasstroom', operating_cash_flow)

    # Investeringen
    investment_in_assets = yuki_result.month_prev('investments')
    investments = investment_in_assets[0] - investment_in_assets[1]  # This month - last month = investments
    add_normal_row('Investeringen', -investments, shift=True)

    # Mutaties eigen vermogen
    equity_mutations = 0
    add_normal_row('Mutaties eigen vermogen', equity_mutations, shift=True)

    # Netto kasstroom
    net_cash_flow = operating_cash_flow - investments + equity_mutations
    add_subtotal_row('Netto kasstroom', net_cash_flow)

    # Toename liquide middelen
    liquid_assets = yuki_result.month_prev('liquid_assets')
    increase_liquid_assets = liquid_assets[0] - liquid_assets[1]
    color = RED if increase_liquid_assets != net_cash_flow else None
    add_normal_row('Toename liquide middelen', increase_liquid_assets, shift=True, value_color=color)

    return VBlock(
        [TextBlock(f'Cashflow analyse', MID_SIZE), grid],
        css_class="page-break-before",
        style="page-break-before: always;",
    )
Пример #19
0
def render_winstgevendheid_page(output_folder: Path, period=None):
    if period:
        period_description = f'Van {period.fromday} tot {period.untilday}'
    if not period:
        period = Period(Day().plus_months(-12))  # Laatste 12 maanden
        period_description = 'De laatste 12 maanden.'
    client_data = winst_per_klant(period)
    per_client = VBlock([
        TextBlock('Per klant', MID_SIZE),
        Table(
            client_data,
            TableConfig(
                id="winst_per_klant",
                headers=list(client_data.columns),
                aligns=[
                    'left', 'right', 'right', 'right', 'right', 'right',
                    'right', 'right'
                ],
                formats=['', '.', '€', '€', '€', '€', '€', '€'],
                totals=[False, True, True, True, True, True, False, False],
            ),
        ),
    ])

    project_data = winst_per_project(period)
    per_project = VBlock([
        TextBlock('Per project', MID_SIZE),
        Table(
            project_data,
            TableConfig(
                id="winst_per_project",
                headers=list(project_data.columns),
                aligns=[
                    'left', 'left', 'right', 'right', 'right', 'right',
                    'right', 'right', 'right'
                ],
                formats=['', '', '.', '€', '€', '€', '€', '€', '€'],
                totals=[
                    False, False, True, True, True, True, True, False, False
                ],
            ),
        ),
    ])

    person_data = winst_per_persoon(period)
    per_person = VBlock([
        TextBlock('Per persoon (voorlopig)', MID_SIZE),
        Table(
            person_data,
            TableConfig(
                id="winst_per_persoon",
                headers=list(person_data.columns),
                aligns=['left', 'right', 'right', 'right'],
                formats=['', '.', '€', '€'],
                totals=[False, True, True, True],
            ),
        ),
    ])

    page = Page([
        TextBlock('Winstgevendheid', HEADER_SIZE),
        TextBlock(
            f'''{period_description} Uitgaande van een productiviteit van {PRODUCTIVITEIT * 100:.0f}% 
                               en €{OVERIGE_KOSTEN_PER_FTE_PER_MAAND} per persoon per maand bureaukosten.''',
            color=GRAY,
        ),
        HBlock([per_client, per_project, per_person]),
    ])
    page.render(output_folder / 'winstgevendheid.html')