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

feat: ability to edit Place of Supply #504

Merged
merged 1 commit into from Mar 10, 2023
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
21 changes: 17 additions & 4 deletions india_compliance/gst_india/constants/custom_fields.py
Expand Up @@ -8,6 +8,17 @@
gst_category_options = "\n".join(GST_CATEGORIES)
default_gst_category = "Unregistered"


def get_place_of_supply_options():
options = []

for state_name, state_number in STATE_NUMBERS.items():
options.append(f"{state_number}-{state_name}")

options.append("96-Other Countries")
return "\n".join(sorted(options))


party_fields = [
{
"fieldname": "tax_details_section",
Expand Down Expand Up @@ -83,10 +94,11 @@
{
"fieldname": "place_of_supply",
"label": "Place of Supply",
"fieldtype": "Data",
"fieldtype": "Autocomplete",
"options": get_place_of_supply_options(),
"insert_after": "company_gstin",
"print_hide": 1,
"read_only": 1,
"read_only": 0,
"translatable": 0,
},
{
Expand Down Expand Up @@ -164,10 +176,11 @@
{
"fieldname": "place_of_supply",
"label": "Place of Supply",
"fieldtype": "Data",
"fieldtype": "Autocomplete",
"options": get_place_of_supply_options(),
"insert_after": "gst_category",
"print_hide": 1,
"read_only": 1,
"read_only": 0,
"length": 50,
"translatable": 0,
},
Expand Down
23 changes: 22 additions & 1 deletion india_compliance/gst_india/overrides/test_transaction.py
Expand Up @@ -431,13 +431,34 @@ def test_purchase_from_unregistered_supplier(self):
doc.insert,
)

def test_purchase_with_different_place_of_supply(self):
if self.is_sales_doctype:
return

doc = create_transaction(
**self.transaction_details,
is_out_state=True,
do_not_save=True,
)

doc.place_of_supply = "96-Other Countries"
doc.save()

# place of supply shouldn't get overwritten
self.assertEqual(doc.place_of_supply, "96-Other Countries")

# IGST should get applied
self.assertIn("IGST", doc.taxes[-1].description)

def test_invalid_gst_account_type(self):
doc = create_transaction(**self.transaction_details, do_not_save=True)
doc.append(
"taxes",
{
"charge_type": "On Net Total",
"account_head": f"{'Input' if self.is_sales_doctype else 'Output'} Tax IGST - _TIRC",
"account_head": (
f"{'Input' if self.is_sales_doctype else 'Output'} Tax IGST - _TIRC"
),
"description": "IGST",
"rate": 18,
"cost_center": "Main - _TIRC",
Expand Down
15 changes: 11 additions & 4 deletions india_compliance/gst_india/overrides/transaction.py
Expand Up @@ -346,7 +346,8 @@ def validate_items(doc):


def set_place_of_supply(doc, method=None):
doc.place_of_supply = get_place_of_supply(doc, doc.doctype)
if not doc.place_of_supply:
doc.place_of_supply = get_place_of_supply(doc, doc.doctype)


def is_inter_state_supply(doc):
Expand Down Expand Up @@ -478,11 +479,13 @@ def get_regional_round_off_accounts(company, account_list):


def update_party_details(party_details, doctype, company):
party_details.update(get_gst_details(party_details, doctype, company))
party_details.update(
get_gst_details(party_details, doctype, company, update_place_of_supply=True)
)


@frappe.whitelist()
def get_gst_details(party_details, doctype, company):
def get_gst_details(party_details, doctype, company, *, update_place_of_supply=False):
"""
This function does not check for permissions since it returns insensitive data
based on already sensitive input (party details)
Expand Down Expand Up @@ -511,7 +514,11 @@ def get_gst_details(party_details, doctype, company):
party_details.update(party_gst_details)
gst_details.update(party_gst_details)

gst_details.place_of_supply = get_place_of_supply(party_details, doctype)
gst_details.place_of_supply = (
party_details.place_of_supply
if (not update_place_of_supply and party_details.place_of_supply)
else get_place_of_supply(party_details, doctype)
)

if is_sales_transaction:
source_gstin = party_details.company_gstin
Expand Down
2 changes: 1 addition & 1 deletion india_compliance/patches.txt
Expand Up @@ -3,7 +3,7 @@ india_compliance.patches.v15.check_version_compatibility

[post_model_sync]
india_compliance.patches.v14.set_default_for_overridden_accounts_setting
execute:from india_compliance.gst_india.setup import create_custom_fields; create_custom_fields() #5
execute:from india_compliance.gst_india.setup import create_custom_fields; create_custom_fields() #6
execute:from india_compliance.gst_india.setup import create_property_setters; create_property_setters()
india_compliance.patches.post_install.update_custom_role_for_e_invoice_summary
india_compliance.patches.v14.remove_ecommerce_gstin_from_purchase_invoice
Expand Down
55 changes: 41 additions & 14 deletions india_compliance/public/js/transaction.js
Expand Up @@ -17,7 +17,7 @@ for (const doctype of TRANSACTION_DOCTYPES) {
}

function fetch_gst_details(doctype) {
const event_fields = ["tax_category", "company_gstin"];
const event_fields = ["tax_category", "company_gstin", "place_of_supply"];

// we are using address below to prevent multiple event triggers
if (in_list(frappe.boot.sales_doctypes, doctype)) {
Expand All @@ -31,24 +31,46 @@ function fetch_gst_details(doctype) {
}

const events = Object.fromEntries(
event_fields.map(field => [field, update_gst_details])
event_fields.map(field => [field, frm => update_gst_details(frm, field)])
);

frappe.ui.form.on(doctype, events);
}

async function update_gst_details(frm) {
if (frm.__gst_update_triggered || frm.updating_party_details || !frm.doc.company) return;
async function update_gst_details(frm, event) {
if (
frm.updating_party_details ||
!frm.doc.company ||
(event === "place_of_supply" && frm.__updating_gst_details)
)
return;

const party_type = india_compliance.get_party_type(frm.doc.doctype).toLowerCase();
const party_fieldname = frm.doc.doctype === "Quotation" ? "party_name" : party_type;
const party = frm.doc[party_fieldname];
if (!party) return;

if (in_list(["company_gstin", "customer_address", "supplier_address"], event)) {
frm.__update_place_of_supply = true;
}

if (frm.__gst_update_triggered) return;
frm.__gst_update_triggered = true;

const args = {
doctype: frm.doc.doctype,
company: frm.doc.company,
};

// wait for GSTINs to get fetched
await frappe.after_ajax().then(() => frm.__gst_update_triggered = false);
await frappe.after_ajax().then(() => {
frm.__gst_update_triggered = false;

if (frm.__update_place_of_supply) {
args.update_place_of_supply = 1;
frm.__update_place_of_supply = false;
}
});

const party_details = {};

Expand All @@ -57,8 +79,12 @@ async function update_gst_details(frm) {
party_details[party_type] = party;
}


const fieldnames_to_set = ["tax_category", "gst_category", "company_gstin"];
const fieldnames_to_set = [
"tax_category",
"gst_category",
"company_gstin",
"place_of_supply",
];

if (in_list(frappe.boot.sales_doctypes, frm.doc.doctype)) {
fieldnames_to_set.push(
Expand All @@ -75,16 +101,17 @@ async function update_gst_details(frm) {
party_details[fieldname] = frm.doc[fieldname];
}

args.party_details = JSON.stringify(party_details);

frappe.call({
method: "india_compliance.gst_india.overrides.transaction.get_gst_details",
args: {
party_details: JSON.stringify(party_details),
doctype: frm.doc.doctype,
company: frm.doc.company,
},
callback(r) {
args,
async callback(r) {
if (!r.message) return;
frm.set_value(r.message);

frm.__updating_gst_details = true;
await frm.set_value(r.message);
frm.__updating_gst_details = false;
},
});
}
Expand Down