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: Tax breakup based on items, missing GST fields #27524

Merged
merged 4 commits into from
Sep 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions erpnext/accounts/doctype/pos_invoice/pos_invoice.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ def validate(self):
self.validate_change_amount()
self.validate_change_account()
self.validate_item_cost_centers()
self.validate_warehouse()
self.validate_serialised_or_batched_item()
self.validate_stock_availablility()
self.validate_return_items_qty()
Expand Down
13 changes: 10 additions & 3 deletions erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
Original file line number Diff line number Diff line change
Expand Up @@ -1420,15 +1420,22 @@ def test_item_wise_tax_breakup_india(self):
itemised_tax, itemised_taxable_amount = get_itemised_tax_breakup_data(si)

expected_itemised_tax = {
"999800": {
"_Test Item": {
"Service Tax": {
"tax_rate": 10.0,
"tax_amount": 1000.0
}
},
"_Test Item 2": {
"Service Tax": {
"tax_rate": 10.0,
"tax_amount": 1500.0
"tax_amount": 500.0
}
}
}
expected_itemised_taxable_amount = {
"999800": 15000.0
"_Test Item": 10000.0,
"_Test Item 2": 5000.0
}

self.assertEqual(itemised_tax, expected_itemised_tax)
Expand Down
1 change: 1 addition & 0 deletions erpnext/patches.txt
Original file line number Diff line number Diff line change
Expand Up @@ -307,4 +307,5 @@ erpnext.patches.v14_0.delete_shopify_doctypes
erpnext.patches.v13_0.replace_supplier_item_group_with_party_specific_item
erpnext.patches.v13_0.update_dates_in_tax_withholding_category
erpnext.patches.v14_0.update_opportunity_currency_fields
erpnext.patches.v13_0.gst_fields_for_pos_invoice
erpnext.patches.v13_0.create_accounting_dimensions_in_pos_doctypes
44 changes: 44 additions & 0 deletions erpnext/patches/v13_0/gst_fields_for_pos_invoice.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
from __future__ import unicode_literals

import frappe
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields


def execute():
company = frappe.get_all('Company', filters = {'country': 'India'}, fields=['name'])
if not company:
return

hsn_sac_field = dict(fieldname='gst_hsn_code', label='HSN/SAC',
fieldtype='Data', fetch_from='item_code.gst_hsn_code', insert_after='description',
allow_on_submit=1, print_hide=1, fetch_if_empty=1)
nil_rated_exempt = dict(fieldname='is_nil_exempt', label='Is Nil Rated or Exempted',
fieldtype='Check', fetch_from='item_code.is_nil_exempt', insert_after='gst_hsn_code',
print_hide=1)
is_non_gst = dict(fieldname='is_non_gst', label='Is Non GST',
fieldtype='Check', fetch_from='item_code.is_non_gst', insert_after='is_nil_exempt',
print_hide=1)
taxable_value = dict(fieldname='taxable_value', label='Taxable Value',
fieldtype='Currency', insert_after='base_net_amount', hidden=1, options="Company:company:default_currency",
print_hide=1)
sales_invoice_gst_fields = [
dict(fieldname='billing_address_gstin', label='Billing Address GSTIN',
fieldtype='Data', insert_after='customer_address', read_only=1,
fetch_from='customer_address.gstin', print_hide=1),
dict(fieldname='customer_gstin', label='Customer GSTIN',
fieldtype='Data', insert_after='shipping_address_name',
fetch_from='shipping_address_name.gstin', print_hide=1),
dict(fieldname='place_of_supply', label='Place of Supply',
fieldtype='Data', insert_after='customer_gstin',
print_hide=1, read_only=1),
dict(fieldname='company_gstin', label='Company GSTIN',
fieldtype='Data', insert_after='company_address',
fetch_from='company_address.gstin', print_hide=1, read_only=1),
]

custom_fields = {
'POS Invoice': sales_invoice_gst_fields,
'POS Invoice Item': [hsn_sac_field, nil_rated_exempt, is_non_gst, taxable_value],
}

create_custom_fields(custom_fields, update=True)
2 changes: 2 additions & 0 deletions erpnext/regional/india/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,7 @@ def make_custom_fields(update=True):
'Purchase Order': purchase_invoice_gst_fields,
'Purchase Receipt': purchase_invoice_gst_fields,
'Sales Invoice': sales_invoice_gst_category + invoice_gst_fields + sales_invoice_shipping_fields + sales_invoice_gst_fields + si_ewaybill_fields,
'POS Invoice': sales_invoice_gst_fields,
'Delivery Note': sales_invoice_gst_fields + ewaybill_fields + sales_invoice_shipping_fields + delivery_note_gst_category,
'Payment Entry': payment_entry_fields,
'Journal Entry': journal_entry_fields,
Expand All @@ -501,6 +502,7 @@ def make_custom_fields(update=True):
'Sales Order Item': [hsn_sac_field, nil_rated_exempt, is_non_gst],
'Delivery Note Item': [hsn_sac_field, nil_rated_exempt, is_non_gst],
'Sales Invoice Item': [hsn_sac_field, nil_rated_exempt, is_non_gst, taxable_value],
'POS Invoice Item': [hsn_sac_field, nil_rated_exempt, is_non_gst, taxable_value],
'Purchase Order Item': [hsn_sac_field, nil_rated_exempt, is_non_gst],
'Purchase Receipt Item': [hsn_sac_field, nil_rated_exempt, is_non_gst],
'Purchase Invoice Item': [hsn_sac_field, nil_rated_exempt, is_non_gst, taxable_value],
Expand Down
31 changes: 16 additions & 15 deletions erpnext/regional/india/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,36 +117,37 @@ def get_itemised_tax_breakup_header(item_doctype, tax_accounts):
else:
return [_("Item"), _("Taxable Amount")] + tax_accounts

def get_itemised_tax_breakup_data(doc, account_wise=False):
def get_itemised_tax_breakup_data(doc, account_wise=False, hsn_wise=False):
itemised_tax = get_itemised_tax(doc.taxes, with_tax_account=account_wise)

itemised_taxable_amount = get_itemised_taxable_amount(doc.items)

if not frappe.get_meta(doc.doctype + " Item").has_field('gst_hsn_code'):
return itemised_tax, itemised_taxable_amount

item_hsn_map = frappe._dict()
for d in doc.items:
item_hsn_map.setdefault(d.item_code or d.item_name, d.get("gst_hsn_code"))
if hsn_wise:
item_hsn_map = frappe._dict()
for d in doc.items:
item_hsn_map.setdefault(d.item_code or d.item_name, d.get("gst_hsn_code"))

hsn_tax = {}
for item, taxes in itemised_tax.items():
hsn_code = item_hsn_map.get(item)
hsn_tax.setdefault(hsn_code, frappe._dict())
item_or_hsn = item if not hsn_wise else item_hsn_map.get(item)
hsn_tax.setdefault(item_or_hsn, frappe._dict())
for tax_desc, tax_detail in taxes.items():
key = tax_desc
if account_wise:
key = tax_detail.get('tax_account')
hsn_tax[hsn_code].setdefault(key, {"tax_rate": 0, "tax_amount": 0})
hsn_tax[hsn_code][key]["tax_rate"] = tax_detail.get("tax_rate")
hsn_tax[hsn_code][key]["tax_amount"] += tax_detail.get("tax_amount")
hsn_tax[item_or_hsn].setdefault(key, {"tax_rate": 0, "tax_amount": 0})
hsn_tax[item_or_hsn][key]["tax_rate"] = tax_detail.get("tax_rate")
hsn_tax[item_or_hsn][key]["tax_amount"] += tax_detail.get("tax_amount")

# set taxable amount
hsn_taxable_amount = frappe._dict()
for item in itemised_taxable_amount:
hsn_code = item_hsn_map.get(item)
hsn_taxable_amount.setdefault(hsn_code, 0)
hsn_taxable_amount[hsn_code] += itemised_taxable_amount.get(item)
item_or_hsn = item if not hsn_wise else item_hsn_map.get(item)
hsn_taxable_amount.setdefault(item_or_hsn, 0)
hsn_taxable_amount[item_or_hsn] += itemised_taxable_amount.get(item)

return hsn_tax, hsn_taxable_amount

Expand Down Expand Up @@ -440,7 +441,7 @@ def get_ewb_data(dt, dn):
data.itemList = []
data.totalValue = doc.total

data = get_item_list(data, doc)
data = get_item_list(data, doc, hsn_wise=True)

disable_rounded = frappe.db.get_single_value('Global Defaults', 'disable_rounded_total')
data.totInvValue = doc.grand_total if disable_rounded else doc.rounded_total
Expand Down Expand Up @@ -551,7 +552,7 @@ def get_address_details(data, doc, company_address, billing_address, dispatch_ad

return data

def get_item_list(data, doc):
def get_item_list(data, doc, hsn_wise=False):
for attr in ['cgstValue', 'sgstValue', 'igstValue', 'cessValue', 'OthValue']:
data[attr] = 0

Expand All @@ -563,7 +564,7 @@ def get_item_list(data, doc):
'cess_account': ['cessRate', 'cessValue']
}
item_data_attrs = ['sgstRate', 'cgstRate', 'igstRate', 'cessRate', 'cessNonAdvol']
hsn_wise_charges, hsn_taxable_amount = get_itemised_tax_breakup_data(doc, account_wise=True)
hsn_wise_charges, hsn_taxable_amount = get_itemised_tax_breakup_data(doc, account_wise=True, hsn_wise=hsn_wise)
for hsn_code, taxable_amount in hsn_taxable_amount.items():
item_data = frappe._dict()
if not hsn_code:
Expand Down