Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 31 additions & 8 deletions casparser/parsers/cdsl.py
Original file line number Diff line number Diff line change
Expand Up @@ -585,11 +585,20 @@ def parse_cdsl(


def _is_summary_demat_row(block: Block) -> bool:
if len(block.cells) != 4:
"""Recognise both physical layouts of the page-2 demat summary row.

PDFium can either join the broker and DP/client details into one
cell or expose them as two adjacent cells. Both layouts represent
the same logical row::

Type | "<BROKER>\nDP Id:... Client Id:..." | folios | value
Type | <BROKER> | "DP Id:... Client Id:..." | folios | value
"""
if len(block.cells) not in (4, 5):
return False
if not DEMAT_TYPE_RE.match(block.cells[0].text.strip()):
return False
return bool(SUMMARY_DPC_RE.search(block.cells[1].text))
return any(SUMMARY_DPC_RE.search(cell.text) for cell in block.cells[1:])


def _is_summary_mf_folios_row(block: Block) -> bool:
Expand All @@ -601,15 +610,29 @@ def _is_summary_mf_folios_row(block: Block) -> bool:
def _account_from_summary_row(
block: Block, owners: List[DematOwner]
) -> Tuple[DematAccount, Tuple[str, str, str]]:
"""Build an account from either supported page-2 summary layout."""
type_word = DEMAT_TYPE_RE.match(block.cells[0].text.strip()).group(1).upper()
broker_dp = block.cells[1].text
lines = [ln.strip() for ln in broker_dp.split("\n") if ln.strip()]
broker = lines[0] if lines else ""
dpc = SUMMARY_DPC_RE.search(broker_dp)
dp_cell_idx = next(
(
index
for index in range(1, len(block.cells))
if SUMMARY_DPC_RE.search(block.cells[index].text)
),
1,
)
dp_cell_text = block.cells[dp_cell_idx].text
dpc = SUMMARY_DPC_RE.search(dp_cell_text)
dp_id = dpc.group(1) if dpc else ""
client_id = dpc.group(2) if dpc else ""
folios = int(_to_decimal(block.cells[2].text))
balance = _to_decimal(block.cells[3].text)
broker_fragments = []
for cell in block.cells[1 : dp_cell_idx + 1]:
for line in cell.text.split("\n"):
fragment = SUMMARY_DPC_RE.sub("", line).strip()
if fragment:
broker_fragments.append(fragment)
broker = " ".join(broker_fragments)
folios = int(_to_decimal(block.cells[-2].text))
balance = _to_decimal(block.cells[-1].text)
ac = DematAccount(
name=broker,
type=_full_type(type_word),
Expand Down
30 changes: 30 additions & 0 deletions tests/test_demat_units.py
Original file line number Diff line number Diff line change
Expand Up @@ -872,6 +872,13 @@ def test_summary_demat_row_4_cell(self):
assert ac.folios == 12
assert ac.balance == Decimal("10400929.50")

assert cdsl_p._is_summary_demat_row(block)
cdsl_ac, cdsl_key = cdsl_p._account_from_summary_row(block, owners=[])
assert cdsl_key == ("NSDL", "IN123456", "99998888")
assert cdsl_ac.name == "ACME BROKER LIMITED"
assert cdsl_ac.folios == 12
assert cdsl_ac.balance == Decimal("10400929.50")

def test_summary_demat_row_5_cell(self):
"""5-cell variant: broker name and DP/Client line as separate
cells (observed on CDSL rows in some NSDL CAS layouts)."""
Expand All @@ -890,6 +897,28 @@ def test_summary_demat_row_5_cell(self):
assert ac.folios == 25
assert ac.balance == Decimal("9734823.11")

assert cdsl_p._is_summary_demat_row(block)
cdsl_ac, cdsl_key = cdsl_p._account_from_summary_row(block, owners=[])
assert cdsl_key == ("CDSL", "11112222", "33334444")
assert cdsl_ac.name == "BETA BROKER LIMITED"
assert cdsl_ac.folios == 25
assert cdsl_ac.balance == Decimal("9734823.11")

def test_summary_demat_row_reconstructs_broker_split_across_cells(self):
block = _block(
_cell("CDSL Demat Account"),
_cell("EXAMPLE BROKER PRIVATE"),
_cell("LIMITED\nDP ID:11112222 Client ID:33334444"),
_cell("25"),
_cell("97,34,823.11"),
page=2,
)

ac, key = cdsl_p._account_from_summary_row(block, owners=[])

assert key == ("CDSL", "11112222", "33334444")
assert ac.name == "EXAMPLE BROKER PRIVATE LIMITED"

def test_summary_demat_row_rejects_wrong_cell_count(self):
# 3 cells: too short.
block = _block(
Expand All @@ -899,6 +928,7 @@ def test_summary_demat_row_rejects_wrong_cell_count(self):
page=2,
)
assert not nsdl_p._is_summary_demat_row(block)
assert not cdsl_p._is_summary_demat_row(block)

def test_parse_bond_summary_row(self):
"""NSDL-flavour summary bonds row — discriminates frequency
Expand Down
Loading