Spaces:
Sleeping
Sleeping
Sahil Garg
commited on
Commit
·
436fd72
1
Parent(s):
c172f37
pnl and bs changed to schedule 3 format
Browse files- bs/balance_sheet_generator.py +313 -448
- bs/balance_sheet_template_handler.py +104 -37
- pnl/profit_loss_statement_generator.py +91 -63
bs/balance_sheet_generator.py
CHANGED
|
@@ -15,6 +15,9 @@ from dotenv import load_dotenv
|
|
| 15 |
from pydantic import BaseModel, Field, ValidationError
|
| 16 |
from pydantic_settings import BaseSettings
|
| 17 |
|
|
|
|
|
|
|
|
|
|
| 18 |
load_dotenv()
|
| 19 |
|
| 20 |
# Configure logging
|
|
@@ -44,6 +47,8 @@ class BalanceSheetItem(BaseModel):
|
|
| 44 |
class BalanceSheetTotals(BaseModel):
|
| 45 |
shareholders_funds_2024: float = 0.0
|
| 46 |
shareholders_funds_2023: float = 0.0
|
|
|
|
|
|
|
| 47 |
non_current_liabilities_2024: float = 0.0
|
| 48 |
non_current_liabilities_2023: float = 0.0
|
| 49 |
current_liabilities_2024: float = 0.0
|
|
@@ -64,79 +69,12 @@ class EnhancedBalanceSheetGenerator:
|
|
| 64 |
self.api_key = api_key
|
| 65 |
self.base_url = "https://openrouter.ai/api/v1/chat/completions"
|
| 66 |
|
| 67 |
-
#
|
| 68 |
-
self.
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
],
|
| 74 |
-
# Reserves patterns
|
| 75 |
-
'reserves_surplus': [
|
| 76 |
-
'reserves and surplus', 'reserves', 'surplus', 'retained earnings',
|
| 77 |
-
'profit and loss', 'general reserves', 'closing balance'
|
| 78 |
-
],
|
| 79 |
-
# Long term borrowings
|
| 80 |
-
'long_term_borrowings': [
|
| 81 |
-
'long term borrowings', 'long-term borrowings', 'borrowings',
|
| 82 |
-
'debt', 'loans', 'financial corporation', 'bank loan'
|
| 83 |
-
],
|
| 84 |
-
# Deferred tax
|
| 85 |
-
'deferred_tax': [
|
| 86 |
-
'deferred tax', 'tax liability', 'deferred tax liability'
|
| 87 |
-
],
|
| 88 |
-
# Trade payables
|
| 89 |
-
'trade_payables': [
|
| 90 |
-
'trade payables', 'payables', 'creditors', 'sundry creditors',
|
| 91 |
-
'capital expenditure', 'other expenses'
|
| 92 |
-
],
|
| 93 |
-
# Other current liabilities
|
| 94 |
-
'other_current_liabilities': [
|
| 95 |
-
'other current liabilities', 'current maturities', 'outstanding liabilities',
|
| 96 |
-
'statutory dues', 'accrued expenses'
|
| 97 |
-
],
|
| 98 |
-
# Short term provisions
|
| 99 |
-
'short_term_provisions': [
|
| 100 |
-
'short term provisions', 'provisions', 'provision for taxation',
|
| 101 |
-
'tax provision'
|
| 102 |
-
],
|
| 103 |
-
# Fixed assets - Tangible
|
| 104 |
-
'tangible_assets': [
|
| 105 |
-
'tangible assets', 'property plant', 'fixed assets', 'buildings',
|
| 106 |
-
'plant', 'equipment', 'net carrying value'
|
| 107 |
-
],
|
| 108 |
-
# Fixed assets - Intangible
|
| 109 |
-
'intangible_assets': [
|
| 110 |
-
'intangible assets', 'software', 'goodwill', 'intangible'
|
| 111 |
-
],
|
| 112 |
-
# Long term loans and advances
|
| 113 |
-
'long_term_loans_advances': [
|
| 114 |
-
'long term loans', 'security deposits', 'long term advances'
|
| 115 |
-
],
|
| 116 |
-
# Inventories
|
| 117 |
-
'inventories': [
|
| 118 |
-
'inventories', 'stock', 'consumables', 'raw materials'
|
| 119 |
-
],
|
| 120 |
-
# Trade receivables
|
| 121 |
-
'trade_receivables': [
|
| 122 |
-
'trade receivables', 'receivables', 'debtors', 'outstanding',
|
| 123 |
-
'other receivables'
|
| 124 |
-
],
|
| 125 |
-
# Cash and bank
|
| 126 |
-
'cash_bank': [
|
| 127 |
-
'cash and bank', 'cash', 'bank balances', 'current accounts',
|
| 128 |
-
'cash on hand', 'fixed deposits'
|
| 129 |
-
],
|
| 130 |
-
# Short term loans and advances
|
| 131 |
-
'short_term_loans_advances': [
|
| 132 |
-
'short term loans', 'prepaid expenses', 'other advances',
|
| 133 |
-
'advance tax', 'statutory authorities'
|
| 134 |
-
],
|
| 135 |
-
# Other current assets
|
| 136 |
-
'other_current_assets': [
|
| 137 |
-
'other current assets', 'accrued income', 'interest accrued'
|
| 138 |
-
]
|
| 139 |
-
}
|
| 140 |
|
| 141 |
def safe_float(self, value: Any) -> float:
|
| 142 |
"""Convert various value formats to float."""
|
|
@@ -146,7 +84,7 @@ class EnhancedBalanceSheetGenerator:
|
|
| 146 |
# Handle strings
|
| 147 |
if isinstance(value, str):
|
| 148 |
# Remove currency symbols and brackets
|
| 149 |
-
cleaned = re.sub(r'[
|
| 150 |
# Handle negative values in brackets
|
| 151 |
if '(' in str(value) and ')' in str(value):
|
| 152 |
cleaned = '-' + cleaned.replace('(', '').replace(')', '')
|
|
@@ -181,25 +119,22 @@ class EnhancedBalanceSheetGenerator:
|
|
| 181 |
return val, 0.0 # Assume it's 2024 value, 2023 is 0
|
| 182 |
|
| 183 |
def call_ai_for_analysis(self, data_summary: str) -> Dict[str, Any]:
|
| 184 |
-
"""Use AI to analyze and extract balance sheet data"""
|
|
|
|
|
|
|
| 185 |
|
| 186 |
prompt = f"""
|
| 187 |
You are a financial analyst. Extract balance sheet data from the following JSON data and create a properly structured balance sheet.
|
| 188 |
|
| 189 |
CRITICAL REQUIREMENTS:
|
| 190 |
1. Extract ALL line items with their 2024 and 2023 values
|
| 191 |
-
2.
|
| 192 |
-
3.
|
| 193 |
-
4.
|
|
|
|
| 194 |
|
| 195 |
-
Expected Balance Sheet Structure:
|
| 196 |
-
|
| 197 |
-
- Shareholders' funds (Share capital, Reserves and surplus)
|
| 198 |
-
- Non-Current liabilities (Long term borrowings, Deferred tax liability)
|
| 199 |
-
- Current liabilities (Trade payables, Other current liabilities, Short term provisions)
|
| 200 |
-
- ASSETS
|
| 201 |
-
- Non-current assets (Fixed assets - Tangible/Intangible, Long term loans and advances)
|
| 202 |
-
- Current assets (Inventories, Trade receivables, Cash and bank balances, Short-term loans and advances, Other current assets)
|
| 203 |
|
| 204 |
Data to analyze:
|
| 205 |
{data_summary}
|
|
@@ -222,14 +157,6 @@ Return ONLY this JSON format:
|
|
| 222 |
"note": "3",
|
| 223 |
"value_2024": 3152.39,
|
| 224 |
"value_2023": 2642.87
|
| 225 |
-
}},
|
| 226 |
-
{{
|
| 227 |
-
"category": "Non-Current liabilities",
|
| 228 |
-
"subcategory": "",
|
| 229 |
-
"name": "Long term borrowings",
|
| 230 |
-
"note": "4",
|
| 231 |
-
"value_2024": 914.46,
|
| 232 |
-
"value_2023": 321.36
|
| 233 |
}}
|
| 234 |
],
|
| 235 |
"totals": {{
|
|
@@ -268,294 +195,189 @@ Return ONLY this JSON format:
|
|
| 268 |
return {"balance_sheet_items": [], "totals": {}}
|
| 269 |
|
| 270 |
def extract_from_json_structure(self, json_data: Dict[str, Any]) -> List[Dict[str, Any]]:
|
| 271 |
-
"""
|
| 272 |
items = []
|
| 273 |
|
| 274 |
company_data = json_data.get("company_financial_data", {})
|
| 275 |
|
| 276 |
-
#
|
| 277 |
-
|
| 278 |
-
|
| 279 |
-
|
| 280 |
-
|
| 281 |
-
|
| 282 |
-
|
| 283 |
-
|
| 284 |
-
|
| 285 |
-
|
| 286 |
-
|
| 287 |
-
|
| 288 |
-
|
| 289 |
-
|
| 290 |
-
|
| 291 |
-
|
| 292 |
-
|
| 293 |
-
|
| 294 |
-
|
| 295 |
-
|
| 296 |
-
|
| 297 |
-
|
| 298 |
-
|
| 299 |
-
|
| 300 |
-
|
| 301 |
-
|
| 302 |
-
|
| 303 |
-
|
| 304 |
-
|
| 305 |
-
|
| 306 |
-
|
| 307 |
-
|
| 308 |
-
|
| 309 |
-
|
| 310 |
-
|
| 311 |
-
|
| 312 |
-
|
| 313 |
-
|
| 314 |
-
|
| 315 |
-
|
| 316 |
-
|
| 317 |
-
|
| 318 |
-
"
|
| 319 |
-
|
| 320 |
-
|
| 321 |
-
|
| 322 |
-
|
| 323 |
-
|
| 324 |
-
|
| 325 |
-
|
| 326 |
-
|
| 327 |
-
|
| 328 |
-
|
| 329 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 330 |
items.append({
|
| 331 |
-
"category":
|
| 332 |
-
"
|
| 333 |
-
"
|
|
|
|
| 334 |
"value_2024": val_2024,
|
| 335 |
"value_2023": val_2023
|
| 336 |
})
|
| 337 |
-
|
| 338 |
-
# Extract Current Liabilities
|
| 339 |
-
current_liabilities = company_data.get("current_liabilities", {})
|
| 340 |
-
|
| 341 |
-
# Trade Payables
|
| 342 |
-
trade_payables = current_liabilities.get("6. Trade Payables", {})
|
| 343 |
-
tp_2024 = tp_2023 = 0
|
| 344 |
-
for key, value in trade_payables.items():
|
| 345 |
-
if key not in ["_metadata", "Particulars", "Disputed dues"] and value is not None:
|
| 346 |
-
val_2024, val_2023 = self.get_value_flexible(value)
|
| 347 |
-
tp_2024 += val_2024
|
| 348 |
-
tp_2023 += val_2023
|
| 349 |
-
|
| 350 |
-
if tp_2024 or tp_2023:
|
| 351 |
-
items.append({
|
| 352 |
-
"category": "Current liabilities",
|
| 353 |
-
"name": "Trade payables",
|
| 354 |
-
"note": "6",
|
| 355 |
-
"value_2024": tp_2024,
|
| 356 |
-
"value_2023": tp_2023
|
| 357 |
-
})
|
| 358 |
-
|
| 359 |
-
# Other Current Liabilities
|
| 360 |
-
other_cl = current_liabilities.get("7. Other Current Liabilities", {})
|
| 361 |
-
ocl_2024 = ocl_2023 = 0
|
| 362 |
-
for key, value in other_cl.items():
|
| 363 |
-
if key != "_metadata" and value is not None:
|
| 364 |
-
val_2024, val_2023 = self.get_value_flexible(value)
|
| 365 |
-
ocl_2024 += val_2024
|
| 366 |
-
ocl_2023 += val_2023
|
| 367 |
-
|
| 368 |
-
if ocl_2024 or ocl_2023:
|
| 369 |
-
items.append({
|
| 370 |
-
"category": "Current liabilities",
|
| 371 |
-
"name": "Other current liabilities",
|
| 372 |
-
"note": "7",
|
| 373 |
-
"value_2024": ocl_2024,
|
| 374 |
-
"value_2023": ocl_2023
|
| 375 |
-
})
|
| 376 |
-
|
| 377 |
-
# Short Term Provisions
|
| 378 |
-
provisions = current_liabilities.get("8. Short Term Provisions", {})
|
| 379 |
-
prov_2024 = prov_2023 = 0
|
| 380 |
-
for key, value in provisions.items():
|
| 381 |
-
if key != "_metadata" and value is not None:
|
| 382 |
-
val_2024, val_2023 = self.get_value_flexible(value)
|
| 383 |
-
prov_2024 += val_2024
|
| 384 |
-
prov_2023 += val_2023
|
| 385 |
-
|
| 386 |
-
if prov_2024 or prov_2023:
|
| 387 |
-
items.append({
|
| 388 |
-
"category": "Current liabilities",
|
| 389 |
-
"name": "Short term provisions",
|
| 390 |
-
"note": "8",
|
| 391 |
-
"value_2024": prov_2024,
|
| 392 |
-
"value_2023": prov_2023
|
| 393 |
-
})
|
| 394 |
-
|
| 395 |
-
# Extract Fixed Assets
|
| 396 |
-
fixed_assets = company_data.get("fixed_assets", {})
|
| 397 |
-
|
| 398 |
-
# Tangible Assets
|
| 399 |
-
tangible = fixed_assets.get("tangible_assets", {}).get("", {})
|
| 400 |
-
if tangible:
|
| 401 |
-
net_carrying = tangible.get("net_carrying_value", {})
|
| 402 |
-
if net_carrying:
|
| 403 |
-
# Handle both dict and list formats for net carrying value
|
| 404 |
-
if isinstance(net_carrying, dict):
|
| 405 |
-
val_2024 = self.safe_float(net_carrying.get("closing", 0))
|
| 406 |
-
val_2023 = self.safe_float(net_carrying.get("opening", 0))
|
| 407 |
-
else:
|
| 408 |
-
val_2024, val_2023 = self.get_value_flexible(net_carrying)
|
| 409 |
-
|
| 410 |
-
if val_2024 or val_2023:
|
| 411 |
-
items.append({
|
| 412 |
-
"category": "Non-current assets",
|
| 413 |
-
"subcategory": "Fixed assets",
|
| 414 |
-
"name": "Tangible assets",
|
| 415 |
-
"note": "9",
|
| 416 |
-
"value_2024": val_2024,
|
| 417 |
-
"value_2023": val_2023
|
| 418 |
-
})
|
| 419 |
-
|
| 420 |
-
# Intangible Assets
|
| 421 |
-
intangible = fixed_assets.get("intangible_assets", {}).get("", {})
|
| 422 |
-
if intangible:
|
| 423 |
-
net_carrying = intangible.get("net_carrying_value", {})
|
| 424 |
-
if net_carrying:
|
| 425 |
-
# Handle both dict and list formats for net carrying value
|
| 426 |
-
if isinstance(net_carrying, dict):
|
| 427 |
-
val_2024 = self.safe_float(net_carrying.get("closing", 0))
|
| 428 |
-
val_2023 = self.safe_float(net_carrying.get("opening", 0))
|
| 429 |
-
else:
|
| 430 |
-
val_2024, val_2023 = self.get_value_flexible(net_carrying)
|
| 431 |
-
|
| 432 |
-
if val_2024 or val_2023:
|
| 433 |
-
items.append({
|
| 434 |
-
"category": "Non-current assets",
|
| 435 |
-
"subcategory": "Fixed assets",
|
| 436 |
-
"name": "Intangible assets",
|
| 437 |
-
"note": "9",
|
| 438 |
-
"value_2024": val_2024,
|
| 439 |
-
"value_2023": val_2023
|
| 440 |
-
})
|
| 441 |
-
|
| 442 |
-
# Long Term Loans and Advances
|
| 443 |
-
lt_loans = company_data.get("loans_and_advances", {}).get("10. Long Term Loans and advances", {})
|
| 444 |
-
lt_2024 = lt_2023 = 0
|
| 445 |
-
for key, value in lt_loans.items():
|
| 446 |
-
if key != "_metadata" and value is not None:
|
| 447 |
-
val_2024, val_2023 = self.get_value_flexible(value)
|
| 448 |
-
lt_2024 += val_2024
|
| 449 |
-
lt_2023 += val_2023
|
| 450 |
-
|
| 451 |
-
if lt_2024 or lt_2023:
|
| 452 |
-
items.append({
|
| 453 |
-
"category": "Non-current assets",
|
| 454 |
-
"name": "Long Term Loans and Advances",
|
| 455 |
-
"note": "10",
|
| 456 |
-
"value_2024": lt_2024,
|
| 457 |
-
"value_2023": lt_2023
|
| 458 |
-
})
|
| 459 |
-
|
| 460 |
-
# Extract Current Assets
|
| 461 |
-
current_assets = company_data.get("current_assets", {})
|
| 462 |
-
|
| 463 |
-
# Inventories
|
| 464 |
-
inventories = current_assets.get("11. Inventories", {})
|
| 465 |
-
inv_2024 = inv_2023 = 0
|
| 466 |
-
for key, value in inventories.items():
|
| 467 |
-
if key != "_metadata" and value is not None:
|
| 468 |
-
val_2024, val_2023 = self.get_value_flexible(value)
|
| 469 |
-
inv_2024 += val_2024
|
| 470 |
-
inv_2023 += val_2023
|
| 471 |
-
|
| 472 |
-
if inv_2024 or inv_2023:
|
| 473 |
-
items.append({
|
| 474 |
-
"category": "Current assets",
|
| 475 |
-
"name": "Inventories",
|
| 476 |
-
"note": "11",
|
| 477 |
-
"value_2024": inv_2024,
|
| 478 |
-
"value_2023": inv_2023
|
| 479 |
-
})
|
| 480 |
-
|
| 481 |
-
# Trade Receivables
|
| 482 |
-
trade_recv = current_assets.get("12. Trade receivables", {})
|
| 483 |
-
tr_2024 = tr_2023 = 0
|
| 484 |
-
for key, value in trade_recv.items():
|
| 485 |
-
if key not in ["_metadata", "Particulars", "trade_receivables_aging"] and value is not None:
|
| 486 |
-
val_2024, val_2023 = self.get_value_flexible(value)
|
| 487 |
-
tr_2024 += val_2024
|
| 488 |
-
tr_2023 += val_2023
|
| 489 |
-
|
| 490 |
-
if tr_2024 or tr_2023:
|
| 491 |
-
items.append({
|
| 492 |
-
"category": "Current assets",
|
| 493 |
-
"name": "Trade receivables",
|
| 494 |
-
"note": "12",
|
| 495 |
-
"value_2024": tr_2024,
|
| 496 |
-
"value_2023": tr_2023
|
| 497 |
-
})
|
| 498 |
-
|
| 499 |
-
# Cash and Bank Balances
|
| 500 |
-
cash_bank = current_assets.get("13. Cash and bank balances", {})
|
| 501 |
-
cb_2024 = cb_2023 = 0
|
| 502 |
-
for key, value in cash_bank.items():
|
| 503 |
-
if key != "_metadata" and value is not None:
|
| 504 |
-
val_2024, val_2023 = self.get_value_flexible(value)
|
| 505 |
-
cb_2024 += val_2024
|
| 506 |
-
cb_2023 += val_2023
|
| 507 |
-
|
| 508 |
-
if cb_2024 or cb_2023:
|
| 509 |
-
items.append({
|
| 510 |
-
"category": "Current assets",
|
| 511 |
-
"name": "Cash and bank balances",
|
| 512 |
-
"note": "13",
|
| 513 |
-
"value_2024": cb_2024,
|
| 514 |
-
"value_2023": cb_2023
|
| 515 |
-
})
|
| 516 |
-
|
| 517 |
-
# Short-term Loans and Advances
|
| 518 |
-
st_loans = company_data.get("loans_and_advances", {}).get("14. Short Term Loans and Advances", {})
|
| 519 |
-
st_2024 = st_2023 = 0
|
| 520 |
-
for key, value in st_loans.items():
|
| 521 |
-
if key != "_metadata" and value is not None:
|
| 522 |
-
val_2024, val_2023 = self.get_value_flexible(value)
|
| 523 |
-
st_2024 += val_2024
|
| 524 |
-
st_2023 += val_2023
|
| 525 |
-
|
| 526 |
-
if st_2024 or st_2023:
|
| 527 |
-
items.append({
|
| 528 |
-
"category": "Current assets",
|
| 529 |
-
"name": "Short-term loans and advances",
|
| 530 |
-
"note": "14",
|
| 531 |
-
"value_2024": st_2024,
|
| 532 |
-
"value_2023": st_2023
|
| 533 |
-
})
|
| 534 |
-
|
| 535 |
-
# Other Current Assets
|
| 536 |
-
other_ca = company_data.get("other_data", {}).get("15. Other Current Assets", {})
|
| 537 |
-
oca_2024 = oca_2023 = 0
|
| 538 |
-
for key, value in other_ca.items():
|
| 539 |
-
if key != "_metadata" and value is not None:
|
| 540 |
-
val_2024, val_2023 = self.get_value_flexible(value)
|
| 541 |
-
oca_2024 += val_2024
|
| 542 |
-
oca_2023 += val_2023
|
| 543 |
-
|
| 544 |
-
if oca_2024 or oca_2023:
|
| 545 |
-
items.append({
|
| 546 |
-
"category": "Current assets",
|
| 547 |
-
"name": "Other current assets",
|
| 548 |
-
"note": "15",
|
| 549 |
-
"value_2024": oca_2024,
|
| 550 |
-
"value_2023": oca_2023
|
| 551 |
-
})
|
| 552 |
-
|
| 553 |
return items
|
| 554 |
|
| 555 |
def calculate_totals(self, items: List[Dict[str, Any]]) -> BalanceSheetTotals:
|
| 556 |
-
"""Calculate section totals and verify balance"""
|
| 557 |
-
totals = {}
|
| 558 |
-
|
| 559 |
# Group by categories
|
| 560 |
categories = {}
|
| 561 |
for item in items:
|
|
@@ -565,10 +387,13 @@ Return ONLY this JSON format:
|
|
| 565 |
categories[cat]["2024"] += item["value_2024"]
|
| 566 |
categories[cat]["2023"] += item["value_2023"]
|
| 567 |
|
| 568 |
-
# Calculate major totals
|
| 569 |
shareholders_funds_2024 = categories.get("Shareholders' funds", {}).get("2024", 0)
|
| 570 |
shareholders_funds_2023 = categories.get("Shareholders' funds", {}).get("2023", 0)
|
| 571 |
|
|
|
|
|
|
|
|
|
|
| 572 |
non_current_liab_2024 = categories.get("Non-Current liabilities", {}).get("2024", 0)
|
| 573 |
non_current_liab_2023 = categories.get("Non-Current liabilities", {}).get("2023", 0)
|
| 574 |
|
|
@@ -581,8 +406,8 @@ Return ONLY this JSON format:
|
|
| 581 |
current_assets_2024 = categories.get("Current assets", {}).get("2024", 0)
|
| 582 |
current_assets_2023 = categories.get("Current assets", {}).get("2023", 0)
|
| 583 |
|
| 584 |
-
total_equity_liab_2024 = shareholders_funds_2024 + non_current_liab_2024 + current_liab_2024
|
| 585 |
-
total_equity_liab_2023 = shareholders_funds_2023 + non_current_liab_2023 + current_liab_2023
|
| 586 |
|
| 587 |
total_assets_2024 = non_current_assets_2024 + current_assets_2024
|
| 588 |
total_assets_2023 = non_current_assets_2023 + current_assets_2023
|
|
@@ -590,6 +415,8 @@ Return ONLY this JSON format:
|
|
| 590 |
return BalanceSheetTotals(
|
| 591 |
shareholders_funds_2024=shareholders_funds_2024,
|
| 592 |
shareholders_funds_2023=shareholders_funds_2023,
|
|
|
|
|
|
|
| 593 |
non_current_liabilities_2024=non_current_liab_2024,
|
| 594 |
non_current_liabilities_2023=non_current_liab_2023,
|
| 595 |
current_liabilities_2024=current_liab_2024,
|
|
@@ -607,7 +434,7 @@ Return ONLY this JSON format:
|
|
| 607 |
)
|
| 608 |
|
| 609 |
def generate_balance_sheet_excel(self, items: List[Dict[str, Any]], totals: BalanceSheetTotals, output_dir: str = "output") -> str:
|
| 610 |
-
"""Generate formatted Excel balance sheet"""
|
| 611 |
os.makedirs(output_dir, exist_ok=True)
|
| 612 |
|
| 613 |
wb = Workbook()
|
|
@@ -615,10 +442,10 @@ Return ONLY this JSON format:
|
|
| 615 |
ws.title = "Balance Sheet"
|
| 616 |
|
| 617 |
# Set column widths
|
| 618 |
-
ws.column_dimensions["A"].width =
|
| 619 |
-
ws.column_dimensions["B"].width =
|
| 620 |
-
ws.column_dimensions["C"].width =
|
| 621 |
-
ws.column_dimensions["D"].width =
|
| 622 |
|
| 623 |
# Styles
|
| 624 |
bold_font = Font(bold=True)
|
|
@@ -660,39 +487,68 @@ Return ONLY this JSON format:
|
|
| 660 |
|
| 661 |
row += 1
|
| 662 |
|
| 663 |
-
# Header
|
| 664 |
-
|
|
|
|
| 665 |
add_row("", "", 0, 0)
|
| 666 |
-
add_row("
|
| 667 |
-
add_row("", "Notes", "March 31, 2024", "March 31, 2023", True)
|
| 668 |
add_row("", "", 0, 0)
|
| 669 |
|
| 670 |
-
#
|
| 671 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 672 |
|
| 673 |
-
# Shareholders' funds
|
| 674 |
-
add_row("Shareholders' funds", "", 0, 0, True)
|
| 675 |
shareholders_items = [item for item in items if item["category"] == "Shareholders' funds"]
|
| 676 |
for item in shareholders_items:
|
| 677 |
-
add_row(item[
|
| 678 |
-
|
| 679 |
add_row("", "", totals.shareholders_funds_2024, totals.shareholders_funds_2023, True)
|
| 680 |
add_row("", "", 0, 0)
|
| 681 |
|
| 682 |
-
#
|
| 683 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 684 |
non_current_liab_items = [item for item in items if item["category"] == "Non-Current liabilities"]
|
| 685 |
for item in non_current_liab_items:
|
| 686 |
-
add_row(item[
|
| 687 |
-
|
| 688 |
add_row("", "", totals.non_current_liabilities_2024, totals.non_current_liabilities_2023, True)
|
| 689 |
add_row("", "", 0, 0)
|
| 690 |
|
| 691 |
-
# Current liabilities
|
| 692 |
-
add_row("Current liabilities", "", 0, 0, True)
|
| 693 |
current_liab_items = [item for item in items if item["category"] == "Current liabilities"]
|
| 694 |
-
|
| 695 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 696 |
|
| 697 |
add_row("", "", totals.current_liabilities_2024, totals.current_liabilities_2023, True)
|
| 698 |
add_row("", "", 0, 0)
|
|
@@ -701,36 +557,38 @@ Return ONLY this JSON format:
|
|
| 701 |
add_row("TOTAL", "", totals.total_equity_liabilities_2024, totals.total_equity_liabilities_2023, True, 0, True)
|
| 702 |
add_row("", "", 0, 0)
|
| 703 |
|
| 704 |
-
# ASSETS
|
| 705 |
-
add_row("ASSETS", "", 0, 0, True)
|
|
|
|
| 706 |
|
| 707 |
# Non-current assets
|
| 708 |
add_row("Non-current assets", "", 0, 0, True)
|
| 709 |
|
| 710 |
-
#
|
| 711 |
-
|
| 712 |
-
if
|
| 713 |
-
add_row("
|
| 714 |
-
|
| 715 |
-
for item in
|
| 716 |
-
add_row(item[
|
| 717 |
-
|
| 718 |
-
|
| 719 |
-
add_row("", "",
|
|
|
|
| 720 |
|
| 721 |
# Other non-current assets
|
| 722 |
-
other_non_current = [item for item in items if item["category"] == "Non-current assets" and item.get("subcategory") != "
|
| 723 |
for item in other_non_current:
|
| 724 |
-
add_row(item[
|
| 725 |
|
| 726 |
add_row("", "", totals.non_current_assets_2024, totals.non_current_assets_2023, True)
|
| 727 |
add_row("", "", 0, 0)
|
| 728 |
|
| 729 |
-
# Current assets
|
| 730 |
-
add_row("Current assets", "", 0, 0, True)
|
| 731 |
current_asset_items = [item for item in items if item["category"] == "Current assets"]
|
| 732 |
for item in current_asset_items:
|
| 733 |
-
add_row(item[
|
| 734 |
|
| 735 |
add_row("", "", totals.current_assets_2024, totals.current_assets_2023, True)
|
| 736 |
add_row("", "", 0, 0)
|
|
@@ -744,9 +602,9 @@ Return ONLY this JSON format:
|
|
| 744 |
balance_2023 = totals.balance_difference_2023
|
| 745 |
|
| 746 |
if balance_2024 < 0.01 and balance_2023 < 0.01:
|
| 747 |
-
add_row(" Balance Sheet is BALANCED", "", 0, 0, True)
|
| 748 |
else:
|
| 749 |
-
add_row(f" Balance Difference: {balance_2024:.2f} | {balance_2023:.2f}", "", 0, 0, True)
|
| 750 |
|
| 751 |
# Save file
|
| 752 |
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
@@ -765,14 +623,14 @@ Return ONLY this JSON format:
|
|
| 765 |
with open(input_file, 'r', encoding='utf-8') as f:
|
| 766 |
json_data = json.load(f)
|
| 767 |
|
| 768 |
-
logger.info("Extracting data
|
| 769 |
|
| 770 |
-
# Method 1: Direct extraction
|
| 771 |
items = self.extract_from_json_structure(json_data)
|
| 772 |
-
logger.info(f"Extracted {len(items)} items
|
| 773 |
|
| 774 |
# Method 2: AI-assisted extraction if needed
|
| 775 |
-
if len(items
|
| 776 |
logger.info("Using AI for additional extraction...")
|
| 777 |
|
| 778 |
# Create summary for AI
|
|
@@ -782,46 +640,52 @@ Return ONLY this JSON format:
|
|
| 782 |
ai_items = ai_result.get("balance_sheet_items", [])
|
| 783 |
logger.info(f"AI extracted {len(ai_items)} additional items")
|
| 784 |
|
| 785 |
-
# Merge items (
|
| 786 |
-
|
| 787 |
-
|
| 788 |
-
|
| 789 |
-
|
| 790 |
-
|
| 791 |
-
|
| 792 |
-
|
| 793 |
-
return None
|
| 794 |
|
| 795 |
# Calculate totals
|
| 796 |
totals = self.calculate_totals(items)
|
| 797 |
|
| 798 |
# Display summary
|
| 799 |
logger.info(f"\n BALANCE SHEET SUMMARY:")
|
| 800 |
-
logger.info(f"
|
| 801 |
-
logger.info(f"
|
| 802 |
-
logger.info(f"
|
| 803 |
-
logger.info(f"
|
| 804 |
-
logger.info(f"
|
| 805 |
-
logger.info(f"
|
| 806 |
-
logger.info(f"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 807 |
|
| 808 |
# Check if balanced
|
| 809 |
is_balanced_2024 = totals.balance_difference_2024 < 0.01
|
| 810 |
is_balanced_2023 = totals.balance_difference_2023 < 0.01
|
| 811 |
|
| 812 |
if is_balanced_2024 and is_balanced_2023:
|
| 813 |
-
logger.info("Balance Sheet is PERFECTLY BALANCED!")
|
| 814 |
else:
|
| 815 |
-
logger.warning("Balance Sheet has differences - may need adjustment")
|
| 816 |
|
| 817 |
-
# Generate Excel
|
| 818 |
output_file = self.generate_balance_sheet_excel(items, totals, output_dir)
|
| 819 |
|
| 820 |
-
logger.info(f"SUCCESS: Generated {output_file}")
|
| 821 |
return output_file
|
| 822 |
|
| 823 |
except Exception as e:
|
| 824 |
-
logger.error(f"Error processing file: {e}", exc_info=True)
|
| 825 |
return None
|
| 826 |
|
| 827 |
def main() -> None:
|
|
@@ -830,7 +694,7 @@ def main() -> None:
|
|
| 830 |
Accepts input file and output directory from command-line arguments or environment variables.
|
| 831 |
Handles errors gracefully and logs all major events.
|
| 832 |
"""
|
| 833 |
-
logger.info("ENHANCED BALANCE SHEET GENERATOR
|
| 834 |
import sys
|
| 835 |
api_key = settings.api_key
|
| 836 |
input_file = settings.input_file
|
|
@@ -840,25 +704,26 @@ def main() -> None:
|
|
| 840 |
if len(sys.argv) > 2:
|
| 841 |
output_dir = sys.argv[2]
|
| 842 |
if not api_key:
|
| 843 |
-
logger.error("Missing OPENROUTER_API_KEY environment variable. Please set your OpenRouter API key in the .env file.")
|
| 844 |
return
|
| 845 |
if not os.path.exists(input_file):
|
| 846 |
-
logger.error(f"Input file not found: {input_file}. Please ensure your JSON data file exists.")
|
| 847 |
return
|
|
|
|
| 848 |
generator = EnhancedBalanceSheetGenerator(api_key)
|
| 849 |
try:
|
| 850 |
result = generator.process(input_file, output_dir)
|
| 851 |
if result:
|
| 852 |
abs_path = os.path.abspath(result)
|
| 853 |
if os.path.exists(abs_path):
|
| 854 |
-
logger.info(f"COMPLETED SUCCESSFULLY! Output file: {abs_path}")
|
| 855 |
print(f"Output file: {abs_path}") # For API subprocess parsing
|
| 856 |
else:
|
| 857 |
-
logger.error(f"PROCESSING FAILED. Output file not created: {abs_path}")
|
| 858 |
else:
|
| 859 |
-
logger.error("PROCESSING FAILED. Please check the error messages above and try again.")
|
| 860 |
except Exception as e:
|
| 861 |
-
logger.error(f"Fatal error: {e}", exc_info=True)
|
| 862 |
|
| 863 |
if __name__ == "__main__":
|
| 864 |
main()
|
|
|
|
| 15 |
from pydantic import BaseModel, Field, ValidationError
|
| 16 |
from pydantic_settings import BaseSettings
|
| 17 |
|
| 18 |
+
# Import the template handler
|
| 19 |
+
from balance_sheet_template_handler import BalanceSheetTemplate, STANDARD_NOTES_MAPPING
|
| 20 |
+
|
| 21 |
load_dotenv()
|
| 22 |
|
| 23 |
# Configure logging
|
|
|
|
| 47 |
class BalanceSheetTotals(BaseModel):
|
| 48 |
shareholders_funds_2024: float = 0.0
|
| 49 |
shareholders_funds_2023: float = 0.0
|
| 50 |
+
share_application_money_2024: float = 0.0
|
| 51 |
+
share_application_money_2023: float = 0.0
|
| 52 |
non_current_liabilities_2024: float = 0.0
|
| 53 |
non_current_liabilities_2023: float = 0.0
|
| 54 |
current_liabilities_2024: float = 0.0
|
|
|
|
| 69 |
self.api_key = api_key
|
| 70 |
self.base_url = "https://openrouter.ai/api/v1/chat/completions"
|
| 71 |
|
| 72 |
+
# Initialize template
|
| 73 |
+
self.template = BalanceSheetTemplate()
|
| 74 |
+
self.field_mappings = self.template.get_field_mappings()
|
| 75 |
+
self.formatting_rules = self.template.get_formatting_rules()
|
| 76 |
+
|
| 77 |
+
logger.info(f"Loaded template with {len(self.template.get_template_structure())} items")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 78 |
|
| 79 |
def safe_float(self, value: Any) -> float:
|
| 80 |
"""Convert various value formats to float."""
|
|
|
|
| 84 |
# Handle strings
|
| 85 |
if isinstance(value, str):
|
| 86 |
# Remove currency symbols and brackets
|
| 87 |
+
cleaned = re.sub(r'[₹,Rs\.\s\(\)]', '', value)
|
| 88 |
# Handle negative values in brackets
|
| 89 |
if '(' in str(value) and ')' in str(value):
|
| 90 |
cleaned = '-' + cleaned.replace('(', '').replace(')', '')
|
|
|
|
| 119 |
return val, 0.0 # Assume it's 2024 value, 2023 is 0
|
| 120 |
|
| 121 |
def call_ai_for_analysis(self, data_summary: str) -> Dict[str, Any]:
|
| 122 |
+
"""Use AI to analyze and extract balance sheet data using the template structure"""
|
| 123 |
+
|
| 124 |
+
template_items = self.template.get_template_structure()
|
| 125 |
|
| 126 |
prompt = f"""
|
| 127 |
You are a financial analyst. Extract balance sheet data from the following JSON data and create a properly structured balance sheet.
|
| 128 |
|
| 129 |
CRITICAL REQUIREMENTS:
|
| 130 |
1. Extract ALL line items with their 2024 and 2023 values
|
| 131 |
+
2. Use the EXACT template structure provided below
|
| 132 |
+
3. Calculate missing totals where needed
|
| 133 |
+
4. Ensure the balance sheet balances (Assets = Equity + Liabilities)
|
| 134 |
+
5. Return ONLY valid JSON in the exact format specified below
|
| 135 |
|
| 136 |
+
Expected Balance Sheet Structure (use this EXACT structure):
|
| 137 |
+
{json.dumps(template_items, indent=2)}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 138 |
|
| 139 |
Data to analyze:
|
| 140 |
{data_summary}
|
|
|
|
| 157 |
"note": "3",
|
| 158 |
"value_2024": 3152.39,
|
| 159 |
"value_2023": 2642.87
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 160 |
}}
|
| 161 |
],
|
| 162 |
"totals": {{
|
|
|
|
| 195 |
return {"balance_sheet_items": [], "totals": {}}
|
| 196 |
|
| 197 |
def extract_from_json_structure(self, json_data: Dict[str, Any]) -> List[Dict[str, Any]]:
|
| 198 |
+
"""Extract data using template structure with flexible JSON mapping"""
|
| 199 |
items = []
|
| 200 |
|
| 201 |
company_data = json_data.get("company_financial_data", {})
|
| 202 |
|
| 203 |
+
# Get template structure to guide extraction
|
| 204 |
+
template_items = self.template.get_template_structure()
|
| 205 |
+
|
| 206 |
+
# Extract based on template structure
|
| 207 |
+
for template_item in template_items:
|
| 208 |
+
item_name = template_item["name"]
|
| 209 |
+
category = template_item["category"]
|
| 210 |
+
subcategory = template_item.get("subcategory", "")
|
| 211 |
+
note = template_item["note"]
|
| 212 |
+
|
| 213 |
+
val_2024 = val_2023 = 0.0
|
| 214 |
+
|
| 215 |
+
# Map template items to JSON data extraction logic
|
| 216 |
+
if item_name == "Share capital":
|
| 217 |
+
share_capital = company_data.get("share_capital", {})
|
| 218 |
+
total_share_capital = share_capital.get("Total issued, subscribed and fully paid-up share capital", {})
|
| 219 |
+
if total_share_capital:
|
| 220 |
+
val_2024, val_2023 = self.get_value_flexible(total_share_capital)
|
| 221 |
+
|
| 222 |
+
elif item_name == "Reserves and surplus":
|
| 223 |
+
reserves = company_data.get("reserves_and_surplus", {})
|
| 224 |
+
closing_balance = reserves.get("Balance, at the end of the year", {})
|
| 225 |
+
if closing_balance:
|
| 226 |
+
val_2024, val_2023 = self.get_value_flexible(closing_balance)
|
| 227 |
+
|
| 228 |
+
elif item_name == "Money received against share warrants":
|
| 229 |
+
# Try to find this in the data - might be in share capital or other sections
|
| 230 |
+
pass # Will be 0 if not found
|
| 231 |
+
|
| 232 |
+
elif item_name == "Share application money pending allotment":
|
| 233 |
+
# Try to find this in the data
|
| 234 |
+
pass # Will be 0 if not found
|
| 235 |
+
|
| 236 |
+
elif item_name == "Long-term borrowings":
|
| 237 |
+
borrowings = company_data.get("borrowings", {}).get("4. Long-Term Borrowings", {})
|
| 238 |
+
for key, value in borrowings.items():
|
| 239 |
+
if key != "_metadata" and value is not None:
|
| 240 |
+
v24, v23 = self.get_value_flexible(value)
|
| 241 |
+
val_2024 += v24
|
| 242 |
+
val_2023 += v23
|
| 243 |
+
|
| 244 |
+
elif item_name == "Deferred tax liabilities (Net)":
|
| 245 |
+
deferred_tax = company_data.get("other_data", {}).get("5. Deferred Tax Liability / (Asset)", {})
|
| 246 |
+
if deferred_tax:
|
| 247 |
+
dtl = deferred_tax.get("Deferred tax liability", {})
|
| 248 |
+
val_2024, val_2023 = self.get_value_flexible(dtl)
|
| 249 |
+
|
| 250 |
+
elif item_name == "Short-term borrowings":
|
| 251 |
+
# Look for short term borrowings in current liabilities
|
| 252 |
+
current_liabilities = company_data.get("current_liabilities", {})
|
| 253 |
+
# This might be in various sections, keep as 0 for now
|
| 254 |
+
pass
|
| 255 |
+
|
| 256 |
+
elif "total outstanding dues of micro enterprises" in item_name:
|
| 257 |
+
# Extract from trade payables - micro enterprises
|
| 258 |
+
current_liabilities = company_data.get("current_liabilities", {})
|
| 259 |
+
trade_payables = current_liabilities.get("6. Trade Payables", {})
|
| 260 |
+
# Look for micro enterprises specific data
|
| 261 |
+
pass
|
| 262 |
+
|
| 263 |
+
elif "total outstanding dues of creditors other than micro" in item_name:
|
| 264 |
+
# Extract from trade payables - others
|
| 265 |
+
current_liabilities = company_data.get("current_liabilities", {})
|
| 266 |
+
trade_payables = current_liabilities.get("6. Trade Payables", {})
|
| 267 |
+
for key, value in trade_payables.items():
|
| 268 |
+
if key not in ["_metadata", "Particulars", "Disputed dues"] and value is not None:
|
| 269 |
+
v24, v23 = self.get_value_flexible(value)
|
| 270 |
+
val_2024 += v24
|
| 271 |
+
val_2023 += v23
|
| 272 |
+
|
| 273 |
+
elif item_name == "Other current liabilities":
|
| 274 |
+
current_liabilities = company_data.get("current_liabilities", {})
|
| 275 |
+
other_cl = current_liabilities.get("7. Other Current Liabilities", {})
|
| 276 |
+
for key, value in other_cl.items():
|
| 277 |
+
if key != "_metadata" and value is not None:
|
| 278 |
+
v24, v23 = self.get_value_flexible(value)
|
| 279 |
+
val_2024 += v24
|
| 280 |
+
val_2023 += v23
|
| 281 |
+
|
| 282 |
+
elif item_name == "Short-term provisions":
|
| 283 |
+
current_liabilities = company_data.get("current_liabilities", {})
|
| 284 |
+
provisions = current_liabilities.get("8. Short Term Provisions", {})
|
| 285 |
+
for key, value in provisions.items():
|
| 286 |
+
if key != "_metadata" and value is not None:
|
| 287 |
+
v24, v23 = self.get_value_flexible(value)
|
| 288 |
+
val_2024 += v24
|
| 289 |
+
val_2023 += v23
|
| 290 |
+
|
| 291 |
+
elif item_name == "Tangible assets":
|
| 292 |
+
fixed_assets = company_data.get("fixed_assets", {})
|
| 293 |
+
tangible = fixed_assets.get("tangible_assets", {}).get("", {})
|
| 294 |
+
if tangible:
|
| 295 |
+
net_carrying = tangible.get("net_carrying_value", {})
|
| 296 |
+
if net_carrying:
|
| 297 |
+
if isinstance(net_carrying, dict):
|
| 298 |
+
val_2024 = self.safe_float(net_carrying.get("closing", 0))
|
| 299 |
+
val_2023 = self.safe_float(net_carrying.get("opening", 0))
|
| 300 |
+
else:
|
| 301 |
+
val_2024, val_2023 = self.get_value_flexible(net_carrying)
|
| 302 |
+
|
| 303 |
+
elif item_name == "Intangible assets":
|
| 304 |
+
fixed_assets = company_data.get("fixed_assets", {})
|
| 305 |
+
intangible = fixed_assets.get("intangible_assets", {}).get("", {})
|
| 306 |
+
if intangible:
|
| 307 |
+
net_carrying = intangible.get("net_carrying_value", {})
|
| 308 |
+
if net_carrying:
|
| 309 |
+
if isinstance(net_carrying, dict):
|
| 310 |
+
val_2024 = self.safe_float(net_carrying.get("closing", 0))
|
| 311 |
+
val_2023 = self.safe_float(net_carrying.get("opening", 0))
|
| 312 |
+
else:
|
| 313 |
+
val_2024, val_2023 = self.get_value_flexible(net_carrying)
|
| 314 |
+
|
| 315 |
+
elif item_name == "Long-term loans and advances":
|
| 316 |
+
lt_loans = company_data.get("loans_and_advances", {}).get("10. Long Term Loans and advances", {})
|
| 317 |
+
for key, value in lt_loans.items():
|
| 318 |
+
if key != "_metadata" and value is not None:
|
| 319 |
+
v24, v23 = self.get_value_flexible(value)
|
| 320 |
+
val_2024 += v24
|
| 321 |
+
val_2023 += v23
|
| 322 |
+
|
| 323 |
+
elif item_name == "Inventories":
|
| 324 |
+
current_assets = company_data.get("current_assets", {})
|
| 325 |
+
inventories = current_assets.get("11. Inventories", {})
|
| 326 |
+
for key, value in inventories.items():
|
| 327 |
+
if key != "_metadata" and value is not None:
|
| 328 |
+
v24, v23 = self.get_value_flexible(value)
|
| 329 |
+
val_2024 += v24
|
| 330 |
+
val_2023 += v23
|
| 331 |
+
|
| 332 |
+
elif item_name == "Trade receivables":
|
| 333 |
+
current_assets = company_data.get("current_assets", {})
|
| 334 |
+
trade_recv = current_assets.get("12. Trade receivables", {})
|
| 335 |
+
for key, value in trade_recv.items():
|
| 336 |
+
if key not in ["_metadata", "Particulars", "trade_receivables_aging"] and value is not None:
|
| 337 |
+
v24, v23 = self.get_value_flexible(value)
|
| 338 |
+
val_2024 += v24
|
| 339 |
+
val_2023 += v23
|
| 340 |
+
|
| 341 |
+
elif item_name == "Cash and cash equivalents":
|
| 342 |
+
current_assets = company_data.get("current_assets", {})
|
| 343 |
+
cash_bank = current_assets.get("13. Cash and bank balances", {})
|
| 344 |
+
for key, value in cash_bank.items():
|
| 345 |
+
if key != "_metadata" and value is not None:
|
| 346 |
+
v24, v23 = self.get_value_flexible(value)
|
| 347 |
+
val_2024 += v24
|
| 348 |
+
val_2023 += v23
|
| 349 |
+
|
| 350 |
+
elif item_name == "Short-term loans and advances":
|
| 351 |
+
st_loans = company_data.get("loans_and_advances", {}).get("14. Short Term Loans and Advances", {})
|
| 352 |
+
for key, value in st_loans.items():
|
| 353 |
+
if key != "_metadata" and value is not None:
|
| 354 |
+
v24, v23 = self.get_value_flexible(value)
|
| 355 |
+
val_2024 += v24
|
| 356 |
+
val_2023 += v23
|
| 357 |
+
|
| 358 |
+
elif item_name == "Other current assets":
|
| 359 |
+
other_ca = company_data.get("other_data", {}).get("15. Other Current Assets", {})
|
| 360 |
+
for key, value in other_ca.items():
|
| 361 |
+
if key != "_metadata" and value is not None:
|
| 362 |
+
v24, v23 = self.get_value_flexible(value)
|
| 363 |
+
val_2024 += v24
|
| 364 |
+
val_2023 += v23
|
| 365 |
+
|
| 366 |
+
# Add item if it has any value or is part of template structure
|
| 367 |
+
if val_2024 != 0 or val_2023 != 0 or True: # Always add template items
|
| 368 |
items.append({
|
| 369 |
+
"category": category,
|
| 370 |
+
"subcategory": subcategory,
|
| 371 |
+
"name": item_name,
|
| 372 |
+
"note": note,
|
| 373 |
"value_2024": val_2024,
|
| 374 |
"value_2023": val_2023
|
| 375 |
})
|
| 376 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 377 |
return items
|
| 378 |
|
| 379 |
def calculate_totals(self, items: List[Dict[str, Any]]) -> BalanceSheetTotals:
|
| 380 |
+
"""Calculate section totals and verify balance using template categories"""
|
|
|
|
|
|
|
| 381 |
# Group by categories
|
| 382 |
categories = {}
|
| 383 |
for item in items:
|
|
|
|
| 387 |
categories[cat]["2024"] += item["value_2024"]
|
| 388 |
categories[cat]["2023"] += item["value_2023"]
|
| 389 |
|
| 390 |
+
# Calculate major totals using template categories
|
| 391 |
shareholders_funds_2024 = categories.get("Shareholders' funds", {}).get("2024", 0)
|
| 392 |
shareholders_funds_2023 = categories.get("Shareholders' funds", {}).get("2023", 0)
|
| 393 |
|
| 394 |
+
share_app_money_2024 = categories.get("Share application money pending allotment", {}).get("2024", 0)
|
| 395 |
+
share_app_money_2023 = categories.get("Share application money pending allotment", {}).get("2023", 0)
|
| 396 |
+
|
| 397 |
non_current_liab_2024 = categories.get("Non-Current liabilities", {}).get("2024", 0)
|
| 398 |
non_current_liab_2023 = categories.get("Non-Current liabilities", {}).get("2023", 0)
|
| 399 |
|
|
|
|
| 406 |
current_assets_2024 = categories.get("Current assets", {}).get("2024", 0)
|
| 407 |
current_assets_2023 = categories.get("Current assets", {}).get("2023", 0)
|
| 408 |
|
| 409 |
+
total_equity_liab_2024 = shareholders_funds_2024 + share_app_money_2024 + non_current_liab_2024 + current_liab_2024
|
| 410 |
+
total_equity_liab_2023 = shareholders_funds_2023 + share_app_money_2023 + non_current_liab_2023 + current_liab_2023
|
| 411 |
|
| 412 |
total_assets_2024 = non_current_assets_2024 + current_assets_2024
|
| 413 |
total_assets_2023 = non_current_assets_2023 + current_assets_2023
|
|
|
|
| 415 |
return BalanceSheetTotals(
|
| 416 |
shareholders_funds_2024=shareholders_funds_2024,
|
| 417 |
shareholders_funds_2023=shareholders_funds_2023,
|
| 418 |
+
share_application_money_2024=share_app_money_2024,
|
| 419 |
+
share_application_money_2023=share_app_money_2023,
|
| 420 |
non_current_liabilities_2024=non_current_liab_2024,
|
| 421 |
non_current_liabilities_2023=non_current_liab_2023,
|
| 422 |
current_liabilities_2024=current_liab_2024,
|
|
|
|
| 434 |
)
|
| 435 |
|
| 436 |
def generate_balance_sheet_excel(self, items: List[Dict[str, Any]], totals: BalanceSheetTotals, output_dir: str = "output") -> str:
|
| 437 |
+
"""Generate formatted Excel balance sheet using template formatting"""
|
| 438 |
os.makedirs(output_dir, exist_ok=True)
|
| 439 |
|
| 440 |
wb = Workbook()
|
|
|
|
| 442 |
ws.title = "Balance Sheet"
|
| 443 |
|
| 444 |
# Set column widths
|
| 445 |
+
ws.column_dimensions["A"].width = 50
|
| 446 |
+
ws.column_dimensions["B"].width = 10
|
| 447 |
+
ws.column_dimensions["C"].width = 20
|
| 448 |
+
ws.column_dimensions["D"].width = 20
|
| 449 |
|
| 450 |
# Styles
|
| 451 |
bold_font = Font(bold=True)
|
|
|
|
| 487 |
|
| 488 |
row += 1
|
| 489 |
|
| 490 |
+
# Header using template formatting
|
| 491 |
+
header = self.formatting_rules.header
|
| 492 |
+
add_row(header["title"], "", 0, 0, True)
|
| 493 |
add_row("", "", 0, 0)
|
| 494 |
+
add_row(header["currency_note"], "", 0, 0)
|
|
|
|
| 495 |
add_row("", "", 0, 0)
|
| 496 |
|
| 497 |
+
# Column headers
|
| 498 |
+
headers = header["column_headers"]
|
| 499 |
+
add_row(headers[0], headers[1], headers[2], headers[3], True)
|
| 500 |
+
add_row("", "", 0, 0)
|
| 501 |
+
|
| 502 |
+
# I. EQUITY AND LIABILITIES
|
| 503 |
+
add_row("I. EQUITY AND LIABILITIES", "", 0, 0, True)
|
| 504 |
+
add_row("", "", 0, 0)
|
| 505 |
|
| 506 |
+
# (1) Shareholders' funds
|
| 507 |
+
add_row("(1) Shareholders' funds", "", 0, 0, True)
|
| 508 |
shareholders_items = [item for item in items if item["category"] == "Shareholders' funds"]
|
| 509 |
for item in shareholders_items:
|
| 510 |
+
add_row(f" ({item['note'][0] if item['note'] else ''}) {item['name']}", item["note"], item["value_2024"], item["value_2023"])
|
|
|
|
| 511 |
add_row("", "", totals.shareholders_funds_2024, totals.shareholders_funds_2023, True)
|
| 512 |
add_row("", "", 0, 0)
|
| 513 |
|
| 514 |
+
# (2) Share application money pending allotment
|
| 515 |
+
share_app_items = [item for item in items if item["category"] == "Share application money pending allotment"]
|
| 516 |
+
if any(item["value_2024"] != 0 or item["value_2023"] != 0 for item in share_app_items):
|
| 517 |
+
add_row("(2) Share application money pending allotment", "", 0, 0, True)
|
| 518 |
+
for item in share_app_items:
|
| 519 |
+
add_row(f" ({item['note']}) {item['name']}", item["note"], item["value_2024"], item["value_2023"])
|
| 520 |
+
add_row("", "", totals.share_application_money_2024, totals.share_application_money_2023, True)
|
| 521 |
+
add_row("", "", 0, 0)
|
| 522 |
+
|
| 523 |
+
# (3) Non-Current liabilities
|
| 524 |
+
add_row("(3) Non-Current liabilities", "", 0, 0, True)
|
| 525 |
non_current_liab_items = [item for item in items if item["category"] == "Non-Current liabilities"]
|
| 526 |
for item in non_current_liab_items:
|
| 527 |
+
add_row(f" ({item['note']}) {item['name']}", item["note"], item["value_2024"], item["value_2023"])
|
|
|
|
| 528 |
add_row("", "", totals.non_current_liabilities_2024, totals.non_current_liabilities_2023, True)
|
| 529 |
add_row("", "", 0, 0)
|
| 530 |
|
| 531 |
+
# (4) Current liabilities
|
| 532 |
+
add_row("(4) Current liabilities", "", 0, 0, True)
|
| 533 |
current_liab_items = [item for item in items if item["category"] == "Current liabilities"]
|
| 534 |
+
|
| 535 |
+
# Group trade payables
|
| 536 |
+
trade_payables_items = [item for item in current_liab_items if item["subcategory"] == "Trade payables"]
|
| 537 |
+
other_current_items = [item for item in current_liab_items if item["subcategory"] != "Trade payables"]
|
| 538 |
+
|
| 539 |
+
# Add other current liability items first
|
| 540 |
+
for item in other_current_items:
|
| 541 |
+
add_row(f" ({item['note']}) {item['name']}", item["note"], item["value_2024"], item["value_2023"])
|
| 542 |
+
|
| 543 |
+
# Add trade payables with subcategory
|
| 544 |
+
if trade_payables_items:
|
| 545 |
+
trade_payables_total_2024 = sum(item["value_2024"] for item in trade_payables_items)
|
| 546 |
+
trade_payables_total_2023 = sum(item["value_2023"] for item in trade_payables_items)
|
| 547 |
+
|
| 548 |
+
add_row(" (11) Trade payables", "11", 0, 0, True, 1)
|
| 549 |
+
for item in trade_payables_items:
|
| 550 |
+
add_row(f" (A) {item['name']}", item["note"], item["value_2024"], item["value_2023"])
|
| 551 |
+
add_row("", "", trade_payables_total_2024, trade_payables_total_2023, True, 2)
|
| 552 |
|
| 553 |
add_row("", "", totals.current_liabilities_2024, totals.current_liabilities_2023, True)
|
| 554 |
add_row("", "", 0, 0)
|
|
|
|
| 557 |
add_row("TOTAL", "", totals.total_equity_liabilities_2024, totals.total_equity_liabilities_2023, True, 0, True)
|
| 558 |
add_row("", "", 0, 0)
|
| 559 |
|
| 560 |
+
# II. ASSETS
|
| 561 |
+
add_row("II. ASSETS", "", 0, 0, True)
|
| 562 |
+
add_row("", "", 0, 0)
|
| 563 |
|
| 564 |
# Non-current assets
|
| 565 |
add_row("Non-current assets", "", 0, 0, True)
|
| 566 |
|
| 567 |
+
# (1) Property, Plant and Equipment
|
| 568 |
+
ppe_items = [item for item in items if item.get("subcategory") == "Property, Plant and Equipment"]
|
| 569 |
+
if ppe_items:
|
| 570 |
+
add_row("(1) Property, Plant and Equipment", "", 0, 0, True, 1)
|
| 571 |
+
ppe_total_2024 = ppe_total_2023 = 0
|
| 572 |
+
for item in ppe_items:
|
| 573 |
+
add_row(f" ({item['note']}) {item['name']}", item["note"], item["value_2024"], item["value_2023"], False, 1)
|
| 574 |
+
ppe_total_2024 += item["value_2024"]
|
| 575 |
+
ppe_total_2023 += item["value_2023"]
|
| 576 |
+
add_row("", "", ppe_total_2024, ppe_total_2023, True, 1)
|
| 577 |
+
add_row("", "", 0, 0)
|
| 578 |
|
| 579 |
# Other non-current assets
|
| 580 |
+
other_non_current = [item for item in items if item["category"] == "Non-current assets" and item.get("subcategory") != "Property, Plant and Equipment"]
|
| 581 |
for item in other_non_current:
|
| 582 |
+
add_row(f"({item['note']}) {item['name']}", item["note"], item["value_2024"], item["value_2023"], False, 1)
|
| 583 |
|
| 584 |
add_row("", "", totals.non_current_assets_2024, totals.non_current_assets_2023, True)
|
| 585 |
add_row("", "", 0, 0)
|
| 586 |
|
| 587 |
+
# (2) Current assets
|
| 588 |
+
add_row("(2) Current assets", "", 0, 0, True)
|
| 589 |
current_asset_items = [item for item in items if item["category"] == "Current assets"]
|
| 590 |
for item in current_asset_items:
|
| 591 |
+
add_row(f" ({item['note']}) {item['name']}", item["note"], item["value_2024"], item["value_2023"], False, 1)
|
| 592 |
|
| 593 |
add_row("", "", totals.current_assets_2024, totals.current_assets_2023, True)
|
| 594 |
add_row("", "", 0, 0)
|
|
|
|
| 602 |
balance_2023 = totals.balance_difference_2023
|
| 603 |
|
| 604 |
if balance_2024 < 0.01 and balance_2023 < 0.01:
|
| 605 |
+
add_row("✓ Balance Sheet is BALANCED", "", 0, 0, True)
|
| 606 |
else:
|
| 607 |
+
add_row(f"⚠ Balance Difference: {balance_2024:.2f} | {balance_2023:.2f}", "", 0, 0, True)
|
| 608 |
|
| 609 |
# Save file
|
| 610 |
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
|
|
| 623 |
with open(input_file, 'r', encoding='utf-8') as f:
|
| 624 |
json_data = json.load(f)
|
| 625 |
|
| 626 |
+
logger.info("Extracting data using template structure...")
|
| 627 |
|
| 628 |
+
# Method 1: Direct extraction using template structure
|
| 629 |
items = self.extract_from_json_structure(json_data)
|
| 630 |
+
logger.info(f"Extracted {len(items)} items using template structure")
|
| 631 |
|
| 632 |
# Method 2: AI-assisted extraction if needed
|
| 633 |
+
if len([item for item in items if item["value_2024"] != 0 or item["value_2023"] != 0]) < 5:
|
| 634 |
logger.info("Using AI for additional extraction...")
|
| 635 |
|
| 636 |
# Create summary for AI
|
|
|
|
| 640 |
ai_items = ai_result.get("balance_sheet_items", [])
|
| 641 |
logger.info(f"AI extracted {len(ai_items)} additional items")
|
| 642 |
|
| 643 |
+
# Merge items (replace template items with AI items if they have values)
|
| 644 |
+
ai_items_dict = {item["name"]: item for item in ai_items}
|
| 645 |
+
|
| 646 |
+
for i, item in enumerate(items):
|
| 647 |
+
if item["name"] in ai_items_dict:
|
| 648 |
+
ai_item = ai_items_dict[item["name"]]
|
| 649 |
+
if ai_item["value_2024"] != 0 or ai_item["value_2023"] != 0:
|
| 650 |
+
items[i] = ai_item
|
|
|
|
| 651 |
|
| 652 |
# Calculate totals
|
| 653 |
totals = self.calculate_totals(items)
|
| 654 |
|
| 655 |
# Display summary
|
| 656 |
logger.info(f"\n BALANCE SHEET SUMMARY:")
|
| 657 |
+
logger.info(f"Template Items: {len(self.template.get_template_structure())}")
|
| 658 |
+
logger.info(f"Items with Values: {len([item for item in items if item['value_2024'] != 0 or item['value_2023'] != 0])}")
|
| 659 |
+
logger.info(f" EQUITY & LIABILITIES 2024:")
|
| 660 |
+
logger.info(f" - Shareholders' funds: Rs. {totals.shareholders_funds_2024:,.2f} Lakhs")
|
| 661 |
+
logger.info(f" - Share application money: Rs. {totals.share_application_money_2024:,.2f} Lakhs")
|
| 662 |
+
logger.info(f" - Non-current liabilities: Rs. {totals.non_current_liabilities_2024:,.2f} Lakhs")
|
| 663 |
+
logger.info(f" - Current liabilities: Rs. {totals.current_liabilities_2024:,.2f} Lakhs")
|
| 664 |
+
logger.info(f" - TOTAL: Rs. {totals.total_equity_liabilities_2024:,.2f} Lakhs")
|
| 665 |
+
logger.info(f" ASSETS 2024:")
|
| 666 |
+
logger.info(f" - Non-current assets: Rs. {totals.non_current_assets_2024:,.2f} Lakhs")
|
| 667 |
+
logger.info(f" - Current assets: Rs. {totals.current_assets_2024:,.2f} Lakhs")
|
| 668 |
+
logger.info(f" - TOTAL: Rs. {totals.total_assets_2024:,.2f} Lakhs")
|
| 669 |
+
logger.info(f" Balance Difference 2024: Rs. {totals.balance_difference_2024:,.2f} Lakhs")
|
| 670 |
+
logger.info(f" Balance Difference 2023: Rs. {totals.balance_difference_2023:,.2f} Lakhs")
|
| 671 |
|
| 672 |
# Check if balanced
|
| 673 |
is_balanced_2024 = totals.balance_difference_2024 < 0.01
|
| 674 |
is_balanced_2023 = totals.balance_difference_2023 < 0.01
|
| 675 |
|
| 676 |
if is_balanced_2024 and is_balanced_2023:
|
| 677 |
+
logger.info(" Balance Sheet is PERFECTLY BALANCED!")
|
| 678 |
else:
|
| 679 |
+
logger.warning(" Balance Sheet has differences - may need adjustment")
|
| 680 |
|
| 681 |
+
# Generate Excel using template formatting
|
| 682 |
output_file = self.generate_balance_sheet_excel(items, totals, output_dir)
|
| 683 |
|
| 684 |
+
logger.info(f" SUCCESS: Generated {output_file}")
|
| 685 |
return output_file
|
| 686 |
|
| 687 |
except Exception as e:
|
| 688 |
+
logger.error(f" Error processing file: {e}", exc_info=True)
|
| 689 |
return None
|
| 690 |
|
| 691 |
def main() -> None:
|
|
|
|
| 694 |
Accepts input file and output directory from command-line arguments or environment variables.
|
| 695 |
Handles errors gracefully and logs all major events.
|
| 696 |
"""
|
| 697 |
+
logger.info(" ENHANCED BALANCE SHEET GENERATOR v3.0 (Template-Based) started.")
|
| 698 |
import sys
|
| 699 |
api_key = settings.api_key
|
| 700 |
input_file = settings.input_file
|
|
|
|
| 704 |
if len(sys.argv) > 2:
|
| 705 |
output_dir = sys.argv[2]
|
| 706 |
if not api_key:
|
| 707 |
+
logger.error(" Missing OPENROUTER_API_KEY environment variable. Please set your OpenRouter API key in the .env file.")
|
| 708 |
return
|
| 709 |
if not os.path.exists(input_file):
|
| 710 |
+
logger.error(f" Input file not found: {input_file}. Please ensure your JSON data file exists.")
|
| 711 |
return
|
| 712 |
+
|
| 713 |
generator = EnhancedBalanceSheetGenerator(api_key)
|
| 714 |
try:
|
| 715 |
result = generator.process(input_file, output_dir)
|
| 716 |
if result:
|
| 717 |
abs_path = os.path.abspath(result)
|
| 718 |
if os.path.exists(abs_path):
|
| 719 |
+
logger.info(f" COMPLETED SUCCESSFULLY! Output file: {abs_path}")
|
| 720 |
print(f"Output file: {abs_path}") # For API subprocess parsing
|
| 721 |
else:
|
| 722 |
+
logger.error(f" PROCESSING FAILED. Output file not created: {abs_path}")
|
| 723 |
else:
|
| 724 |
+
logger.error(" PROCESSING FAILED. Please check the error messages above and try again.")
|
| 725 |
except Exception as e:
|
| 726 |
+
logger.error(f" Fatal error: {e}", exc_info=True)
|
| 727 |
|
| 728 |
if __name__ == "__main__":
|
| 729 |
main()
|
bs/balance_sheet_template_handler.py
CHANGED
|
@@ -30,21 +30,49 @@ class BalanceSheetTemplate:
|
|
| 30 |
"""
|
| 31 |
|
| 32 |
def __init__(self):
|
| 33 |
-
# Complete Balance Sheet Structure Template
|
| 34 |
self.template_structure: List[Dict[str, Any]] = [
|
|
|
|
|
|
|
| 35 |
BalanceSheetItem(section="EQUITY AND LIABILITIES", category="Shareholders' funds", subcategory="", name="Share capital", note="2").dict(),
|
| 36 |
BalanceSheetItem(section="EQUITY AND LIABILITIES", category="Shareholders' funds", subcategory="", name="Reserves and surplus", note="3").dict(),
|
| 37 |
-
BalanceSheetItem(section="EQUITY AND LIABILITIES", category="
|
| 38 |
-
|
| 39 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 40 |
BalanceSheetItem(section="EQUITY AND LIABILITIES", category="Current liabilities", subcategory="", name="Other current liabilities", note="7").dict(),
|
| 41 |
-
BalanceSheetItem(section="EQUITY AND LIABILITIES", category="Current liabilities", subcategory="", name="Short
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 45 |
BalanceSheetItem(section="ASSETS", category="Current assets", subcategory="", name="Inventories", note="11").dict(),
|
| 46 |
BalanceSheetItem(section="ASSETS", category="Current assets", subcategory="", name="Trade receivables", note="12").dict(),
|
| 47 |
-
BalanceSheetItem(section="ASSETS", category="Current assets", subcategory="", name="Cash and
|
| 48 |
BalanceSheetItem(section="ASSETS", category="Current assets", subcategory="", name="Short-term loans and advances", note="14").dict(),
|
| 49 |
BalanceSheetItem(section="ASSETS", category="Current assets", subcategory="", name="Other current assets", note="15").dict()
|
| 50 |
]
|
|
@@ -53,8 +81,8 @@ class BalanceSheetTemplate:
|
|
| 53 |
self.formatting_rules: FormattingRules = FormattingRules(
|
| 54 |
header={
|
| 55 |
"title": "Balance Sheet as at March 31, 2024",
|
| 56 |
-
"currency_note": "(
|
| 57 |
-
"column_headers": ["", "
|
| 58 |
},
|
| 59 |
sections={
|
| 60 |
"EQUITY AND LIABILITIES": {"display_name": "EQUITY AND LIABILITIES", "order": 1},
|
|
@@ -62,13 +90,15 @@ class BalanceSheetTemplate:
|
|
| 62 |
},
|
| 63 |
categories={
|
| 64 |
"Shareholders' funds": {"display_name": "Shareholders' funds", "show_total": True, "total_label": "", "order": 1},
|
| 65 |
-
"
|
| 66 |
-
"Current liabilities": {"display_name": "Current liabilities", "show_total": True, "total_label": "", "order": 3},
|
| 67 |
-
"
|
| 68 |
-
"
|
|
|
|
| 69 |
},
|
| 70 |
subcategories={
|
| 71 |
-
"
|
|
|
|
| 72 |
},
|
| 73 |
totals={
|
| 74 |
"TOTAL_EQUITY_LIABILITIES": {"display_name": "TOTAL", "position": "after_equity_liabilities", "is_grand_total": True},
|
|
@@ -76,21 +106,33 @@ class BalanceSheetTemplate:
|
|
| 76 |
}
|
| 77 |
)
|
| 78 |
|
| 79 |
-
# Field mapping patterns for data extraction
|
| 80 |
self.field_mappings: Dict[str, List[str]] = {
|
| 81 |
'share_capital': ['share capital', 'equity share', 'paid up', 'issued shares', 'authorised shares', 'subscribed', 'fully paid'],
|
| 82 |
'reserves_surplus': ['reserves and surplus', 'reserves', 'surplus', 'retained earnings', 'profit and loss', 'general reserves', 'closing balance'],
|
|
|
|
|
|
|
| 83 |
'long_term_borrowings': ['long term borrowings', 'long-term borrowings', 'borrowings', 'debt', 'loans', 'financial corporation', 'bank loan'],
|
| 84 |
-
'
|
| 85 |
-
'
|
|
|
|
|
|
|
|
|
|
|
|
|
| 86 |
'other_current_liabilities': ['other current liabilities', 'current maturities', 'outstanding liabilities', 'statutory dues', 'accrued expenses'],
|
| 87 |
'short_term_provisions': ['short term provisions', 'provisions', 'provision for taxation', 'tax provision'],
|
| 88 |
'tangible_assets': ['tangible assets', 'property plant', 'fixed assets', 'buildings', 'plant', 'equipment', 'net carrying value'],
|
| 89 |
'intangible_assets': ['intangible assets', 'software', 'goodwill', 'intangible'],
|
|
|
|
|
|
|
|
|
|
|
|
|
| 90 |
'long_term_loans_advances': ['long term loans', 'security deposits', 'long term advances'],
|
|
|
|
|
|
|
| 91 |
'inventories': ['inventories', 'stock', 'consumables', 'raw materials'],
|
| 92 |
'trade_receivables': ['trade receivables', 'receivables', 'debtors', 'outstanding', 'other receivables'],
|
| 93 |
-
'
|
| 94 |
'short_term_loans_advances': ['short term loans', 'prepaid expenses', 'other advances', 'advance tax', 'statutory authorities'],
|
| 95 |
'other_current_assets': ['other current assets', 'accrued income', 'interest accrued']
|
| 96 |
}
|
|
@@ -137,11 +179,12 @@ class BalanceSheetTemplate:
|
|
| 137 |
# For backward compatibility - alias the class
|
| 138 |
BalanceSheet = BalanceSheetTemplate
|
| 139 |
|
| 140 |
-
# Module level constants for quick access
|
| 141 |
BALANCE_SHEET_SECTIONS: List[str] = ["EQUITY AND LIABILITIES", "ASSETS"]
|
| 142 |
|
| 143 |
BALANCE_SHEET_CATEGORIES: List[str] = [
|
| 144 |
"Shareholders' funds",
|
|
|
|
| 145 |
"Non-Current liabilities",
|
| 146 |
"Current liabilities",
|
| 147 |
"Non-current assets",
|
|
@@ -150,18 +193,30 @@ BALANCE_SHEET_CATEGORIES: List[str] = [
|
|
| 150 |
|
| 151 |
STANDARD_NOTES_MAPPING: Dict[str, str] = {
|
| 152 |
"Share capital": "2",
|
| 153 |
-
"Reserves and surplus": "3",
|
| 154 |
-
"
|
| 155 |
-
"
|
| 156 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 157 |
"Other current liabilities": "7",
|
| 158 |
-
"Short
|
| 159 |
"Tangible assets": "9",
|
| 160 |
"Intangible assets": "9",
|
| 161 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 162 |
"Inventories": "11",
|
| 163 |
-
"Trade receivables": "12",
|
| 164 |
-
"Cash and
|
| 165 |
"Short-term loans and advances": "14",
|
| 166 |
"Other current assets": "15"
|
| 167 |
}
|
|
@@ -169,17 +224,29 @@ STANDARD_NOTES_MAPPING: Dict[str, str] = {
|
|
| 169 |
SIMPLE_TEMPLATE: List[Dict[str, Any]] = [
|
| 170 |
{"category": "Shareholders' funds", "name": "Share capital", "note": "2"},
|
| 171 |
{"category": "Shareholders' funds", "name": "Reserves and surplus", "note": "3"},
|
| 172 |
-
{"category": "
|
| 173 |
-
{"category": "
|
| 174 |
-
{"category": "Current liabilities", "name": "
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 175 |
{"category": "Current liabilities", "name": "Other current liabilities", "note": "7"},
|
| 176 |
-
{"category": "Current liabilities", "name": "Short
|
| 177 |
-
{"category": "Non-current assets", "subcategory": "
|
| 178 |
-
{"category": "Non-current assets", "subcategory": "
|
| 179 |
-
{"category": "Non-current assets", "
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 180 |
{"category": "Current assets", "name": "Inventories", "note": "11"},
|
| 181 |
{"category": "Current assets", "name": "Trade receivables", "note": "12"},
|
| 182 |
-
{"category": "Current assets", "name": "Cash and
|
| 183 |
{"category": "Current assets", "name": "Short-term loans and advances", "note": "14"},
|
| 184 |
{"category": "Current assets", "name": "Other current assets", "note": "15"}
|
| 185 |
]
|
|
|
|
| 30 |
"""
|
| 31 |
|
| 32 |
def __init__(self):
|
| 33 |
+
# Updated Complete Balance Sheet Structure Template
|
| 34 |
self.template_structure: List[Dict[str, Any]] = [
|
| 35 |
+
# EQUITY AND LIABILITIES
|
| 36 |
+
# (1) Shareholders' funds
|
| 37 |
BalanceSheetItem(section="EQUITY AND LIABILITIES", category="Shareholders' funds", subcategory="", name="Share capital", note="2").dict(),
|
| 38 |
BalanceSheetItem(section="EQUITY AND LIABILITIES", category="Shareholders' funds", subcategory="", name="Reserves and surplus", note="3").dict(),
|
| 39 |
+
BalanceSheetItem(section="EQUITY AND LIABILITIES", category="Shareholders' funds", subcategory="", name="Money received against share warrants", note=" ").dict(),
|
| 40 |
+
|
| 41 |
+
# (2) Share application money pending allotment
|
| 42 |
+
BalanceSheetItem(section="EQUITY AND LIABILITIES", category="Share application money pending allotment", subcategory="", name="Share application money pending allotment", note=" ").dict(),
|
| 43 |
+
|
| 44 |
+
# (3) Non-current liabilities
|
| 45 |
+
BalanceSheetItem(section="EQUITY AND LIABILITIES", category="Non-Current liabilities", subcategory="", name="Long-term borrowings", note="4").dict(),
|
| 46 |
+
BalanceSheetItem(section="EQUITY AND LIABILITIES", category="Non-Current liabilities", subcategory="", name="Deferred tax liabilities (Net)", note="5").dict(),
|
| 47 |
+
BalanceSheetItem(section="EQUITY AND LIABILITIES", category="Non-Current liabilities", subcategory="", name="Other Long-term liabilities", note="8").dict(),
|
| 48 |
+
BalanceSheetItem(section="EQUITY AND LIABILITIES", category="Non-Current liabilities", subcategory="", name="Long-term provisions", note="9").dict(),
|
| 49 |
+
|
| 50 |
+
# (4) Current liabilities
|
| 51 |
+
BalanceSheetItem(section="EQUITY AND LIABILITIES", category="Current liabilities", subcategory="", name="Short-term borrowings", note="10").dict(),
|
| 52 |
+
BalanceSheetItem(section="EQUITY AND LIABILITIES", category="Current liabilities", subcategory="Trade payables", name="total outstanding dues of micro enterprises and small enterprises", note=" ", indent_level=2).dict(),
|
| 53 |
+
BalanceSheetItem(section="EQUITY AND LIABILITIES", category="Current liabilities", subcategory="Trade payables", name="total outstanding dues of creditors other than micro enterprises and small enterprises", note=" ", indent_level=2).dict(),
|
| 54 |
BalanceSheetItem(section="EQUITY AND LIABILITIES", category="Current liabilities", subcategory="", name="Other current liabilities", note="7").dict(),
|
| 55 |
+
BalanceSheetItem(section="EQUITY AND LIABILITIES", category="Current liabilities", subcategory="", name="Short-term provisions", note="8").dict(),
|
| 56 |
+
|
| 57 |
+
# ASSETS
|
| 58 |
+
# Non-current assets
|
| 59 |
+
# (1) Property, Plant and Equipment
|
| 60 |
+
BalanceSheetItem(section="ASSETS", category="Non-current assets", subcategory="Property, Plant and Equipment", name="Tangible assets", note="9", indent_level=2).dict(),
|
| 61 |
+
BalanceSheetItem(section="ASSETS", category="Non-current assets", subcategory="Property, Plant and Equipment", name="Intangible assets", note="9", indent_level=2).dict(),
|
| 62 |
+
BalanceSheetItem(section="ASSETS", category="Non-current assets", subcategory="Property, Plant and Equipment", name="Capital work-in-progress", note=" ", indent_level=2).dict(),
|
| 63 |
+
BalanceSheetItem(section="ASSETS", category="Non-current assets", subcategory="Property, Plant and Equipment", name="Intangible assets under development", note="9", indent_level=2).dict(),
|
| 64 |
+
|
| 65 |
+
# Other Non-current assets
|
| 66 |
+
BalanceSheetItem(section="ASSETS", category="Non-current assets", subcategory="", name="Non-current investments", note=" ").dict(),
|
| 67 |
+
BalanceSheetItem(section="ASSETS", category="Non-current assets", subcategory="", name="Deferred tax assets (net)", note=" ").dict(),
|
| 68 |
+
BalanceSheetItem(section="ASSETS", category="Non-current assets", subcategory="", name="Long-term loans and advances", note="10").dict(),
|
| 69 |
+
BalanceSheetItem(section="ASSETS", category="Non-current assets", subcategory="", name="Other non-current assets", note=" ").dict(),
|
| 70 |
+
|
| 71 |
+
# (2) Current assets
|
| 72 |
+
BalanceSheetItem(section="ASSETS", category="Current assets", subcategory="", name="Current investments", note=" ").dict(),
|
| 73 |
BalanceSheetItem(section="ASSETS", category="Current assets", subcategory="", name="Inventories", note="11").dict(),
|
| 74 |
BalanceSheetItem(section="ASSETS", category="Current assets", subcategory="", name="Trade receivables", note="12").dict(),
|
| 75 |
+
BalanceSheetItem(section="ASSETS", category="Current assets", subcategory="", name="Cash and cash equivalents", note="13").dict(),
|
| 76 |
BalanceSheetItem(section="ASSETS", category="Current assets", subcategory="", name="Short-term loans and advances", note="14").dict(),
|
| 77 |
BalanceSheetItem(section="ASSETS", category="Current assets", subcategory="", name="Other current assets", note="15").dict()
|
| 78 |
]
|
|
|
|
| 81 |
self.formatting_rules: FormattingRules = FormattingRules(
|
| 82 |
header={
|
| 83 |
"title": "Balance Sheet as at March 31, 2024",
|
| 84 |
+
"currency_note": "(Rupees in...........)",
|
| 85 |
+
"column_headers": ["Particulars", "Note No.", "Figures as at the end of current reporting period", "Figures as at the end of the previous reporting period"]
|
| 86 |
},
|
| 87 |
sections={
|
| 88 |
"EQUITY AND LIABILITIES": {"display_name": "EQUITY AND LIABILITIES", "order": 1},
|
|
|
|
| 90 |
},
|
| 91 |
categories={
|
| 92 |
"Shareholders' funds": {"display_name": "Shareholders' funds", "show_total": True, "total_label": "", "order": 1},
|
| 93 |
+
"Share application money pending allotment": {"display_name": "Share application money pending allotment", "show_total": True, "total_label": "", "order": 2},
|
| 94 |
+
"Non-Current liabilities": {"display_name": "Non-Current liabilities", "show_total": True, "total_label": "", "order": 3},
|
| 95 |
+
"Current liabilities": {"display_name": "Current liabilities", "show_total": True, "total_label": "", "order": 4},
|
| 96 |
+
"Non-current assets": {"display_name": "Non-current assets", "show_total": True, "total_label": "", "order": 5},
|
| 97 |
+
"Current assets": {"display_name": "Current assets", "show_total": True, "total_label": "", "order": 6}
|
| 98 |
},
|
| 99 |
subcategories={
|
| 100 |
+
"Property, Plant and Equipment": {"display_name": "Property, Plant and Equipment", "show_total": True, "total_label": "", "parent_category": "Non-current assets"},
|
| 101 |
+
"Trade payables": {"display_name": "Trade payables", "show_total": True, "total_label": "", "parent_category": "Current liabilities"}
|
| 102 |
},
|
| 103 |
totals={
|
| 104 |
"TOTAL_EQUITY_LIABILITIES": {"display_name": "TOTAL", "position": "after_equity_liabilities", "is_grand_total": True},
|
|
|
|
| 106 |
}
|
| 107 |
)
|
| 108 |
|
| 109 |
+
# Updated Field mapping patterns for data extraction
|
| 110 |
self.field_mappings: Dict[str, List[str]] = {
|
| 111 |
'share_capital': ['share capital', 'equity share', 'paid up', 'issued shares', 'authorised shares', 'subscribed', 'fully paid'],
|
| 112 |
'reserves_surplus': ['reserves and surplus', 'reserves', 'surplus', 'retained earnings', 'profit and loss', 'general reserves', 'closing balance'],
|
| 113 |
+
'money_against_warrants': ['money received against share warrants', 'share warrants', 'warrants'],
|
| 114 |
+
'share_application_money': ['share application money', 'application money pending', 'pending allotment'],
|
| 115 |
'long_term_borrowings': ['long term borrowings', 'long-term borrowings', 'borrowings', 'debt', 'loans', 'financial corporation', 'bank loan'],
|
| 116 |
+
'deferred_tax_liabilities': ['deferred tax liabilities', 'deferred tax liability', 'tax liability'],
|
| 117 |
+
'other_long_term_liabilities': ['other long-term liabilities', 'long term liabilities', 'other long term'],
|
| 118 |
+
'long_term_provisions': ['long-term provisions', 'long term provisions', 'provisions'],
|
| 119 |
+
'short_term_borrowings': ['short-term borrowings', 'short term borrowings', 'current borrowings'],
|
| 120 |
+
'trade_payables_micro': ['total outstanding dues of micro enterprises', 'micro enterprises', 'small enterprises dues'],
|
| 121 |
+
'trade_payables_others': ['total outstanding dues of creditors other than micro', 'other creditors', 'creditors other than micro'],
|
| 122 |
'other_current_liabilities': ['other current liabilities', 'current maturities', 'outstanding liabilities', 'statutory dues', 'accrued expenses'],
|
| 123 |
'short_term_provisions': ['short term provisions', 'provisions', 'provision for taxation', 'tax provision'],
|
| 124 |
'tangible_assets': ['tangible assets', 'property plant', 'fixed assets', 'buildings', 'plant', 'equipment', 'net carrying value'],
|
| 125 |
'intangible_assets': ['intangible assets', 'software', 'goodwill', 'intangible'],
|
| 126 |
+
'capital_work_progress': ['capital work-in-progress', 'work in progress', 'construction in progress', 'CWIP'],
|
| 127 |
+
'intangible_under_development': ['intangible assets under development', 'intangible under development', 'development'],
|
| 128 |
+
'non_current_investments': ['non-current investments', 'long term investments', 'investments'],
|
| 129 |
+
'deferred_tax_assets': ['deferred tax assets', 'tax assets'],
|
| 130 |
'long_term_loans_advances': ['long term loans', 'security deposits', 'long term advances'],
|
| 131 |
+
'other_non_current_assets': ['other non-current assets', 'other long term assets'],
|
| 132 |
+
'current_investments': ['current investments', 'short term investments', 'marketable securities'],
|
| 133 |
'inventories': ['inventories', 'stock', 'consumables', 'raw materials'],
|
| 134 |
'trade_receivables': ['trade receivables', 'receivables', 'debtors', 'outstanding', 'other receivables'],
|
| 135 |
+
'cash_equivalents': ['cash and cash equivalents', 'cash', 'bank balances', 'current accounts', 'cash on hand', 'fixed deposits'],
|
| 136 |
'short_term_loans_advances': ['short term loans', 'prepaid expenses', 'other advances', 'advance tax', 'statutory authorities'],
|
| 137 |
'other_current_assets': ['other current assets', 'accrued income', 'interest accrued']
|
| 138 |
}
|
|
|
|
| 179 |
# For backward compatibility - alias the class
|
| 180 |
BalanceSheet = BalanceSheetTemplate
|
| 181 |
|
| 182 |
+
# Updated Module level constants for quick access
|
| 183 |
BALANCE_SHEET_SECTIONS: List[str] = ["EQUITY AND LIABILITIES", "ASSETS"]
|
| 184 |
|
| 185 |
BALANCE_SHEET_CATEGORIES: List[str] = [
|
| 186 |
"Shareholders' funds",
|
| 187 |
+
"Share application money pending allotment",
|
| 188 |
"Non-Current liabilities",
|
| 189 |
"Current liabilities",
|
| 190 |
"Non-current assets",
|
|
|
|
| 193 |
|
| 194 |
STANDARD_NOTES_MAPPING: Dict[str, str] = {
|
| 195 |
"Share capital": "2",
|
| 196 |
+
"Reserves and surplus": "3",
|
| 197 |
+
"Money received against share warrants": " ",
|
| 198 |
+
"Share application money pending allotment": " ",
|
| 199 |
+
"Long-term borrowings": "4",
|
| 200 |
+
"Deferred tax liabilities (Net)": "5",
|
| 201 |
+
"Other Long-term liabilities": "8",
|
| 202 |
+
"Long-term provisions": "9",
|
| 203 |
+
"Short-term borrowings": " ",
|
| 204 |
+
"total outstanding dues of micro enterprises and small enterprises": " ",
|
| 205 |
+
"total outstanding dues of creditors other than micro enterprises and small enterprises": " ",
|
| 206 |
"Other current liabilities": "7",
|
| 207 |
+
"Short-term provisions": "8",
|
| 208 |
"Tangible assets": "9",
|
| 209 |
"Intangible assets": "9",
|
| 210 |
+
"Capital work-in-progress": " ",
|
| 211 |
+
"Intangible assets under development": "9",
|
| 212 |
+
"Non-current investments": " ",
|
| 213 |
+
"Deferred tax assets (net)": " ",
|
| 214 |
+
"Long-term loans and advances": "10",
|
| 215 |
+
"Other non-current assets": " ",
|
| 216 |
+
"Current investments": " ",
|
| 217 |
"Inventories": "11",
|
| 218 |
+
"Trade receivables": "12",
|
| 219 |
+
"Cash and cash equivalents": "13",
|
| 220 |
"Short-term loans and advances": "14",
|
| 221 |
"Other current assets": "15"
|
| 222 |
}
|
|
|
|
| 224 |
SIMPLE_TEMPLATE: List[Dict[str, Any]] = [
|
| 225 |
{"category": "Shareholders' funds", "name": "Share capital", "note": "2"},
|
| 226 |
{"category": "Shareholders' funds", "name": "Reserves and surplus", "note": "3"},
|
| 227 |
+
{"category": "Shareholders' funds", "name": "Money received against share warrants", "note": " "},
|
| 228 |
+
{"category": "Share application money pending allotment", "name": "Share application money pending allotment", "note": " "},
|
| 229 |
+
{"category": "Non-Current liabilities", "name": "Long-term borrowings", "note": "4"},
|
| 230 |
+
{"category": "Non-Current liabilities", "name": "Deferred tax liabilities (Net)", "note": "5"},
|
| 231 |
+
{"category": "Non-Current liabilities", "name": "Other Long-term liabilities", "note": "8"},
|
| 232 |
+
{"category": "Non-Current liabilities", "name": "Long-term provisions", "note": "9"},
|
| 233 |
+
{"category": "Current liabilities", "name": "Short-term borrowings", "note": " "},
|
| 234 |
+
{"category": "Current liabilities", "subcategory": "Trade payables", "name": "total outstanding dues of micro enterprises and small enterprises", "note": " "},
|
| 235 |
+
{"category": "Current liabilities", "subcategory": "Trade payables", "name": "total outstanding dues of creditors other than micro enterprises and small enterprises", "note": " "},
|
| 236 |
{"category": "Current liabilities", "name": "Other current liabilities", "note": "7"},
|
| 237 |
+
{"category": "Current liabilities", "name": "Short-term provisions", "note": "8"},
|
| 238 |
+
{"category": "Non-current assets", "subcategory": "Property, Plant and Equipment", "name": "Tangible assets", "note": "9"},
|
| 239 |
+
{"category": "Non-current assets", "subcategory": "Property, Plant and Equipment", "name": "Intangible assets", "note": "9"},
|
| 240 |
+
{"category": "Non-current assets", "subcategory": "Property, Plant and Equipment", "name": "Capital work-in-progress", "note": " "},
|
| 241 |
+
{"category": "Non-current assets", "subcategory": "Property, Plant and Equipment", "name": "Intangible assets under development", "note": "9"},
|
| 242 |
+
{"category": "Non-current assets", "name": "Non-current investments", "note": " "},
|
| 243 |
+
{"category": "Non-current assets", "name": "Deferred tax assets (net)", "note": " "},
|
| 244 |
+
{"category": "Non-current assets", "name": "Long-term loans and advances", "note": "10"},
|
| 245 |
+
{"category": "Non-current assets", "name": "Other non-current assets", "note": " "},
|
| 246 |
+
{"category": "Current assets", "name": "Current investments", "note": " "},
|
| 247 |
{"category": "Current assets", "name": "Inventories", "note": "11"},
|
| 248 |
{"category": "Current assets", "name": "Trade receivables", "note": "12"},
|
| 249 |
+
{"category": "Current assets", "name": "Cash and cash equivalents", "note": "13"},
|
| 250 |
{"category": "Current assets", "name": "Short-term loans and advances", "note": "14"},
|
| 251 |
{"category": "Current assets", "name": "Other current assets", "note": "15"}
|
| 252 |
]
|
pnl/profit_loss_statement_generator.py
CHANGED
|
@@ -218,56 +218,74 @@ class PnLGenerator:
|
|
| 218 |
right_align = Alignment(horizontal="right", vertical="center")
|
| 219 |
ws.column_dimensions["A"].width = 45
|
| 220 |
ws.column_dimensions["B"].width = 8
|
| 221 |
-
ws.column_dimensions["C"].width =
|
| 222 |
-
ws.column_dimensions["D"].width =
|
| 223 |
row = 1
|
| 224 |
ws.merge_cells("A1:D1")
|
| 225 |
-
ws["A1"] = "
|
| 226 |
ws["A1"].font = title_font
|
| 227 |
ws["A1"].alignment = center_align
|
| 228 |
-
ws["A1"].border = top_bottom_border
|
| 229 |
row += 2
|
| 230 |
-
ws["
|
| 231 |
-
ws["
|
| 232 |
-
ws["
|
| 233 |
row += 1
|
| 234 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 235 |
for col, header in enumerate(headers, 1):
|
| 236 |
cell = ws.cell(row=row, column=col)
|
| 237 |
cell.value = header
|
| 238 |
cell.font = header_font
|
| 239 |
-
cell.border =
|
| 240 |
-
cell.alignment = center_align if col >
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 241 |
row += 1
|
| 242 |
|
| 243 |
def add_data_row(description: str, note_ref: str, val_2024: float, val_2023: float,
|
| 244 |
-
is_bold: bool = False
|
| 245 |
"""Add a data row with proper formatting."""
|
| 246 |
nonlocal row
|
| 247 |
cell_a = ws.cell(row=row, column=1)
|
| 248 |
cell_a.value = description
|
| 249 |
-
cell_a.font = bold_font if
|
| 250 |
cell_a.alignment = left_align
|
| 251 |
-
|
| 252 |
-
|
| 253 |
cell_b = ws.cell(row=row, column=2)
|
| 254 |
cell_b.value = note_ref if note_ref else ""
|
| 255 |
cell_b.font = normal_font
|
| 256 |
cell_b.alignment = center_align
|
| 257 |
-
|
| 258 |
-
|
| 259 |
cell_c = ws.cell(row=row, column=3)
|
| 260 |
-
cell_c.value = self.format_currency(val_2024)
|
| 261 |
cell_c.font = bold_font if is_bold else normal_font
|
| 262 |
-
cell_c.alignment =
|
| 263 |
-
|
| 264 |
-
|
| 265 |
cell_d = ws.cell(row=row, column=4)
|
| 266 |
-
cell_d.value = self.format_currency(val_2023)
|
| 267 |
cell_d.font = bold_font if is_bold else normal_font
|
| 268 |
-
cell_d.alignment =
|
| 269 |
-
|
| 270 |
-
cell_d.border = thin_border
|
| 271 |
row += 1
|
| 272 |
|
| 273 |
logger.info("Extracting financial data...")
|
|
@@ -280,56 +298,66 @@ class PnLGenerator:
|
|
| 280 |
loss_sale_2024, loss_sale_2023 = self.get_loss_on_sale_data()
|
| 281 |
finance_2024, finance_2023 = self.get_finance_costs_data()
|
| 282 |
|
| 283 |
-
#
|
| 284 |
-
add_data_row("
|
| 285 |
-
add_data_row("
|
| 286 |
-
add_data_row("Other income", "17", other_income_2024, other_income_2023)
|
| 287 |
total_revenue_2024 = revenue_2024 + other_income_2024
|
| 288 |
total_revenue_2023 = revenue_2023 + other_income_2023
|
| 289 |
-
add_data_row("Total
|
| 290 |
|
| 291 |
-
#
|
| 292 |
-
add_data_row("Expenses", "", 0, 0
|
| 293 |
add_data_row("Cost of materials consumed", "18", materials_2024, materials_2023)
|
| 294 |
-
add_data_row("
|
| 295 |
-
add_data_row("
|
| 296 |
-
add_data_row("
|
| 297 |
-
add_data_row("
|
|
|
|
| 298 |
add_data_row("Finance costs", "23", finance_2024, finance_2023)
|
|
|
|
|
|
|
|
|
|
| 299 |
total_expenses_2024 = materials_2024 + employee_2024 + other_exp_2024 + depreciation_2024 + loss_sale_2024 + finance_2024
|
| 300 |
total_expenses_2023 = materials_2023 + employee_2023 + other_exp_2023 + depreciation_2023 + loss_sale_2023 + finance_2023
|
| 301 |
-
add_data_row("Total
|
| 302 |
-
|
| 303 |
-
|
| 304 |
-
|
| 305 |
-
|
| 306 |
-
|
| 307 |
-
|
| 308 |
-
|
| 309 |
-
add_data_row("
|
| 310 |
-
add_data_row("
|
| 311 |
-
|
| 312 |
-
|
| 313 |
-
add_data_row("
|
| 314 |
-
add_data_row("
|
| 315 |
-
|
| 316 |
-
|
| 317 |
-
|
| 318 |
-
|
| 319 |
-
add_data_row("
|
| 320 |
-
|
| 321 |
-
|
| 322 |
-
|
| 323 |
-
|
| 324 |
-
add_data_row("
|
| 325 |
-
add_data_row("
|
|
|
|
| 326 |
|
| 327 |
# Footer
|
| 328 |
row += 2
|
| 329 |
ws.merge_cells(f"A{row}:D{row}")
|
| 330 |
-
ws[f"A{row}"] = "
|
| 331 |
ws[f"A{row}"].font = normal_font
|
| 332 |
ws[f"A{row}"].alignment = left_align
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 333 |
|
| 334 |
# Save the file
|
| 335 |
try:
|
|
@@ -339,8 +367,8 @@ class PnLGenerator:
|
|
| 339 |
self.print_financial_summary(
|
| 340 |
total_revenue_2024, total_revenue_2023,
|
| 341 |
total_expenses_2024, total_expenses_2023,
|
| 342 |
-
|
| 343 |
-
|
| 344 |
)
|
| 345 |
return True
|
| 346 |
except PermissionError:
|
|
|
|
| 218 |
right_align = Alignment(horizontal="right", vertical="center")
|
| 219 |
ws.column_dimensions["A"].width = 45
|
| 220 |
ws.column_dimensions["B"].width = 8
|
| 221 |
+
ws.column_dimensions["C"].width = 20
|
| 222 |
+
ws.column_dimensions["D"].width = 20
|
| 223 |
row = 1
|
| 224 |
ws.merge_cells("A1:D1")
|
| 225 |
+
ws["A1"] = "PART II - STATEMENT OF PROFIT AND LOSS"
|
| 226 |
ws["A1"].font = title_font
|
| 227 |
ws["A1"].alignment = center_align
|
|
|
|
| 228 |
row += 2
|
| 229 |
+
ws["A3"] = "Name of the Company............................"
|
| 230 |
+
ws["A3"].font = normal_font
|
| 231 |
+
ws["A3"].alignment = left_align
|
| 232 |
row += 1
|
| 233 |
+
ws["A4"] = "Profit and loss statement for the year ended ..........................."
|
| 234 |
+
ws["A4"].font = normal_font
|
| 235 |
+
ws["A4"].alignment = left_align
|
| 236 |
+
row += 2
|
| 237 |
+
ws["D6"] = "(Rupees in...........)"
|
| 238 |
+
ws["D6"].font = normal_font
|
| 239 |
+
ws["D6"].alignment = right_align
|
| 240 |
+
row += 2
|
| 241 |
+
|
| 242 |
+
# Table headers
|
| 243 |
+
headers = ["Particulars", "Note No.", "Figures as at the end of current reporting period", "Figures as at the end of the previous reporting period"]
|
| 244 |
for col, header in enumerate(headers, 1):
|
| 245 |
cell = ws.cell(row=row, column=col)
|
| 246 |
cell.value = header
|
| 247 |
cell.font = header_font
|
| 248 |
+
cell.border = thin_border
|
| 249 |
+
cell.alignment = center_align if col > 1 else left_align
|
| 250 |
+
row += 1
|
| 251 |
+
|
| 252 |
+
# Column numbers
|
| 253 |
+
col_numbers = ["1", "2", "3", "4"]
|
| 254 |
+
for col, num in enumerate(col_numbers, 1):
|
| 255 |
+
cell = ws.cell(row=row, column=col)
|
| 256 |
+
cell.value = num
|
| 257 |
+
cell.font = header_font
|
| 258 |
+
cell.border = thin_border
|
| 259 |
+
cell.alignment = center_align
|
| 260 |
row += 1
|
| 261 |
|
| 262 |
def add_data_row(description: str, note_ref: str, val_2024: float, val_2023: float,
|
| 263 |
+
is_bold: bool = False) -> None:
|
| 264 |
"""Add a data row with proper formatting."""
|
| 265 |
nonlocal row
|
| 266 |
cell_a = ws.cell(row=row, column=1)
|
| 267 |
cell_a.value = description
|
| 268 |
+
cell_a.font = bold_font if is_bold else normal_font
|
| 269 |
cell_a.alignment = left_align
|
| 270 |
+
cell_a.border = thin_border
|
| 271 |
+
|
| 272 |
cell_b = ws.cell(row=row, column=2)
|
| 273 |
cell_b.value = note_ref if note_ref else ""
|
| 274 |
cell_b.font = normal_font
|
| 275 |
cell_b.alignment = center_align
|
| 276 |
+
cell_b.border = thin_border
|
| 277 |
+
|
| 278 |
cell_c = ws.cell(row=row, column=3)
|
| 279 |
+
cell_c.value = "xxx" if val_2024 == 0 and description != "Total Revenue (I + II)" and description != "Total expenses" and "Total" not in description and "Profit" not in description else self.format_currency(val_2024)
|
| 280 |
cell_c.font = bold_font if is_bold else normal_font
|
| 281 |
+
cell_c.alignment = center_align
|
| 282 |
+
cell_c.border = thin_border
|
| 283 |
+
|
| 284 |
cell_d = ws.cell(row=row, column=4)
|
| 285 |
+
cell_d.value = "xxx" if val_2023 == 0 and description != "Total Revenue (I + II)" and description != "Total expenses" and "Total" not in description and "Profit" not in description else self.format_currency(val_2023)
|
| 286 |
cell_d.font = bold_font if is_bold else normal_font
|
| 287 |
+
cell_d.alignment = center_align
|
| 288 |
+
cell_d.border = thin_border
|
|
|
|
| 289 |
row += 1
|
| 290 |
|
| 291 |
logger.info("Extracting financial data...")
|
|
|
|
| 298 |
loss_sale_2024, loss_sale_2023 = self.get_loss_on_sale_data()
|
| 299 |
finance_2024, finance_2023 = self.get_finance_costs_data()
|
| 300 |
|
| 301 |
+
# Revenue Section
|
| 302 |
+
add_data_row("I. Revenue from operations", "16", revenue_2024, revenue_2023)
|
| 303 |
+
add_data_row("II. Other income", "17", other_income_2024, other_income_2023)
|
|
|
|
| 304 |
total_revenue_2024 = revenue_2024 + other_income_2024
|
| 305 |
total_revenue_2023 = revenue_2023 + other_income_2023
|
| 306 |
+
add_data_row("III. Total Revenue (I + II)", "", total_revenue_2024, total_revenue_2023, is_bold=True)
|
| 307 |
|
| 308 |
+
# Expenses Section
|
| 309 |
+
add_data_row("IV. Expenses:", "", 0, 0)
|
| 310 |
add_data_row("Cost of materials consumed", "18", materials_2024, materials_2023)
|
| 311 |
+
add_data_row("Purchases of Stock-in-Trade", "", 0, 0)
|
| 312 |
+
add_data_row("Changes in inventories of finished goods", "", 0, 0)
|
| 313 |
+
add_data_row("work-in-progress and", "", 0, 0)
|
| 314 |
+
add_data_row("Stock-in-Trade", "", 0, 0)
|
| 315 |
+
add_data_row("Employee benefits expense", "19", employee_2024, employee_2023)
|
| 316 |
add_data_row("Finance costs", "23", finance_2024, finance_2023)
|
| 317 |
+
add_data_row("Depreciation and amortisation expense", "21", depreciation_2024, depreciation_2023)
|
| 318 |
+
add_data_row("Other expenses", "20", other_exp_2024, other_exp_2023)
|
| 319 |
+
|
| 320 |
total_expenses_2024 = materials_2024 + employee_2024 + other_exp_2024 + depreciation_2024 + loss_sale_2024 + finance_2024
|
| 321 |
total_expenses_2023 = materials_2023 + employee_2023 + other_exp_2023 + depreciation_2023 + loss_sale_2023 + finance_2023
|
| 322 |
+
add_data_row("Total expenses", "", total_expenses_2024, total_expenses_2023, is_bold=True)
|
| 323 |
+
|
| 324 |
+
profit_before_exceptional_2024 = total_revenue_2024 - total_expenses_2024
|
| 325 |
+
profit_before_exceptional_2023 = total_revenue_2023 - total_expenses_2023
|
| 326 |
+
add_data_row("V. Profit before exceptional and extraordinary items and tax (III - IV)", "", profit_before_exceptional_2024, profit_before_exceptional_2023, is_bold=True)
|
| 327 |
+
|
| 328 |
+
add_data_row("VI. Exceptional items", "", 0, 0)
|
| 329 |
+
add_data_row("VII. Profit before extraordinary items and tax (V - VI)", "", profit_before_exceptional_2024, profit_before_exceptional_2023, is_bold=True)
|
| 330 |
+
add_data_row("VIII. Extraordinary items", "", 0, 0)
|
| 331 |
+
add_data_row("IX. Profit before tax (VII- VIII)", "", profit_before_exceptional_2024, profit_before_exceptional_2023, is_bold=True)
|
| 332 |
+
|
| 333 |
+
# Tax Section
|
| 334 |
+
add_data_row("X. Tax expense:", "", 0, 0)
|
| 335 |
+
add_data_row("(1) Current tax", "", 0, 0)
|
| 336 |
+
add_data_row("(2) Deferred tax", "", 0, 0)
|
| 337 |
+
|
| 338 |
+
add_data_row("XI. Profit (Loss) for the period from continuing operations (VII-VIII)", "", profit_before_exceptional_2024, profit_before_exceptional_2023, is_bold=True)
|
| 339 |
+
add_data_row("XII. Profit/(loss) from discontinuing operations", "", 0, 0)
|
| 340 |
+
add_data_row("XIII. Tax expense of discontinuing operations", "", 0, 0)
|
| 341 |
+
add_data_row("XIV. Profit/(loss) from Discontinuing operations (after tax) (XII-XIII)", "", 0, 0)
|
| 342 |
+
add_data_row("XV. Profit (Loss) for the period (XI + XIV)", "", profit_before_exceptional_2024, profit_before_exceptional_2023, is_bold=True)
|
| 343 |
+
|
| 344 |
+
# Earnings per share
|
| 345 |
+
add_data_row("XVI. Earnings per equity share:", "", 0, 0)
|
| 346 |
+
add_data_row("(1) Basic", "", 0, 0)
|
| 347 |
+
add_data_row("(2) Diluted", "", 0, 0)
|
| 348 |
|
| 349 |
# Footer
|
| 350 |
row += 2
|
| 351 |
ws.merge_cells(f"A{row}:D{row}")
|
| 352 |
+
ws[f"A{row}"] = "See accompanying notes to the financial statements."
|
| 353 |
ws[f"A{row}"].font = normal_font
|
| 354 |
ws[f"A{row}"].alignment = left_align
|
| 355 |
+
row += 1
|
| 356 |
+
|
| 357 |
+
ws.merge_cells(f"A{row}:D{row}")
|
| 358 |
+
ws[f"A{row}"] = "GENERAL INSTRUCTIONS FOR PREPARATION OF STATEMENT OF"
|
| 359 |
+
ws[f"A{row}"].font = title_font
|
| 360 |
+
ws[f"A{row}"].alignment = center_align
|
| 361 |
|
| 362 |
# Save the file
|
| 363 |
try:
|
|
|
|
| 367 |
self.print_financial_summary(
|
| 368 |
total_revenue_2024, total_revenue_2023,
|
| 369 |
total_expenses_2024, total_expenses_2023,
|
| 370 |
+
profit_before_exceptional_2024, profit_before_exceptional_2023,
|
| 371 |
+
profit_before_exceptional_2024, profit_before_exceptional_2023
|
| 372 |
)
|
| 373 |
return True
|
| 374 |
except PermissionError:
|