Functional Weave
Code in Rust

banking.bacs-processing-date@1.0.0

impl/python.py

2,100 bytes · the Python implementation · view raw

Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.

from typing import List, Sequence

from .banking_bacs_processing_date_types import BacsCycle
from .dates_add_business_days import add_business_days  ← from dates.add-business-days ^1.0.0 · built alongside by fune
from .dates_bank_holidays import bank_holidays  ← from dates.bank-holidays ^1.0.0 · built alongside by fune
from .dates_day_of_week import is_weekday  ← from dates.day-of-week ^1.0.0 · built alongside by fune


def _holidays_for(years: Sequence[int]) -> List[str]:
    out: List[str] = []
    for year in years:
        out.extend(holiday.date for holiday in bank_holidays("england-and-wales", year))
    return out


def _cycle_with(submission_iso: str, after_cutoff: bool, holidays: Sequence[str]) -> BacsCycle:
    processing_day = is_weekday(submission_iso) and submission_iso not in holidays
    input_day = submission_iso if processing_day and not after_cutoff else add_business_days(submission_iso, 1, holidays)
    return BacsCycle(
        input_day=input_day,
        processing_day=add_business_days(input_day, 1, holidays),
        entry_day=add_business_days(input_day, 2, holidays),
    )


def bacs_processing_dates(submission_iso: str, after_cutoff: bool) -> BacsCycle:
    """The Bacs cycle a file joins.

    Bacs works on processing days - Monday to Friday except England and Wales
    bank holidays - and a file joins the cycle whose input day is the day it
    arrives, if that is a processing day and it arrived before the 22:30
    deadline, or the next processing day otherwise. Processing is the next
    processing day and entry the one after.
    """
    if not isinstance(after_cutoff, bool):
        raise TypeError("afterCutoff must be a boolean, received %r" % (after_cutoff,))
    # is_weekday validates the date before its year is read.
    is_weekday(submission_iso)
    year = int(submission_iso[0:4])
    # Only reach for next year's calendar when the cycle really runs into it,
    # so a December submission in the last published year still works as long
    # as its cycle ends inside that year.
    cycle = _cycle_with(submission_iso, after_cutoff, _holidays_for([year]))
    if cycle.entry_day[0:4] != submission_iso[0:4]:
        cycle = _cycle_with(submission_iso, after_cutoff, _holidays_for([year, year + 1]))
    return cycle