diff --git a/frontend/src/lib/interval.ts b/frontend/src/lib/interval.ts index 440c7cc51..458b700b0 100644 --- a/frontend/src/lib/interval.ts +++ b/frontend/src/lib/interval.ts @@ -1,28 +1,36 @@ import { _ } from "../i18n"; -export type Interval = "year" | "quarter" | "month" | "week" | "day"; +export type Interval = + | "year" + | "quarter" + | "month" + | "fortnight" + | "week" + | "day"; export const DEFAULT_INTERVAL: Interval = "month"; export const INTERVALS: Interval[] = [ - "year", - "quarter", - "month", - "week", - "day", + "year", + "quarter", + "month", + "fortnight", + "week", + "day", ]; export function getInterval(s: string | null): Interval { - return INTERVALS.includes(s as Interval) ? (s as Interval) : DEFAULT_INTERVAL; + return INTERVALS.includes(s as Interval) ? (s as Interval) : DEFAULT_INTERVAL; } /** Get the translateable label for an interval. */ export function intervalLabel(s: Interval): string { - return { - year: _("Yearly"), - quarter: _("Quarterly"), - month: _("Monthly"), - week: _("Weekly"), - day: _("Daily"), - }[s]; + return { + year: _("Yearly"), + quarter: _("Quarterly"), + month: _("Monthly"), + fortnight: _("Fortnightly"), + week: _("Weekly"), + day: _("Daily"), + }[s]; } diff --git a/src/fava/core/budgets.py b/src/fava/core/budgets.py index ce8638d91..d3b09393a 100644 --- a/src/fava/core/budgets.py +++ b/src/fava/core/budgets.py @@ -103,6 +103,7 @@ def parse_budgets( interval_map = { "daily": Interval.DAY, "weekly": Interval.WEEK, + "fortnightly": Interval.FORTNIGHT, "monthly": Interval.MONTH, "quarterly": Interval.QUARTER, "yearly": Interval.YEAR, diff --git a/src/fava/help/budgets.md b/src/fava/help/budgets.md index a67fa59e0..1c790713b 100644 --- a/src/fava/help/budgets.md +++ b/src/fava/help/budgets.md @@ -6,6 +6,7 @@ Beancount file:
@@ -13,10 +14,10 @@ Beancount file: If budgets are specified, Fava's reports and charts will display remaining budgets and related information. -The budget directives can be specified `daily`, `weekly`, `monthly`, `quarterly` -and `yearly`. The specified budget is valid until another budget directive for -the account is specified. The budget is broken down to a daily budget, and -summed up for a range of dates as needed. +The budget directives can be specified `daily`, `weekly`, `fortnightly`, +`monthly`, `quarterly` and `yearly`. The specified budget is valid until another +budget directive for the account is specified. The budget is broken down to a +daily budget, and summed up for a range of dates as needed. This makes the budgets very flexible, allowing for a monthly budget, being taken over by a weekly budget, and so on. diff --git a/src/fava/translations/bg/LC_MESSAGES/messages.po b/src/fava/translations/bg/LC_MESSAGES/messages.po index 01c07f8f9..621b29831 100644 --- a/src/fava/translations/bg/LC_MESSAGES/messages.po +++ b/src/fava/translations/bg/LC_MESSAGES/messages.po @@ -431,6 +431,10 @@ msgstr "Тримесечен" msgid "Monthly" msgstr "Месечен" +#: frontend/src/lib/interval.ts:25 src/fava/util/date.py:105 +msgid "Fortnightly" +msgstr "На всеки две седмици" + #: frontend/src/lib/interval.ts:25 src/fava/util/date.py:105 msgid "Weekly" msgstr "Седмичен" @@ -592,4 +596,3 @@ msgstr "Изтриване..." #: frontend/src/sidebar/AsideContents.svelte:60 msgid "Add Journal Entry" msgstr "" - diff --git a/src/fava/translations/ca/LC_MESSAGES/messages.po b/src/fava/translations/ca/LC_MESSAGES/messages.po index c1e6e883c..2a5271bde 100644 --- a/src/fava/translations/ca/LC_MESSAGES/messages.po +++ b/src/fava/translations/ca/LC_MESSAGES/messages.po @@ -431,6 +431,10 @@ msgstr "Per trimestre" msgid "Monthly" msgstr "Per mes" +#: frontend/src/lib/interval.ts:25 src/fava/util/date.py:105 +msgid "Fortnightly" +msgstr "Per quinzena" + #: frontend/src/lib/interval.ts:25 src/fava/util/date.py:105 msgid "Weekly" msgstr "Per setmana" @@ -592,4 +596,3 @@ msgstr "S'està suprimint..." #: frontend/src/sidebar/AsideContents.svelte:60 msgid "Add Journal Entry" msgstr "" - diff --git a/src/fava/translations/de/LC_MESSAGES/messages.po b/src/fava/translations/de/LC_MESSAGES/messages.po index 5fc04681c..b29d363dc 100644 --- a/src/fava/translations/de/LC_MESSAGES/messages.po +++ b/src/fava/translations/de/LC_MESSAGES/messages.po @@ -431,6 +431,10 @@ msgstr "Quartalsweise" msgid "Monthly" msgstr "Monatlich" +#: frontend/src/lib/interval.ts:25 src/fava/util/date.py:105 +msgid "Fortnightly" +msgstr "Zweiwöchentlich" + #: frontend/src/lib/interval.ts:25 src/fava/util/date.py:105 msgid "Weekly" msgstr "Wöchentlich" @@ -592,4 +596,3 @@ msgstr "Wird gelöscht..." #: frontend/src/sidebar/AsideContents.svelte:60 msgid "Add Journal Entry" msgstr "Journal-Eintrag hinzufügen" - diff --git a/src/fava/translations/fa/LC_MESSAGES/messages.po b/src/fava/translations/fa/LC_MESSAGES/messages.po index d15520d90..4a3018094 100644 --- a/src/fava/translations/fa/LC_MESSAGES/messages.po +++ b/src/fava/translations/fa/LC_MESSAGES/messages.po @@ -431,6 +431,10 @@ msgstr "سه‌ماهه" msgid "Monthly" msgstr "ماهانه" +#: frontend/src/lib/interval.ts:25 src/fava/util/date.py:105 +msgid "Fortnightly" +msgstr "هر دو هفته یکبار" + #: frontend/src/lib/interval.ts:25 src/fava/util/date.py:105 msgid "Weekly" msgstr "هفتگی" @@ -592,4 +596,3 @@ msgstr "" #: frontend/src/sidebar/AsideContents.svelte:60 msgid "Add Journal Entry" msgstr "" - diff --git a/src/fava/translations/fr/LC_MESSAGES/messages.po b/src/fava/translations/fr/LC_MESSAGES/messages.po index fd6ef82db..0237d38b2 100644 --- a/src/fava/translations/fr/LC_MESSAGES/messages.po +++ b/src/fava/translations/fr/LC_MESSAGES/messages.po @@ -431,6 +431,10 @@ msgstr "Trimestriel" msgid "Monthly" msgstr "Mensuel" +#: frontend/src/lib/interval.ts:25 src/fava/util/date.py:105 +msgid "Fortnightly" +msgstr "Bimensuel" + #: frontend/src/lib/interval.ts:25 src/fava/util/date.py:105 msgid "Weekly" msgstr "Hebdomadaire" @@ -592,4 +596,3 @@ msgstr "Suppression..." #: frontend/src/sidebar/AsideContents.svelte:60 msgid "Add Journal Entry" msgstr "" - diff --git a/src/fava/translations/nl/LC_MESSAGES/messages.po b/src/fava/translations/nl/LC_MESSAGES/messages.po index d0d327e6e..d86737ac4 100644 --- a/src/fava/translations/nl/LC_MESSAGES/messages.po +++ b/src/fava/translations/nl/LC_MESSAGES/messages.po @@ -431,6 +431,10 @@ msgstr "Per kwartaal" msgid "Monthly" msgstr "Maandelijks" +#: frontend/src/lib/interval.ts:25 src/fava/util/date.py:105 +msgid "Fortnightly" +msgstr "Tweewekelijks" + #: frontend/src/lib/interval.ts:25 src/fava/util/date.py:105 msgid "Weekly" msgstr "Wekelijks" @@ -592,4 +596,3 @@ msgstr "" #: frontend/src/sidebar/AsideContents.svelte:60 msgid "Add Journal Entry" msgstr "" - diff --git a/src/fava/translations/pt_BR/LC_MESSAGES/messages.po b/src/fava/translations/pt_BR/LC_MESSAGES/messages.po index c6fdc9120..6c54c3586 100644 --- a/src/fava/translations/pt_BR/LC_MESSAGES/messages.po +++ b/src/fava/translations/pt_BR/LC_MESSAGES/messages.po @@ -432,6 +432,10 @@ msgstr "Trimestral" msgid "Monthly" msgstr "Mensal" +#: frontend/src/lib/interval.ts:25 src/fava/util/date.py:105 +msgid "Fortnightly" +msgstr "Quinzenal" + #: frontend/src/lib/interval.ts:25 src/fava/util/date.py:105 msgid "Weekly" msgstr "Semanal" @@ -593,4 +597,3 @@ msgstr "Apagando..." #: frontend/src/sidebar/AsideContents.svelte:60 msgid "Add Journal Entry" msgstr "Adicionar Entrada" - diff --git a/src/fava/translations/ru/LC_MESSAGES/messages.po b/src/fava/translations/ru/LC_MESSAGES/messages.po index 9191d1dfe..6b0659543 100644 --- a/src/fava/translations/ru/LC_MESSAGES/messages.po +++ b/src/fava/translations/ru/LC_MESSAGES/messages.po @@ -430,6 +430,10 @@ msgstr "По кварталам" msgid "Monthly" msgstr "По месяцам" +#: frontend/src/lib/interval.ts:25 src/fava/util/date.py:105 +msgid "Fortnightly" +msgstr "Каждые две недели" + #: frontend/src/lib/interval.ts:25 src/fava/util/date.py:105 msgid "Weekly" msgstr "По неделям" @@ -591,4 +595,3 @@ msgstr "" #: frontend/src/sidebar/AsideContents.svelte:60 msgid "Add Journal Entry" msgstr "" - diff --git a/src/fava/translations/sk/LC_MESSAGES/messages.po b/src/fava/translations/sk/LC_MESSAGES/messages.po index 818de4c33..b49b20f0f 100644 --- a/src/fava/translations/sk/LC_MESSAGES/messages.po +++ b/src/fava/translations/sk/LC_MESSAGES/messages.po @@ -436,6 +436,10 @@ msgstr "Kvartálne" msgid "Monthly" msgstr "Mesačne" +#: frontend/src/lib/interval.ts:25 src/fava/util/date.py:105 +msgid "Fortnightly" +msgstr "Dvotýždenne" + #: frontend/src/lib/interval.ts:25 src/fava/util/date.py:105 msgid "Weekly" msgstr "Týždenne" @@ -597,4 +601,3 @@ msgstr "" #: frontend/src/sidebar/AsideContents.svelte:60 msgid "Add Journal Entry" msgstr "" - diff --git a/src/fava/translations/sv/LC_MESSAGES/messages.po b/src/fava/translations/sv/LC_MESSAGES/messages.po index f28d8db1b..f942bf52a 100644 --- a/src/fava/translations/sv/LC_MESSAGES/messages.po +++ b/src/fava/translations/sv/LC_MESSAGES/messages.po @@ -431,6 +431,10 @@ msgstr "Kvartalsvis" msgid "Monthly" msgstr "Månatligt" +#: frontend/src/lib/interval.ts:25 src/fava/util/date.py:105 +msgid "Fortnightly" +msgstr "Var fjortonde dag" + #: frontend/src/lib/interval.ts:25 src/fava/util/date.py:105 msgid "Weekly" msgstr "Veckovis" @@ -592,4 +596,3 @@ msgstr "" #: frontend/src/sidebar/AsideContents.svelte:60 msgid "Add Journal Entry" msgstr "" - diff --git a/src/fava/translations/uk/LC_MESSAGES/messages.po b/src/fava/translations/uk/LC_MESSAGES/messages.po index 3d2c1c976..724c8e275 100644 --- a/src/fava/translations/uk/LC_MESSAGES/messages.po +++ b/src/fava/translations/uk/LC_MESSAGES/messages.po @@ -431,6 +431,10 @@ msgstr "Щоквартально" msgid "Monthly" msgstr "Щомісячно" +#: frontend/src/lib/interval.ts:25 src/fava/util/date.py:105 +msgid "Fortnightly" +msgstr "двотижневий" + #: frontend/src/lib/interval.ts:25 src/fava/util/date.py:105 msgid "Weekly" msgstr "Щотижнево" @@ -592,4 +596,3 @@ msgstr "" #: frontend/src/sidebar/AsideContents.svelte:60 msgid "Add Journal Entry" msgstr "" - diff --git a/src/fava/translations/zh/LC_MESSAGES/messages.po b/src/fava/translations/zh/LC_MESSAGES/messages.po index c33b23d2d..83096a00c 100644 --- a/src/fava/translations/zh/LC_MESSAGES/messages.po +++ b/src/fava/translations/zh/LC_MESSAGES/messages.po @@ -431,6 +431,10 @@ msgstr "按季" msgid "Monthly" msgstr "按月" +#: frontend/src/lib/interval.ts:25 src/fava/util/date.py:105 +msgid "Fortnightly" +msgstr "按两周" + #: frontend/src/lib/interval.ts:25 src/fava/util/date.py:105 msgid "Weekly" msgstr "按周" @@ -592,4 +596,3 @@ msgstr "正在删除..." #: frontend/src/sidebar/AsideContents.svelte:60 msgid "Add Journal Entry" msgstr "添加日记账条目" - diff --git a/src/fava/translations/zh_Hant_TW/LC_MESSAGES/messages.po b/src/fava/translations/zh_Hant_TW/LC_MESSAGES/messages.po index a5cc1dccc..c883bab52 100644 --- a/src/fava/translations/zh_Hant_TW/LC_MESSAGES/messages.po +++ b/src/fava/translations/zh_Hant_TW/LC_MESSAGES/messages.po @@ -432,6 +432,10 @@ msgstr "季度" msgid "Monthly" msgstr "月" +#: frontend/src/lib/interval.ts:25 src/fava/util/date.py:105 +msgid "Fortnightly" +msgstr "兩周" + #: frontend/src/lib/interval.ts:25 src/fava/util/date.py:105 msgid "Weekly" msgstr "週" @@ -594,4 +598,3 @@ msgstr "正在删除..." #: frontend/src/sidebar/AsideContents.svelte:60 msgid "Add Journal Entry" msgstr "" - diff --git a/src/fava/util/date.py b/src/fava/util/date.py index 3aa26deae..302bda5cf 100644 --- a/src/fava/util/date.py +++ b/src/fava/util/date.py @@ -93,6 +93,7 @@ class Interval(Enum): YEAR = "year" QUARTER = "quarter" MONTH = "month" + FORTNIGHT = "fortnight" WEEK = "week" DAY = "day" @@ -103,6 +104,7 @@ def label(self) -> str: Interval.YEAR: gettext("Yearly"), Interval.QUARTER: gettext("Quarterly"), Interval.MONTH: gettext("Monthly"), + Interval.FORTNIGHT: gettext("Fortnightly"), Interval.WEEK: gettext("Weekly"), Interval.DAY: gettext("Daily"), } @@ -501,7 +503,7 @@ def days_in_daterange( yield start_date + timedelta(diff) -def number_of_days_in_period(interval: Interval, date: datetime.date) -> int: +def number_of_days_in_period(interval: Interval, date: datetime.date) -> int: # noqa: PLR0911 """Get number of days in the surrounding interval. Args: @@ -516,6 +518,8 @@ def number_of_days_in_period(interval: Interval, date: datetime.date) -> int: return 1 if interval is Interval.WEEK: return 7 + if interval is Interval.FORTNIGHT: + return 14 if interval is Interval.MONTH: date = datetime.date(date.year, date.month, 1) return (get_next_interval(date, Interval.MONTH) - date).days diff --git a/tests/test_core_budgets.py b/tests/test_core_budgets.py index ea7ffbf13..6e71e5786 100644 --- a/tests/test_core_budgets.py +++ b/tests/test_core_budgets.py @@ -79,6 +79,18 @@ def test_budgets_weekly(budgets_doc: BudgetDict) -> None: assert budget["EUR"] == num +def test_budgets_fortnightly(budgets_doc: BudgetDict) -> None: + """ + 2016-05-01 custom "budget" Expenses:Books "fortnightly" 42 EUR""" + + for start, end, num in [ + (date(2016, 5, 1), date(2016, 5, 2), Decimal(21) / 7), + (date(2016, 9, 1), date(2016, 9, 2), Decimal(21) / 7), + ]: + budget = calculate_budget(budgets_doc, "Expenses:Books", start, end) + assert budget["EUR"] == num + + def test_budgets_monthly(budgets_doc: BudgetDict) -> None: """ 2014-05-01 custom "budget" Expenses:Books "monthly" 100 EUR"""