Coverage for jbank/helpers.py: 78%
58 statements
« prev ^ index » next coverage.py v7.2.2, created at 2023-03-27 13:36 +0700
« prev ^ index » next coverage.py v7.2.2, created at 2023-03-27 13:36 +0700
1# pylint: disable=c-extension-no-member
2import logging
3import os
4from datetime import date, timedelta
5from typing import Any, Tuple, Optional, List
6import pytz
7from django.conf import settings
8from django.core.files import File
9from django.db import models
10from django.utils.timezone import now
11from django.utils.translation import gettext_lazy as _
12from jacc.models import Account, AccountType, EntryType
13import re
14from lxml import etree, objectify # type: ignore # pytype: disable=import-error
15from jutil.command import get_date_range_by_name
16from jutil.parse import parse_datetime
17from jutil.format import strip_media_root, is_media_full_path
19MESSAGE_STATEMENT_RECORD_FIELDS = ("messages", "client_messages", "bank_messages")
21logger = logging.getLogger(__name__)
24def get_or_create_bank_account_entry_types() -> List[EntryType]:
25 e_type_codes = [
26 settings.E_BANK_DEPOSIT,
27 settings.E_BANK_WITHDRAW,
28 settings.E_BANK_REFERENCE_PAYMENT,
29 settings.E_BANK_REFUND,
30 settings.E_BANK_PAYOUT,
31 ]
32 e_types: List[EntryType] = []
33 for code in e_type_codes:
34 e_type = EntryType.objects.get_or_create(
35 code=code,
36 defaults={
37 "identifier": code,
38 "name": code,
39 "is_settlement": True,
40 "is_payment": code in [settings.E_BANK_DEPOSIT, settings.E_BANK_REFERENCE_PAYMENT],
41 },
42 )[0]
43 e_types.append(e_type)
44 return e_types
47def get_or_create_bank_account(account_number: str, currency: str = "EUR") -> Account:
48 a_type = AccountType.objects.get_or_create(code=settings.ACCOUNT_BANK_ACCOUNT, is_asset=True, defaults={"name": _("bank account")})[0]
49 acc, created = Account.objects.get_or_create(name=account_number, type=a_type, currency=currency)
50 if created:
51 get_or_create_bank_account_entry_types()
52 return acc
55def make_msg_id() -> str:
56 return re.sub(r"[^\d]", "", now().isoformat())[:-4]
59def validate_xml(content: bytes, xsd_file_name: str):
60 """
61 Validates XML using XSD
62 """
63 schema = etree.XMLSchema(file=xsd_file_name)
64 parser = objectify.makeparser(schema=schema)
65 objectify.fromstring(content, parser)
68def parse_date_or_relative_date(value: str, tz: Any = None) -> Optional[date]:
69 try:
70 return parse_datetime(value, tz=tz).date()
71 except Exception:
72 return get_date_range_by_name(value.replace("-", "_"), tz=tz)[0].date()
75def parse_start_and_end_date(tz: Any, **options) -> Tuple[Optional[date], Optional[date]]:
76 start_date = None
77 end_date = None
78 time_now = now().astimezone(tz if tz else pytz.utc)
79 if options["start_date"]:
80 start_date = parse_date_or_relative_date(options["start_date"], tz=tz)
81 end_date = time_now.astimezone(tz).date() + timedelta(days=1)
82 if options["end_date"]:
83 end_date = parse_date_or_relative_date(options["end_date"], tz=tz)
84 return start_date, end_date
87def save_or_store_media(file: models.FileField, filename: str):
88 """
89 Saves FileField filename as relative path if it's under MEDIA_ROOT.
90 Otherwise writes file under media root.
91 """
92 if is_media_full_path(filename):
93 file.name = strip_media_root(filename) # type: ignore
94 else:
95 with open(filename, "rb") as fp:
96 plain_filename = os.path.basename(filename)
97 file.save(plain_filename, File(fp)) # type: ignore # noqa