Skip to content
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

fix: JSON Mapping for ITC-04 filing #2273

Merged
merged 29 commits into from
Feb 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
ea22e8f
fix: JSON mapping for itc_04
Ninad1306 Jun 21, 2024
fddcc72
refactor: generalize JSON mapping for broader use
Ninad1306 Jun 21, 2024
eb3dc17
Merge branch 'resilient-tech:develop' into itc_04_filing
Ninad1306 Jun 24, 2024
06b2e65
fix: JSON mapping changes
Ninad1306 Jun 24, 2024
ef334f5
refactor: avoid duplication
Ninad1306 Jun 24, 2024
c985870
fix: redundant class removed
Ninad1306 Jun 25, 2024
ba97635
fix: display challan_no at invoice level
Ninad1306 Jun 25, 2024
11a31a9
Merge branch 'resilient-tech:develop' into itc_04_filing
Ninad1306 Jul 24, 2024
b988ccf
Merge branch 'resilient-tech:develop' into itc_04_filing
Ninad1306 Oct 1, 2024
337177b
fix: json download for itc-04 return
Ninad1306 Oct 2, 2024
aeabe5e
fix: query for original challan date
Ninad1306 Oct 2, 2024
ae960b4
fix: update query for job worker challan no
Ninad1306 Oct 2, 2024
9544f64
fix: add description in query
Ninad1306 Oct 2, 2024
6663a82
fix: changes in mapping keys
Ninad1306 Dec 25, 2024
42eda61
test: validate itc-04 json mapping
Ninad1306 Dec 25, 2024
320dd62
Merge branch 'develop' of github.com:resilient-tech/india-compliance …
Ninad1306 Feb 3, 2025
abf19de
fix: Merge branch 'develop' into itc_04_filing
ljain112 Feb 4, 2025
cf2468b
fix: utility for period options
Ninad1306 Feb 5, 2025
a1dc04d
fix: changes in mapping and field names
Ninad1306 Feb 5, 2025
410cf1f
fix: change in goods_type mapping
Ninad1306 Feb 5, 2025
eb21774
fix: allow annual returns
Ninad1306 Feb 6, 2025
a652c98
test: validate itc-04 json export
Ninad1306 Feb 6, 2025
22ca60a
chore: resolve codacy issues
Ninad1306 Feb 6, 2025
eeb581c
refactor: rename table names with better var names
vorasmit Feb 11, 2025
f3fc27b
Merge branch 'develop' into itc_04_filing
vorasmit Feb 11, 2025
2380cf7
fix: correct idx for months
vorasmit Feb 11, 2025
22c499c
fix: guess period and remove dialog
Ninad1306 Feb 12, 2025
0003aff
fix: change in name of constant
Ninad1306 Feb 12, 2025
3dc5c0c
fix: use enums
Ninad1306 Feb 13, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,9 @@ def make_stock_transfer_entry(**args):
doc = frappe.get_doc(ste_dict)
doc.insert()

if args.do_not_submit:
return doc

return doc.submit()


Expand All @@ -170,6 +173,13 @@ def make_stock_entry(**args):
return se


def create_subcontracting_data():
make_raw_materials()
make_service_items()
make_subcontracted_items()
make_boms()


SERVICE_ITEM = {
"item_code": "Subcontracted Service Item 1",
"qty": 10,
Expand All @@ -183,10 +193,7 @@ class TestSubcontractingTransaction(IntegrationTestCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
make_raw_materials()
make_service_items()
make_subcontracted_items()
make_boms()
create_subcontracting_data()

frappe.db.set_single_value(
"GST Settings",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,18 @@ frappe.query_reports["GST Job Work Stock Movement"] = {
reqd: 1,
},
],
onload: function (query_report) {
query_report.page.add_inner_button(__("Export JSON"), function () {
frappe.call({
method: "india_compliance.gst_india.utils.itc_04.itc_04_export.download_itc_04_json",
args: { filters: query_report.get_values() },
callback: r => {
india_compliance.trigger_file_download(
JSON.stringify(r.message.data),
r.message.filename
);
},
});
});
},
};
139 changes: 21 additions & 118 deletions india_compliance/gst_india/utils/gstr_1/gstr_1_json_map.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@
import frappe
from frappe.utils import flt

from india_compliance.gst_india.constants import STATE_NUMBERS, UOM_MAP
from india_compliance.gst_india.constants import UOM_MAP
from india_compliance.gst_india.report.gstr_1.gstr_1 import (
GSTR1DocumentIssuedSummary,
GSTR11A11BData,
)
from india_compliance.gst_india.utils import MONTHS, get_gst_accounts_by_type
from india_compliance.gst_india.utils.__init__ import get_party_for_gstin
from india_compliance.gst_india.utils import (
MONTHS,
get_gst_accounts_by_type,
get_party_for_gstin,
)
from india_compliance.gst_india.utils.gstr_1 import (
CATEGORY_SUB_CATEGORY_MAPPING,
SUB_CATEGORY_GOV_CATEGORY_MAPPING,
Expand All @@ -24,20 +27,20 @@
GSTR1_SubCategory,
)
from india_compliance.gst_india.utils.gstr_1.gstr_1_data import GSTR1Invoices
from india_compliance.gst_india.utils.gstr_mapper_utils import GovDataMapper

############################################################################################################
### Map Govt JSON to Internal Data Structure ###############################################################
############################################################################################################


class GovDataMapper:
class GSTR1DataMapper(GovDataMapper):
"""
GST Developer API Documentation for Returns - https://developer.gst.gov.in/apiportal/taxpayer/returns

GSTR-1 JSON format - https://developer.gst.gov.in/pages/apiportal/data/Returns/GSTR1%20-%20Save%20GSTR1%20data/v4.0/GSTR1%20-%20Save%20GSTR1%20data%20attributes.xlsx
"""

KEY_MAPPING = {}
# default item amounts
DEFAULT_ITEM_AMOUNTS = {
GSTR1_ItemField.TAXABLE_VALUE.value: 0,
Expand Down Expand Up @@ -68,110 +71,10 @@ class GovDataMapper:
}

def __init__(self):
self.set_total_defaults()

self.value_formatters_for_internal = {}
self.value_formatters_for_gov = {}
super().__init__()
self.gstin_party_map = {}
# value formatting constants

self.STATE_NUMBERS = self.reverse_dict(STATE_NUMBERS)

def format_data(
self, data: dict, default_data: dict = None, for_gov: bool = False
) -> dict:
"""
Objective: Convert Object from one format to another.
eg: Govt JSON to Internal Data Structure

Args:
data (dict): Data to be converted
default_data (dict, optional): Default Data to be added. Hardcoded values.
for_gov (bool, optional): If the data is to be converted to Govt JSON. Defaults to False.
else it will be converted to Internal Data Structure.

Steps:
1. Use key mapping to map the keys from one format to another.
2. Use value formatters to format the values of the keys.
3. Round values
"""
output = {}

if default_data:
for key, value in default_data.items():
if not (value or value == 0):
continue

output[key] = value

key_mapping = self.KEY_MAPPING.copy()

if for_gov:
key_mapping = self.reverse_dict(key_mapping)

value_formatters = (
self.value_formatters_for_gov
if for_gov
else self.value_formatters_for_internal
)

for old_key, new_key in key_mapping.items():
invoice_data_value = data.get(old_key, "")

if not for_gov and old_key == "flag":
continue

if new_key in self.DISCARD_IF_ZERO_FIELDS and not invoice_data_value:
continue

if not (invoice_data_value or invoice_data_value == 0):
# continue if value is None or empty object
continue

value_formatter = value_formatters.get(old_key)

if callable(value_formatter):
output[new_key] = value_formatter(invoice_data_value, data)
else:
output[new_key] = invoice_data_value

if new_key in self.FLOAT_FIELDS:
output[new_key] = flt(output[new_key], 2)

return output

# common utils

def update_totals(self, invoice, items):
"""
Update item totals to the invoice row
"""
total_data = self.TOTAL_DEFAULTS.copy()

for item in items:
for field, value in item.items():
total_field = f"total_{field}"

if total_field not in total_data:
continue

invoice[total_field] = invoice.setdefault(total_field, 0) + value

def set_total_defaults(self):
self.TOTAL_DEFAULTS = {
f"total_{key}": 0 for key in self.DEFAULT_ITEM_AMOUNTS.keys()
}

def reverse_dict(self, data):
return {v: k for k, v in data.items()}

# common value formatters
def map_place_of_supply(self, pos, *args):
if pos.isnumeric():
return f"{pos}-{self.STATE_NUMBERS.get(pos)}"

return pos.split("-")[0]

def format_item_for_internal(self, items, *args):
return [
{
Expand Down Expand Up @@ -205,7 +108,7 @@ def format_date_for_gov(self, date, *args):
return datetime.strptime(date, "%Y-%m-%d").strftime("%d-%m-%Y")


class B2B(GovDataMapper):
class B2B(GSTR1DataMapper):
"""
GST API Version - v4.0

Expand Down Expand Up @@ -386,7 +289,7 @@ def document_category_mapping(self, sub_category, data):
return self.DOCUMENT_CATEGORIES.get(sub_category, sub_category)


class B2CL(GovDataMapper):
class B2CL(GSTR1DataMapper):
"""
GST API Version - v4.0

Expand Down Expand Up @@ -518,7 +421,7 @@ def convert_to_gov_data_format(self, input_data, **kwargs):
return list(pos_data.values())


class Exports(GovDataMapper):
class Exports(GSTR1DataMapper):
"""
GST API Version - v4.0

Expand Down Expand Up @@ -670,7 +573,7 @@ def format_item_for_gov(self, items, *args):
return [self.format_data(item, for_gov=True) for item in items]


class B2CS(GovDataMapper):
class B2CS(GSTR1DataMapper):
"""
GST API Version - v4.0

Expand Down Expand Up @@ -768,7 +671,7 @@ def format_data(self, data, default_data=None, for_gov=False):
return data


class NilRated(GovDataMapper):
class NilRated(GSTR1DataMapper):
"""
GST API Version - v4.0

Expand Down Expand Up @@ -880,7 +783,7 @@ def document_category_mapping(self, doc_category, data):
return self.DOCUMENT_CATEGORIES.get(doc_category, doc_category)


class CDNR(GovDataMapper):
class CDNR(GSTR1DataMapper):
"""
GST API Version - v4.0

Expand Down Expand Up @@ -1074,7 +977,7 @@ def format_doc_value(self, value, data):
return value * -1 if data[GovDataField.NOTE_TYPE.value] == "C" else value


class CDNUR(GovDataMapper):
class CDNUR(GSTR1DataMapper):
"""
GST API Version - v4.0

Expand Down Expand Up @@ -1214,7 +1117,7 @@ def format_doc_value(self, value, data):
return value * -1 if data[GovDataField.NOTE_TYPE.value] == "C" else value


class HSNSUM(GovDataMapper):
class HSNSUM(GSTR1DataMapper):
"""
GST API Version - v4.0

Expand Down Expand Up @@ -1341,7 +1244,7 @@ def map_uom(self, uom, data=None):
return f"OTH-{UOM_MAP.get('OTH')}"


class AT(GovDataMapper):
class AT(GSTR1DataMapper):
"""
GST API Version - v4.0

Expand Down Expand Up @@ -1524,7 +1427,7 @@ class TXPD(AT):
MULTIPLIER = -1


class DOC_ISSUE(GovDataMapper):
class DOC_ISSUE(GSTR1DataMapper):
"""
GST API Version - v4.0

Expand Down Expand Up @@ -1665,7 +1568,7 @@ def get_document_nature(self, doc_nature, *args):
return self.DOCUMENT_NATURE.get(doc_nature, doc_nature)


class SUPECOM(GovDataMapper):
class SUPECOM(GSTR1DataMapper):
"""
GST API Version - v4.0

Expand Down Expand Up @@ -1742,7 +1645,7 @@ def convert_to_gov_data_format(self, input_data, **kwargs):
return output


class RETSUM(GovDataMapper):
class RETSUM(GSTR1DataMapper):
"""
Convert GSTR-1 Summary as returned by the API to the internal format

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
)
from india_compliance.gst_india.utils import get_party_for_gstin as _get_party_for_gstin
from india_compliance.gst_india.utils.gstr_1 import (
SUB_CATEGORY_GOV_CATEGORY_MAPPING,
GovDataField,
GSTR1_B2B_InvoiceType,
GSTR1_DataField,
Expand Down Expand Up @@ -40,11 +39,7 @@ def normalize_data(data):


def process_mapped_data(data):
return list(
get_category_wise_data(
normalize_data(copy.deepcopy(data)), SUB_CATEGORY_GOV_CATEGORY_MAPPING
).values()
)[0]
return list(get_category_wise_data(normalize_data(copy.deepcopy(data))).values())[0]


class TestB2B(IntegrationTestCase):
Expand Down
Loading
Loading