Coverage for jbank/helpers.py: 78%

58 statements  

« 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 

18 

19MESSAGE_STATEMENT_RECORD_FIELDS = ("messages", "client_messages", "bank_messages") 

20 

21logger = logging.getLogger(__name__) 

22 

23 

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 

45 

46 

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 

53 

54 

55def make_msg_id() -> str: 

56 return re.sub(r"[^\d]", "", now().isoformat())[:-4] 

57 

58 

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) 

66 

67 

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() 

73 

74 

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 

85 

86 

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