Skip to content

feat: Align Excel Report with Government Format #3451

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: develop
Choose a base branch
from

Conversation

karm1000
Copy link
Member

@karm1000 karm1000 commented Jun 13, 2025

image

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features
    • Excel export now automatically selects the appropriate template version based on the GST filing period.
    • Data is inserted into existing Excel sheets when possible, improving organization and readability.
    • Added support for field-level data transformations during Excel export, including specific handling for note types.
  • Bug Fixes
    • Corrected sheet naming for B2B data to ensure consistency.
  • Refactor
    • Enhanced Excel export logic for more flexible data insertion and template handling.

Copy link

coderabbitai bot commented Jun 13, 2025

Walkthrough

The updates introduce conditional logic for Excel template selection based on GST return periods, enhance the Excel exporting utility to support loading from templates and inserting data into existing sheets, and adjust an enum value for sheet name consistency. Method signatures and a class constructor are updated to support these changes.

Changes

File(s) Change Summary
india_compliance/gst_india/doctype/gstr_1_beta/gstr_1_export.py Added logic to select Excel template and bifurcate HSN data based on period; updated method signatures and sheet logic.
india_compliance/gst_india/utils/exporter.py Updated ExcelExporter to accept a template file, add insert_data for inserting into existing sheets; enhanced Worksheet methods.
india_compliance/gst_india/utils/gstr_1/init.py Changed GovExcelSheetName.B2B enum value to remove spaces after commas.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant GovExcel
    participant ExcelExporter

    User->>GovExcel: generate(gstin, period)
    GovExcel->>GovExcel: Extract filing month/year from period
    GovExcel->>GovExcel: Determine bifurcate_hsn flag
    GovExcel->>GovExcel: Select Excel template (V2.0 or V2.1)
    GovExcel->>ExcelExporter: build_excel(data, bifurcate_hsn)
    ExcelExporter->>ExcelExporter: Load template or create workbook
    GovExcel->>ExcelExporter: Insert or create sheets with data
    ExcelExporter-->>GovExcel: Return Excel file
Loading

Suggested labels

squash

Suggested reviewers

  • vorasmit

Poem

In the warren where spreadsheets bloom,
A rabbit tweaks the template room—
Sheets now fit just right,
With commas held tight,
And data hops in, dispelling the gloom!
🐇📊


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 526e95c and 0918656.

📒 Files selected for processing (1)
  • india_compliance/gst_india/doctype/gstr_1_beta/gstr_1_export.py (17 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • india_compliance/gst_india/doctype/gstr_1_beta/gstr_1_export.py
⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: Python Unit Tests
  • GitHub Check: Mergify Merge Protections
  • GitHub Check: Summary
✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@karm1000 karm1000 requested review from vorasmit and ljain112 June 13, 2025 09:33
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
india_compliance/gst_india/doctype/gstr_1_beta/gstr_1_export.py (1)

192-221: Template sheet removal executed in every loop iteration

excel.remove_sheet("Sheet") is inside the else branch, so if multiple unmapped
categories exist we call it repeatedly; subsequent calls are no-ops but waste cycles.

@@
-                excel.create_sheet(
+                if "Sheet" in excel.wb.sheetnames:
+                    excel.remove_sheet("Sheet")
+
+                excel.create_sheet(

Moving the removal outside the loop (just after ExcelExporter(file) construction) is cleaner and avoids redundant map look-ups.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 70220df and 75171bb.

⛔ Files ignored due to path filters (2)
  • india_compliance/gst_india/data/GSTR1_Excel_Workbook_Template_V2.0.xlsx is excluded by !**/*.xlsx
  • india_compliance/gst_india/data/GSTR1_Excel_Workbook_Template_V2.1.xlsx is excluded by !**/*.xlsx
📒 Files selected for processing (3)
  • india_compliance/gst_india/doctype/gstr_1_beta/gstr_1_export.py (3 hunks)
  • india_compliance/gst_india/utils/exporter.py (2 hunks)
  • india_compliance/gst_india/utils/gstr_1/__init__.py (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
india_compliance/gst_india/utils/gstr_1/__init__.py (1)
india_compliance/gst_india/utils/gstr_1/gstr_1_json_map.py (1)
  • B2B (135-301)
india_compliance/gst_india/doctype/gstr_1_beta/gstr_1_export.py (3)
india_compliance/gst_india/utils/__init__.py (2)
  • get_data_file_path (327-328)
  • get_period (1032-1041)
india_compliance/gst_india/utils/exporter.py (4)
  • ExcelExporter (11-66)
  • insert_data (33-40)
  • create_sheet (18-31)
  • remove_sheet (52-55)
india_compliance/gst_india/doctype/gst_return_log/gst_return_log.py (1)
  • load_data (35-50)
🪛 Pylint (3.3.7)
india_compliance/gst_india/utils/exporter.py

[refactor] 33-33: Too many arguments (6/5)

(R0913)


[refactor] 33-33: Too many positional arguments (6/5)

(R0917)

🪛 GitHub Actions: Linters
india_compliance/gst_india/doctype/gstr_1_beta/gstr_1_export.py

[error] 251-251: flake8: undefined name 'df' (F821)


[error] 1050-1050: flake8: undefined name 'df' (F821)

⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: Python Unit Tests
  • GitHub Check: Mergify Merge Protections
  • GitHub Check: Summary
🔇 Additional comments (2)
india_compliance/gst_india/utils/gstr_1/__init__.py (1)

298-298: Enum value change acknowledged

String trimmed to b2b,sez,de. No functional issues spotted; mapping dicts below already reference .value, so nothing else to update.

india_compliance/gst_india/doctype/gstr_1_beta/gstr_1_export.py (1)

144-147: Period slicing is brittle for future period formats

return_period[:2] / return_period[2:] assumes a fixed MMYYYY layout.
Quarterly periods (042024Q1, 062024, etc.) or any future format will break the date parsing and the HSN bifurcation logic.

-        month, year = gstr_1_log.return_period[:2], gstr_1_log.return_period[2:]
-        filing_from = getdate(f"{year}-{month}-01")
+        try:
+            filing_from = datetime.strptime(gstr_1_log.return_period, "%m%Y").date()
+        except ValueError:
+            # fallback for quarter/other formats – default to template V2.1
+            filing_from = HSN_BIFURCATION_FROM

Please verify all period variants used in production (monthly & quarterly) still hit the intended template.

Comment on lines 33 to 41
def insert_data(self, sheet_name, fields, data, start_row=1, start_column=1):
sheet = self.wb[sheet_name]

for i, row in enumerate(data, start=start_row):
for j, field in enumerate(fields, start=start_column):
value = row.get(field)
if value is not None:
sheet.cell(row=i, column=j, value=value)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Validate sheet existence & propagate data shape errors

self.wb[sheet_name] throws a KeyError if the template doesn’t contain the sheet – easy to hit when template versions drift.
Additionally, silently skipping None values can leave stale data in reused rows.

-        sheet = self.wb[sheet_name]
+        try:
+            sheet = self.wb[sheet_name]
+        except KeyError as exc:
+            raise ValueError(f"Worksheet '{sheet_name}' not found in workbook") from exc

Consider:

  • Clearing the destination row before writing, or writing explicit "" to keep column alignment.
  • Using sheet.max_row + 1 when start_row is omitted to append instead of overwrite.

These small checks will prevent corrupt exports and make debugging easier.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def insert_data(self, sheet_name, fields, data, start_row=1, start_column=1):
sheet = self.wb[sheet_name]
for i, row in enumerate(data, start=start_row):
for j, field in enumerate(fields, start=start_column):
value = row.get(field)
if value is not None:
sheet.cell(row=i, column=j, value=value)
def insert_data(self, sheet_name, fields, data, start_row=1, start_column=1):
- sheet = self.wb[sheet_name]
+ try:
+ sheet = self.wb[sheet_name]
+ except KeyError as exc:
+ raise ValueError(f"Worksheet '{sheet_name}' not found in workbook") from exc
+
for i, row in enumerate(data, start=start_row):
for j, field in enumerate(fields, start=start_column):
value = row.get(field)
if value is not None:
sheet.cell(row=i, column=j, value=value)
🧰 Tools
🪛 Pylint (3.3.7)

[refactor] 33-33: Too many arguments (6/5)

(R0913)


[refactor] 33-33: Too many positional arguments (6/5)

(R0917)

🤖 Prompt for AI Agents
In india_compliance/gst_india/utils/exporter.py around lines 33 to 41, add a
check to verify if sheet_name exists in self.wb before accessing it to avoid
KeyError. Modify the code to clear or overwrite the entire destination row to
prevent stale data by writing empty strings for None values instead of skipping
them. Also, if start_row is not provided, default it to sheet.max_row + 1 to
append data rather than overwrite existing rows. These changes will ensure safer
data insertion and better alignment.

Comment on lines 11 to 17
class ExcelExporter:
def __init__(self):
self.wb = openpyxl.Workbook()
def __init__(self, file=None):
if file:
self.wb = openpyxl.load_workbook(file, read_only=False, keep_links=False)
else:
self.wb = openpyxl.Workbook()

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Guard against missing template & avoid masking built-in file

openpyxl.load_workbook will raise FileNotFoundError / InvalidFileException when the path is wrong or points to a non-xlsx.
Right now we let that bubble as an unhandled traceback.
Also, the parameter is named file, shadowing Python’s built-in file type.

-    def __init__(self, file=None):
-        if file:
-            self.wb = openpyxl.load_workbook(file, read_only=False, keep_links=False)
+    def __init__(self, template_path: str | BytesIO | None = None):
+        """
+        :param template_path: Existing workbook (path or file-like).  
+                             If None, a new workbook is created.
+        """
+        try:
+            if template_path:
+                self.wb = openpyxl.load_workbook(
+                    template_path, read_only=False, keep_links=False
+                )
+            else:
+                self.wb = openpyxl.Workbook()
+        except FileNotFoundError:
+            frappe.throw(_("Excel template not found: {0}").format(template_path))
+        except Exception as exc:
+            frappe.throw(_("Failed to load Excel template: {0}").format(exc))

This makes the API safer and eliminates the name clash.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
class ExcelExporter:
def __init__(self):
self.wb = openpyxl.Workbook()
def __init__(self, file=None):
if file:
self.wb = openpyxl.load_workbook(file, read_only=False, keep_links=False)
else:
self.wb = openpyxl.Workbook()
class ExcelExporter:
def __init__(self, template_path: str | BytesIO | None = None):
"""
:param template_path: Existing workbook (path or file-like).
If None, a new workbook is created.
"""
try:
if template_path:
self.wb = openpyxl.load_workbook(
template_path, read_only=False, keep_links=False
)
else:
self.wb = openpyxl.Workbook()
except FileNotFoundError:
frappe.throw(_("Excel template not found: {0}").format(template_path))
except Exception as exc:
frappe.throw(_("Failed to load Excel template: {0}").format(exc))
🤖 Prompt for AI Agents
In india_compliance/gst_india/utils/exporter.py around lines 11 to 17, rename
the constructor parameter from 'file' to something like 'filepath' to avoid
shadowing the built-in 'file' type. Add a try-except block around the call to
openpyxl.load_workbook to catch FileNotFoundError and InvalidFileException, and
handle these exceptions gracefully, such as by logging an error or raising a
custom exception, to prevent unhandled tracebacks when the file is missing or
invalid.

Copy link

codecov bot commented Jun 13, 2025

Codecov Report

Attention: Patch coverage is 11.42857% with 31 lines in your changes missing coverage. Please review.

Project coverage is 59.34%. Comparing base (06cd0f1) to head (0918656).

Files with missing lines Patch % Lines
india_compliance/gst_india/utils/exporter.py 15.00% 17 Missing ⚠️
...nce/gst_india/doctype/gstr_1_beta/gstr_1_export.py 0.00% 14 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##           develop    #3451      +/-   ##
===========================================
- Coverage    59.43%   59.34%   -0.10%     
===========================================
  Files          136      136              
  Lines        13930    13955      +25     
===========================================
+ Hits          8279     8281       +2     
- Misses        5651     5674      +23     
Files with missing lines Coverage Δ
...ndia_compliance/gst_india/utils/gstr_1/__init__.py 99.11% <100.00%> (ø)
...nce/gst_india/doctype/gstr_1_beta/gstr_1_export.py 0.00% <0.00%> (ø)
india_compliance/gst_india/utils/exporter.py 19.10% <15.00%> (-0.90%) ⬇️

Impacted file tree graph

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
india_compliance/gst_india/utils/exporter.py (1)

12-17: Constructor still shadows built-in file and lacks safe load handling

Previous review already pointed this out, but the argument is still named file and openpyxl.load_workbook is still un-guarded.
Uncaught FileNotFoundError, InvalidFileException, etc. will propagate as a traceback.

-    def __init__(self, file=None):
-        if file:
-            self.wb = openpyxl.load_workbook(file, read_only=False, keep_links=False)
+    def __init__(self, template_path: str | BytesIO | None = None):
+        try:
+            if template_path:
+                self.wb = openpyxl.load_workbook(
+                    template_path, read_only=False, keep_links=False
+                )
+            else:
+                self.wb = openpyxl.Workbook()
+        except FileNotFoundError as exc:
+            frappe.throw(_("Excel template not found: {0}").format(template_path))
+        except Exception as exc:
+            frappe.throw(_("Failed to load Excel template: {0}").format(exc))

This also removes the name clash with the built-in file.

🧹 Nitpick comments (1)
india_compliance/gst_india/utils/exporter.py (1)

147-156: Signature is getting unwieldy – eight positional params trigger pylint R0913/R0917

The new helper already hits the “too many arguments” warning. Group related arguments into a dataclass or accept **options to keep the public API stable while remaining lint-clean.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ccc4ae2 and 05379c6.

⛔ Files ignored due to path filters (2)
  • india_compliance/gst_india/data/GSTR1_Excel_Template_V2.0.xlsx is excluded by !**/*.xlsx
  • india_compliance/gst_india/data/GSTR1_Excel_Template_V2.1.xlsx is excluded by !**/*.xlsx
📒 Files selected for processing (2)
  • india_compliance/gst_india/doctype/gstr_1_beta/gstr_1_export.py (5 hunks)
  • india_compliance/gst_india/utils/exporter.py (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • india_compliance/gst_india/doctype/gstr_1_beta/gstr_1_export.py
🧰 Additional context used
🪛 Pylint (3.3.7)
india_compliance/gst_india/utils/exporter.py

[refactor] 147-147: Too many arguments (8/5)

(R0913)


[refactor] 147-147: Too many positional arguments (8/5)

(R0917)


[refactor] 147-147: Too many local variables (16/15)

(R0914)

⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: Mergify Merge Protections
  • GitHub Check: Summary
🔇 Additional comments (1)
india_compliance/gst_india/utils/exporter.py (1)

246-248: custom_styles lookup silently fails if style_name is None

This is a corollary of the bug above – once fixed, ensure style_name is never None before dereferencing:

if style_name:
    header = kwargs.get("header") or self.headers[column - 1]
    custom_styles = header.get(style_name)
    if custom_styles:
        style.update(custom_styles)

Fail fast with a clear error when an unknown flag is supplied.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🔭 Outside diff range comments (1)
india_compliance/gst_india/utils/exporter.py (1)

233-243: apply_format still non-deterministic – header key competes with style flag

header is fetched but not removed before popitem(), so it may be the item popped, breaking style resolution (style_name = None).

-        key, value = kwargs.popitem()
+        # remove meta-parameter first
+        header_cfg = kwargs.pop("header", None)
+        if len(kwargs) != 1:
+            raise ValueError(
+                "Exactly one style flag (is_data / is_header / is_total / is_filter) "
+                "must be supplied"
+            )
+        key, value = kwargs.popitem()
♻️ Duplicate comments (3)
india_compliance/gst_india/utils/exporter.py (3)

12-17: Still shadowing the built-in file and no guard for invalid paths

The constructor continues to receive a parameter named file, overriding Python’s built-in and missing the previously-requested try/except around openpyxl.load_workbook. Any wrong path will raise an unhandled stack-trace.

-    def __init__(self, file=None):
-        if file:
-            self.wb = openpyxl.load_workbook(file, read_only=False, keep_links=False)
+    def __init__(self, template_path: str | BytesIO | None = None):
+        """
+        :param template_path: Path / file-like object pointing to an .xlsx template.
+                              If None, a new blank workbook is created.
+        """
+        try:
+            if template_path:
+                self.wb = openpyxl.load_workbook(
+                    template_path, read_only=False, keep_links=False
+                )
+                self.wb.calculation.fullCalcOnLoad = False
+            else:
+                self.wb = openpyxl.Workbook()
+        except FileNotFoundError as exc:
+            frappe.throw(_("Excel template not found: {0}").format(template_path))
+        except Exception as exc:  # noqa: BLE001 – propagate a user-friendly message
+            frappe.throw(_("Failed to load Excel template: {0}").format(exc))
-            self.wb.calculation.fullCalcOnLoad = False
-        else:
-            self.wb = openpyxl.Workbook()

34-45: Worksheet() re-instantiated on every call & no sheet existence check

Spinning up a fresh Worksheet() each time discards state and still hits KeyError when sheet_name is missing. This is exactly the issue pointed out in the previous review.

-        Worksheet().insert_data(workbook=self.wb, **kwargs)
+        ws_util = Worksheet()
+        try:
+            ws_util.insert_data(workbook=self.wb, **kwargs)
+        except KeyError as exc:
+            raise ValueError(
+                f"Worksheet '{kwargs.get('sheet_name')}' not found in template"
+            ) from exc

157-160: Unprotected access to workbook[sheet_name] will crash on mismatched templates

A missing sheet name detonates the export flow with a KeyError. Wrap it and raise a descriptive error as suggested earlier.

🧹 Nitpick comments (2)
india_compliance/gst_india/utils/exporter.py (2)

147-156: API surface is growing unwieldy – 8 positional params trigger Pylint R0913/R0917

insert_data now takes 8 positional arguments. Consider collapsing rarely-used ones into **options or using a small dataclass/@dataclass for clarity.


233-243: Style override lookup can crash when header is None

If the caller does not pass a header kwarg (usual path), header = None or self.headers[column-1] is fine.
But when header is passed as None, header.get() will raise. Add a guard.

-        header = kwargs.get("header") or self.headers[column - 1]
-        custom_styles = header.get(style_name)
+        header_dict = kwargs.get("header") or self.headers[column - 1]
+        custom_styles = header_dict.get(style_name) if header_dict else None
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 05379c6 and 88e0c22.

⛔ Files ignored due to path filters (2)
  • india_compliance/gst_india/data/GSTR1_Excel_Template_V2.0.xlsx is excluded by !**/*.xlsx
  • india_compliance/gst_india/data/GSTR1_Excel_Template_V2.1.xlsx is excluded by !**/*.xlsx
📒 Files selected for processing (2)
  • india_compliance/gst_india/doctype/gstr_1_beta/gstr_1_export.py (17 hunks)
  • india_compliance/gst_india/utils/exporter.py (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • india_compliance/gst_india/doctype/gstr_1_beta/gstr_1_export.py
🧰 Additional context used
🪛 Pylint (3.3.7)
india_compliance/gst_india/utils/exporter.py

[refactor] 147-147: Too many arguments (8/5)

(R0913)


[refactor] 147-147: Too many positional arguments (8/5)

(R0917)


[refactor] 147-147: Too many local variables (16/15)

(R0914)

⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: Mergify Merge Protections
  • GitHub Check: Summary

Comment on lines 162 to 172
for i, row in enumerate(data, start=start_row):
for j, header in enumerate(headers, start=start_column):
fieldname, transform = header.get("fieldname"), header.get("transform")
value = row.get(fieldname)

if transform:
value = transform(value)

if value is not None:
sheet.cell(row=i, column=j, value=value)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Row data with None leaves stale values & no style is applied

  1. Skipping None writes means reused template rows may keep old values.
  2. Inserting without calling apply_style leads to inconsistent fonts/number-formats compared to create().

At minimum, write an empty string for None and optionally call a light apply_style for consistency.

-                if value is not None:
-                    sheet.cell(row=i, column=j, value=value)
+                cell = sheet.cell(row=i, column=j, value=value or "")
+                # optional: propagate default data style
+                # self.apply_style(i, j, self.data_format)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
for i, row in enumerate(data, start=start_row):
for j, header in enumerate(headers, start=start_column):
fieldname, transform = header.get("fieldname"), header.get("transform")
value = row.get(fieldname)
if transform:
value = transform(value)
if value is not None:
sheet.cell(row=i, column=j, value=value)
for i, row in enumerate(data, start=start_row):
for j, header in enumerate(headers, start=start_column):
fieldname, transform = header.get("fieldname"), header.get("transform")
value = row.get(fieldname)
if transform:
value = transform(value)
- if value is not None:
- sheet.cell(row=i, column=j, value=value)
+ # always write something (empty string for None) to avoid stale cells
+ cell = sheet.cell(row=i, column=j, value=value if value is not None else "")
+ # optional: propagate default data style
+ # self.apply_style(i, j, self.data_format)
🤖 Prompt for AI Agents
In india_compliance/gst_india/utils/exporter.py around lines 162 to 172, the
code skips writing cells when the value is None, causing stale values to remain
in reused template rows and styles not being applied consistently. To fix this,
modify the code to write an empty string to the cell when the value is None
instead of skipping it, and ensure that apply_style is called on the cell to
maintain consistent fonts and number formats as done in the create() method.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🔭 Outside diff range comments (1)
india_compliance/gst_india/utils/exporter.py (1)

228-239: apply_format is still non-deterministic when both header and a style flag are passed

kwargs.popitem() pops an arbitrary key; if it removes header, style_name becomes NoneAttributeError. Past feedback highlighted this.

-        key, value = kwargs.popitem()
+        # Separate meta-parameter `header`
+        header = kwargs.pop("header", None)
+        if len(kwargs) != 1:
+            raise ValueError("Exactly one style flag (is_data / is_header / …) must be passed")
+        key, value = kwargs.popitem()
♻️ Duplicate comments (3)
india_compliance/gst_india/utils/exporter.py (3)

155-167: Stale template values & missing style propagation

  1. Skipping cells when value is None keeps old data in reused rows.
  2. No call to apply_format, so rows inserted into templates lose consistent styling.
-                if value is not None:
-                    sheet.cell(row=i, column=j, value=value)
+                cell = sheet.cell(row=i, column=j, value="" if value is None else value)
+                # Propagate default data style for consistency
+                # self.apply_format(row=i, column=j, is_data=True, header=header)

34-44: insert_data duplicates Worksheet() per call & still risks KeyError

Every invocation creates a new Worksheet() instance, losing any internal state, and Workbook[sheet_name] is accessed without validation.

-        Worksheet().insert_data(workbook=self.wb, **kwargs)
+        ws_util = Worksheet()
+        try:
+            ws_util.insert_data(workbook=self.wb, **kwargs)
+        except KeyError as exc:
+            raise ValueError(f"Worksheet '{kwargs.get('sheet_name')}' not found in template") from exc

11-18: Rename file parameter and add defensive load handling

– Shadows the built-in file type.
openpyxl.load_workbook exceptions (FileNotFoundError, InvalidFileException) are still unhandled, bubbling a raw traceback to users.

-    def __init__(self, file=None):
-        if file:
-            self.wb = openpyxl.load_workbook(file, read_only=False, keep_links=False)
+    def __init__(self, template_path: str | BytesIO | None = None):
+        try:
+            if template_path:
+                self.wb = openpyxl.load_workbook(
+                    template_path, read_only=False, keep_links=False
+                )
+            else:
+                self.wb = openpyxl.Workbook()
+        except FileNotFoundError:
+            frappe.throw(_("Excel template not found: {0}").format(template_path))
+        except Exception as exc:
+            frappe.throw(_("Failed to load Excel template: {0}").format(exc))
🧹 Nitpick comments (1)
india_compliance/gst_india/doctype/gstr_1_beta/gstr_1_export.py (1)

261-272: get_fields helper is unused – dead code

The method is never referenced after introduction; consider removing or wiring it up.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 88e0c22 and 526e95c.

📒 Files selected for processing (3)
  • india_compliance/gst_india/doctype/gstr_1_beta/gstr_1_export.py (17 hunks)
  • india_compliance/gst_india/utils/exporter.py (4 hunks)
  • india_compliance/gst_india/utils/gstr_1/__init__.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • india_compliance/gst_india/utils/gstr_1/init.py
🧰 Additional context used
🧬 Code Graph Analysis (1)
india_compliance/gst_india/doctype/gstr_1_beta/gstr_1_export.py (3)
india_compliance/gst_india/utils/__init__.py (2)
  • get_data_file_path (327-328)
  • get_period (1032-1041)
india_compliance/gst_india/utils/exporter.py (5)
  • ExcelExporter (11-71)
  • insert_data (34-43)
  • insert_data (146-166)
  • create_sheet (19-32)
  • remove_sheet (57-60)
india_compliance/gst_india/doctype/gst_return_log/gst_return_log.py (1)
  • load_data (35-50)
🪛 Pylint (3.3.7)
india_compliance/gst_india/doctype/gstr_1_beta/gstr_1_export.py

[error] 468-468: Instance of 'str' has no 'value' member

(E1101)


[error] 469-469: Class 'GovExcelField' has no 'TRANSACTION_TYPE' member

(E1101)

india_compliance/gst_india/utils/exporter.py

[refactor] 146-146: Too many arguments (7/5)

(R0913)


[refactor] 146-146: Too many positional arguments (7/5)

(R0917)

⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: Python Unit Tests
  • GitHub Check: Mergify Merge Protections
  • GitHub Check: Summary
🔇 Additional comments (1)
india_compliance/gst_india/doctype/gstr_1_beta/gstr_1_export.py (1)

199-204: Template path may be None – guard before loading

self.TEMPLATE_EXCEL_FILE.get(version) returns None if the dict key is mistyped.
ExcelExporter(None) falls back to an empty workbook, silently ignoring the missing template.

-        file = self.TEMPLATE_EXCEL_FILE.get(version)
-        excel = ExcelExporter(file)
+        template_path = self.TEMPLATE_EXCEL_FILE.get(version)
+        if not template_path:
+            frappe.throw(_(f"Excel template for version {version} not found."))
+
+        excel = ExcelExporter(template_path)

Comment on lines +299 to 300
"transform": lambda x: x.strftime("%d-%b-%y"),
},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

strftime without None guard – crashes on blank dates

Several headers use lambda x: x.strftime("%d-%b-%y").
If the source field is None, this raises AttributeError.

Example fix (apply to all similar lambdas):

-"transform": lambda x: x.strftime("%d-%b-%y"),
+"transform": lambda x: x.strftime("%d-%b-%y") if x else "",

Also applies to: 363-363, 466-471, 533-533, 589-589, 610-610

🤖 Prompt for AI Agents
In india_compliance/gst_india/doctype/gstr_1_beta/gstr_1_export.py at lines
299-300 and similarly at lines 363, 466-471, 533, 589, and 610, the lambda
functions use x.strftime("%d-%b-%y") without checking if x is None, causing
AttributeError on blank dates. Update these lambdas to first check if x is not
None before calling strftime, returning an empty string or None otherwise to
prevent crashes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants