def render_vrije_dagen_page(output_folder: Path): table = VBlock([ Table( vrije_dagen_overzicht(), TableConfig( headers=[ 'Naam', 'Vorig jaar', 'Dit jaar nieuw', 'Dit jaar beschikbaar', 'Totaal', 'Pool' ], aligns=['left', 'right', 'right', 'right', 'right', 'right'], formats=['', '.5', '.5', '.5', '.5', '.5'], totals=[0, 0, 0, 0, 0, 1], ), ), ]) page = Page([ TextBlock('Vrije dagen', HEADER_SIZE), table, TextBlock( f'Pool = Dagen van vorig jaar + Dagen dit jaar * deel van het jaar dat is geweest ({fraction_of_the_year_past()*100:.0f}%).' ), ]) page.render(output_folder / 'freedays.html')
def render_verzuim_page(output_folder: Path): timesheet = Timesheet() months = 3 period = Period(Day().plus_months(-months)) table = VBlock([ Table( verzuim_list(period), TableConfig( headers=["Naam", "Dag", "soort", "Dagen"], aligns=["left", "left", "left", "right"], formats=[ "", "", "", ".5", ], totals=[0, 0, 0, 1], ), ), ]) verzuim = verzuimpercentage(period) verzuim_color = dependent_color(verzuim, 3, 1.5) page = Page([ TextBlock("Verzuim", HEADER_SIZE), TextBlock(f"De afgelopen {months} maanden", color=GRAY), HBlock([ VBlock([ TextBlock("Geboekte uren", DEF_SIZE, color="gray", padding=5), TextBlock("Verzuim uren", DEF_SIZE, color="gray"), TextBlock("Verzuimopercentage", DEF_SIZE, color="gray"), ]), VBlock([ TextBlock( timesheet.normal_hours(period), DEF_SIZE, text_format=".", padding=5, ), TextBlock(timesheet.absence_hours(period), DEF_SIZE, text_format="."), TextBlock(verzuim, verzuim_color, text_format="%1"), ]), ]), table, ]) page.render(output_folder / "absence.html")
def commerce_block(): minimal_interesting_amount = 20000 sales_waarde_value = sales_waarde() top_sales = top_x_sales(minimal_amount=minimal_interesting_amount) sales_waarde_color = dependent_color(sales_waarde_value, 250000, 350000) commerce = VBlock([ TextBlock("Commerce", HEADER_SIZE), TextBlock("Saleswaarde", MID_SIZE, padding=10), TextBlock( "Verwachte omzet maal kans van alle actieve<br/>salestrajecten.", color=GRAY, ), TextBlock( sales_waarde_value, HEADER_SIZE, text_format="K", color=sales_waarde_color, tooltip="Som van openstaande trajecten<br/>maal hun kans.", ), TrendLines().chart("sales_waarde", 250, 150, min_y_axis=0, x_start=months_ago(6)), VBlock( [ TextBlock(f"Top {len(top_sales)} sales kansen", MID_SIZE), TextBlock( f"Met een verwachte waarde van minimaal € {minimal_interesting_amount}.", color=GRAY, ), Table( top_sales, TableConfig(headers=[], aligns=["left", "right"], formats=["", "€"]), ), ], link="sales.html", ), # klanten_block() travelbase_block(), ]) return commerce
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
def render_travelbase_page(output_folder): bookings = get_bookings_per_week(booking_type='bookings') if not isinstance(bookings, pd.DataFrame): return # An error occurred, no use to proceed totals = [(brand, bookings[brand].sum()) for brand in BRANDS] totals_table = Table( totals, TableConfig(aligns=['left', 'right'], formats=['', '0'], totals=[0, 1])) page = Page([ TextBlock('Travelbase', HEADER_SIZE), TextBlock( 'Aantal boekingen per week. Weken lopen van maandag t/m zondag.', color='gray'), bar_chart(bookings, 600, 400), totals_table, ]) page.render(output_folder / 'travelbase.html')
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
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')
def render_debiteuren_page(output_folder: Path): debiteuren = debiteuren_leeftijd_analyse() if not isinstance(debiteuren, pd.DataFrame): return # Error occurred, no use to proceed page = Page([ TextBlock('Debiteuren', HEADER_SIZE), Table( debiteuren, TableConfig( headers=[ 'klant', 'openstaand', '<30 dg', '30-60 dg', '60-90 dg', '> 90 dg' ], aligns=['left', 'right', 'right', 'right', 'right', 'right'], formats=['', '.', '.', '.', '.', '.'], totals=[0, 1, 1, 1, 1, 1], row_linking=lambda line, value: 'https://oberview.oberon.nl/facturen/openstaand', ), ), ]) page.render(output_folder / 'debiteuren.html')
def render_detailpage(): data = [[key, saved_cache[key]] for key in sorted(saved_cache.keys())] page = Page([Table(data)]) page.render('output/details.html')
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')
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')