File size: 4,498 Bytes
dd6303a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
"""
Text Utilities
--------------
Helpers for number-to-words, date formatting, amount formatting etc.
Used by generators to fill human-readable fields.
"""

import re
from datetime import datetime


# ── Number to Bangladeshi words ──────────────────────────────────────────────

_ONES = ["", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight",
         "Nine", "Ten", "Eleven", "Twelve", "Thirteen", "Fourteen", "Fifteen",
         "Sixteen", "Seventeen", "Eighteen", "Nineteen"]
_TENS = ["", "", "Twenty", "Thirty", "Forty", "Fifty", "Sixty", "Seventy", "Eighty", "Ninety"]


def _below_hundred(n: int) -> str:
    if n < 20:
        return _ONES[n]
    return (_TENS[n // 10] + (" " + _ONES[n % 10] if n % 10 else "")).strip()


def _below_thousand(n: int) -> str:
    if n < 100:
        return _below_hundred(n)
    return (_ONES[n // 100] + " Hundred" + (" " + _below_hundred(n % 100) if n % 100 else "")).strip()


def amount_to_words_bd(amount: float) -> str:
    """
    Convert amount to Bangladeshi words using Crore/Lac/Thousand system.
    e.g. 7500000 -> "Seven Crore Fifty Lac"
    """
    n = int(round(amount))
    if n == 0:
        return "Zero"

    parts = []
    crore = n // 10_000_000
    n %= 10_000_000
    lac = n // 100_000
    n %= 100_000
    thousand = n // 1000
    n %= 1000

    if crore:
        parts.append(_below_thousand(crore) + " Crore")
    if lac:
        parts.append(_below_thousand(lac) + " Lac")
    if thousand:
        parts.append(_below_thousand(thousand) + " Thousand")
    if n:
        parts.append(_below_thousand(n))

    return " ".join(parts)


def format_bdt(amount: float) -> str:
    """Format as 'Tk. 75,00,000.00' (Bangladeshi comma grouping)."""
    n = int(round(amount))
    s = str(n)
    # BD style: last 3 digits, then groups of 2
    if len(s) > 3:
        result = s[-3:]
        s = s[:-3]
        while s:
            result = s[-2:] + "," + result
            s = s[:-2]
        return f"Tk. {result}.00"
    return f"Tk. {s}.00"


# ── Date helpers ──────────────────────────────────────────────────────────────

_MONTH_MAP = {
    "jan": "January", "feb": "February", "mar": "March", "apr": "April",
    "may": "May", "jun": "June", "jul": "July", "aug": "August",
    "sep": "September", "oct": "October", "nov": "November", "dec": "December",
}


def parse_date_flexible(date_str: str) -> datetime:
    """Try multiple date formats and return a datetime object."""
    formats = [
        "%d-%b-%Y", "%d-%B-%Y", "%d/%m/%Y", "%Y-%m-%d",
        "%d-%m-%Y", "%d.%m.%Y",
    ]
    for fmt in formats:
        try:
            return datetime.strptime(date_str.strip(), fmt)
        except ValueError:
            continue
    raise ValueError(f"Cannot parse date: {date_str!r}")


def date_to_long(date_str: str) -> str:
    """Convert '30-Jun-2022' to '30th June 2022'."""
    try:
        dt = parse_date_flexible(date_str)
        day = dt.day
        suffix = "th" if 11 <= day <= 13 else {1: "st", 2: "nd", 3: "rd"}.get(day % 10, "th")
        return f"{day}{suffix} {dt.strftime('%B %Y')}"
    except ValueError:
        return date_str


def date_to_document(date_str: str) -> str:
    """Convert '15-Apr-2021' to '15-04-2021'."""
    try:
        dt = parse_date_flexible(date_str)
        return dt.strftime("%d-%m-%Y")
    except ValueError:
        return date_str


def bg_validity_date(completion_date_str: str, extra_days: int = 150) -> str:
    """
    Bank Guarantee validity = completion date + ~150 days (28 days after tender validity).
    Returns formatted date like '24-August-2021'.
    """
    try:
        dt = parse_date_flexible(completion_date_str)
        from datetime import timedelta
        dt2 = dt + timedelta(days=extra_days)
        return f"{dt2.day}-{dt2.strftime('%B')}-{dt2.year}"
    except ValueError:
        return ""


# ── String helpers ────────────────────────────────────────────────────────────

def work_period_label(start: str, end: str) -> str:
    """e.g. '15-Apr-2021 to 30-Jun-2022'"""
    return f"{start}\nto \n{end}\n"


def tender_id_label(tender_id: str) -> str:
    return f"Tender ID: {tender_id}"