mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-31 02:44:52 +00:00
Merge branch 'develop' into hr-separation
This commit is contained in:
@@ -1,8 +0,0 @@
|
||||
// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('E Invoice Request Log', {
|
||||
// refresh: function(frm) {
|
||||
|
||||
// }
|
||||
});
|
||||
@@ -1,102 +0,0 @@
|
||||
{
|
||||
"actions": [],
|
||||
"autoname": "EINV-REQ-.#####",
|
||||
"creation": "2020-12-08 12:54:08.175992",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"user",
|
||||
"url",
|
||||
"headers",
|
||||
"response",
|
||||
"column_break_7",
|
||||
"timestamp",
|
||||
"reference_invoice",
|
||||
"data"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "user",
|
||||
"fieldtype": "Link",
|
||||
"label": "User",
|
||||
"options": "User"
|
||||
},
|
||||
{
|
||||
"fieldname": "reference_invoice",
|
||||
"fieldtype": "Data",
|
||||
"label": "Reference Invoice"
|
||||
},
|
||||
{
|
||||
"fieldname": "headers",
|
||||
"fieldtype": "Code",
|
||||
"label": "Headers",
|
||||
"options": "JSON"
|
||||
},
|
||||
{
|
||||
"fieldname": "data",
|
||||
"fieldtype": "Code",
|
||||
"label": "Data",
|
||||
"options": "JSON"
|
||||
},
|
||||
{
|
||||
"default": "Now",
|
||||
"fieldname": "timestamp",
|
||||
"fieldtype": "Datetime",
|
||||
"label": "Timestamp"
|
||||
},
|
||||
{
|
||||
"fieldname": "response",
|
||||
"fieldtype": "Code",
|
||||
"label": "Response",
|
||||
"options": "JSON"
|
||||
},
|
||||
{
|
||||
"fieldname": "url",
|
||||
"fieldtype": "Data",
|
||||
"label": "URL"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_7",
|
||||
"fieldtype": "Column Break"
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"modified": "2021-01-13 12:06:57.253111",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Regional",
|
||||
"name": "E Invoice Request Log",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"share": 1
|
||||
},
|
||||
{
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Accounts User",
|
||||
"share": 1
|
||||
},
|
||||
{
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Accounts Manager",
|
||||
"share": 1
|
||||
}
|
||||
],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC"
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
# import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
|
||||
class EInvoiceRequestLog(Document):
|
||||
pass
|
||||
@@ -1,11 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
# import frappe
|
||||
import unittest
|
||||
|
||||
|
||||
class TestEInvoiceRequestLog(unittest.TestCase):
|
||||
pass
|
||||
@@ -1,11 +0,0 @@
|
||||
// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('E Invoice Settings', {
|
||||
refresh(frm) {
|
||||
const docs_link = 'https://docs.erpnext.com/docs/v13/user/manual/en/regional/india/setup-e-invoicing';
|
||||
frm.dashboard.set_headline(
|
||||
__("Read {0} for more information on E Invoicing features.", [`<a href='${docs_link}'>documentation</a>`])
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -1,97 +0,0 @@
|
||||
{
|
||||
"actions": [],
|
||||
"creation": "2020-09-24 16:23:16.235722",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"enable",
|
||||
"section_break_2",
|
||||
"sandbox_mode",
|
||||
"applicable_from",
|
||||
"credentials",
|
||||
"advanced_settings_section",
|
||||
"client_id",
|
||||
"column_break_8",
|
||||
"client_secret",
|
||||
"auth_token",
|
||||
"token_expiry"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "enable",
|
||||
"fieldtype": "Check",
|
||||
"label": "Enable"
|
||||
},
|
||||
{
|
||||
"depends_on": "enable",
|
||||
"fieldname": "section_break_2",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "auth_token",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "token_expiry",
|
||||
"fieldtype": "Datetime",
|
||||
"hidden": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "credentials",
|
||||
"fieldtype": "Table",
|
||||
"label": "Credentials",
|
||||
"mandatory_depends_on": "enable",
|
||||
"options": "E Invoice User"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "sandbox_mode",
|
||||
"fieldtype": "Check",
|
||||
"label": "Sandbox Mode"
|
||||
},
|
||||
{
|
||||
"fieldname": "applicable_from",
|
||||
"fieldtype": "Date",
|
||||
"in_list_view": 1,
|
||||
"label": "Applicable From",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"fieldname": "advanced_settings_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Advanced Settings"
|
||||
},
|
||||
{
|
||||
"fieldname": "client_id",
|
||||
"fieldtype": "Data",
|
||||
"label": "Client ID"
|
||||
},
|
||||
{
|
||||
"fieldname": "client_secret",
|
||||
"fieldtype": "Password",
|
||||
"label": "Client Secret"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_8",
|
||||
"fieldtype": "Column Break"
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"issingle": 1,
|
||||
"links": [],
|
||||
"modified": "2021-11-16 19:50:28.029517",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Regional",
|
||||
"name": "E Invoice Settings",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
|
||||
|
||||
class EInvoiceSettings(Document):
|
||||
def validate(self):
|
||||
if self.enable and not self.credentials:
|
||||
frappe.throw(_("You must add atleast one credentials to be able to use E Invoicing."))
|
||||
@@ -1,11 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
# import frappe
|
||||
import unittest
|
||||
|
||||
|
||||
class TestEInvoiceSettings(unittest.TestCase):
|
||||
pass
|
||||
@@ -1,57 +0,0 @@
|
||||
{
|
||||
"actions": [],
|
||||
"creation": "2020-12-22 15:02:46.229474",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"company",
|
||||
"gstin",
|
||||
"username",
|
||||
"password"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "gstin",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
"label": "GSTIN",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "username",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
"label": "Username",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "password",
|
||||
"fieldtype": "Password",
|
||||
"in_list_view": 1,
|
||||
"label": "Password",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "company",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Company",
|
||||
"options": "Company",
|
||||
"reqd": 1
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2021-03-22 12:16:56.365616",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Regional",
|
||||
"name": "E Invoice User",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
# import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
|
||||
class EInvoiceUser(Document):
|
||||
pass
|
||||
@@ -1,28 +0,0 @@
|
||||
// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('GST HSN Code', {
|
||||
refresh: function(frm) {
|
||||
if(! frm.doc.__islocal && frm.doc.taxes.length){
|
||||
frm.add_custom_button(__('Update Taxes for Items'), function(){
|
||||
frappe.confirm(
|
||||
'Are you sure? It will overwrite taxes for all items with HSN Code <b>'+frm.doc.name+'</b>.',
|
||||
function(){
|
||||
frappe.call({
|
||||
args:{
|
||||
taxes: frm.doc.taxes,
|
||||
hsn_code: frm.doc.name
|
||||
},
|
||||
method: 'erpnext.regional.doctype.gst_hsn_code.gst_hsn_code.update_taxes_in_item_master',
|
||||
callback: function(r) {
|
||||
if(r.message){
|
||||
frappe.show_alert(__('Item taxes updated'));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -1,63 +0,0 @@
|
||||
{
|
||||
"actions": [],
|
||||
"autoname": "field:hsn_code",
|
||||
"creation": "2017-06-21 10:48:56.422086",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"hsn_code",
|
||||
"description",
|
||||
"gst_rates",
|
||||
"taxes"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "hsn_code",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
"label": "HSN Code",
|
||||
"reqd": 1,
|
||||
"show_days": 1,
|
||||
"show_seconds": 1,
|
||||
"unique": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "description",
|
||||
"fieldtype": "Small Text",
|
||||
"in_list_view": 1,
|
||||
"label": "Description",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "taxes",
|
||||
"fieldtype": "Table",
|
||||
"label": "Taxes",
|
||||
"options": "Item Tax",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "gst_rates",
|
||||
"fieldtype": "Table",
|
||||
"label": "GST Rates",
|
||||
"options": "HSN Tax Rate",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
}
|
||||
],
|
||||
"links": [],
|
||||
"modified": "2022-05-11 13:42:27.286643",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Regional",
|
||||
"name": "GST HSN Code",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 1,
|
||||
"search_fields": "hsn_code, description",
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"title_field": "hsn_code",
|
||||
"track_changes": 1
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
|
||||
class GSTHSNCode(Document):
|
||||
pass
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def update_taxes_in_item_master(taxes, hsn_code):
|
||||
items = frappe.get_list("Item", filters={"gst_hsn_code": hsn_code})
|
||||
|
||||
taxes = frappe.parse_json(taxes)
|
||||
frappe.enqueue(update_item_document, items=items, taxes=taxes)
|
||||
return 1
|
||||
|
||||
|
||||
def update_item_document(items, taxes):
|
||||
for item in items:
|
||||
item_to_be_updated = frappe.get_doc("Item", item.name)
|
||||
item_to_be_updated.taxes = []
|
||||
for tax in taxes:
|
||||
tax = frappe._dict(tax)
|
||||
item_to_be_updated.append(
|
||||
"taxes",
|
||||
{
|
||||
"item_tax_template": tax.item_tax_template,
|
||||
"tax_category": tax.tax_category,
|
||||
"valid_from": tax.valid_from,
|
||||
},
|
||||
)
|
||||
item_to_be_updated.save()
|
||||
@@ -1,8 +0,0 @@
|
||||
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
|
||||
import unittest
|
||||
|
||||
|
||||
class TestGSTHSNCode(unittest.TestCase):
|
||||
pass
|
||||
@@ -1,50 +0,0 @@
|
||||
// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('GST Settings', {
|
||||
refresh: function(frm) {
|
||||
frm.add_custom_button(__('Send GST Update Reminder'), () => {
|
||||
return new Promise((resolve) => {
|
||||
return frappe.call({
|
||||
method: 'erpnext.regional.doctype.gst_settings.gst_settings.send_reminder'
|
||||
}).always(() => { resolve(); });
|
||||
});
|
||||
});
|
||||
|
||||
frm.add_custom_button(__('Sync HSN Codes'), () => {
|
||||
frappe.call({
|
||||
"method": "erpnext.regional.doctype.gst_settings.gst_settings.update_hsn_codes"
|
||||
});
|
||||
});
|
||||
|
||||
$(frm.fields_dict.gst_summary.wrapper).empty().html(
|
||||
`<table class="table table-bordered">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Total Addresses</td><td>${frm.doc.__onload.data.total_addresses}</td>
|
||||
</tr><tr>
|
||||
<td>Total Addresses with GST</td><td>${frm.doc.__onload.data.total_addresses_with_gstin}</td>
|
||||
</tr>
|
||||
</tbody></table>`
|
||||
);
|
||||
},
|
||||
|
||||
setup: function(frm) {
|
||||
$.each(["cgst_account", "sgst_account", "igst_account", "cess_account"], function(i, field) {
|
||||
frm.events.filter_accounts(frm, field);
|
||||
});
|
||||
},
|
||||
|
||||
filter_accounts: function(frm, account_field) {
|
||||
frm.set_query(account_field, "gst_accounts", function(doc, cdt, cdn) {
|
||||
var row = locals[cdt][cdn];
|
||||
return {
|
||||
filters: {
|
||||
company: row.company,
|
||||
account_type: "Tax",
|
||||
is_group: 0
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -1,85 +0,0 @@
|
||||
{
|
||||
"actions": [],
|
||||
"creation": "2017-06-27 15:09:01.318003",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"gst_summary",
|
||||
"gst_tax_settings_section",
|
||||
"round_off_gst_values",
|
||||
"column_break_4",
|
||||
"hsn_wise_tax_breakup",
|
||||
"gstin_email_sent_on",
|
||||
"section_break_4",
|
||||
"gst_accounts",
|
||||
"b2c_limit"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "gst_summary",
|
||||
"fieldtype": "HTML",
|
||||
"label": "GST Summary"
|
||||
},
|
||||
{
|
||||
"fieldname": "gstin_email_sent_on",
|
||||
"fieldtype": "Date",
|
||||
"label": "GSTIN Email Sent On",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_4",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "gst_accounts",
|
||||
"fieldtype": "Table",
|
||||
"label": "GST Accounts",
|
||||
"options": "GST Account"
|
||||
},
|
||||
{
|
||||
"default": "250000",
|
||||
"description": "Set Invoice Value for B2C. B2CL and B2CS calculated based on this invoice value.",
|
||||
"fieldname": "b2c_limit",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
"label": "B2C Limit",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"description": "Enabling this option will round off individual GST components in all the Invoices",
|
||||
"fieldname": "round_off_gst_values",
|
||||
"fieldtype": "Check",
|
||||
"label": "Round Off GST Values"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "hsn_wise_tax_breakup",
|
||||
"fieldtype": "Check",
|
||||
"label": "Tax Breakup Table Based On HSN Code"
|
||||
},
|
||||
{
|
||||
"fieldname": "gst_tax_settings_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "GST Tax Settings"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_4",
|
||||
"fieldtype": "Column Break"
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"issingle": 1,
|
||||
"links": [],
|
||||
"modified": "2021-10-11 18:10:14.242614",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Regional",
|
||||
"name": "GST Settings",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
||||
@@ -1,160 +0,0 @@
|
||||
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
|
||||
import json
|
||||
import os
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.contacts.doctype.contact.contact import get_default_contact
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import date_diff, flt, get_url, nowdate
|
||||
|
||||
|
||||
class EmailMissing(frappe.ValidationError):
|
||||
pass
|
||||
|
||||
|
||||
class GSTSettings(Document):
|
||||
def onload(self):
|
||||
data = frappe._dict()
|
||||
data.total_addresses = frappe.db.sql(
|
||||
'''select count(*) from tabAddress where country = "India"'''
|
||||
)
|
||||
data.total_addresses_with_gstin = frappe.db.sql(
|
||||
"""select distinct count(*)
|
||||
from tabAddress where country = "India" and ifnull(gstin, '')!='' """
|
||||
)
|
||||
self.set_onload("data", data)
|
||||
|
||||
def validate(self):
|
||||
# Validate duplicate accounts
|
||||
self.validate_duplicate_accounts()
|
||||
|
||||
def validate_duplicate_accounts(self):
|
||||
account_list = []
|
||||
for account in self.get("gst_accounts"):
|
||||
for fieldname in ["cgst_account", "sgst_account", "igst_account", "cess_account"]:
|
||||
if account.get(fieldname) in account_list:
|
||||
frappe.throw(
|
||||
_("Account {0} appears multiple times").format(frappe.bold(account.get(fieldname)))
|
||||
)
|
||||
|
||||
if account.get(fieldname):
|
||||
account_list.append(account.get(fieldname))
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def send_reminder():
|
||||
frappe.has_permission("GST Settings", throw=True)
|
||||
|
||||
last_sent = frappe.db.get_single_value("GST Settings", "gstin_email_sent_on")
|
||||
if last_sent and date_diff(nowdate(), last_sent) < 3:
|
||||
frappe.throw(_("Please wait 3 days before resending the reminder."))
|
||||
|
||||
frappe.db.set_value("GST Settings", "GST Settings", "gstin_email_sent_on", nowdate())
|
||||
|
||||
# enqueue if large number of customers, suppliser
|
||||
frappe.enqueue(
|
||||
"erpnext.regional.doctype.gst_settings.gst_settings.send_gstin_reminder_to_all_parties"
|
||||
)
|
||||
frappe.msgprint(_("Email Reminders will be sent to all parties with email contacts"))
|
||||
|
||||
|
||||
def send_gstin_reminder_to_all_parties():
|
||||
parties = []
|
||||
for address_name in frappe.db.sql(
|
||||
"""select name
|
||||
from tabAddress where country = "India" and ifnull(gstin, '')='' """
|
||||
):
|
||||
address = frappe.get_doc("Address", address_name[0])
|
||||
for link in address.links:
|
||||
party = frappe.get_doc(link.link_doctype, link.link_name)
|
||||
if link.link_doctype in ("Customer", "Supplier"):
|
||||
t = (link.link_doctype, link.link_name, address.email_id)
|
||||
if not t in parties:
|
||||
parties.append(t)
|
||||
|
||||
sent_to = []
|
||||
for party in parties:
|
||||
# get email from default contact
|
||||
try:
|
||||
email_id = _send_gstin_reminder(party[0], party[1], party[2], sent_to)
|
||||
sent_to.append(email_id)
|
||||
except EmailMissing:
|
||||
pass
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def send_gstin_reminder(party_type, party):
|
||||
"""Send GSTIN reminder to one party (called from Customer, Supplier form)"""
|
||||
frappe.has_permission(party_type, throw=True)
|
||||
email = _send_gstin_reminder(party_type, party)
|
||||
if email:
|
||||
frappe.msgprint(_("Reminder to update GSTIN Sent"), title="Reminder sent", indicator="green")
|
||||
|
||||
|
||||
def _send_gstin_reminder(party_type, party, default_email_id=None, sent_to=None):
|
||||
"""Send GST Reminder email"""
|
||||
email_id = frappe.db.get_value("Contact", get_default_contact(party_type, party), "email_id")
|
||||
if not email_id:
|
||||
# get email from address
|
||||
email_id = default_email_id
|
||||
|
||||
if not email_id:
|
||||
frappe.throw(_("Email not found in default contact"), exc=EmailMissing)
|
||||
|
||||
if sent_to and email_id in sent_to:
|
||||
return
|
||||
|
||||
frappe.sendmail(
|
||||
subject="Please update your GSTIN",
|
||||
recipients=email_id,
|
||||
message="""
|
||||
<p>Hello,</p>
|
||||
<p>Please help us send you GST Ready Invoices.</p>
|
||||
<p>
|
||||
<a href="{0}?party={1}">
|
||||
Click here to update your GSTIN Number in our system
|
||||
</a>
|
||||
</p>
|
||||
<p style="color: #aaa; font-size: 11px; margin-top: 30px;">
|
||||
Get your GST Ready ERP system at <a href="https://erpnext.com">https://erpnext.com</a>
|
||||
<br>
|
||||
ERPNext is a free and open source ERP system.
|
||||
</p>
|
||||
""".format(
|
||||
os.path.join(get_url(), "/regional/india/update-gstin"), party
|
||||
),
|
||||
)
|
||||
|
||||
return email_id
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def update_hsn_codes():
|
||||
frappe.enqueue(enqueue_update)
|
||||
frappe.msgprint(_("HSN/SAC Code sync started, this may take a few minutes..."))
|
||||
|
||||
|
||||
def enqueue_update():
|
||||
with open(os.path.join(os.path.dirname(__file__), "hsn_code_data.json"), "r") as f:
|
||||
hsn_codes = json.loads(f.read())
|
||||
|
||||
for hsn_code in hsn_codes:
|
||||
try:
|
||||
hsn_code_doc = frappe.get_doc("GST HSN Code", hsn_code.get("hsn_code"))
|
||||
hsn_code_doc.set("gst_rates", [])
|
||||
for rate in hsn_code.get("gst_rates"):
|
||||
hsn_code_doc.append(
|
||||
"gst_rates",
|
||||
{
|
||||
"minimum_taxable_value": flt(hsn_code.get("minimum_taxable_value")),
|
||||
"tax_rate": flt(rate.get("tax_rate")),
|
||||
},
|
||||
)
|
||||
|
||||
hsn_code_doc.save()
|
||||
except Exception as e:
|
||||
pass
|
||||
@@ -1,8 +0,0 @@
|
||||
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
|
||||
import unittest
|
||||
|
||||
|
||||
class TestGSTSettings(unittest.TestCase):
|
||||
pass
|
||||
@@ -1,297 +0,0 @@
|
||||
<style>
|
||||
.print-format {
|
||||
padding: 15mm;
|
||||
font-size: 8.0pt !important;
|
||||
font-family: Tahoma, sans-serif;
|
||||
}
|
||||
.disabled {
|
||||
background-color: #d9d9d9;
|
||||
}
|
||||
|
||||
</style>
|
||||
<div>
|
||||
<h3 class="text-center">{{ __("GSTR3B-Form")}}</h3>
|
||||
<h5>{{__("GSTIN")}}:   {{ data.gstin }}</h5>
|
||||
<h5>{{__("Period")}}:   {{ data.ret_period }}</h5>
|
||||
</div>
|
||||
|
||||
<h5>3.1  {{__("Details of Outward Supplies and inward supplies liable to reverse charge")}}</h5>
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{__("Nature Of Supplies")}}</th>
|
||||
<th>{{__("Total Taxable value")}}</th>
|
||||
<th>{{__("Integrated Tax")}}</th>
|
||||
<th>{{__("Central Tax")}}</th>
|
||||
<th>{{__("State/UT Tax")}}</th>
|
||||
<th>{{__("Cess")}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>(a) {{__("Outward taxable supplies(other than zero rated, nil rated and exempted)")}}</td>
|
||||
<td class="right">{{ flt(data.sup_details.osup_det.txval, 2) }}</td>
|
||||
<td class="right">{{ flt(data.sup_details.osup_det.iamt, 2) }}</td>
|
||||
<td class="right">{{ flt(data.sup_details.osup_det.camt, 2) }}</td>
|
||||
<td class="right">{{ flt(data.sup_details.osup_det.samt, 2) }}</td>
|
||||
<td class="right">{{ flt(data.sup_details.osup_det.csamt, 2) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>(b) {{__("Outward taxable supplies(zero rated)")}}</td>
|
||||
<td class="right">{{ flt(data.sup_details.osup_zero.txval, 2) }}</td>
|
||||
<td class="right">{{ flt(data.sup_details.osup_zero.iamt, 2) }}</td>
|
||||
<td class="disabled"></td>
|
||||
<td class="disabled"></td>
|
||||
<td class="right">{{ flt(data.sup_details.osup_zero.csamt, 2) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>(b) {{__("Other outward supplies(Nil rated,Exempted)")}}</td>
|
||||
<td class="right">{{ data.sup_details.osup_nil_exmp.txval }}</td>
|
||||
<td class="disabled"></td>
|
||||
<td class="disabled"></td>
|
||||
<td class="disabled"></td>
|
||||
<td class="disabled"></td>
|
||||
<tr>
|
||||
<td>(d) {{__("Inward Supplies(liable to reverse charge)")}}</td>
|
||||
<td class="right">{{ flt(data.sup_details.isup_rev.txval, 2) }}</td>
|
||||
<td class="right">{{ flt(data.sup_details.isup_rev.iamt, 2) }}</td>
|
||||
<td class="right">{{ flt(data.sup_details.isup_rev.camt, 2) }}</td>
|
||||
<td class="right">{{ flt(data.sup_details.isup_rev.samt, 2) }}</td>
|
||||
<td class="right">{{ flt(data.sup_details.isup_rev.csamt,2) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>(e) {{__("Non-GST outward supplies")}}</td>
|
||||
<td class="right">{{ data.sup_details.osup_nongst.txval }}</td>
|
||||
<td class="disabled"></td>
|
||||
<td class="disabled"></td>
|
||||
<td class="disabled"></td>
|
||||
<td class="disabled"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h5>
|
||||
3.2  {{__("Of the supplies shown in 3.1 (a) above, details of inter-State supplies made to unregisterd
|
||||
persons, composition taxable persons and UIN holders")}}
|
||||
</h5>
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>{{__("Place Of Supply (State/UT)")}}</th>
|
||||
<th>{{__("Total Taxable Value")}}</th>
|
||||
<th>{{__("Amount of Integrated Tax")}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{{__("Supplies made to Unregistered Persons")}}</td>
|
||||
<td class="right">
|
||||
{% for row in data.inter_sup.unreg_details %}
|
||||
{% if row %}
|
||||
{{ row.pos }}<br>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</td>
|
||||
<td class="right">
|
||||
{% for row in data.inter_sup.unreg_details %}
|
||||
{% if row %}
|
||||
{{ flt(row.txval, 2) }}<br>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</td>
|
||||
<td class="right">
|
||||
{% for row in data.inter_sup.unreg_details %}
|
||||
{% if row %}
|
||||
{{ flt(row.iamt, 2) }}<br>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{__("Supplies made to Composition Taxable Persons")}}</td>
|
||||
<td class="right">
|
||||
{% for row in data.inter_sup.comp_details %}
|
||||
{% if row %}
|
||||
{{ row.pos }}<br>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</td>
|
||||
<td class="right">
|
||||
{% for row in data.inter_sup.comp_details %}
|
||||
{% if row %}
|
||||
{{ flt(row.txval, 2) }}<br>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</td>
|
||||
<td class="right">
|
||||
{% for row in data.inter_sup.comp_details %}
|
||||
{% if row %}
|
||||
{{ flt(row.iamt, 2) }}<br>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{__("Supplies made to UIN holders")}}</td>
|
||||
<td class="right">
|
||||
{% for row in data.inter_sup.uin_details %}
|
||||
{% if row %}
|
||||
{{ row.pos }}<br>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</td>
|
||||
<td class="right">
|
||||
{% for row in data.inter_sup.uin_details %}
|
||||
{% if row %}
|
||||
{{ flt(row.txval, 2) }}<br>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</td>
|
||||
<td class="right">
|
||||
{% for row in data.inter_sup.uin_details %}
|
||||
{% if row %}
|
||||
{{ flt(row.iamt, 2) }}<br>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h5>4.   {{__("Eligible ITC")}}</h5>
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Details</th>
|
||||
<th>Integrated Tax</th>
|
||||
<th>Central Tax</th>
|
||||
<th>State/UT tax</th>
|
||||
<th>Cess</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><b>(A) {{__("ITC Available (whether in full or part)")}}</b></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>  (1) {{__("Import of goods")}} </td>
|
||||
<td class="right">{{ flt(data.itc_elg.itc_avl[0].iamt, 2) }}</td>
|
||||
<td class="right">{{ flt(data.itc_elg.itc_avl[0].camt, 2) }}</td>
|
||||
<td class="right">{{ flt(data.itc_elg.itc_avl[0].samt, 2) }}</td>
|
||||
<td class="right">{{ flt(data.itc_elg.itc_avl[0].csamt, 2) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>  (2) {{__("Import of services")}}</td>
|
||||
<td class="right">{{ flt(data.itc_elg.itc_avl[1].iamt, 2) }}</td>
|
||||
<td class="right">{{ flt(data.itc_elg.itc_avl[1].camt, 2) }}</td>
|
||||
<td class="right">{{ flt(data.itc_elg.itc_avl[1].samt, 2) }}</td>
|
||||
<td class="right">{{ flt(data.itc_elg.itc_avl[1].csamt, 2) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>  (3) {{__("Inward supplies liable to reverse charge (other than 1 & 2 above)")}}</td>
|
||||
<td class="right">{{ flt(data.itc_elg.itc_avl[2].iamt, 2) }}</td>
|
||||
<td class="right">{{ flt(data.itc_elg.itc_avl[2].camt, 2) }}</td>
|
||||
<td class="right">{{ flt(data.itc_elg.itc_avl[2].samt, 2) }}</td>
|
||||
<td class="right">{{ flt(data.itc_elg.itc_avl[2].csamt, 2) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>  (4) {{__("Inward supplies from ISD")}}</td>
|
||||
<td class="right">{{ flt(data.itc_elg.itc_avl[3].iamt, 2) }}</td>
|
||||
<td class="right">{{ flt(data.itc_elg.itc_avl[3].camt, 2) }}</td>
|
||||
<td class="right">{{ flt(data.itc_elg.itc_avl[3].samt, 2) }}</td>
|
||||
<td class="right">{{ flt(data.itc_elg.itc_avl[3].csamt, 2) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>  (5) {{__("All other ITC")}}</td>
|
||||
<td class="right">{{ flt(data.itc_elg.itc_avl[4].iamt, 2) }}</td>
|
||||
<td class="right">{{ flt(data.itc_elg.itc_avl[4].camt, 2) }}</td>
|
||||
<td class="right">{{ flt(data.itc_elg.itc_avl[4].samt, 2) }}</td>
|
||||
<td class="right">{{ flt(data.itc_elg.itc_avl[4].csamt, 2) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>(B) {{__("ITC Reversed")}}</b></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>  (1) {{__("As per rules 42 & 43 of CGST Rules")}}</td>
|
||||
<td class="right">{{ flt(data.itc_elg.itc_rev[0].iamt, 2) }}</td>
|
||||
<td class="right">{{ flt(data.itc_elg.itc_rev[0].camt, 2) }}</td>
|
||||
<td class="right">{{ flt(data.itc_elg.itc_rev[0].samt, 2) }}</td>
|
||||
<td class="right">{{ flt(data.itc_elg.itc_rev[0].csamt, 2) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>  (2) {{__("Others")}}</td>
|
||||
<td class="right">{{ flt(data.itc_elg.itc_rev[1].iamt, 2) }}</td>
|
||||
<td class="right">{{ flt(data.itc_elg.itc_rev[1].camt, 2) }}</td>
|
||||
<td class="right">{{ flt(data.itc_elg.itc_rev[1].samt, 2) }}</td>
|
||||
<td class="right">{{ flt(data.itc_elg.itc_rev[1].csamt, 2) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>(C) {{__("Net ITC Available(A) - (B)")}}</b></td>
|
||||
<td class="right">{{ flt(data.itc_elg.itc_net.iamt, 2) }}</td>
|
||||
<td class="right">{{ flt(data.itc_elg.itc_net.camt, 2) }}</td>
|
||||
<td class="right">{{ flt(data.itc_elg.itc_net.samt, 2) }}</td>
|
||||
<td class="right">{{ flt(data.itc_elg.itc_net.csamt, 2) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>(D) {{__("Ineligible ITC")}}</b></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>  (1) {{__("As per section 17(5)")}}</td>
|
||||
<td class="right">{{ flt(data.itc_elg.itc_inelg[0].iamt, 2) }}</td>
|
||||
<td class="right">{{ flt(data.itc_elg.itc_inelg[0].camt, 2) }}</td>
|
||||
<td class="right">{{ flt(data.itc_elg.itc_inelg[0].samt, 2) }}</td>
|
||||
<td class="right">{{ flt(data.itc_elg.itc_inelg[0].csamt, 2) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>  (2) {{__("Others")}}</td>
|
||||
<td class="right">{{ flt(data.itc_elg.itc_inelg[1].iamt, 2) }}</td>
|
||||
<td class="right">{{ flt(data.itc_elg.itc_inelg[1].camt, 2) }}</td>
|
||||
<td class="right">{{ flt(data.itc_elg.itc_inelg[1].samt, 2) }}</td>
|
||||
<td class="right">{{ flt(data.itc_elg.itc_inelg[1].csamt, 2) }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h5>5.    {{__("Values of exempt, nil rated and non-GST inward supplies")}}</h5>
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{__("Nature of Supplies")}}</th>
|
||||
<th>{{__("Inter-State Supplies")}}</th>
|
||||
<th>{{__("Intra-State Supplies")}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{{__("From a supplier under composition scheme, Exempt and Nil rated")}}</td>
|
||||
<td class="right">{{ flt(data.inward_sup.isup_details[0].inter, 2) }}</td>
|
||||
<td class="right">{{ flt(data.inward_sup.isup_details[0].intra, 2) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{__("Non GST Inward Supplies")}}</td>
|
||||
<td class="right">{{ flt(data.inward_sup.isup_details[1].inter, 2) }}</td>
|
||||
<td class="right">{{ flt(data.inward_sup.isup_details[1].intra, 2) }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<style>
|
||||
|
||||
.right{
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -1,64 +0,0 @@
|
||||
// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('GSTR 3B Report', {
|
||||
refresh : function(frm) {
|
||||
frm.doc.__unsaved = 1;
|
||||
if(!frm.is_new()) {
|
||||
frm.set_intro(__("Please save the report again to rebuild or update"));
|
||||
frm.add_custom_button(__('Download JSON'), function() {
|
||||
var w = window.open(
|
||||
frappe.urllib.get_full_url(
|
||||
"/api/method/erpnext.regional.doctype.gstr_3b_report.gstr_3b_report.make_json?"
|
||||
+"name="+encodeURIComponent(frm.doc.name)));
|
||||
|
||||
if(!w) {
|
||||
frappe.msgprint(__("Please enable pop-ups")); return;
|
||||
}
|
||||
});
|
||||
frm.add_custom_button(__('View Form'), function() {
|
||||
frappe.call({
|
||||
"method" : "erpnext.regional.doctype.gstr_3b_report.gstr_3b_report.view_report",
|
||||
"args" : {
|
||||
name : frm.doc.name,
|
||||
},
|
||||
"callback" : function(r){
|
||||
|
||||
let data = r.message;
|
||||
|
||||
frappe.ui.get_print_settings(false, print_settings => {
|
||||
|
||||
frappe.render_grid({
|
||||
template: 'gstr_3b_report',
|
||||
title: __(this.doctype),
|
||||
print_settings: print_settings,
|
||||
data: data,
|
||||
columns:[]
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
let current_year = new Date().getFullYear();
|
||||
let options = [current_year, current_year-1, current_year-2];
|
||||
frm.set_df_property('year', 'options', options);
|
||||
},
|
||||
|
||||
setup: function(frm) {
|
||||
frm.set_query('company_address', function(doc) {
|
||||
if(!doc.company) {
|
||||
frappe.throw(__('Please set Company'));
|
||||
}
|
||||
|
||||
return {
|
||||
query: 'frappe.contacts.doctype.address.address.address_query',
|
||||
filters: {
|
||||
link_doctype: 'Company',
|
||||
link_name: doc.company
|
||||
}
|
||||
};
|
||||
});
|
||||
},
|
||||
});
|
||||
@@ -1,62 +0,0 @@
|
||||
{
|
||||
"actions": [],
|
||||
"autoname": "format:GSTR3B-{month}-{year}-{company_address}",
|
||||
"creation": "2019-02-04 11:35:55.964639",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"company",
|
||||
"company_address",
|
||||
"year",
|
||||
"month",
|
||||
"json_output",
|
||||
"missing_field_invoices"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "company",
|
||||
"fieldtype": "Link",
|
||||
"label": "Company",
|
||||
"options": "Company"
|
||||
},
|
||||
{
|
||||
"fieldname": "company_address",
|
||||
"fieldtype": "Link",
|
||||
"label": "Company Address",
|
||||
"options": "Address"
|
||||
},
|
||||
{
|
||||
"fieldname": "year",
|
||||
"fieldtype": "Select",
|
||||
"label": "Year"
|
||||
},
|
||||
{
|
||||
"fieldname": "month",
|
||||
"fieldtype": "Select",
|
||||
"label": "Month",
|
||||
"options": "January\nFebruary\nMarch\nApril\nMay\nJune\nJuly\nAugust\nSeptember\nOctober\nNovember\nDecember"
|
||||
},
|
||||
{
|
||||
"fieldname": "json_output",
|
||||
"fieldtype": "Code",
|
||||
"label": "JSON Output"
|
||||
},
|
||||
{
|
||||
"fieldname": "missing_field_invoices",
|
||||
"fieldtype": "Small Text",
|
||||
"label": "Invoices with no Place Of Supply",
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"links": [],
|
||||
"modified": "2020-04-04 19:32:30.772908",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Regional",
|
||||
"name": "GSTR 3B Report",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
||||
@@ -1,525 +0,0 @@
|
||||
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
|
||||
import json
|
||||
import os
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import cstr, flt
|
||||
|
||||
from erpnext.regional.india import state_numbers
|
||||
|
||||
|
||||
class GSTR3BReport(Document):
|
||||
def validate(self):
|
||||
self.get_data()
|
||||
|
||||
def get_data(self):
|
||||
self.report_dict = json.loads(get_json("gstr_3b_report_template"))
|
||||
|
||||
self.gst_details = self.get_company_gst_details()
|
||||
self.report_dict["gstin"] = self.gst_details.get("gstin")
|
||||
self.report_dict["ret_period"] = get_period(self.month, self.year)
|
||||
self.month_no = get_period(self.month)
|
||||
self.account_heads = self.get_account_heads()
|
||||
|
||||
self.get_outward_supply_details("Sales Invoice")
|
||||
self.set_outward_taxable_supplies()
|
||||
|
||||
self.get_outward_supply_details("Purchase Invoice", reverse_charge=True)
|
||||
self.set_supplies_liable_to_reverse_charge()
|
||||
|
||||
itc_details = self.get_itc_details()
|
||||
self.set_itc_details(itc_details)
|
||||
self.get_itc_reversal_entries()
|
||||
inward_nil_exempt = self.get_inward_nil_exempt(self.gst_details.get("gst_state"))
|
||||
self.set_inward_nil_exempt(inward_nil_exempt)
|
||||
|
||||
self.missing_field_invoices = self.get_missing_field_invoices()
|
||||
self.json_output = frappe.as_json(self.report_dict)
|
||||
|
||||
def set_inward_nil_exempt(self, inward_nil_exempt):
|
||||
self.report_dict["inward_sup"]["isup_details"][0]["inter"] = flt(
|
||||
inward_nil_exempt.get("gst").get("inter"), 2
|
||||
)
|
||||
self.report_dict["inward_sup"]["isup_details"][0]["intra"] = flt(
|
||||
inward_nil_exempt.get("gst").get("intra"), 2
|
||||
)
|
||||
self.report_dict["inward_sup"]["isup_details"][1]["inter"] = flt(
|
||||
inward_nil_exempt.get("non_gst").get("inter"), 2
|
||||
)
|
||||
self.report_dict["inward_sup"]["isup_details"][1]["intra"] = flt(
|
||||
inward_nil_exempt.get("non_gst").get("intra"), 2
|
||||
)
|
||||
|
||||
def set_itc_details(self, itc_details):
|
||||
itc_eligible_type_map = {
|
||||
"IMPG": "Import Of Capital Goods",
|
||||
"IMPS": "Import Of Service",
|
||||
"ISRC": "ITC on Reverse Charge",
|
||||
"ISD": "Input Service Distributor",
|
||||
"OTH": "All Other ITC",
|
||||
}
|
||||
|
||||
itc_ineligible_map = {"RUL": "Ineligible As Per Section 17(5)", "OTH": "Ineligible Others"}
|
||||
|
||||
net_itc = self.report_dict["itc_elg"]["itc_net"]
|
||||
|
||||
for d in self.report_dict["itc_elg"]["itc_avl"]:
|
||||
itc_type = itc_eligible_type_map.get(d["ty"])
|
||||
for key in ["iamt", "camt", "samt", "csamt"]:
|
||||
d[key] = flt(itc_details.get(itc_type, {}).get(key))
|
||||
net_itc[key] += flt(d[key], 2)
|
||||
|
||||
for d in self.report_dict["itc_elg"]["itc_inelg"]:
|
||||
itc_type = itc_ineligible_map.get(d["ty"])
|
||||
for key in ["iamt", "camt", "samt", "csamt"]:
|
||||
d[key] = flt(itc_details.get(itc_type, {}).get(key))
|
||||
|
||||
def get_itc_reversal_entries(self):
|
||||
reversal_entries = frappe.db.sql(
|
||||
"""
|
||||
SELECT ja.account, j.reversal_type, sum(credit_in_account_currency) as amount
|
||||
FROM `tabJournal Entry` j, `tabJournal Entry Account` ja
|
||||
where j.docstatus = 1
|
||||
and j.is_opening = 'No'
|
||||
and ja.parent = j.name
|
||||
and j.voucher_type = 'Reversal Of ITC'
|
||||
and month(j.posting_date) = %s and year(j.posting_date) = %s
|
||||
and j.company = %s and j.company_gstin = %s
|
||||
GROUP BY ja.account, j.reversal_type""",
|
||||
(self.month_no, self.year, self.company, self.gst_details.get("gstin")),
|
||||
as_dict=1,
|
||||
)
|
||||
|
||||
net_itc = self.report_dict["itc_elg"]["itc_net"]
|
||||
|
||||
for entry in reversal_entries:
|
||||
if entry.reversal_type == "As per rules 42 & 43 of CGST Rules":
|
||||
index = 0
|
||||
else:
|
||||
index = 1
|
||||
|
||||
for key in ["camt", "samt", "iamt", "csamt"]:
|
||||
if entry.account in self.account_heads.get(key):
|
||||
self.report_dict["itc_elg"]["itc_rev"][index][key] += flt(entry.amount)
|
||||
net_itc[key] -= flt(entry.amount)
|
||||
|
||||
def get_itc_details(self):
|
||||
itc_amounts = frappe.db.sql(
|
||||
"""
|
||||
SELECT eligibility_for_itc, sum(itc_integrated_tax) as itc_integrated_tax,
|
||||
sum(itc_central_tax) as itc_central_tax,
|
||||
sum(itc_state_tax) as itc_state_tax,
|
||||
sum(itc_cess_amount) as itc_cess_amount
|
||||
FROM `tabPurchase Invoice`
|
||||
WHERE docstatus = 1
|
||||
and is_opening = 'No'
|
||||
and month(posting_date) = %s and year(posting_date) = %s and company = %s
|
||||
and company_gstin = %s
|
||||
GROUP BY eligibility_for_itc
|
||||
""",
|
||||
(self.month_no, self.year, self.company, self.gst_details.get("gstin")),
|
||||
as_dict=1,
|
||||
)
|
||||
|
||||
itc_details = {}
|
||||
for d in itc_amounts:
|
||||
itc_details.setdefault(
|
||||
d.eligibility_for_itc,
|
||||
{
|
||||
"iamt": d.itc_integrated_tax,
|
||||
"camt": d.itc_central_tax,
|
||||
"samt": d.itc_state_tax,
|
||||
"csamt": d.itc_cess_amount,
|
||||
},
|
||||
)
|
||||
|
||||
return itc_details
|
||||
|
||||
def get_inward_nil_exempt(self, state):
|
||||
inward_nil_exempt = frappe.db.sql(
|
||||
"""
|
||||
SELECT p.place_of_supply, p.supplier_address,
|
||||
i.base_amount, i.is_nil_exempt, i.is_non_gst
|
||||
FROM `tabPurchase Invoice` p , `tabPurchase Invoice Item` i
|
||||
WHERE p.docstatus = 1 and p.name = i.parent
|
||||
and p.is_opening = 'No'
|
||||
and (i.is_nil_exempt = 1 or i.is_non_gst = 1 or p.gst_category = 'Registered Composition') and
|
||||
month(p.posting_date) = %s and year(p.posting_date) = %s
|
||||
and p.company = %s and p.company_gstin = %s
|
||||
""",
|
||||
(self.month_no, self.year, self.company, self.gst_details.get("gstin")),
|
||||
as_dict=1,
|
||||
)
|
||||
|
||||
inward_nil_exempt_details = {
|
||||
"gst": {"intra": 0.0, "inter": 0.0},
|
||||
"non_gst": {"intra": 0.0, "inter": 0.0},
|
||||
}
|
||||
|
||||
address_state_map = get_address_state_map()
|
||||
|
||||
for d in inward_nil_exempt:
|
||||
if not d.place_of_supply:
|
||||
d.place_of_supply = "00-" + cstr(state)
|
||||
|
||||
supplier_state = address_state_map.get(d.supplier_address) or state
|
||||
|
||||
if (d.is_nil_exempt == 1 or d.get("gst_category") == "Registered Composition") and cstr(
|
||||
supplier_state
|
||||
) == cstr(d.place_of_supply.split("-")[1]):
|
||||
inward_nil_exempt_details["gst"]["intra"] += d.base_amount
|
||||
elif (d.is_nil_exempt == 1 or d.get("gst_category") == "Registered Composition") and cstr(
|
||||
supplier_state
|
||||
) != cstr(d.place_of_supply.split("-")[1]):
|
||||
inward_nil_exempt_details["gst"]["inter"] += d.base_amount
|
||||
elif d.is_non_gst == 1 and cstr(supplier_state) == cstr(d.place_of_supply.split("-")[1]):
|
||||
inward_nil_exempt_details["non_gst"]["intra"] += d.base_amount
|
||||
elif d.is_non_gst == 1 and cstr(supplier_state) != cstr(d.place_of_supply.split("-")[1]):
|
||||
inward_nil_exempt_details["non_gst"]["inter"] += d.base_amount
|
||||
|
||||
return inward_nil_exempt_details
|
||||
|
||||
def get_outward_supply_details(self, doctype, reverse_charge=None):
|
||||
self.get_outward_tax_invoices(doctype, reverse_charge=reverse_charge)
|
||||
self.get_outward_items(doctype)
|
||||
self.get_outward_tax_details(doctype)
|
||||
|
||||
def get_outward_tax_invoices(self, doctype, reverse_charge=None):
|
||||
self.invoices = []
|
||||
self.invoice_detail_map = {}
|
||||
condition = ""
|
||||
|
||||
if reverse_charge:
|
||||
condition += "AND reverse_charge = 'Y'"
|
||||
|
||||
invoice_details = frappe.db.sql(
|
||||
"""
|
||||
SELECT
|
||||
name, gst_category, export_type, place_of_supply
|
||||
FROM
|
||||
`tab{doctype}`
|
||||
WHERE
|
||||
docstatus = 1
|
||||
AND month(posting_date) = %s
|
||||
AND year(posting_date) = %s
|
||||
AND company = %s
|
||||
AND company_gstin = %s
|
||||
AND is_opening = 'No'
|
||||
{reverse_charge}
|
||||
ORDER BY name
|
||||
""".format(
|
||||
doctype=doctype, reverse_charge=condition
|
||||
),
|
||||
(self.month_no, self.year, self.company, self.gst_details.get("gstin")),
|
||||
as_dict=1,
|
||||
)
|
||||
|
||||
for d in invoice_details:
|
||||
self.invoice_detail_map.setdefault(d.name, d)
|
||||
self.invoices.append(d.name)
|
||||
|
||||
def get_outward_items(self, doctype):
|
||||
self.invoice_items = frappe._dict()
|
||||
self.is_nil_exempt = []
|
||||
self.is_non_gst = []
|
||||
|
||||
if self.get("invoices"):
|
||||
item_details = frappe.db.sql(
|
||||
"""
|
||||
SELECT
|
||||
item_code, parent, taxable_value, base_net_amount, item_tax_rate,
|
||||
is_nil_exempt, is_non_gst
|
||||
FROM
|
||||
`tab%s Item`
|
||||
WHERE parent in (%s)
|
||||
"""
|
||||
% (doctype, ", ".join(["%s"] * len(self.invoices))),
|
||||
tuple(self.invoices),
|
||||
as_dict=1,
|
||||
)
|
||||
|
||||
for d in item_details:
|
||||
self.invoice_items.setdefault(d.parent, {}).setdefault(d.item_code, 0.0)
|
||||
self.invoice_items[d.parent][d.item_code] += d.get("taxable_value", 0) or d.get(
|
||||
"base_net_amount", 0
|
||||
)
|
||||
|
||||
if d.is_nil_exempt and d.item_code not in self.is_nil_exempt:
|
||||
self.is_nil_exempt.append(d.item_code)
|
||||
|
||||
if d.is_non_gst and d.item_code not in self.is_non_gst:
|
||||
self.is_non_gst.append(d.item_code)
|
||||
|
||||
def get_outward_tax_details(self, doctype):
|
||||
if doctype == "Sales Invoice":
|
||||
tax_template = "Sales Taxes and Charges"
|
||||
elif doctype == "Purchase Invoice":
|
||||
tax_template = "Purchase Taxes and Charges"
|
||||
|
||||
self.items_based_on_tax_rate = {}
|
||||
self.invoice_cess = frappe._dict()
|
||||
self.cgst_sgst_invoices = []
|
||||
|
||||
if self.get("invoices"):
|
||||
tax_details = frappe.db.sql(
|
||||
"""
|
||||
SELECT
|
||||
parent, account_head, item_wise_tax_detail, base_tax_amount_after_discount_amount
|
||||
FROM `tab%s`
|
||||
WHERE
|
||||
parenttype = %s and docstatus = 1
|
||||
and parent in (%s)
|
||||
ORDER BY account_head
|
||||
"""
|
||||
% (tax_template, "%s", ", ".join(["%s"] * len(self.invoices))),
|
||||
tuple([doctype] + list(self.invoices)),
|
||||
)
|
||||
|
||||
for parent, account, item_wise_tax_detail, tax_amount in tax_details:
|
||||
if account in self.account_heads.get("csamt"):
|
||||
self.invoice_cess.setdefault(parent, tax_amount)
|
||||
else:
|
||||
if item_wise_tax_detail:
|
||||
try:
|
||||
item_wise_tax_detail = json.loads(item_wise_tax_detail)
|
||||
cgst_or_sgst = False
|
||||
if account in self.account_heads.get("camt") or account in self.account_heads.get("samt"):
|
||||
cgst_or_sgst = True
|
||||
|
||||
for item_code, tax_amounts in item_wise_tax_detail.items():
|
||||
if not (
|
||||
cgst_or_sgst
|
||||
or account in self.account_heads.get("iamt")
|
||||
or (item_code in self.is_non_gst + self.is_nil_exempt)
|
||||
):
|
||||
continue
|
||||
|
||||
tax_rate = tax_amounts[0]
|
||||
if tax_rate:
|
||||
if cgst_or_sgst:
|
||||
tax_rate *= 2
|
||||
if parent not in self.cgst_sgst_invoices:
|
||||
self.cgst_sgst_invoices.append(parent)
|
||||
|
||||
rate_based_dict = self.items_based_on_tax_rate.setdefault(parent, {}).setdefault(
|
||||
tax_rate, []
|
||||
)
|
||||
if item_code not in rate_based_dict:
|
||||
rate_based_dict.append(item_code)
|
||||
except ValueError:
|
||||
continue
|
||||
|
||||
if self.get("invoice_items"):
|
||||
# Build itemised tax for export invoices, nil and exempted where tax table is blank
|
||||
for invoice, items in self.invoice_items.items():
|
||||
if (
|
||||
invoice not in self.items_based_on_tax_rate
|
||||
and self.invoice_detail_map.get(invoice, {}).get("export_type") == "Without Payment of Tax"
|
||||
and self.invoice_detail_map.get(invoice, {}).get("gst_category") == "Overseas"
|
||||
):
|
||||
self.items_based_on_tax_rate.setdefault(invoice, {}).setdefault(0, items.keys())
|
||||
else:
|
||||
for item in items.keys():
|
||||
if (
|
||||
item in self.is_nil_exempt + self.is_non_gst
|
||||
and item not in self.items_based_on_tax_rate.get(invoice, {}).get(0, [])
|
||||
):
|
||||
self.items_based_on_tax_rate.setdefault(invoice, {}).setdefault(0, [])
|
||||
self.items_based_on_tax_rate[invoice][0].append(item)
|
||||
|
||||
def set_outward_taxable_supplies(self):
|
||||
inter_state_supply_details = {}
|
||||
for inv, items_based_on_rate in self.items_based_on_tax_rate.items():
|
||||
gst_category = self.invoice_detail_map.get(inv, {}).get("gst_category")
|
||||
place_of_supply = (
|
||||
self.invoice_detail_map.get(inv, {}).get("place_of_supply") or "00-Other Territory"
|
||||
)
|
||||
export_type = self.invoice_detail_map.get(inv, {}).get("export_type")
|
||||
|
||||
for rate, items in items_based_on_rate.items():
|
||||
for item_code, taxable_value in self.invoice_items.get(inv).items():
|
||||
if item_code in items:
|
||||
if item_code in self.is_nil_exempt:
|
||||
self.report_dict["sup_details"]["osup_nil_exmp"]["txval"] += taxable_value
|
||||
elif item_code in self.is_non_gst:
|
||||
self.report_dict["sup_details"]["osup_nongst"]["txval"] += taxable_value
|
||||
elif rate == 0 or (gst_category == "Overseas" and export_type == "Without Payment of Tax"):
|
||||
self.report_dict["sup_details"]["osup_zero"]["txval"] += taxable_value
|
||||
else:
|
||||
if inv in self.cgst_sgst_invoices:
|
||||
tax_rate = rate / 2
|
||||
self.report_dict["sup_details"]["osup_det"]["camt"] += taxable_value * tax_rate / 100
|
||||
self.report_dict["sup_details"]["osup_det"]["samt"] += taxable_value * tax_rate / 100
|
||||
self.report_dict["sup_details"]["osup_det"]["txval"] += taxable_value
|
||||
else:
|
||||
self.report_dict["sup_details"]["osup_det"]["iamt"] += taxable_value * rate / 100
|
||||
self.report_dict["sup_details"]["osup_det"]["txval"] += taxable_value
|
||||
if (
|
||||
gst_category in ["Unregistered", "Registered Composition", "UIN Holders"]
|
||||
and self.gst_details.get("gst_state") != place_of_supply.split("-")[1]
|
||||
):
|
||||
inter_state_supply_details.setdefault(
|
||||
(gst_category, place_of_supply),
|
||||
{"txval": 0.0, "pos": place_of_supply.split("-")[0], "iamt": 0.0},
|
||||
)
|
||||
inter_state_supply_details[(gst_category, place_of_supply)]["txval"] += taxable_value
|
||||
inter_state_supply_details[(gst_category, place_of_supply)]["iamt"] += (
|
||||
taxable_value * rate / 100
|
||||
)
|
||||
|
||||
if self.invoice_cess.get(inv):
|
||||
self.report_dict["sup_details"]["osup_det"]["csamt"] += flt(self.invoice_cess.get(inv), 2)
|
||||
|
||||
self.set_inter_state_supply(inter_state_supply_details)
|
||||
|
||||
def set_supplies_liable_to_reverse_charge(self):
|
||||
for inv, items_based_on_rate in self.items_based_on_tax_rate.items():
|
||||
for rate, items in items_based_on_rate.items():
|
||||
for item_code, taxable_value in self.invoice_items.get(inv).items():
|
||||
if item_code in items:
|
||||
if inv in self.cgst_sgst_invoices:
|
||||
tax_rate = rate / 2
|
||||
self.report_dict["sup_details"]["isup_rev"]["camt"] += taxable_value * tax_rate / 100
|
||||
self.report_dict["sup_details"]["isup_rev"]["samt"] += taxable_value * tax_rate / 100
|
||||
self.report_dict["sup_details"]["isup_rev"]["txval"] += taxable_value
|
||||
else:
|
||||
self.report_dict["sup_details"]["isup_rev"]["iamt"] += taxable_value * rate / 100
|
||||
self.report_dict["sup_details"]["isup_rev"]["txval"] += taxable_value
|
||||
|
||||
def set_inter_state_supply(self, inter_state_supply):
|
||||
for key, value in inter_state_supply.items():
|
||||
if key[0] == "Unregistered":
|
||||
self.report_dict["inter_sup"]["unreg_details"].append(value)
|
||||
|
||||
if key[0] == "Registered Composition":
|
||||
self.report_dict["inter_sup"]["comp_details"].append(value)
|
||||
|
||||
if key[0] == "UIN Holders":
|
||||
self.report_dict["inter_sup"]["uin_details"].append(value)
|
||||
|
||||
def get_company_gst_details(self):
|
||||
gst_details = frappe.get_all(
|
||||
"Address",
|
||||
fields=["gstin", "gst_state", "gst_state_number"],
|
||||
filters={"name": self.company_address},
|
||||
)
|
||||
|
||||
if gst_details:
|
||||
return gst_details[0]
|
||||
else:
|
||||
frappe.throw(
|
||||
_("Please enter GSTIN and state for the Company Address {0}").format(self.company_address)
|
||||
)
|
||||
|
||||
def get_account_heads(self):
|
||||
account_map = {
|
||||
"sgst_account": "samt",
|
||||
"cess_account": "csamt",
|
||||
"cgst_account": "camt",
|
||||
"igst_account": "iamt",
|
||||
}
|
||||
|
||||
account_heads = {}
|
||||
gst_settings_accounts = frappe.get_all(
|
||||
"GST Account",
|
||||
filters={"company": self.company, "is_reverse_charge_account": 0},
|
||||
fields=["cgst_account", "sgst_account", "igst_account", "cess_account"],
|
||||
)
|
||||
|
||||
if not gst_settings_accounts:
|
||||
frappe.throw(_("Please set GST Accounts in GST Settings"))
|
||||
|
||||
for d in gst_settings_accounts:
|
||||
for acc, val in d.items():
|
||||
account_heads.setdefault(account_map.get(acc), []).append(val)
|
||||
|
||||
return account_heads
|
||||
|
||||
def get_missing_field_invoices(self):
|
||||
missing_field_invoices = []
|
||||
|
||||
for doctype in ["Sales Invoice", "Purchase Invoice"]:
|
||||
|
||||
if doctype == "Sales Invoice":
|
||||
party_type = "Customer"
|
||||
party = "customer"
|
||||
else:
|
||||
party_type = "Supplier"
|
||||
party = "supplier"
|
||||
|
||||
docnames = frappe.db.sql(
|
||||
"""
|
||||
SELECT t1.name FROM `tab{doctype}` t1, `tab{party_type}` t2
|
||||
WHERE t1.docstatus = 1 and t1.is_opening = 'No'
|
||||
and month(t1.posting_date) = %s and year(t1.posting_date) = %s
|
||||
and t1.company = %s and t1.place_of_supply IS NULL and t1.{party} = t2.name and
|
||||
t2.gst_category != 'Overseas'
|
||||
""".format(
|
||||
doctype=doctype, party_type=party_type, party=party
|
||||
),
|
||||
(self.month_no, self.year, self.company),
|
||||
as_dict=1,
|
||||
) # nosec
|
||||
|
||||
for d in docnames:
|
||||
missing_field_invoices.append(d.name)
|
||||
|
||||
return ",".join(missing_field_invoices)
|
||||
|
||||
|
||||
def get_address_state_map():
|
||||
return frappe._dict(frappe.get_all("Address", fields=["name", "gst_state"], as_list=1))
|
||||
|
||||
|
||||
def get_json(template):
|
||||
file_path = os.path.join(os.path.dirname(__file__), "{template}.json".format(template=template))
|
||||
with open(file_path, "r") as f:
|
||||
return cstr(f.read())
|
||||
|
||||
|
||||
def get_state_code(state):
|
||||
state_code = state_numbers.get(state)
|
||||
|
||||
return state_code
|
||||
|
||||
|
||||
def get_period(month, year=None):
|
||||
month_no = {
|
||||
"January": 1,
|
||||
"February": 2,
|
||||
"March": 3,
|
||||
"April": 4,
|
||||
"May": 5,
|
||||
"June": 6,
|
||||
"July": 7,
|
||||
"August": 8,
|
||||
"September": 9,
|
||||
"October": 10,
|
||||
"November": 11,
|
||||
"December": 12,
|
||||
}.get(month)
|
||||
|
||||
if year:
|
||||
return str(month_no).zfill(2) + str(year)
|
||||
else:
|
||||
return month_no
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def view_report(name):
|
||||
json_data = frappe.get_value("GSTR 3B Report", name, "json_output")
|
||||
return json.loads(json_data)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def make_json(name):
|
||||
json_data = frappe.get_value("GSTR 3B Report", name, "json_output")
|
||||
file_name = "GST3B.json"
|
||||
frappe.local.response.filename = file_name
|
||||
frappe.local.response.filecontent = json_data
|
||||
frappe.local.response.type = "download"
|
||||
@@ -1,127 +0,0 @@
|
||||
{
|
||||
"gstin": "",
|
||||
"ret_period": "",
|
||||
"inward_sup": {
|
||||
"isup_details": [
|
||||
{
|
||||
"ty": "GST",
|
||||
"intra": 0,
|
||||
"inter": 0
|
||||
},
|
||||
{
|
||||
"ty": "NONGST",
|
||||
"inter": 0,
|
||||
"intra": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
"sup_details": {
|
||||
"osup_zero": {
|
||||
"csamt": 0,
|
||||
"txval": 0,
|
||||
"iamt": 0
|
||||
},
|
||||
"osup_nil_exmp": {
|
||||
"txval": 0
|
||||
},
|
||||
"osup_det": {
|
||||
"samt": 0,
|
||||
"csamt": 0,
|
||||
"txval": 0,
|
||||
"camt": 0,
|
||||
"iamt": 0
|
||||
},
|
||||
"isup_rev": {
|
||||
"samt": 0,
|
||||
"csamt": 0,
|
||||
"txval": 0,
|
||||
"camt": 0,
|
||||
"iamt": 0
|
||||
},
|
||||
"osup_nongst": {
|
||||
"txval": 0
|
||||
}
|
||||
},
|
||||
"inter_sup": {
|
||||
"unreg_details": [],
|
||||
"comp_details": [],
|
||||
"uin_details": []
|
||||
},
|
||||
"itc_elg": {
|
||||
"itc_avl": [
|
||||
{
|
||||
"csamt": 0,
|
||||
"samt": 0,
|
||||
"ty": "IMPG",
|
||||
"camt": 0,
|
||||
"iamt": 0
|
||||
},
|
||||
{
|
||||
"csamt": 0,
|
||||
"samt": 0,
|
||||
"ty": "IMPS",
|
||||
"camt": 0,
|
||||
"iamt": 0
|
||||
},
|
||||
{
|
||||
"samt": 0,
|
||||
"csamt": 0,
|
||||
"ty": "ISRC",
|
||||
"camt": 0,
|
||||
"iamt": 0
|
||||
},
|
||||
{
|
||||
"ty": "ISD",
|
||||
"iamt": 0,
|
||||
"camt": 0,
|
||||
"samt": 0,
|
||||
"csamt": 0
|
||||
},
|
||||
{
|
||||
"samt": 0,
|
||||
"csamt": 0,
|
||||
"ty": "OTH",
|
||||
"camt": 0,
|
||||
"iamt": 0
|
||||
}
|
||||
],
|
||||
"itc_rev": [
|
||||
{
|
||||
"ty": "RUL",
|
||||
"iamt": 0,
|
||||
"camt": 0,
|
||||
"samt": 0,
|
||||
"csamt": 0
|
||||
},
|
||||
{
|
||||
"ty": "OTH",
|
||||
"iamt": 0,
|
||||
"camt": 0,
|
||||
"samt": 0,
|
||||
"csamt": 0
|
||||
}
|
||||
],
|
||||
"itc_net": {
|
||||
"samt": 0,
|
||||
"csamt": 0,
|
||||
"camt": 0,
|
||||
"iamt": 0
|
||||
},
|
||||
"itc_inelg": [
|
||||
{
|
||||
"ty": "RUL",
|
||||
"iamt": 0,
|
||||
"camt": 0,
|
||||
"samt": 0,
|
||||
"csamt": 0
|
||||
},
|
||||
{
|
||||
"ty": "OTH",
|
||||
"iamt": 0,
|
||||
"camt": 0,
|
||||
"samt": 0,
|
||||
"csamt": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,534 +0,0 @@
|
||||
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
|
||||
import json
|
||||
import unittest
|
||||
|
||||
import frappe
|
||||
from frappe.utils import getdate
|
||||
|
||||
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
|
||||
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
||||
from erpnext.stock.doctype.item.test_item import make_item
|
||||
|
||||
test_dependencies = ["Territory", "Customer Group", "Supplier Group", "Item"]
|
||||
|
||||
|
||||
class TestGSTR3BReport(unittest.TestCase):
|
||||
def setUp(self):
|
||||
frappe.set_user("Administrator")
|
||||
|
||||
frappe.db.sql("delete from `tabSales Invoice` where company='_Test Company GST'")
|
||||
frappe.db.sql("delete from `tabPurchase Invoice` where company='_Test Company GST'")
|
||||
frappe.db.sql("delete from `tabGSTR 3B Report` where company='_Test Company GST'")
|
||||
|
||||
make_company()
|
||||
make_item("Milk", properties={"is_nil_exempt": 1, "standard_rate": 0.000000})
|
||||
set_account_heads()
|
||||
make_customers()
|
||||
make_suppliers()
|
||||
|
||||
def test_gstr_3b_report(self):
|
||||
month_number_mapping = {
|
||||
1: "January",
|
||||
2: "February",
|
||||
3: "March",
|
||||
4: "April",
|
||||
5: "May",
|
||||
6: "June",
|
||||
7: "July",
|
||||
8: "August",
|
||||
9: "September",
|
||||
10: "October",
|
||||
11: "November",
|
||||
12: "December",
|
||||
}
|
||||
|
||||
make_sales_invoice()
|
||||
create_purchase_invoices()
|
||||
|
||||
if frappe.db.exists("GSTR 3B Report", "GSTR3B-March-2019-_Test Address GST-Billing"):
|
||||
report = frappe.get_doc("GSTR 3B Report", "GSTR3B-March-2019-_Test Address GST-Billing")
|
||||
report.save()
|
||||
else:
|
||||
report = frappe.get_doc(
|
||||
{
|
||||
"doctype": "GSTR 3B Report",
|
||||
"company": "_Test Company GST",
|
||||
"company_address": "_Test Address GST-Billing",
|
||||
"year": getdate().year,
|
||||
"month": month_number_mapping.get(getdate().month),
|
||||
}
|
||||
).insert()
|
||||
|
||||
output = json.loads(report.json_output)
|
||||
|
||||
self.assertEqual(output["sup_details"]["osup_det"]["iamt"], 54)
|
||||
self.assertEqual(output["inter_sup"]["unreg_details"][0]["iamt"], 18),
|
||||
self.assertEqual(output["sup_details"]["osup_nil_exmp"]["txval"], 100),
|
||||
self.assertEqual(output["inward_sup"]["isup_details"][0]["intra"], 250)
|
||||
self.assertEqual(output["itc_elg"]["itc_avl"][4]["samt"], 22.50)
|
||||
self.assertEqual(output["itc_elg"]["itc_avl"][4]["camt"], 22.50)
|
||||
|
||||
def test_gst_rounding(self):
|
||||
gst_settings = frappe.get_doc("GST Settings")
|
||||
gst_settings.round_off_gst_values = 1
|
||||
gst_settings.save()
|
||||
|
||||
current_country = frappe.flags.country
|
||||
frappe.flags.country = "India"
|
||||
|
||||
si = create_sales_invoice(
|
||||
company="_Test Company GST",
|
||||
customer="_Test GST Customer",
|
||||
currency="INR",
|
||||
warehouse="Finished Goods - _GST",
|
||||
debit_to="Debtors - _GST",
|
||||
income_account="Sales - _GST",
|
||||
expense_account="Cost of Goods Sold - _GST",
|
||||
cost_center="Main - _GST",
|
||||
rate=216,
|
||||
do_not_save=1,
|
||||
)
|
||||
|
||||
si.append(
|
||||
"taxes",
|
||||
{
|
||||
"charge_type": "On Net Total",
|
||||
"account_head": "Output Tax IGST - _GST",
|
||||
"cost_center": "Main - _GST",
|
||||
"description": "IGST @ 18.0",
|
||||
"rate": 18,
|
||||
},
|
||||
)
|
||||
|
||||
si.save()
|
||||
# Check for 39 instead of 38.88
|
||||
self.assertEqual(si.taxes[0].base_tax_amount_after_discount_amount, 39)
|
||||
|
||||
frappe.flags.country = current_country
|
||||
gst_settings.round_off_gst_values = 1
|
||||
gst_settings.save()
|
||||
|
||||
def test_gst_category_auto_update(self):
|
||||
if not frappe.db.exists("Customer", "_Test GST Customer With GSTIN"):
|
||||
customer = frappe.get_doc(
|
||||
{
|
||||
"customer_group": "_Test Customer Group",
|
||||
"customer_name": "_Test GST Customer With GSTIN",
|
||||
"customer_type": "Individual",
|
||||
"doctype": "Customer",
|
||||
"territory": "_Test Territory",
|
||||
}
|
||||
).insert()
|
||||
|
||||
self.assertEqual(customer.gst_category, "Unregistered")
|
||||
|
||||
if not frappe.db.exists("Address", "_Test GST Category-1-Billing"):
|
||||
address = frappe.get_doc(
|
||||
{
|
||||
"address_line1": "_Test Address Line 1",
|
||||
"address_title": "_Test GST Category-1",
|
||||
"address_type": "Billing",
|
||||
"city": "_Test City",
|
||||
"state": "Test State",
|
||||
"country": "India",
|
||||
"doctype": "Address",
|
||||
"is_primary_address": 1,
|
||||
"phone": "+91 0000000000",
|
||||
"gstin": "29AZWPS7135H1ZG",
|
||||
"gst_state": "Karnataka",
|
||||
"gst_state_number": "29",
|
||||
}
|
||||
).insert()
|
||||
|
||||
address.append(
|
||||
"links", {"link_doctype": "Customer", "link_name": "_Test GST Customer With GSTIN"}
|
||||
)
|
||||
|
||||
address.save()
|
||||
|
||||
customer.load_from_db()
|
||||
self.assertEqual(customer.gst_category, "Registered Regular")
|
||||
|
||||
|
||||
def make_sales_invoice():
|
||||
si = create_sales_invoice(
|
||||
company="_Test Company GST",
|
||||
customer="_Test GST Customer",
|
||||
currency="INR",
|
||||
warehouse="Finished Goods - _GST",
|
||||
debit_to="Debtors - _GST",
|
||||
income_account="Sales - _GST",
|
||||
expense_account="Cost of Goods Sold - _GST",
|
||||
cost_center="Main - _GST",
|
||||
do_not_save=1,
|
||||
)
|
||||
|
||||
si.append(
|
||||
"taxes",
|
||||
{
|
||||
"charge_type": "On Net Total",
|
||||
"account_head": "Output Tax IGST - _GST",
|
||||
"cost_center": "Main - _GST",
|
||||
"description": "IGST @ 18.0",
|
||||
"rate": 18,
|
||||
},
|
||||
)
|
||||
|
||||
si.submit()
|
||||
|
||||
si1 = create_sales_invoice(
|
||||
company="_Test Company GST",
|
||||
customer="_Test GST SEZ Customer",
|
||||
currency="INR",
|
||||
warehouse="Finished Goods - _GST",
|
||||
debit_to="Debtors - _GST",
|
||||
income_account="Sales - _GST",
|
||||
expense_account="Cost of Goods Sold - _GST",
|
||||
cost_center="Main - _GST",
|
||||
do_not_save=1,
|
||||
)
|
||||
|
||||
si1.append(
|
||||
"taxes",
|
||||
{
|
||||
"charge_type": "On Net Total",
|
||||
"account_head": "Output Tax IGST - _GST",
|
||||
"cost_center": "Main - _GST",
|
||||
"description": "IGST @ 18.0",
|
||||
"rate": 18,
|
||||
},
|
||||
)
|
||||
|
||||
si1.submit()
|
||||
|
||||
si2 = create_sales_invoice(
|
||||
company="_Test Company GST",
|
||||
customer="_Test Unregistered Customer",
|
||||
currency="INR",
|
||||
warehouse="Finished Goods - _GST",
|
||||
debit_to="Debtors - _GST",
|
||||
income_account="Sales - _GST",
|
||||
expense_account="Cost of Goods Sold - _GST",
|
||||
cost_center="Main - _GST",
|
||||
do_not_save=1,
|
||||
)
|
||||
|
||||
si2.append(
|
||||
"taxes",
|
||||
{
|
||||
"charge_type": "On Net Total",
|
||||
"account_head": "Output Tax IGST - _GST",
|
||||
"cost_center": "Main - _GST",
|
||||
"description": "IGST @ 18.0",
|
||||
"rate": 18,
|
||||
},
|
||||
)
|
||||
|
||||
si2.submit()
|
||||
|
||||
si3 = create_sales_invoice(
|
||||
company="_Test Company GST",
|
||||
customer="_Test GST Customer",
|
||||
currency="INR",
|
||||
item="Milk",
|
||||
warehouse="Finished Goods - _GST",
|
||||
debit_to="Debtors - _GST",
|
||||
income_account="Sales - _GST",
|
||||
expense_account="Cost of Goods Sold - _GST",
|
||||
cost_center="Main - _GST",
|
||||
do_not_save=1,
|
||||
)
|
||||
si3.submit()
|
||||
|
||||
|
||||
def create_purchase_invoices():
|
||||
pi = make_purchase_invoice(
|
||||
company="_Test Company GST",
|
||||
supplier="_Test Registered Supplier",
|
||||
currency="INR",
|
||||
warehouse="Finished Goods - _GST",
|
||||
cost_center="Main - _GST",
|
||||
expense_account="Cost of Goods Sold - _GST",
|
||||
do_not_save=1,
|
||||
)
|
||||
|
||||
pi.eligibility_for_itc = "All Other ITC"
|
||||
|
||||
pi.append(
|
||||
"taxes",
|
||||
{
|
||||
"charge_type": "On Net Total",
|
||||
"account_head": "Input Tax CGST - _GST",
|
||||
"cost_center": "Main - _GST",
|
||||
"description": "CGST @ 9.0",
|
||||
"rate": 9,
|
||||
},
|
||||
)
|
||||
|
||||
pi.append(
|
||||
"taxes",
|
||||
{
|
||||
"charge_type": "On Net Total",
|
||||
"account_head": "Input Tax SGST - _GST",
|
||||
"cost_center": "Main - _GST",
|
||||
"description": "SGST @ 9.0",
|
||||
"rate": 9,
|
||||
},
|
||||
)
|
||||
|
||||
pi.submit()
|
||||
|
||||
pi1 = make_purchase_invoice(
|
||||
company="_Test Company GST",
|
||||
supplier="_Test Registered Supplier",
|
||||
currency="INR",
|
||||
warehouse="Finished Goods - _GST",
|
||||
cost_center="Main - _GST",
|
||||
expense_account="Cost of Goods Sold - _GST",
|
||||
item="Milk",
|
||||
do_not_save=1,
|
||||
)
|
||||
|
||||
pi1.shipping_address = "_Test Supplier GST-1-Billing"
|
||||
pi1.save()
|
||||
|
||||
pi1.submit()
|
||||
|
||||
pi2 = make_purchase_invoice(
|
||||
company="_Test Company GST",
|
||||
customer="_Test Registered Supplier",
|
||||
currency="INR",
|
||||
item="Milk",
|
||||
warehouse="Finished Goods - _GST",
|
||||
expense_account="Cost of Goods Sold - _GST",
|
||||
cost_center="Main - _GST",
|
||||
rate=250,
|
||||
qty=1,
|
||||
do_not_save=1,
|
||||
)
|
||||
pi2.submit()
|
||||
|
||||
|
||||
def make_suppliers():
|
||||
if not frappe.db.exists("Supplier", "_Test Registered Supplier"):
|
||||
frappe.get_doc(
|
||||
{
|
||||
"supplier_group": "_Test Supplier Group",
|
||||
"supplier_name": "_Test Registered Supplier",
|
||||
"gst_category": "Registered Regular",
|
||||
"supplier_type": "Individual",
|
||||
"doctype": "Supplier",
|
||||
}
|
||||
).insert()
|
||||
|
||||
if not frappe.db.exists("Supplier", "_Test Unregistered Supplier"):
|
||||
frappe.get_doc(
|
||||
{
|
||||
"supplier_group": "_Test Supplier Group",
|
||||
"supplier_name": "_Test Unregistered Supplier",
|
||||
"gst_category": "Unregistered",
|
||||
"supplier_type": "Individual",
|
||||
"doctype": "Supplier",
|
||||
}
|
||||
).insert()
|
||||
|
||||
if not frappe.db.exists("Address", "_Test Supplier GST-1-Billing"):
|
||||
address = frappe.get_doc(
|
||||
{
|
||||
"address_line1": "_Test Address Line 1",
|
||||
"address_title": "_Test Supplier GST-1",
|
||||
"address_type": "Billing",
|
||||
"city": "_Test City",
|
||||
"state": "Test State",
|
||||
"country": "India",
|
||||
"doctype": "Address",
|
||||
"is_primary_address": 1,
|
||||
"phone": "+91 0000000000",
|
||||
"gstin": "29AACCV0498C1Z9",
|
||||
"gst_state": "Karnataka",
|
||||
}
|
||||
).insert()
|
||||
|
||||
address.append("links", {"link_doctype": "Supplier", "link_name": "_Test Registered Supplier"})
|
||||
|
||||
address.is_shipping_address = 1
|
||||
address.save()
|
||||
|
||||
if not frappe.db.exists("Address", "_Test Supplier GST-2-Billing"):
|
||||
address = frappe.get_doc(
|
||||
{
|
||||
"address_line1": "_Test Address Line 1",
|
||||
"address_title": "_Test Supplier GST-2",
|
||||
"address_type": "Billing",
|
||||
"city": "_Test City",
|
||||
"state": "Test State",
|
||||
"country": "India",
|
||||
"doctype": "Address",
|
||||
"is_primary_address": 1,
|
||||
"phone": "+91 0000000000",
|
||||
"gst_state": "Karnataka",
|
||||
}
|
||||
).insert()
|
||||
|
||||
address.append("links", {"link_doctype": "Supplier", "link_name": "_Test Unregistered Supplier"})
|
||||
|
||||
address.save()
|
||||
|
||||
|
||||
def make_customers():
|
||||
if not frappe.db.exists("Customer", "_Test GST Customer"):
|
||||
frappe.get_doc(
|
||||
{
|
||||
"customer_group": "_Test Customer Group",
|
||||
"customer_name": "_Test GST Customer",
|
||||
"gst_category": "Registered Regular",
|
||||
"customer_type": "Individual",
|
||||
"doctype": "Customer",
|
||||
"territory": "_Test Territory",
|
||||
}
|
||||
).insert()
|
||||
|
||||
if not frappe.db.exists("Customer", "_Test GST SEZ Customer"):
|
||||
frappe.get_doc(
|
||||
{
|
||||
"customer_group": "_Test Customer Group",
|
||||
"customer_name": "_Test GST SEZ Customer",
|
||||
"gst_category": "SEZ",
|
||||
"customer_type": "Individual",
|
||||
"doctype": "Customer",
|
||||
"territory": "_Test Territory",
|
||||
}
|
||||
).insert()
|
||||
|
||||
if not frappe.db.exists("Customer", "_Test Unregistered Customer"):
|
||||
frappe.get_doc(
|
||||
{
|
||||
"customer_group": "_Test Customer Group",
|
||||
"customer_name": "_Test Unregistered Customer",
|
||||
"gst_category": "Unregistered",
|
||||
"customer_type": "Individual",
|
||||
"doctype": "Customer",
|
||||
"territory": "_Test Territory",
|
||||
}
|
||||
).insert()
|
||||
|
||||
if not frappe.db.exists("Address", "_Test GST-1-Billing"):
|
||||
address = frappe.get_doc(
|
||||
{
|
||||
"address_line1": "_Test Address Line 1",
|
||||
"address_title": "_Test GST-1",
|
||||
"address_type": "Billing",
|
||||
"city": "_Test City",
|
||||
"state": "Test State",
|
||||
"country": "India",
|
||||
"doctype": "Address",
|
||||
"is_primary_address": 1,
|
||||
"phone": "+91 0000000000",
|
||||
"gstin": "29AZWPS7135H1ZG",
|
||||
"gst_state": "Karnataka",
|
||||
"gst_state_number": "29",
|
||||
}
|
||||
).insert()
|
||||
|
||||
address.append("links", {"link_doctype": "Customer", "link_name": "_Test GST Customer"})
|
||||
|
||||
address.save()
|
||||
|
||||
if not frappe.db.exists("Address", "_Test GST-2-Billing"):
|
||||
address = frappe.get_doc(
|
||||
{
|
||||
"address_line1": "_Test Address Line 1",
|
||||
"address_title": "_Test GST-2",
|
||||
"address_type": "Billing",
|
||||
"city": "_Test City",
|
||||
"state": "Test State",
|
||||
"country": "India",
|
||||
"doctype": "Address",
|
||||
"is_primary_address": 1,
|
||||
"phone": "+91 0000000000",
|
||||
"gst_state": "Haryana",
|
||||
}
|
||||
).insert()
|
||||
|
||||
address.append("links", {"link_doctype": "Customer", "link_name": "_Test Unregistered Customer"})
|
||||
|
||||
address.save()
|
||||
|
||||
if not frappe.db.exists("Address", "_Test GST-3-Billing"):
|
||||
address = frappe.get_doc(
|
||||
{
|
||||
"address_line1": "_Test Address Line 1",
|
||||
"address_title": "_Test GST-3",
|
||||
"address_type": "Billing",
|
||||
"city": "_Test City",
|
||||
"state": "Test State",
|
||||
"country": "India",
|
||||
"doctype": "Address",
|
||||
"is_primary_address": 1,
|
||||
"phone": "+91 0000000000",
|
||||
"gst_state": "Gujarat",
|
||||
}
|
||||
).insert()
|
||||
|
||||
address.append("links", {"link_doctype": "Customer", "link_name": "_Test GST SEZ Customer"})
|
||||
|
||||
address.save()
|
||||
|
||||
|
||||
def make_company():
|
||||
if frappe.db.exists("Company", "_Test Company GST"):
|
||||
return
|
||||
|
||||
company = frappe.new_doc("Company")
|
||||
company.company_name = "_Test Company GST"
|
||||
company.abbr = "_GST"
|
||||
company.default_currency = "INR"
|
||||
company.country = "India"
|
||||
company.insert()
|
||||
|
||||
if not frappe.db.exists("Address", "_Test Address GST-Billing"):
|
||||
address = frappe.get_doc(
|
||||
{
|
||||
"address_title": "_Test Address GST",
|
||||
"address_line1": "_Test Address Line 1",
|
||||
"address_type": "Billing",
|
||||
"city": "_Test City",
|
||||
"state": "Test State",
|
||||
"country": "India",
|
||||
"doctype": "Address",
|
||||
"is_primary_address": 1,
|
||||
"phone": "+91 0000000000",
|
||||
"gstin": "27AAECE4835E1ZR",
|
||||
"gst_state": "Maharashtra",
|
||||
"gst_state_number": "27",
|
||||
}
|
||||
).insert()
|
||||
|
||||
address.append("links", {"link_doctype": "Company", "link_name": "_Test Company GST"})
|
||||
|
||||
address.save()
|
||||
|
||||
|
||||
def set_account_heads():
|
||||
gst_settings = frappe.get_doc("GST Settings")
|
||||
|
||||
gst_account = frappe.get_all(
|
||||
"GST Account",
|
||||
fields=["cgst_account", "sgst_account", "igst_account"],
|
||||
filters={"company": "_Test Company GST"},
|
||||
)
|
||||
|
||||
if not gst_account:
|
||||
gst_settings.append(
|
||||
"gst_accounts",
|
||||
{
|
||||
"company": "_Test Company GST",
|
||||
"cgst_account": "Output Tax CGST - _GST",
|
||||
"sgst_account": "Output Tax SGST - _GST",
|
||||
"igst_account": "Output Tax IGST - _GST",
|
||||
},
|
||||
)
|
||||
|
||||
gst_settings.save()
|
||||
@@ -1,82 +0,0 @@
|
||||
states = [
|
||||
"",
|
||||
"Andaman and Nicobar Islands",
|
||||
"Andhra Pradesh",
|
||||
"Arunachal Pradesh",
|
||||
"Assam",
|
||||
"Bihar",
|
||||
"Chandigarh",
|
||||
"Chhattisgarh",
|
||||
"Dadra and Nagar Haveli and Daman and Diu",
|
||||
"Delhi",
|
||||
"Goa",
|
||||
"Gujarat",
|
||||
"Haryana",
|
||||
"Himachal Pradesh",
|
||||
"Jammu and Kashmir",
|
||||
"Jharkhand",
|
||||
"Karnataka",
|
||||
"Kerala",
|
||||
"Ladakh",
|
||||
"Lakshadweep Islands",
|
||||
"Madhya Pradesh",
|
||||
"Maharashtra",
|
||||
"Manipur",
|
||||
"Meghalaya",
|
||||
"Mizoram",
|
||||
"Nagaland",
|
||||
"Odisha",
|
||||
"Other Territory",
|
||||
"Pondicherry",
|
||||
"Punjab",
|
||||
"Rajasthan",
|
||||
"Sikkim",
|
||||
"Tamil Nadu",
|
||||
"Telangana",
|
||||
"Tripura",
|
||||
"Uttar Pradesh",
|
||||
"Uttarakhand",
|
||||
"West Bengal",
|
||||
]
|
||||
|
||||
state_numbers = {
|
||||
"Andaman and Nicobar Islands": "35",
|
||||
"Andhra Pradesh": "37",
|
||||
"Arunachal Pradesh": "12",
|
||||
"Assam": "18",
|
||||
"Bihar": "10",
|
||||
"Chandigarh": "04",
|
||||
"Chhattisgarh": "22",
|
||||
"Dadra and Nagar Haveli and Daman and Diu": "26",
|
||||
"Delhi": "07",
|
||||
"Goa": "30",
|
||||
"Gujarat": "24",
|
||||
"Haryana": "06",
|
||||
"Himachal Pradesh": "02",
|
||||
"Jammu and Kashmir": "01",
|
||||
"Jharkhand": "20",
|
||||
"Karnataka": "29",
|
||||
"Kerala": "32",
|
||||
"Ladakh": "38",
|
||||
"Lakshadweep Islands": "31",
|
||||
"Madhya Pradesh": "23",
|
||||
"Maharashtra": "27",
|
||||
"Manipur": "14",
|
||||
"Meghalaya": "17",
|
||||
"Mizoram": "15",
|
||||
"Nagaland": "13",
|
||||
"Odisha": "21",
|
||||
"Other Territory": "97",
|
||||
"Pondicherry": "34",
|
||||
"Punjab": "03",
|
||||
"Rajasthan": "08",
|
||||
"Sikkim": "11",
|
||||
"Tamil Nadu": "33",
|
||||
"Telangana": "36",
|
||||
"Tripura": "16",
|
||||
"Uttar Pradesh": "09",
|
||||
"Uttarakhand": "05",
|
||||
"West Bengal": "19",
|
||||
}
|
||||
|
||||
number_state_mapping = {v: k for k, v in state_numbers.items()}
|
||||
@@ -1,27 +0,0 @@
|
||||
{{
|
||||
"SlNo": "{item.sr_no}",
|
||||
"PrdDesc": "{item.description}",
|
||||
"IsServc": "{item.is_service_item}",
|
||||
"HsnCd": "{item.gst_hsn_code}",
|
||||
"Barcde": "{item.barcode}",
|
||||
"Unit": "{item.uom}",
|
||||
"Qty": "{item.qty}",
|
||||
"FreeQty": "{item.free_qty}",
|
||||
"UnitPrice": "{item.unit_rate}",
|
||||
"TotAmt": "{item.gross_amount}",
|
||||
"Discount": "{item.discount_amount}",
|
||||
"AssAmt": "{item.taxable_value}",
|
||||
"PrdSlNo": "{item.serial_no}",
|
||||
"GstRt": "{item.tax_rate}",
|
||||
"IgstAmt": "{item.igst_amount}",
|
||||
"CgstAmt": "{item.cgst_amount}",
|
||||
"SgstAmt": "{item.sgst_amount}",
|
||||
"CesRt": "{item.cess_rate}",
|
||||
"CesAmt": "{item.cess_amount}",
|
||||
"CesNonAdvlAmt": "{item.cess_nadv_amount}",
|
||||
"StateCesRt": "{item.state_cess_rate}",
|
||||
"StateCesAmt": "{item.state_cess_amount}",
|
||||
"StateCesNonAdvlAmt": "{item.state_cess_nadv_amount}",
|
||||
"OthChrg": "{item.other_charges}",
|
||||
"TotItemVal": "{item.total_value}"
|
||||
}}
|
||||
@@ -1,110 +0,0 @@
|
||||
{{
|
||||
"Version": "1.1",
|
||||
"TranDtls": {{
|
||||
"TaxSch": "{transaction_details.tax_scheme}",
|
||||
"SupTyp": "{transaction_details.supply_type}",
|
||||
"RegRev": "{transaction_details.reverse_charge}",
|
||||
"EcmGstin": "{transaction_details.ecom_gstin}",
|
||||
"IgstOnIntra": "{transaction_details.igst_on_intra}"
|
||||
}},
|
||||
"DocDtls": {{
|
||||
"Typ": "{doc_details.invoice_type}",
|
||||
"No": "{doc_details.invoice_name}",
|
||||
"Dt": "{doc_details.invoice_date}"
|
||||
}},
|
||||
"SellerDtls": {{
|
||||
"Gstin": "{seller_details.gstin}",
|
||||
"LglNm": "{seller_details.legal_name}",
|
||||
"TrdNm": "{seller_details.trade_name}",
|
||||
"Loc": "{seller_details.location}",
|
||||
"Pin": "{seller_details.pincode}",
|
||||
"Stcd": "{seller_details.state_code}",
|
||||
"Addr1": "{seller_details.address_line1}",
|
||||
"Addr2": "{seller_details.address_line2}",
|
||||
"Ph": "{seller_details.phone}",
|
||||
"Em": "{seller_details.email}"
|
||||
}},
|
||||
"BuyerDtls": {{
|
||||
"Gstin": "{buyer_details.gstin}",
|
||||
"LglNm": "{buyer_details.legal_name}",
|
||||
"TrdNm": "{buyer_details.trade_name}",
|
||||
"Addr1": "{buyer_details.address_line1}",
|
||||
"Addr2": "{buyer_details.address_line2}",
|
||||
"Loc": "{buyer_details.location}",
|
||||
"Pin": "{buyer_details.pincode}",
|
||||
"Stcd": "{buyer_details.state_code}",
|
||||
"Ph": "{buyer_details.phone}",
|
||||
"Em": "{buyer_details.email}",
|
||||
"Pos": "{buyer_details.place_of_supply}"
|
||||
}},
|
||||
"DispDtls": {{
|
||||
"Nm": "{dispatch_details.legal_name}",
|
||||
"Addr1": "{dispatch_details.address_line1}",
|
||||
"Addr2": "{dispatch_details.address_line2}",
|
||||
"Loc": "{dispatch_details.location}",
|
||||
"Pin": "{dispatch_details.pincode}",
|
||||
"Stcd": "{dispatch_details.state_code}"
|
||||
}},
|
||||
"ShipDtls": {{
|
||||
"Gstin": "{shipping_details.gstin}",
|
||||
"LglNm": "{shipping_details.legal_name}",
|
||||
"TrdNm": "{shipping_details.trader_name}",
|
||||
"Addr1": "{shipping_details.address_line1}",
|
||||
"Addr2": "{shipping_details.address_line2}",
|
||||
"Loc": "{shipping_details.location}",
|
||||
"Pin": "{shipping_details.pincode}",
|
||||
"Stcd": "{shipping_details.state_code}"
|
||||
}},
|
||||
"ItemList": [
|
||||
{item_list}
|
||||
],
|
||||
"ValDtls": {{
|
||||
"AssVal": "{invoice_value_details.base_total}",
|
||||
"CgstVal": "{invoice_value_details.total_cgst_amt}",
|
||||
"SgstVal": "{invoice_value_details.total_sgst_amt}",
|
||||
"IgstVal": "{invoice_value_details.total_igst_amt}",
|
||||
"CesVal": "{invoice_value_details.total_cess_amt}",
|
||||
"Discount": "{invoice_value_details.invoice_discount_amt}",
|
||||
"RndOffAmt": "{invoice_value_details.round_off}",
|
||||
"OthChrg": "{invoice_value_details.total_other_charges}",
|
||||
"TotInvVal": "{invoice_value_details.base_grand_total}",
|
||||
"TotInvValFc": "{invoice_value_details.grand_total}"
|
||||
}},
|
||||
"PayDtls": {{
|
||||
"Nm": "{payment_details.payee_name}",
|
||||
"AccDet": "{payment_details.account_no}",
|
||||
"Mode": "{payment_details.mode_of_payment}",
|
||||
"FinInsBr": "{payment_details.ifsc_code}",
|
||||
"PayTerm": "{payment_details.terms}",
|
||||
"PaidAmt": "{payment_details.paid_amount}",
|
||||
"PaymtDue": "{payment_details.outstanding_amount}"
|
||||
}},
|
||||
"RefDtls": {{
|
||||
"DocPerdDtls": {{
|
||||
"InvStDt": "{period_details.start_date}",
|
||||
"InvEndDt": "{period_details.end_date}"
|
||||
}},
|
||||
"PrecDocDtls": [{{
|
||||
"InvNo": "{prev_doc_details.invoice_name}",
|
||||
"InvDt": "{prev_doc_details.invoice_date}"
|
||||
}}]
|
||||
}},
|
||||
"ExpDtls": {{
|
||||
"ShipBNo": "{export_details.bill_no}",
|
||||
"ShipBDt": "{export_details.bill_date}",
|
||||
"Port": "{export_details.port}",
|
||||
"ForCur": "{export_details.foreign_curr_code}",
|
||||
"CntCode": "{export_details.country_code}",
|
||||
"ExpDuty": "{export_details.export_duty}"
|
||||
}},
|
||||
"EwbDtls": {{
|
||||
"TransId": "{eway_bill_details.gstin}",
|
||||
"TransName": "{eway_bill_details.name}",
|
||||
"TransMode": "{eway_bill_details.mode_of_transport}",
|
||||
"Distance": "{eway_bill_details.distance}",
|
||||
"TransDocNo": "{eway_bill_details.document_name}",
|
||||
"TransDocDt": "{eway_bill_details.document_date}",
|
||||
"VehNo": "{eway_bill_details.vehicle_no}",
|
||||
"VehType": "{eway_bill_details.vehicle_type}"
|
||||
}}
|
||||
}}
|
||||
@@ -1,957 +0,0 @@
|
||||
{
|
||||
"Version": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 6,
|
||||
"description": "Version of the schema"
|
||||
},
|
||||
"Irn": {
|
||||
"type": "string",
|
||||
"minLength": 64,
|
||||
"maxLength": 64,
|
||||
"description": "Invoice Reference Number"
|
||||
},
|
||||
"TranDtls": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"TaxSch": {
|
||||
"type": "string",
|
||||
"minLength": 3,
|
||||
"maxLength": 10,
|
||||
"enum": ["GST"],
|
||||
"description": "GST- Goods and Services Tax Scheme"
|
||||
},
|
||||
"SupTyp": {
|
||||
"type": "string",
|
||||
"minLength": 3,
|
||||
"maxLength": 10,
|
||||
"enum": ["B2B", "SEZWP", "SEZWOP", "EXPWP", "EXPWOP", "DEXP"],
|
||||
"description": "Type of Supply: B2B-Business to Business, SEZWP - SEZ with payment, SEZWOP - SEZ without payment, EXPWP - Export with Payment, EXPWOP - Export without payment,DEXP - Deemed Export"
|
||||
},
|
||||
"RegRev": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 1,
|
||||
"enum": ["Y", "N"],
|
||||
"description": "Y- whether the tax liability is payable under reverse charge"
|
||||
},
|
||||
"EcmGstin": {
|
||||
"type": "string",
|
||||
"minLength": 15,
|
||||
"maxLength": 15,
|
||||
"pattern": "([0-9]{2}[0-9A-Z]{13})",
|
||||
"description": "E-Commerce GSTIN",
|
||||
"validationMsg": "E-Commerce GSTIN is invalid"
|
||||
},
|
||||
"IgstOnIntra": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 1,
|
||||
"enum": ["Y", "N"],
|
||||
"description": "Y- indicates the supply is intra state but chargeable to IGST"
|
||||
}
|
||||
},
|
||||
"required": ["TaxSch", "SupTyp"]
|
||||
},
|
||||
"DocDtls": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"Typ": {
|
||||
"type": "string",
|
||||
"minLength": 3,
|
||||
"maxLength": 3,
|
||||
"enum": ["INV", "CRN", "DBN"],
|
||||
"description": "Document Type"
|
||||
},
|
||||
"No": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 16,
|
||||
"pattern": "^([A-Z1-9]{1}[A-Z0-9/-]{0,15})$",
|
||||
"description": "Document Number",
|
||||
"validationMsg": "Document Number should not be starting with 0, / and -"
|
||||
},
|
||||
"Dt": {
|
||||
"type": "string",
|
||||
"minLength": 10,
|
||||
"maxLength": 10,
|
||||
"pattern": "[0-3][0-9]/[0-1][0-9]/[2][0][1-2][0-9]",
|
||||
"description": "Document Date"
|
||||
}
|
||||
},
|
||||
"required": ["Typ", "No", "Dt"]
|
||||
},
|
||||
"SellerDtls": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"Gstin": {
|
||||
"type": "string",
|
||||
"minLength": 15,
|
||||
"maxLength": 15,
|
||||
"pattern": "([0-9]{2}[0-9A-Z]{13})",
|
||||
"description": "Supplier GSTIN",
|
||||
"validationMsg": "Company GSTIN is invalid"
|
||||
},
|
||||
"LglNm": {
|
||||
"type": "string",
|
||||
"minLength": 3,
|
||||
"maxLength": 100,
|
||||
"description": "Legal Name"
|
||||
},
|
||||
"TrdNm": {
|
||||
"type": "string",
|
||||
"minLength": 3,
|
||||
"maxLength": 100,
|
||||
"description": "Tradename"
|
||||
},
|
||||
"Addr1": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 100,
|
||||
"description": "Address Line 1"
|
||||
},
|
||||
"Addr2": {
|
||||
"type": "string",
|
||||
"minLength": 3,
|
||||
"maxLength": 100,
|
||||
"description": "Address Line 2"
|
||||
},
|
||||
"Loc": {
|
||||
"type": "string",
|
||||
"minLength": 3,
|
||||
"maxLength": 50,
|
||||
"description": "Location"
|
||||
},
|
||||
"Pin": {
|
||||
"type": "number",
|
||||
"minimum": 100000,
|
||||
"maximum": 999999,
|
||||
"description": "Pincode"
|
||||
},
|
||||
"Stcd": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 2,
|
||||
"description": "Supplier State Code"
|
||||
},
|
||||
"Ph": {
|
||||
"type": "string",
|
||||
"minLength": 6,
|
||||
"maxLength": 12,
|
||||
"description": "Phone"
|
||||
},
|
||||
"Em": {
|
||||
"type": "string",
|
||||
"minLength": 6,
|
||||
"maxLength": 100,
|
||||
"description": "Email-Id"
|
||||
}
|
||||
},
|
||||
"required": ["Gstin", "LglNm", "Addr1", "Loc", "Pin", "Stcd"]
|
||||
},
|
||||
"BuyerDtls": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"Gstin": {
|
||||
"type": "string",
|
||||
"minLength": 3,
|
||||
"maxLength": 15,
|
||||
"pattern": "^(([0-9]{2}[0-9A-Z]{13})|URP)$",
|
||||
"description": "Buyer GSTIN",
|
||||
"validationMsg": "Customer GSTIN is invalid"
|
||||
},
|
||||
"LglNm": {
|
||||
"type": "string",
|
||||
"minLength": 3,
|
||||
"maxLength": 100,
|
||||
"description": "Legal Name"
|
||||
},
|
||||
"TrdNm": {
|
||||
"type": "string",
|
||||
"minLength": 3,
|
||||
"maxLength": 100,
|
||||
"description": "Trade Name"
|
||||
},
|
||||
"Pos": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 2,
|
||||
"description": "Place of Supply State code"
|
||||
},
|
||||
"Addr1": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 100,
|
||||
"description": "Address Line 1"
|
||||
},
|
||||
"Addr2": {
|
||||
"type": "string",
|
||||
"minLength": 3,
|
||||
"maxLength": 100,
|
||||
"description": "Address Line 2"
|
||||
},
|
||||
"Loc": {
|
||||
"type": "string",
|
||||
"minLength": 3,
|
||||
"maxLength": 100,
|
||||
"description": "Location"
|
||||
},
|
||||
"Pin": {
|
||||
"type": "number",
|
||||
"minimum": 100000,
|
||||
"maximum": 999999,
|
||||
"description": "Pincode"
|
||||
},
|
||||
"Stcd": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 2,
|
||||
"description": "Buyer State Code"
|
||||
},
|
||||
"Ph": {
|
||||
"type": "string",
|
||||
"minLength": 6,
|
||||
"maxLength": 12,
|
||||
"description": "Phone"
|
||||
},
|
||||
"Em": {
|
||||
"type": "string",
|
||||
"minLength": 6,
|
||||
"maxLength": 100,
|
||||
"description": "Email-Id"
|
||||
}
|
||||
},
|
||||
"required": ["Gstin", "LglNm", "Pos", "Addr1", "Loc", "Stcd"]
|
||||
},
|
||||
"DispDtls": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"Nm": {
|
||||
"type": "string",
|
||||
"minLength": 3,
|
||||
"maxLength": 100,
|
||||
"description": "Dispatch Address Name"
|
||||
},
|
||||
"Addr1": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 100,
|
||||
"description": "Address Line 1"
|
||||
},
|
||||
"Addr2": {
|
||||
"type": "string",
|
||||
"minLength": 3,
|
||||
"maxLength": 100,
|
||||
"description": "Address Line 2"
|
||||
},
|
||||
"Loc": {
|
||||
"type": "string",
|
||||
"minLength": 3,
|
||||
"maxLength": 100,
|
||||
"description": "Location"
|
||||
},
|
||||
"Pin": {
|
||||
"type": "number",
|
||||
"minimum": 100000,
|
||||
"maximum": 999999,
|
||||
"description": "Pincode"
|
||||
},
|
||||
"Stcd": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 2,
|
||||
"description": "State Code"
|
||||
}
|
||||
},
|
||||
"required": ["Nm", "Addr1", "Loc", "Pin", "Stcd"]
|
||||
},
|
||||
"ShipDtls": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"Gstin": {
|
||||
"type": "string",
|
||||
"maxLength": 15,
|
||||
"minLength": 3,
|
||||
"pattern": "^(([0-9]{2}[0-9A-Z]{13})|URP)$",
|
||||
"description": "Shipping Address GSTIN",
|
||||
"validationMsg": "Shipping Address GSTIN is invalid"
|
||||
},
|
||||
"LglNm": {
|
||||
"type": "string",
|
||||
"minLength": 3,
|
||||
"maxLength": 100,
|
||||
"description": "Legal Name"
|
||||
},
|
||||
"TrdNm": {
|
||||
"type": "string",
|
||||
"minLength": 3,
|
||||
"maxLength": 100,
|
||||
"description": "Trade Name"
|
||||
},
|
||||
"Addr1": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 100,
|
||||
"description": "Address Line 1"
|
||||
},
|
||||
"Addr2": {
|
||||
"type": "string",
|
||||
"minLength": 3,
|
||||
"maxLength": 100,
|
||||
"description": "Address Line 2"
|
||||
},
|
||||
"Loc": {
|
||||
"type": "string",
|
||||
"minLength": 3,
|
||||
"maxLength": 100,
|
||||
"description": "Location"
|
||||
},
|
||||
"Pin": {
|
||||
"type": "number",
|
||||
"minimum": 100000,
|
||||
"maximum": 999999,
|
||||
"description": "Pincode"
|
||||
},
|
||||
"Stcd": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 2,
|
||||
"description": "State Code"
|
||||
}
|
||||
},
|
||||
"required": ["LglNm", "Addr1", "Loc", "Pin", "Stcd"]
|
||||
},
|
||||
"ItemList": {
|
||||
"type": "Array",
|
||||
"properties": {
|
||||
"SlNo": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 6,
|
||||
"description": "Serial No. of Item"
|
||||
},
|
||||
"PrdDesc": {
|
||||
"type": "string",
|
||||
"minLength": 3,
|
||||
"maxLength": 300,
|
||||
"description": "Item Name"
|
||||
},
|
||||
"IsServc": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 1,
|
||||
"enum": ["Y", "N"],
|
||||
"description": "Is Service Item"
|
||||
},
|
||||
"HsnCd": {
|
||||
"type": "string",
|
||||
"minLength": 4,
|
||||
"maxLength": 8,
|
||||
"description": "HSN Code"
|
||||
},
|
||||
"Barcde": {
|
||||
"type": "string",
|
||||
"minLength": 3,
|
||||
"maxLength": 30,
|
||||
"description": "Barcode"
|
||||
},
|
||||
"Qty": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 9999999999.999,
|
||||
"description": "Quantity"
|
||||
},
|
||||
"FreeQty": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 9999999999.999,
|
||||
"description": "Free Quantity"
|
||||
},
|
||||
"Unit": {
|
||||
"type": "string",
|
||||
"minLength": 3,
|
||||
"maxLength": 8,
|
||||
"description": "UOM"
|
||||
},
|
||||
"UnitPrice": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 999999999999.999,
|
||||
"description": "Rate"
|
||||
},
|
||||
"TotAmt": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 999999999999.99,
|
||||
"description": "Gross Amount"
|
||||
},
|
||||
"Discount": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 999999999999.99,
|
||||
"description": "Discount"
|
||||
},
|
||||
"PreTaxVal": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 999999999999.99,
|
||||
"description": "Pre tax value"
|
||||
},
|
||||
"AssAmt": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 999999999999.99,
|
||||
"description": "Taxable Value"
|
||||
},
|
||||
"GstRt": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 999.999,
|
||||
"description": "GST Rate"
|
||||
},
|
||||
"IgstAmt": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 999999999999.99,
|
||||
"description": "IGST Amount"
|
||||
},
|
||||
"CgstAmt": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 999999999999.99,
|
||||
"description": "CGST Amount"
|
||||
},
|
||||
"SgstAmt": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 999999999999.99,
|
||||
"description": "SGST Amount"
|
||||
},
|
||||
"CesRt": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 999.999,
|
||||
"description": "Cess Rate"
|
||||
},
|
||||
"CesAmt": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 999999999999.99,
|
||||
"description": "Cess Amount (Advalorem)"
|
||||
},
|
||||
"CesNonAdvlAmt": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 999999999999.99,
|
||||
"description": "Cess Amount (Non-Advalorem)"
|
||||
},
|
||||
"StateCesRt": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 999.999,
|
||||
"description": "State CESS Rate"
|
||||
},
|
||||
"StateCesAmt": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 999999999999.99,
|
||||
"description": "State CESS Amount"
|
||||
},
|
||||
"StateCesNonAdvlAmt": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 999999999999.99,
|
||||
"description": "State CESS Amount (Non Advalorem)"
|
||||
},
|
||||
"OthChrg": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 999999999999.99,
|
||||
"description": "Other Charges"
|
||||
},
|
||||
"TotItemVal": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 999999999999.99,
|
||||
"description": "Total Item Value"
|
||||
},
|
||||
"OrdLineRef": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 50,
|
||||
"description": "Order line reference"
|
||||
},
|
||||
"OrgCntry": {
|
||||
"type": "string",
|
||||
"minLength": 2,
|
||||
"maxLength": 2,
|
||||
"description": "Origin Country"
|
||||
},
|
||||
"PrdSlNo": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 20,
|
||||
"description": "Serial number"
|
||||
},
|
||||
"BchDtls": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"Nm": {
|
||||
"type": "string",
|
||||
"minLength": 3,
|
||||
"maxLength": 20,
|
||||
"description": "Batch number"
|
||||
},
|
||||
"ExpDt": {
|
||||
"type": "string",
|
||||
"maxLength": 10,
|
||||
"minLength": 10,
|
||||
"pattern": "[0-3][0-9]/[0-1][0-9]/[2][0][1-2][0-9]",
|
||||
"description": "Batch Expiry Date"
|
||||
},
|
||||
"WrDt": {
|
||||
"type": "string",
|
||||
"maxLength": 10,
|
||||
"minLength": 10,
|
||||
"pattern": "[0-3][0-9]/[0-1][0-9]/[2][0][1-2][0-9]",
|
||||
"description": "Warranty Date"
|
||||
}
|
||||
},
|
||||
"required": ["Nm"]
|
||||
},
|
||||
"AttribDtls": {
|
||||
"type": "Array",
|
||||
"Attribute": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"Nm": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 100,
|
||||
"description": "Attribute name of the item"
|
||||
},
|
||||
"Val": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 100,
|
||||
"description": "Attribute value of the item"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"SlNo",
|
||||
"IsServc",
|
||||
"HsnCd",
|
||||
"UnitPrice",
|
||||
"TotAmt",
|
||||
"AssAmt",
|
||||
"GstRt",
|
||||
"TotItemVal"
|
||||
]
|
||||
},
|
||||
"ValDtls": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"AssVal": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 99999999999999.99,
|
||||
"description": "Total Assessable value of all items"
|
||||
},
|
||||
"CgstVal": {
|
||||
"type": "number",
|
||||
"maximum": 99999999999999.99,
|
||||
"minimum": 0,
|
||||
"description": "Total CGST value of all items"
|
||||
},
|
||||
"SgstVal": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 99999999999999.99,
|
||||
"description": "Total SGST value of all items"
|
||||
},
|
||||
"IgstVal": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 99999999999999.99,
|
||||
"description": "Total IGST value of all items"
|
||||
},
|
||||
"CesVal": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 99999999999999.99,
|
||||
"description": "Total CESS value of all items"
|
||||
},
|
||||
"StCesVal": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 99999999999999.99,
|
||||
"description": "Total State CESS value of all items"
|
||||
},
|
||||
"Discount": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 99999999999999.99,
|
||||
"description": "Invoice Discount"
|
||||
},
|
||||
"OthChrg": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 99999999999999.99,
|
||||
"description": "Other Charges"
|
||||
},
|
||||
"RndOffAmt": {
|
||||
"type": "number",
|
||||
"minimum": -99.99,
|
||||
"maximum": 99.99,
|
||||
"description": "Rounded off Amount"
|
||||
},
|
||||
"TotInvVal": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 99999999999999.99,
|
||||
"description": "Final Invoice Value "
|
||||
},
|
||||
"TotInvValFc": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 99999999999999.99,
|
||||
"description": "Final Invoice value in Foreign Currency"
|
||||
}
|
||||
},
|
||||
"required": ["AssVal", "TotInvVal"]
|
||||
},
|
||||
"PayDtls": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"Nm": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 100,
|
||||
"description": "Payee Name"
|
||||
},
|
||||
"AccDet": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 18,
|
||||
"description": "Bank Account Number of Payee"
|
||||
},
|
||||
"Mode": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 18,
|
||||
"description": "Mode of Payment"
|
||||
},
|
||||
"FinInsBr": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 11,
|
||||
"description": "Branch or IFSC code"
|
||||
},
|
||||
"PayTerm": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 100,
|
||||
"description": "Terms of Payment"
|
||||
},
|
||||
"PayInstr": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 100,
|
||||
"description": "Payment Instruction"
|
||||
},
|
||||
"CrTrn": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 100,
|
||||
"description": "Credit Transfer"
|
||||
},
|
||||
"DirDr": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 100,
|
||||
"description": "Direct Debit"
|
||||
},
|
||||
"CrDay": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 9999,
|
||||
"description": "Credit Days"
|
||||
},
|
||||
"PaidAmt": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 99999999999999.99,
|
||||
"description": "Advance Amount"
|
||||
},
|
||||
"PaymtDue": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 99999999999999.99,
|
||||
"description": "Outstanding Amount"
|
||||
}
|
||||
}
|
||||
},
|
||||
"RefDtls": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"InvRm": {
|
||||
"type": "string",
|
||||
"maxLength": 100,
|
||||
"minLength": 3,
|
||||
"pattern": "^[0-9A-Za-z/-]{3,100}$",
|
||||
"description": "Remarks/Note"
|
||||
},
|
||||
"DocPerdDtls": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"InvStDt": {
|
||||
"type": "string",
|
||||
"maxLength": 10,
|
||||
"minLength": 10,
|
||||
"pattern": "[0-3][0-9]/[0-1][0-9]/[2][0][1-2][0-9]",
|
||||
"description": "Invoice Period Start Date"
|
||||
},
|
||||
"InvEndDt": {
|
||||
"type": "string",
|
||||
"maxLength": 10,
|
||||
"minLength": 10,
|
||||
"pattern": "[0-3][0-9]/[0-1][0-9]/[2][0][1-2][0-9]",
|
||||
"description": "Invoice Period End Date"
|
||||
}
|
||||
},
|
||||
"required": ["InvStDt ", "InvEndDt "]
|
||||
},
|
||||
"PrecDocDtls": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"InvNo": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 16,
|
||||
"pattern": "^[1-9A-Z]{1}[0-9A-Z/-]{1,15}$",
|
||||
"description": "Reference of Original Invoice"
|
||||
},
|
||||
"InvDt": {
|
||||
"type": "string",
|
||||
"maxLength": 10,
|
||||
"minLength": 10,
|
||||
"pattern": "[0-3][0-9]/[0-1][0-9]/[2][0][1-2][0-9]",
|
||||
"description": "Date of Orginal Invoice"
|
||||
},
|
||||
"OthRefNo": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 20,
|
||||
"description": "Other Reference"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["InvNo", "InvDt"],
|
||||
"ContrDtls": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"RecAdvRefr": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 20,
|
||||
"pattern": "^([0-9A-Za-z/-]){1,20}$",
|
||||
"description": "Receipt Advice No."
|
||||
},
|
||||
"RecAdvDt": {
|
||||
"type": "string",
|
||||
"minLength": 10,
|
||||
"maxLength": 10,
|
||||
"pattern": "[0-3][0-9]/[0-1][0-9]/[2][0][1-2][0-9]",
|
||||
"description": "Date of receipt advice"
|
||||
},
|
||||
"TendRefr": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 20,
|
||||
"pattern": "^([0-9A-Za-z/-]){1,20}$",
|
||||
"description": "Lot/Batch Reference No."
|
||||
},
|
||||
"ContrRefr": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 20,
|
||||
"pattern": "^([0-9A-Za-z/-]){1,20}$",
|
||||
"description": "Contract Reference Number"
|
||||
},
|
||||
"ExtRefr": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 20,
|
||||
"pattern": "^([0-9A-Za-z/-]){1,20}$",
|
||||
"description": "Any other reference"
|
||||
},
|
||||
"ProjRefr": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 20,
|
||||
"pattern": "^([0-9A-Za-z/-]){1,20}$",
|
||||
"description": "Project Reference Number"
|
||||
},
|
||||
"PORefr": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 16,
|
||||
"pattern": "^([0-9A-Za-z/-]){1,16}$",
|
||||
"description": "PO Reference Number"
|
||||
},
|
||||
"PORefDt": {
|
||||
"type": "string",
|
||||
"minLength": 10,
|
||||
"maxLength": 10,
|
||||
"pattern": "[0-3][0-9]/[0-1][0-9]/[2][0][1-2][0-9]",
|
||||
"description": "PO Reference date"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"AddlDocDtls": {
|
||||
"type": "Array",
|
||||
"properties": {
|
||||
"Url": {
|
||||
"type": "string",
|
||||
"minLength": 3,
|
||||
"maxLength": 100,
|
||||
"description": "Supporting document URL"
|
||||
},
|
||||
"Docs": {
|
||||
"type": "string",
|
||||
"minLength": 3,
|
||||
"maxLength": 1000,
|
||||
"description": "Supporting document in Base64 Format"
|
||||
},
|
||||
"Info": {
|
||||
"type": "string",
|
||||
"minLength": 3,
|
||||
"maxLength": 1000,
|
||||
"description": "Any additional information"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"ExpDtls": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"ShipBNo": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 20,
|
||||
"description": "Shipping Bill No."
|
||||
},
|
||||
"ShipBDt": {
|
||||
"type": "string",
|
||||
"minLength": 10,
|
||||
"maxLength": 10,
|
||||
"pattern": "[0-3][0-9]/[0-1][0-9]/[2][0][1-2][0-9]",
|
||||
"description": "Shipping Bill Date"
|
||||
},
|
||||
"Port": {
|
||||
"type": "string",
|
||||
"minLength": 2,
|
||||
"maxLength": 10,
|
||||
"pattern": "^[0-9A-Za-z]{2,10}$",
|
||||
"description": "Port Code. Refer the master"
|
||||
},
|
||||
"RefClm": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 1,
|
||||
"description": "Claiming Refund. Y/N"
|
||||
},
|
||||
"ForCur": {
|
||||
"type": "string",
|
||||
"minLength": 3,
|
||||
"maxLength": 16,
|
||||
"description": "Additional Currency Code. Refer the master"
|
||||
},
|
||||
"CntCode": {
|
||||
"type": "string",
|
||||
"minLength": 2,
|
||||
"maxLength": 2,
|
||||
"description": "Country Code. Refer the master"
|
||||
},
|
||||
"ExpDuty": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 999999999999.99,
|
||||
"description": "Export Duty"
|
||||
}
|
||||
}
|
||||
},
|
||||
"EwbDtls": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"TransId": {
|
||||
"type": "string",
|
||||
"minLength": 15,
|
||||
"maxLength": 15,
|
||||
"description": "Transporter GSTIN"
|
||||
},
|
||||
"TransName": {
|
||||
"type": "string",
|
||||
"minLength": 3,
|
||||
"maxLength": 100,
|
||||
"description": "Transporter Name"
|
||||
},
|
||||
"TransMode": {
|
||||
"type": "string",
|
||||
"maxLength": 1,
|
||||
"minLength": 1,
|
||||
"enum": ["1", "2", "3", "4"],
|
||||
"description": "Mode of Transport"
|
||||
},
|
||||
"Distance": {
|
||||
"type": "number",
|
||||
"minimum": 1,
|
||||
"maximum": 9999,
|
||||
"description": "Distance"
|
||||
},
|
||||
"TransDocNo": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 15,
|
||||
"pattern": "^([0-9A-Z/-]){1,15}$",
|
||||
"description": "Tranport Document Number",
|
||||
"validationMsg": "Transport Receipt No is invalid"
|
||||
},
|
||||
"TransDocDt": {
|
||||
"type": "string",
|
||||
"minLength": 10,
|
||||
"maxLength": 10,
|
||||
"pattern": "[0-3][0-9]/[0-1][0-9]/[2][0][1-2][0-9]",
|
||||
"description": "Transport Document Date"
|
||||
},
|
||||
"VehNo": {
|
||||
"type": "string",
|
||||
"minLength": 4,
|
||||
"maxLength": 20,
|
||||
"description": "Vehicle Number"
|
||||
},
|
||||
"VehType": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 1,
|
||||
"enum": ["O", "R"],
|
||||
"description": "Vehicle Type"
|
||||
}
|
||||
},
|
||||
"required": ["Distance"]
|
||||
},
|
||||
"required": [
|
||||
"Version",
|
||||
"TranDtls",
|
||||
"DocDtls",
|
||||
"SellerDtls",
|
||||
"BuyerDtls",
|
||||
"ItemList",
|
||||
"ValDtls"
|
||||
]
|
||||
}
|
||||
@@ -1,387 +0,0 @@
|
||||
erpnext.setup_einvoice_actions = (doctype) => {
|
||||
frappe.ui.form.on(doctype, {
|
||||
async refresh(frm) {
|
||||
if (frm.doc.docstatus == 2) return;
|
||||
|
||||
const res = await frappe.call({
|
||||
method: 'erpnext.regional.india.e_invoice.utils.validate_eligibility',
|
||||
args: { doc: frm.doc }
|
||||
});
|
||||
const invoice_eligible = res.message;
|
||||
|
||||
if (!invoice_eligible) return;
|
||||
|
||||
const { doctype, irn, irn_cancelled, ewaybill, eway_bill_cancelled, name, qrcode_image, __unsaved } = frm.doc;
|
||||
|
||||
const add_custom_button = (label, action) => {
|
||||
if (!frm.custom_buttons[label]) {
|
||||
frm.add_custom_button(label, action, __('E Invoicing'));
|
||||
}
|
||||
};
|
||||
|
||||
if (!irn && !__unsaved) {
|
||||
const action = () => {
|
||||
if (frm.doc.__unsaved) {
|
||||
frappe.throw(__('Please save the document to generate IRN.'));
|
||||
}
|
||||
frappe.call({
|
||||
method: 'erpnext.regional.india.e_invoice.utils.get_einvoice',
|
||||
args: { doctype, docname: name },
|
||||
freeze: true,
|
||||
callback: (res) => {
|
||||
const einvoice = res.message;
|
||||
show_einvoice_preview(frm, einvoice);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
add_custom_button(__("Generate IRN"), action);
|
||||
}
|
||||
|
||||
if (irn && !irn_cancelled && !ewaybill) {
|
||||
const fields = [
|
||||
{
|
||||
"label": "Reason",
|
||||
"fieldname": "reason",
|
||||
"fieldtype": "Select",
|
||||
"reqd": 1,
|
||||
"default": "1-Duplicate",
|
||||
"options": ["1-Duplicate", "2-Data Entry Error", "3-Order Cancelled", "4-Other"]
|
||||
},
|
||||
{
|
||||
"label": "Remark",
|
||||
"fieldname": "remark",
|
||||
"fieldtype": "Data",
|
||||
"reqd": 1
|
||||
}
|
||||
];
|
||||
const action = () => {
|
||||
const d = new frappe.ui.Dialog({
|
||||
title: __("Cancel IRN"),
|
||||
fields: fields,
|
||||
primary_action: function() {
|
||||
const data = d.get_values();
|
||||
frappe.call({
|
||||
method: 'erpnext.regional.india.e_invoice.utils.cancel_irn',
|
||||
args: {
|
||||
doctype,
|
||||
docname: name,
|
||||
irn: irn,
|
||||
reason: data.reason.split('-')[0],
|
||||
remark: data.remark
|
||||
},
|
||||
freeze: true,
|
||||
callback: () => frm.reload_doc() || d.hide(),
|
||||
error: () => d.hide()
|
||||
});
|
||||
},
|
||||
primary_action_label: __('Submit')
|
||||
});
|
||||
d.show();
|
||||
};
|
||||
add_custom_button(__("Cancel IRN"), action);
|
||||
}
|
||||
|
||||
if (irn && !irn_cancelled && !ewaybill) {
|
||||
const action = () => {
|
||||
const d = new frappe.ui.Dialog({
|
||||
title: __('Generate E-Way Bill'),
|
||||
size: "large",
|
||||
fields: get_ewaybill_fields(frm),
|
||||
primary_action: function() {
|
||||
const data = d.get_values();
|
||||
frappe.call({
|
||||
method: 'erpnext.regional.india.e_invoice.utils.generate_eway_bill',
|
||||
args: {
|
||||
doctype,
|
||||
docname: name,
|
||||
irn,
|
||||
...data
|
||||
},
|
||||
freeze: true,
|
||||
callback: () => {
|
||||
frappe.show_alert({
|
||||
message: __('E-Way Bill Generated successfully'),
|
||||
indicator: 'green'
|
||||
}, 7);
|
||||
frm.reload_doc();
|
||||
d.hide();
|
||||
},
|
||||
error: () => {
|
||||
frappe.show_alert({
|
||||
message: __('E-Way Bill was not Generated'),
|
||||
indicator: 'red'
|
||||
}, 7);
|
||||
d.hide();
|
||||
}
|
||||
});
|
||||
},
|
||||
primary_action_label: __('Submit')
|
||||
});
|
||||
d.fields_dict.transporter.df.onchange = function () {
|
||||
const transporter = d.fields_dict.transporter.value;
|
||||
if (transporter) {
|
||||
frappe.db.get_value('Supplier', transporter, ['gst_transporter_id', 'supplier_name'])
|
||||
.then(({ message }) => {
|
||||
d.set_value('gst_transporter_id', message.gst_transporter_id);
|
||||
d.set_value('transporter_name', message.supplier_name);
|
||||
});
|
||||
} else {
|
||||
d.set_value('gst_transporter_id', '');
|
||||
d.set_value('transporter_name', '');
|
||||
}
|
||||
};
|
||||
d.fields_dict.driver.df.onchange = function () {
|
||||
const driver = d.fields_dict.driver.value;
|
||||
if (driver) {
|
||||
frappe.db.get_value('Driver', driver, ['full_name'])
|
||||
.then(({ message }) => {
|
||||
d.set_value('driver_name', message.full_name);
|
||||
});
|
||||
} else {
|
||||
d.set_value('driver_name', '');
|
||||
}
|
||||
};
|
||||
d.show();
|
||||
};
|
||||
|
||||
add_custom_button(__("Generate E-Way Bill"), action);
|
||||
}
|
||||
|
||||
if (irn && ewaybill && !irn_cancelled && !eway_bill_cancelled) {
|
||||
const action = () => {
|
||||
// This confirm is added to just reduce unnecesory API calls. All required logic is implemented on server side.
|
||||
frappe.confirm(
|
||||
__("Have you cancelled e-way bill on the portal?"),
|
||||
() => {
|
||||
frappe.call({
|
||||
method: "erpnext.regional.india.e_invoice.utils.cancel_eway_bill",
|
||||
args: { doctype, docname: name },
|
||||
freeze: true,
|
||||
callback: () => frm.reload_doc(),
|
||||
});
|
||||
},
|
||||
() => {
|
||||
frappe.show_alert(
|
||||
{
|
||||
message: __(
|
||||
"Please cancel e-way bill on the portal first."
|
||||
),
|
||||
indicator: "orange",
|
||||
},
|
||||
5
|
||||
);
|
||||
}
|
||||
);
|
||||
};
|
||||
add_custom_button(__("Cancel E-Way Bill"), action);
|
||||
}
|
||||
|
||||
if (irn && !irn_cancelled) {
|
||||
let is_qrcode_attached = false;
|
||||
if (qrcode_image && frm.attachments) {
|
||||
let attachments = frm.attachments.get_attachments();
|
||||
if (attachments.length != 0) {
|
||||
for (let i = 0; i < attachments.length; i++) {
|
||||
if (attachments[i].file_url == qrcode_image) {
|
||||
is_qrcode_attached = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!is_qrcode_attached) {
|
||||
const action = () => {
|
||||
if (frm.doc.__unsaved) {
|
||||
frappe.throw(__('Please save the document to generate QRCode.'));
|
||||
}
|
||||
const dialog = frappe.msgprint({
|
||||
title: __("Generate QRCode"),
|
||||
message: __("Generate and attach QR Code using IRN?"),
|
||||
primary_action: {
|
||||
action: function() {
|
||||
frappe.call({
|
||||
method: 'erpnext.regional.india.e_invoice.utils.generate_qrcode',
|
||||
args: { doctype, docname: name },
|
||||
freeze: true,
|
||||
callback: () => frm.reload_doc() || dialog.hide(),
|
||||
error: () => dialog.hide()
|
||||
});
|
||||
}
|
||||
},
|
||||
primary_action_label: __('Yes')
|
||||
});
|
||||
dialog.show();
|
||||
};
|
||||
add_custom_button(__("Generate QRCode"), action);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const get_ewaybill_fields = (frm) => {
|
||||
return [
|
||||
{
|
||||
fieldname: "eway_part_a_section_break",
|
||||
fieldtype: "Section Break",
|
||||
label: "Part A",
|
||||
},
|
||||
{
|
||||
fieldname: "transporter",
|
||||
label: "Transporter",
|
||||
fieldtype: "Link",
|
||||
options: "Supplier",
|
||||
default: frm.doc.transporter,
|
||||
},
|
||||
{
|
||||
fieldname: "transporter_name",
|
||||
label: "Transporter Name",
|
||||
fieldtype: "Data",
|
||||
read_only: 1,
|
||||
default: frm.doc.transporter_name,
|
||||
depends_on: "transporter",
|
||||
},
|
||||
{
|
||||
fieldname: "part_a_column_break",
|
||||
fieldtype: "Column Break",
|
||||
},
|
||||
{
|
||||
fieldname: "gst_transporter_id",
|
||||
label: "GST Transporter ID",
|
||||
fieldtype: "Data",
|
||||
default: frm.doc.gst_transporter_id,
|
||||
},
|
||||
{
|
||||
fieldname: "distance",
|
||||
label: "Distance (in km)",
|
||||
fieldtype: "Float",
|
||||
default: frm.doc.distance,
|
||||
description: 'Set as zero to auto calculate distance using pin codes',
|
||||
},
|
||||
{
|
||||
fieldname: "eway_part_b_section_break",
|
||||
fieldtype: "Section Break",
|
||||
label: "Part B",
|
||||
},
|
||||
{
|
||||
fieldname: "mode_of_transport",
|
||||
label: "Mode of Transport",
|
||||
fieldtype: "Select",
|
||||
options: `\nRoad\nAir\nRail\nShip`,
|
||||
default: frm.doc.mode_of_transport,
|
||||
},
|
||||
{
|
||||
fieldname: "gst_vehicle_type",
|
||||
label: "GST Vehicle Type",
|
||||
fieldtype: "Select",
|
||||
options: `Regular\nOver Dimensional Cargo (ODC)`,
|
||||
depends_on: 'eval:(doc.mode_of_transport === "Road")',
|
||||
default: frm.doc.gst_vehicle_type,
|
||||
},
|
||||
{
|
||||
fieldname: "vehicle_no",
|
||||
label: "Vehicle No",
|
||||
fieldtype: "Data",
|
||||
default: frm.doc.vehicle_no,
|
||||
},
|
||||
{
|
||||
fieldname: "part_b_column_break",
|
||||
fieldtype: "Column Break",
|
||||
},
|
||||
{
|
||||
fieldname: "lr_date",
|
||||
label: "Transport Receipt Date",
|
||||
fieldtype: "Date",
|
||||
default: frm.doc.lr_date,
|
||||
},
|
||||
{
|
||||
fieldname: "lr_no",
|
||||
label: "Transport Receipt No",
|
||||
fieldtype: "Data",
|
||||
default: frm.doc.lr_no,
|
||||
},
|
||||
{
|
||||
fieldname: "driver",
|
||||
label: "Driver",
|
||||
fieldtype: "Link",
|
||||
options: "Driver",
|
||||
default: frm.doc.driver,
|
||||
},
|
||||
{
|
||||
fieldname: "driver_name",
|
||||
label: "Driver Name",
|
||||
fieldtype: "Data",
|
||||
fetch_from: "driver.full_name",
|
||||
read_only: 1,
|
||||
default: frm.doc.driver_name,
|
||||
depends_on: "driver",
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
const request_irn_generation = (frm) => {
|
||||
frappe.call({
|
||||
method: 'erpnext.regional.india.e_invoice.utils.generate_irn',
|
||||
args: { doctype: frm.doc.doctype, docname: frm.doc.name },
|
||||
freeze: true,
|
||||
callback: () => frm.reload_doc()
|
||||
});
|
||||
};
|
||||
|
||||
const get_preview_dialog = (frm, action) => {
|
||||
const dialog = new frappe.ui.Dialog({
|
||||
title: __("Preview"),
|
||||
size: "large",
|
||||
fields: [
|
||||
{
|
||||
"label": "Preview",
|
||||
"fieldname": "preview_html",
|
||||
"fieldtype": "HTML"
|
||||
}
|
||||
],
|
||||
primary_action: () => action(frm) || dialog.hide(),
|
||||
primary_action_label: __('Generate IRN')
|
||||
});
|
||||
return dialog;
|
||||
};
|
||||
|
||||
const show_einvoice_preview = (frm, einvoice) => {
|
||||
const preview_dialog = get_preview_dialog(frm, request_irn_generation);
|
||||
|
||||
// initialize e-invoice fields
|
||||
einvoice["Irn"] = einvoice["AckNo"] = ''; einvoice["AckDt"] = frappe.datetime.nowdate();
|
||||
frm.doc.signed_einvoice = JSON.stringify(einvoice);
|
||||
|
||||
// initialize preview wrapper
|
||||
const $preview_wrapper = preview_dialog.get_field("preview_html").$wrapper;
|
||||
$preview_wrapper.html(
|
||||
`<div>
|
||||
<div class="print-preview">
|
||||
<div class="print-format"></div>
|
||||
</div>
|
||||
<div class="page-break-message text-muted text-center text-medium margin-top"></div>
|
||||
</div>`
|
||||
);
|
||||
|
||||
frappe.call({
|
||||
method: "frappe.www.printview.get_html_and_style",
|
||||
args: {
|
||||
doc: frm.doc,
|
||||
print_format: "GST E-Invoice",
|
||||
no_letterhead: 1
|
||||
},
|
||||
callback: function (r) {
|
||||
if (!r.exc) {
|
||||
$preview_wrapper.find(".print-format").html(r.message.html);
|
||||
const style = `
|
||||
.print-format { box-shadow: 0px 0px 5px rgba(0,0,0,0.2); padding: 0.30in; min-height: 80vh; }
|
||||
.print-preview { min-height: 0px; }
|
||||
.modal-dialog { width: 720px; }`;
|
||||
|
||||
frappe.dom.set_style(style, "custom-print-style");
|
||||
preview_dialog.show();
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,177 +0,0 @@
|
||||
[
|
||||
{
|
||||
"state_number": "33",
|
||||
"state_code": "TN",
|
||||
"state_name": "Tamil Nadu"
|
||||
},
|
||||
{
|
||||
"state_number": "35",
|
||||
"state_code": "UP",
|
||||
"state_name": "Uttar Pradesh"
|
||||
},
|
||||
{
|
||||
"state_number": "05",
|
||||
"state_code": "UT",
|
||||
"state_name": "Uttarakhand"
|
||||
},
|
||||
{
|
||||
"state_number": "19",
|
||||
"state_code": "WB",
|
||||
"state_name": "West Bengal"
|
||||
},
|
||||
{
|
||||
"state_number": "16",
|
||||
"state_code": "TR",
|
||||
"state_name": "Tripura"
|
||||
},
|
||||
{
|
||||
"state_number": "36",
|
||||
"state_code": "TS",
|
||||
"state_name": "Telangana"
|
||||
},
|
||||
{
|
||||
"state_number": "11",
|
||||
"state_code": "SK",
|
||||
"state_name": "Sikkim"
|
||||
},
|
||||
{
|
||||
"state_number": "08",
|
||||
"state_code": "RJ",
|
||||
"state_name": "Rajasthan"
|
||||
},
|
||||
{
|
||||
"state_number": "03",
|
||||
"state_code": "PB",
|
||||
"state_name": "Punjab"
|
||||
},
|
||||
{
|
||||
"state_number": "34",
|
||||
"state_code": "PY",
|
||||
"state_name": "Pondicherry"
|
||||
},
|
||||
{
|
||||
"state_number": "21",
|
||||
"state_code": "OR",
|
||||
"state_name": "Odisha"
|
||||
},
|
||||
{
|
||||
"state_number": "13",
|
||||
"state_code": "NL",
|
||||
"state_name": "Nagaland"
|
||||
},
|
||||
{
|
||||
"state_number": "15",
|
||||
"state_code": "MI",
|
||||
"state_name": "Mizoram"
|
||||
},
|
||||
{
|
||||
"state_number": "17",
|
||||
"state_code": "ME",
|
||||
"state_name": "Meghalaya"
|
||||
},
|
||||
{
|
||||
"state_number": "14",
|
||||
"state_code": "MN",
|
||||
"state_name": "Manipur"
|
||||
},
|
||||
{
|
||||
"state_number": "27",
|
||||
"state_code": "MH",
|
||||
"state_name": "Maharashtra"
|
||||
},
|
||||
{
|
||||
"state_number": "23",
|
||||
"state_code": "MP",
|
||||
"state_name": "Madhya Pradesh"
|
||||
},
|
||||
{
|
||||
"state_number": "31",
|
||||
"state_code": "LD",
|
||||
"state_name": "Lakshadweep Islands"
|
||||
},
|
||||
{
|
||||
"state_number": "32",
|
||||
"state_code": "KL",
|
||||
"state_name": "Kerala"
|
||||
},
|
||||
{
|
||||
"state_number": "29",
|
||||
"state_code": "KA",
|
||||
"state_name": "Karnataka"
|
||||
},
|
||||
{
|
||||
"state_number": "20",
|
||||
"state_code": "JH",
|
||||
"state_name": "Jharkhand"
|
||||
},
|
||||
{
|
||||
"state_number": "01",
|
||||
"state_code": "JK",
|
||||
"state_name": "Jammu and Kashmir"
|
||||
},
|
||||
{
|
||||
"state_number": "02",
|
||||
"state_code": "HP",
|
||||
"state_name": "Himachal Pradesh"
|
||||
},
|
||||
{
|
||||
"state_number": "06",
|
||||
"state_code": "HR",
|
||||
"state_name": "Haryana"
|
||||
},
|
||||
{
|
||||
"state_number": "24",
|
||||
"state_code": "GJ",
|
||||
"state_name": "Gujarat"
|
||||
},
|
||||
{
|
||||
"state_number": "30",
|
||||
"state_code": "GA",
|
||||
"state_name": "Goa"
|
||||
},
|
||||
{
|
||||
"state_number": "07",
|
||||
"state_code": "DL",
|
||||
"state_name": "Delhi"
|
||||
},
|
||||
{
|
||||
"state_number": "26",
|
||||
"state_code": "DN",
|
||||
"state_name": "Dadra and Nagar Haveli and Daman and Diu"
|
||||
},
|
||||
{
|
||||
"state_number": "22",
|
||||
"state_code": "CT",
|
||||
"state_name": "Chhattisgarh"
|
||||
},
|
||||
{
|
||||
"state_number": "04",
|
||||
"state_code": "CH",
|
||||
"state_name": "Chandigarh"
|
||||
},
|
||||
{
|
||||
"state_number": "10",
|
||||
"state_code": "BH",
|
||||
"state_name": "Bihar"
|
||||
},
|
||||
{
|
||||
"state_number": "18",
|
||||
"state_code": "AS",
|
||||
"state_name": "Assam"
|
||||
},
|
||||
{
|
||||
"state_number": "12",
|
||||
"state_code": "AR",
|
||||
"state_name": "Arunachal Pradesh"
|
||||
},
|
||||
{
|
||||
"state_number": "37",
|
||||
"state_code": "AD",
|
||||
"state_name": "Andhra Pradesh (New)"
|
||||
},
|
||||
{
|
||||
"state_number": "38",
|
||||
"state_code": "LA",
|
||||
"state_name": "Ladakh"
|
||||
}
|
||||
]
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,25 +0,0 @@
|
||||
erpnext.setup_gst_reminder_button = (doctype) => {
|
||||
frappe.ui.form.on(doctype, {
|
||||
refresh: (frm) => {
|
||||
if(!frm.is_new()) {
|
||||
var missing = false;
|
||||
frm.doc.__onload.addr_list && frm.doc.__onload.addr_list.forEach((d) => {
|
||||
if(!d.gstin) missing = true;
|
||||
});
|
||||
if (!missing) return;
|
||||
|
||||
frm.add_custom_button('Send GST Update Reminder', () => {
|
||||
return new Promise((resolve) => {
|
||||
return frappe.call({
|
||||
method: 'erpnext.regional.doctype.gst_settings.gst_settings.send_gstin_reminder',
|
||||
args: {
|
||||
party_type: frm.doc.doctype,
|
||||
party: frm.doc.name,
|
||||
}
|
||||
}).always(() => { resolve(); });
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,52 +0,0 @@
|
||||
erpnext.setup_auto_gst_taxation = (doctype) => {
|
||||
frappe.ui.form.on(doctype, {
|
||||
company_address: function(frm) {
|
||||
frm.trigger('get_tax_template');
|
||||
},
|
||||
shipping_address: function(frm) {
|
||||
frm.trigger('get_tax_template');
|
||||
},
|
||||
supplier_address: function(frm) {
|
||||
frm.trigger('get_tax_template');
|
||||
},
|
||||
tax_category: function(frm) {
|
||||
frm.trigger('get_tax_template');
|
||||
},
|
||||
customer_address: function(frm) {
|
||||
frm.trigger('get_tax_template');
|
||||
},
|
||||
get_tax_template: function(frm) {
|
||||
if (!frm.doc.company) return;
|
||||
|
||||
let party_details = {
|
||||
'shipping_address': frm.doc.shipping_address || '',
|
||||
'shipping_address_name': frm.doc.shipping_address_name || '',
|
||||
'customer_address': frm.doc.customer_address || '',
|
||||
'company_address': frm.doc.company_address,
|
||||
'supplier_address': frm.doc.supplier_address,
|
||||
'customer': frm.doc.customer,
|
||||
'supplier': frm.doc.supplier,
|
||||
'supplier_gstin': frm.doc.supplier_gstin,
|
||||
'company_gstin': frm.doc.company_gstin,
|
||||
'tax_category': frm.doc.tax_category
|
||||
};
|
||||
|
||||
frappe.call({
|
||||
method: 'erpnext.regional.india.utils.get_regional_address_details',
|
||||
args: {
|
||||
party_details: JSON.stringify(party_details),
|
||||
doctype: frm.doc.doctype,
|
||||
company: frm.doc.company
|
||||
},
|
||||
debounce: 2000,
|
||||
callback: function(r) {
|
||||
if(r.message) {
|
||||
frm.set_value('taxes_and_charges', r.message.taxes_and_charges);
|
||||
frm.set_value('taxes', r.message.taxes);
|
||||
frm.set_value('place_of_supply', r.message.place_of_supply);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
|
||||
import frappe
|
||||
|
||||
from erpnext.regional.india.utils import validate_document_name
|
||||
|
||||
|
||||
class TestIndiaUtils(unittest.TestCase):
|
||||
@patch("frappe.get_cached_value")
|
||||
def test_validate_document_name(self, mock_get_cached):
|
||||
mock_get_cached.return_value = "India" # mock country
|
||||
posting_date = "2021-05-01"
|
||||
|
||||
invalid_names = ["SI$1231", "012345678901234567", "SI 2020 05", "SI.2020.0001", "PI2021 - 001"]
|
||||
for name in invalid_names:
|
||||
doc = frappe._dict(name=name, posting_date=posting_date)
|
||||
self.assertRaises(frappe.ValidationError, validate_document_name, doc)
|
||||
|
||||
valid_names = ["012345678901236", "SI/2020/0001", "SI/2020-0001", "2020-PI-0001", "PI2020-0001"]
|
||||
for name in valid_names:
|
||||
doc = frappe._dict(name=name, posting_date=posting_date)
|
||||
try:
|
||||
validate_document_name(doc)
|
||||
except frappe.ValidationError:
|
||||
self.fail("Valid name {} throwing error".format(name))
|
||||
|
||||
@patch("frappe.get_cached_value")
|
||||
def test_validate_document_name_not_india(self, mock_get_cached):
|
||||
mock_get_cached.return_value = "Not India"
|
||||
doc = frappe._dict(name="SI$123", posting_date="2021-05-01")
|
||||
|
||||
try:
|
||||
validate_document_name(doc)
|
||||
except frappe.ValidationError:
|
||||
self.fail("Regional validation related to India are being applied to other countries")
|
||||
@@ -1,937 +0,0 @@
|
||||
import json
|
||||
import re
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.model.utils import get_fetch_values
|
||||
from frappe.utils import cint, cstr, date_diff, flt, getdate
|
||||
|
||||
from erpnext.controllers.accounts_controller import get_taxes_and_charges
|
||||
from erpnext.controllers.taxes_and_totals import get_itemised_tax, get_itemised_taxable_amount
|
||||
from erpnext.regional.india import number_state_mapping, state_numbers, states
|
||||
|
||||
GST_INVOICE_NUMBER_FORMAT = re.compile(r"^[a-zA-Z0-9\-/]+$") # alphanumeric and - /
|
||||
GSTIN_FORMAT = re.compile(
|
||||
"^[0-9]{2}[A-Z]{4}[0-9A-Z]{1}[0-9]{4}[A-Z]{1}[1-9A-Z]{1}[1-9A-Z]{1}[0-9A-Z]{1}$"
|
||||
)
|
||||
GSTIN_UIN_FORMAT = re.compile("^[0-9]{4}[A-Z]{3}[0-9]{5}[0-9A-Z]{3}")
|
||||
PAN_NUMBER_FORMAT = re.compile("[A-Z]{5}[0-9]{4}[A-Z]{1}")
|
||||
|
||||
|
||||
def validate_gstin_for_india(doc, method):
|
||||
if hasattr(doc, "gst_state"):
|
||||
set_gst_state_and_state_number(doc)
|
||||
|
||||
if not hasattr(doc, "gstin") or not doc.gstin:
|
||||
return
|
||||
|
||||
gst_category = []
|
||||
|
||||
if hasattr(doc, "gst_category"):
|
||||
if len(doc.links):
|
||||
link_doctype = doc.links[0].get("link_doctype")
|
||||
link_name = doc.links[0].get("link_name")
|
||||
|
||||
if link_doctype in ["Customer", "Supplier"]:
|
||||
gst_category = frappe.db.get_value(link_doctype, {"name": link_name}, ["gst_category"])
|
||||
|
||||
doc.gstin = doc.gstin.upper().strip()
|
||||
if not doc.gstin or doc.gstin == "NA":
|
||||
return
|
||||
|
||||
if len(doc.gstin) != 15:
|
||||
frappe.throw(_("A GSTIN must have 15 characters."), title=_("Invalid GSTIN"))
|
||||
|
||||
if gst_category and gst_category == "UIN Holders":
|
||||
if not GSTIN_UIN_FORMAT.match(doc.gstin):
|
||||
frappe.throw(
|
||||
_(
|
||||
"The input you've entered doesn't match the GSTIN format for UIN Holders or Non-Resident OIDAR Service Providers"
|
||||
),
|
||||
title=_("Invalid GSTIN"),
|
||||
)
|
||||
else:
|
||||
if not GSTIN_FORMAT.match(doc.gstin):
|
||||
frappe.throw(
|
||||
_("The input you've entered doesn't match the format of GSTIN."), title=_("Invalid GSTIN")
|
||||
)
|
||||
|
||||
validate_gstin_check_digit(doc.gstin)
|
||||
|
||||
if not doc.gst_state:
|
||||
frappe.throw(_("Please enter GST state"), title=_("Invalid State"))
|
||||
|
||||
if doc.gst_state_number != doc.gstin[:2]:
|
||||
frappe.throw(
|
||||
_("First 2 digits of GSTIN should match with State number {0}.").format(doc.gst_state_number),
|
||||
title=_("Invalid GSTIN"),
|
||||
)
|
||||
|
||||
|
||||
def validate_pan_for_india(doc, method):
|
||||
if doc.get("country") != "India" or not doc.get("pan"):
|
||||
return
|
||||
|
||||
if not PAN_NUMBER_FORMAT.match(doc.pan):
|
||||
frappe.throw(_("Invalid PAN No. The input you've entered doesn't match the format of PAN."))
|
||||
|
||||
|
||||
def validate_tax_category(doc, method):
|
||||
if doc.get("gst_state") and frappe.db.get_value(
|
||||
"Tax Category",
|
||||
{
|
||||
"gst_state": doc.gst_state,
|
||||
"is_inter_state": doc.is_inter_state,
|
||||
"is_reverse_charge": doc.is_reverse_charge,
|
||||
},
|
||||
):
|
||||
if doc.is_inter_state:
|
||||
frappe.throw(
|
||||
_("Inter State tax category for GST State {0} already exists").format(doc.gst_state)
|
||||
)
|
||||
else:
|
||||
frappe.throw(
|
||||
_("Intra State tax category for GST State {0} already exists").format(doc.gst_state)
|
||||
)
|
||||
|
||||
|
||||
def update_gst_category(doc, method):
|
||||
for link in doc.links:
|
||||
if link.link_doctype in ["Customer", "Supplier"]:
|
||||
meta = frappe.get_meta(link.link_doctype)
|
||||
if doc.get("gstin") and meta.has_field("gst_category"):
|
||||
frappe.db.set_value(
|
||||
link.link_doctype,
|
||||
{"name": link.link_name, "gst_category": "Unregistered"},
|
||||
"gst_category",
|
||||
"Registered Regular",
|
||||
)
|
||||
|
||||
|
||||
def set_gst_state_and_state_number(doc):
|
||||
if not doc.gst_state and doc.state:
|
||||
state = doc.state.lower()
|
||||
states_lowercase = {s.lower(): s for s in states}
|
||||
if state in states_lowercase:
|
||||
doc.gst_state = states_lowercase[state]
|
||||
else:
|
||||
return
|
||||
doc.gst_state_number = state_numbers.get(doc.gst_state)
|
||||
|
||||
|
||||
def validate_gstin_check_digit(gstin, label="GSTIN"):
|
||||
"""Function to validate the check digit of the GSTIN."""
|
||||
factor = 1
|
||||
total = 0
|
||||
code_point_chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
mod = len(code_point_chars)
|
||||
input_chars = gstin[:-1]
|
||||
for char in input_chars:
|
||||
digit = factor * code_point_chars.find(char)
|
||||
digit = (digit // mod) + (digit % mod)
|
||||
total += digit
|
||||
factor = 2 if factor == 1 else 1
|
||||
if gstin[-1] != code_point_chars[((mod - (total % mod)) % mod)]:
|
||||
frappe.throw(
|
||||
_(
|
||||
"""Invalid {0}! The check digit validation has failed. Please ensure you've typed the {0} correctly."""
|
||||
).format(label)
|
||||
)
|
||||
|
||||
|
||||
def get_itemised_tax_breakup_header(item_doctype, tax_accounts):
|
||||
hsn_wise_in_gst_settings = frappe.db.get_single_value("GST Settings", "hsn_wise_tax_breakup")
|
||||
if frappe.get_meta(item_doctype).has_field("gst_hsn_code") and hsn_wise_in_gst_settings:
|
||||
return [_("HSN/SAC"), _("Taxable Amount")] + tax_accounts
|
||||
else:
|
||||
return [_("Item"), _("Taxable Amount")] + tax_accounts
|
||||
|
||||
|
||||
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
|
||||
|
||||
hsn_wise_in_gst_settings = frappe.db.get_single_value("GST Settings", "hsn_wise_tax_breakup")
|
||||
|
||||
tax_breakup_hsn_wise = hsn_wise or hsn_wise_in_gst_settings
|
||||
if tax_breakup_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():
|
||||
item_or_hsn = item if not tax_breakup_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[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:
|
||||
item_or_hsn = item if not tax_breakup_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
|
||||
|
||||
|
||||
def set_place_of_supply(doc, method=None):
|
||||
doc.place_of_supply = get_place_of_supply(doc, doc.doctype)
|
||||
|
||||
|
||||
def validate_document_name(doc, method=None):
|
||||
"""Validate GST invoice number requirements."""
|
||||
|
||||
country = frappe.get_cached_value("Company", doc.company, "country")
|
||||
|
||||
# Date was chosen as start of next FY to avoid irritating current users.
|
||||
if country != "India" or getdate(doc.posting_date) < getdate("2021-04-01"):
|
||||
return
|
||||
|
||||
if len(doc.name) > 16:
|
||||
frappe.throw(
|
||||
_(
|
||||
"Maximum length of document number should be 16 characters as per GST rules. Please change the naming series."
|
||||
)
|
||||
)
|
||||
|
||||
if not GST_INVOICE_NUMBER_FORMAT.match(doc.name):
|
||||
frappe.throw(
|
||||
_(
|
||||
"Document name should only contain alphanumeric values, dash(-) and slash(/) characters as per GST rules. Please change the naming series."
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
# don't remove this function it is used in tests
|
||||
def test_method():
|
||||
"""test function"""
|
||||
return "overridden"
|
||||
|
||||
|
||||
def get_place_of_supply(party_details, doctype):
|
||||
if not frappe.get_meta("Address").has_field("gst_state"):
|
||||
return
|
||||
|
||||
if doctype in ("Sales Invoice", "Delivery Note", "Sales Order", "Quotation"):
|
||||
address_name = party_details.customer_address or party_details.shipping_address_name
|
||||
elif doctype in ("Purchase Invoice", "Purchase Order", "Purchase Receipt"):
|
||||
address_name = party_details.shipping_address or party_details.supplier_address
|
||||
|
||||
if address_name:
|
||||
address = frappe.db.get_value(
|
||||
"Address", address_name, ["gst_state", "gst_state_number", "gstin"], as_dict=1
|
||||
)
|
||||
if address and address.gst_state and address.gst_state_number:
|
||||
party_details.gstin = address.gstin
|
||||
return cstr(address.gst_state_number) + "-" + cstr(address.gst_state)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_regional_address_details(party_details, doctype, company):
|
||||
if isinstance(party_details, str):
|
||||
party_details = json.loads(party_details)
|
||||
party_details = frappe._dict(party_details)
|
||||
|
||||
update_party_details(party_details, doctype)
|
||||
|
||||
party_details.place_of_supply = get_place_of_supply(party_details, doctype)
|
||||
|
||||
if is_internal_transfer(party_details, doctype):
|
||||
party_details.taxes_and_charges = ""
|
||||
party_details.taxes = []
|
||||
return party_details
|
||||
|
||||
if doctype in ("Sales Invoice", "Delivery Note", "Sales Order", "Quotation"):
|
||||
master_doctype = "Sales Taxes and Charges Template"
|
||||
tax_template_by_category = get_tax_template_based_on_category(
|
||||
master_doctype, company, party_details
|
||||
)
|
||||
|
||||
elif doctype in ("Purchase Invoice", "Purchase Order", "Purchase Receipt"):
|
||||
master_doctype = "Purchase Taxes and Charges Template"
|
||||
tax_template_by_category = get_tax_template_based_on_category(
|
||||
master_doctype, company, party_details
|
||||
)
|
||||
|
||||
if tax_template_by_category:
|
||||
party_details["taxes_and_charges"] = tax_template_by_category
|
||||
party_details["taxes"] = get_taxes_and_charges(master_doctype, tax_template_by_category)
|
||||
return party_details
|
||||
|
||||
if not party_details.place_of_supply:
|
||||
return party_details
|
||||
if not party_details.company_gstin:
|
||||
return party_details
|
||||
|
||||
if (
|
||||
doctype in ("Sales Invoice", "Delivery Note", "Sales Order", "Quotation")
|
||||
and party_details.company_gstin
|
||||
and party_details.company_gstin[:2] != party_details.place_of_supply[:2]
|
||||
) or (
|
||||
doctype in ("Purchase Invoice", "Purchase Order", "Purchase Receipt")
|
||||
and party_details.supplier_gstin
|
||||
and party_details.supplier_gstin[:2] != party_details.place_of_supply[:2]
|
||||
):
|
||||
default_tax = get_tax_template(master_doctype, company, 1, party_details.company_gstin[:2])
|
||||
else:
|
||||
default_tax = get_tax_template(master_doctype, company, 0, party_details.company_gstin[:2])
|
||||
|
||||
if not default_tax:
|
||||
return party_details
|
||||
|
||||
party_details["taxes_and_charges"] = default_tax
|
||||
party_details["taxes"] = get_taxes_and_charges(master_doctype, default_tax)
|
||||
|
||||
return party_details
|
||||
|
||||
|
||||
def update_party_details(party_details, doctype):
|
||||
for address_field in [
|
||||
"shipping_address",
|
||||
"company_address",
|
||||
"supplier_address",
|
||||
"shipping_address_name",
|
||||
"customer_address",
|
||||
]:
|
||||
if party_details.get(address_field):
|
||||
party_details.update(get_fetch_values(doctype, address_field, party_details.get(address_field)))
|
||||
|
||||
|
||||
def is_internal_transfer(party_details, doctype):
|
||||
if doctype in ("Sales Invoice", "Delivery Note", "Sales Order", "Quotation"):
|
||||
destination_gstin = party_details.company_gstin
|
||||
elif doctype in ("Purchase Invoice", "Purchase Order", "Purchase Receipt"):
|
||||
destination_gstin = party_details.supplier_gstin
|
||||
|
||||
if not destination_gstin or party_details.gstin:
|
||||
return False
|
||||
|
||||
if party_details.gstin == destination_gstin:
|
||||
return True
|
||||
else:
|
||||
False
|
||||
|
||||
|
||||
def get_tax_template_based_on_category(master_doctype, company, party_details):
|
||||
if not party_details.get("tax_category"):
|
||||
return
|
||||
|
||||
default_tax = frappe.db.get_value(
|
||||
master_doctype, {"company": company, "tax_category": party_details.get("tax_category")}, "name"
|
||||
)
|
||||
|
||||
return default_tax
|
||||
|
||||
|
||||
def get_tax_template(master_doctype, company, is_inter_state, state_code):
|
||||
tax_categories = frappe.get_all(
|
||||
"Tax Category",
|
||||
fields=["name", "is_inter_state", "gst_state"],
|
||||
filters={"is_inter_state": is_inter_state, "is_reverse_charge": 0, "disabled": 0},
|
||||
)
|
||||
|
||||
default_tax = ""
|
||||
|
||||
for tax_category in tax_categories:
|
||||
if tax_category.gst_state == number_state_mapping[state_code] or (
|
||||
not default_tax and not tax_category.gst_state
|
||||
):
|
||||
default_tax = frappe.db.get_value(
|
||||
master_doctype, {"company": company, "disabled": 0, "tax_category": tax_category.name}, "name"
|
||||
)
|
||||
return default_tax
|
||||
|
||||
|
||||
def get_ewb_data(dt, dn):
|
||||
|
||||
ewaybills = []
|
||||
for doc_name in dn:
|
||||
doc = frappe.get_doc(dt, doc_name)
|
||||
|
||||
validate_doc(doc)
|
||||
|
||||
data = frappe._dict(
|
||||
{
|
||||
"transporterId": "",
|
||||
"TotNonAdvolVal": 0,
|
||||
}
|
||||
)
|
||||
|
||||
data.userGstin = data.fromGstin = doc.company_gstin
|
||||
data.supplyType = "O"
|
||||
|
||||
if dt == "Delivery Note":
|
||||
data.subSupplyType = 1
|
||||
elif doc.gst_category in ["Registered Regular", "SEZ"]:
|
||||
data.subSupplyType = 1
|
||||
elif doc.gst_category in ["Overseas", "Deemed Export"]:
|
||||
data.subSupplyType = 3
|
||||
else:
|
||||
frappe.throw(_("Unsupported GST Category for E-Way Bill JSON generation"))
|
||||
|
||||
data.docType = "INV"
|
||||
data.docDate = frappe.utils.formatdate(doc.posting_date, "dd/mm/yyyy")
|
||||
|
||||
company_address = frappe.get_doc("Address", doc.company_address)
|
||||
billing_address = frappe.get_doc("Address", doc.customer_address)
|
||||
|
||||
# added dispatch address
|
||||
dispatch_address = (
|
||||
frappe.get_doc("Address", doc.dispatch_address_name)
|
||||
if doc.dispatch_address_name
|
||||
else company_address
|
||||
)
|
||||
shipping_address = frappe.get_doc("Address", doc.shipping_address_name)
|
||||
|
||||
data = get_address_details(data, doc, company_address, billing_address, dispatch_address)
|
||||
|
||||
data.itemList = []
|
||||
data.totalValue = doc.net_total
|
||||
|
||||
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
|
||||
|
||||
data = get_transport_details(data, doc)
|
||||
|
||||
fields = {
|
||||
"/. -": {
|
||||
"docNo": doc.name,
|
||||
"fromTrdName": doc.company,
|
||||
"toTrdName": doc.customer_name,
|
||||
"transDocNo": doc.lr_no,
|
||||
},
|
||||
"@#/,&. -": {
|
||||
"fromAddr1": company_address.address_line1,
|
||||
"fromAddr2": company_address.address_line2,
|
||||
"fromPlace": company_address.city,
|
||||
"toAddr1": shipping_address.address_line1,
|
||||
"toAddr2": shipping_address.address_line2,
|
||||
"toPlace": shipping_address.city,
|
||||
"transporterName": doc.transporter_name,
|
||||
},
|
||||
}
|
||||
|
||||
for allowed_chars, field_map in fields.items():
|
||||
for key, value in field_map.items():
|
||||
if not value:
|
||||
data[key] = ""
|
||||
else:
|
||||
data[key] = re.sub(r"[^\w" + allowed_chars + "]", "", value)
|
||||
|
||||
ewaybills.append(data)
|
||||
|
||||
data = {"version": "1.0.0421", "billLists": ewaybills}
|
||||
|
||||
return data
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def generate_ewb_json(dt, dn):
|
||||
dn = json.loads(dn)
|
||||
return get_ewb_data(dt, dn)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def download_ewb_json():
|
||||
data = json.loads(frappe.local.form_dict.data)
|
||||
frappe.local.response.filecontent = json.dumps(data, indent=4, sort_keys=True)
|
||||
frappe.local.response.type = "download"
|
||||
|
||||
filename_prefix = "Bulk"
|
||||
docname = frappe.local.form_dict.docname
|
||||
if docname:
|
||||
if docname.startswith("["):
|
||||
docname = json.loads(docname)
|
||||
if len(docname) == 1:
|
||||
docname = docname[0]
|
||||
|
||||
if not isinstance(docname, list):
|
||||
# removes characters not allowed in a filename (https://stackoverflow.com/a/38766141/4767738)
|
||||
filename_prefix = re.sub(r"[^\w_.)( -]", "", docname)
|
||||
|
||||
frappe.local.response.filename = "{0}_e-WayBill_Data_{1}.json".format(
|
||||
filename_prefix, frappe.utils.random_string(5)
|
||||
)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_gstins_for_company(company):
|
||||
company_gstins = []
|
||||
if company:
|
||||
company_gstins = frappe.db.sql(
|
||||
"""select
|
||||
distinct `tabAddress`.gstin
|
||||
from
|
||||
`tabAddress`, `tabDynamic Link`
|
||||
where
|
||||
`tabDynamic Link`.parent = `tabAddress`.name and
|
||||
`tabDynamic Link`.parenttype = 'Address' and
|
||||
`tabDynamic Link`.link_doctype = 'Company' and
|
||||
`tabDynamic Link`.link_name = %(company)s""",
|
||||
{"company": company},
|
||||
)
|
||||
return company_gstins
|
||||
|
||||
|
||||
def get_address_details(data, doc, company_address, billing_address, dispatch_address):
|
||||
data.fromPincode = validate_pincode(company_address.pincode, "Company Address")
|
||||
data.fromStateCode = validate_state_code(company_address.gst_state_number, "Company Address")
|
||||
data.actualFromStateCode = validate_state_code(
|
||||
dispatch_address.gst_state_number, "Dispatch Address"
|
||||
)
|
||||
|
||||
if not doc.billing_address_gstin or len(doc.billing_address_gstin) < 15:
|
||||
data.toGstin = "URP"
|
||||
set_gst_state_and_state_number(billing_address)
|
||||
else:
|
||||
data.toGstin = doc.billing_address_gstin
|
||||
|
||||
data.toPincode = validate_pincode(billing_address.pincode, "Customer Address")
|
||||
data.toStateCode = validate_state_code(billing_address.gst_state_number, "Customer Address")
|
||||
|
||||
if doc.customer_address != doc.shipping_address_name:
|
||||
data.transType = 2
|
||||
shipping_address = frappe.get_doc("Address", doc.shipping_address_name)
|
||||
set_gst_state_and_state_number(shipping_address)
|
||||
data.toPincode = validate_pincode(shipping_address.pincode, "Shipping Address")
|
||||
data.actualToStateCode = validate_state_code(
|
||||
shipping_address.gst_state_number, "Shipping Address"
|
||||
)
|
||||
else:
|
||||
data.transType = 1
|
||||
data.actualToStateCode = data.toStateCode
|
||||
shipping_address = billing_address
|
||||
|
||||
if doc.gst_category == "SEZ":
|
||||
data.toStateCode = 99
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def get_item_list(data, doc, hsn_wise=False):
|
||||
for attr in ["cgstValue", "sgstValue", "igstValue", "cessValue", "OthValue"]:
|
||||
data[attr] = 0
|
||||
|
||||
gst_accounts = get_gst_accounts(doc.company, account_wise=True)
|
||||
tax_map = {
|
||||
"sgst_account": ["sgstRate", "sgstValue"],
|
||||
"cgst_account": ["cgstRate", "cgstValue"],
|
||||
"igst_account": ["igstRate", "igstValue"],
|
||||
"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=hsn_wise
|
||||
)
|
||||
for item_or_hsn, taxable_amount in hsn_taxable_amount.items():
|
||||
item_data = frappe._dict()
|
||||
if not item_or_hsn:
|
||||
frappe.throw(_("GST HSN Code does not exist for one or more items"))
|
||||
item_data.hsnCode = int(item_or_hsn) if hsn_wise else item_or_hsn
|
||||
item_data.taxableAmount = taxable_amount
|
||||
item_data.qtyUnit = ""
|
||||
for attr in item_data_attrs:
|
||||
item_data[attr] = 0
|
||||
|
||||
for account, tax_detail in hsn_wise_charges.get(item_or_hsn, {}).items():
|
||||
account_type = gst_accounts.get(account, "")
|
||||
for tax_acc, attrs in tax_map.items():
|
||||
if account_type == tax_acc:
|
||||
item_data[attrs[0]] = tax_detail.get("tax_rate")
|
||||
data[attrs[1]] += tax_detail.get("tax_amount")
|
||||
break
|
||||
else:
|
||||
data.OthValue += tax_detail.get("tax_amount")
|
||||
|
||||
data.itemList.append(item_data)
|
||||
|
||||
# Tax amounts rounded to 2 decimals to avoid exceeding max character limit
|
||||
for attr in ["sgstValue", "cgstValue", "igstValue", "cessValue"]:
|
||||
data[attr] = flt(data[attr], 2)
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def validate_doc(doc):
|
||||
if doc.docstatus != 1:
|
||||
frappe.throw(_("E-Way Bill JSON can only be generated from submitted document"))
|
||||
|
||||
if doc.is_return:
|
||||
frappe.throw(_("E-Way Bill JSON cannot be generated for Sales Return as of now"))
|
||||
|
||||
if doc.ewaybill:
|
||||
frappe.throw(_("e-Way Bill already exists for this document"))
|
||||
|
||||
reqd_fields = [
|
||||
"company_gstin",
|
||||
"company_address",
|
||||
"customer_address",
|
||||
"shipping_address_name",
|
||||
"mode_of_transport",
|
||||
"distance",
|
||||
]
|
||||
|
||||
for fieldname in reqd_fields:
|
||||
if not doc.get(fieldname):
|
||||
frappe.throw(
|
||||
_("{} is required to generate E-Way Bill JSON").format(doc.meta.get_label(fieldname))
|
||||
)
|
||||
|
||||
if len(doc.company_gstin) < 15:
|
||||
frappe.throw(_("You must be a registered supplier to generate e-Way Bill"))
|
||||
|
||||
|
||||
def get_transport_details(data, doc):
|
||||
if doc.distance > 4000:
|
||||
frappe.throw(_("Distance cannot be greater than 4000 kms"))
|
||||
|
||||
data.transDistance = int(round(doc.distance))
|
||||
|
||||
transport_modes = {"Road": 1, "Rail": 2, "Air": 3, "Ship": 4}
|
||||
|
||||
vehicle_types = {"Regular": "R", "Over Dimensional Cargo (ODC)": "O"}
|
||||
|
||||
data.transMode = transport_modes.get(doc.mode_of_transport)
|
||||
|
||||
if doc.mode_of_transport == "Road":
|
||||
if not doc.gst_transporter_id and not doc.vehicle_no:
|
||||
frappe.throw(
|
||||
_("Either GST Transporter ID or Vehicle No is required if Mode of Transport is Road")
|
||||
)
|
||||
if doc.vehicle_no:
|
||||
data.vehicleNo = doc.vehicle_no.replace(" ", "")
|
||||
if not doc.gst_vehicle_type:
|
||||
frappe.throw(_("Vehicle Type is required if Mode of Transport is Road"))
|
||||
else:
|
||||
data.vehicleType = vehicle_types.get(doc.gst_vehicle_type)
|
||||
else:
|
||||
if not doc.lr_no or not doc.lr_date:
|
||||
frappe.throw(_("Transport Receipt No and Date are mandatory for your chosen Mode of Transport"))
|
||||
|
||||
if doc.lr_no:
|
||||
data.transDocNo = doc.lr_no
|
||||
|
||||
if doc.lr_date:
|
||||
data.transDocDate = frappe.utils.formatdate(doc.lr_date, "dd/mm/yyyy")
|
||||
|
||||
if doc.gst_transporter_id:
|
||||
if doc.gst_transporter_id[0:2] != "88":
|
||||
validate_gstin_check_digit(doc.gst_transporter_id, label="GST Transporter ID")
|
||||
data.transporterId = doc.gst_transporter_id
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def validate_pincode(pincode, address):
|
||||
pin_not_found = "Pin Code doesn't exist for {}"
|
||||
incorrect_pin = "Pin Code for {} is incorrecty formatted. It must be 6 digits (without spaces)"
|
||||
|
||||
if not pincode:
|
||||
frappe.throw(_(pin_not_found.format(address)))
|
||||
|
||||
pincode = pincode.replace(" ", "")
|
||||
if not pincode.isdigit() or len(pincode) != 6:
|
||||
frappe.throw(_(incorrect_pin.format(address)))
|
||||
else:
|
||||
return int(pincode)
|
||||
|
||||
|
||||
def validate_state_code(state_code, address):
|
||||
no_state_code = "GST State Code not found for {0}. Please set GST State in {0}"
|
||||
if not state_code:
|
||||
frappe.throw(_(no_state_code.format(address)))
|
||||
else:
|
||||
return int(state_code)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_gst_accounts(
|
||||
company=None, account_wise=False, only_reverse_charge=0, only_non_reverse_charge=0
|
||||
):
|
||||
filters = {"parent": "GST Settings"}
|
||||
|
||||
if company:
|
||||
filters.update({"company": company})
|
||||
if only_reverse_charge:
|
||||
filters.update({"is_reverse_charge_account": 1})
|
||||
elif only_non_reverse_charge:
|
||||
filters.update({"is_reverse_charge_account": 0})
|
||||
|
||||
gst_accounts = frappe._dict()
|
||||
gst_settings_accounts = frappe.get_all(
|
||||
"GST Account",
|
||||
filters=filters,
|
||||
fields=["cgst_account", "sgst_account", "igst_account", "cess_account", "utgst_account"],
|
||||
)
|
||||
|
||||
if not gst_settings_accounts and not frappe.flags.in_test and not frappe.flags.in_migrate:
|
||||
frappe.throw(_("Please set GST Accounts in GST Settings"))
|
||||
|
||||
for d in gst_settings_accounts:
|
||||
for acc, val in d.items():
|
||||
if not account_wise:
|
||||
gst_accounts.setdefault(acc, []).append(val)
|
||||
elif val:
|
||||
gst_accounts[val] = acc
|
||||
|
||||
return gst_accounts
|
||||
|
||||
|
||||
def validate_sez_and_export_invoices(doc, method):
|
||||
country = frappe.get_cached_value("Company", doc.company, "country")
|
||||
|
||||
if country != "India":
|
||||
return
|
||||
|
||||
if (
|
||||
doc.get("gst_category") in ("SEZ", "Overseas")
|
||||
and doc.get("export_type") == "Without Payment of Tax"
|
||||
):
|
||||
gst_accounts = get_gst_accounts(doc.company)
|
||||
|
||||
for tax in doc.get("taxes"):
|
||||
for tax in doc.get("taxes"):
|
||||
if (
|
||||
tax.account_head
|
||||
in gst_accounts.get("igst_account", [])
|
||||
+ gst_accounts.get("sgst_account", [])
|
||||
+ gst_accounts.get("cgst_account", [])
|
||||
and tax.tax_amount_after_discount_amount
|
||||
):
|
||||
frappe.throw(_("GST cannot be applied on SEZ or Export invoices without payment of tax"))
|
||||
|
||||
|
||||
def validate_reverse_charge_transaction(doc, method):
|
||||
country = frappe.get_cached_value("Company", doc.company, "country")
|
||||
|
||||
if country != "India":
|
||||
return
|
||||
|
||||
base_gst_tax = 0
|
||||
base_reverse_charge_booked = 0
|
||||
|
||||
if doc.reverse_charge == "Y":
|
||||
gst_accounts = get_gst_accounts(doc.company, only_reverse_charge=1)
|
||||
reverse_charge_accounts = (
|
||||
gst_accounts.get("cgst_account")
|
||||
+ gst_accounts.get("sgst_account")
|
||||
+ gst_accounts.get("igst_account")
|
||||
)
|
||||
|
||||
gst_accounts = get_gst_accounts(doc.company, only_non_reverse_charge=1)
|
||||
non_reverse_charge_accounts = (
|
||||
gst_accounts.get("cgst_account")
|
||||
+ gst_accounts.get("sgst_account")
|
||||
+ gst_accounts.get("igst_account")
|
||||
)
|
||||
|
||||
for tax in doc.get("taxes"):
|
||||
if tax.account_head in non_reverse_charge_accounts:
|
||||
if tax.add_deduct_tax == "Add":
|
||||
base_gst_tax += tax.base_tax_amount_after_discount_amount
|
||||
else:
|
||||
base_gst_tax += tax.base_tax_amount_after_discount_amount
|
||||
elif tax.account_head in reverse_charge_accounts:
|
||||
if tax.add_deduct_tax == "Add":
|
||||
base_reverse_charge_booked += tax.base_tax_amount_after_discount_amount
|
||||
else:
|
||||
base_reverse_charge_booked += tax.base_tax_amount_after_discount_amount
|
||||
|
||||
if base_gst_tax != base_reverse_charge_booked:
|
||||
msg = _("Booked reverse charge is not equal to applied tax amount")
|
||||
msg += "<br>"
|
||||
msg += _(
|
||||
"Please refer {gst_document_link} to learn more about how to setup and create reverse charge invoice"
|
||||
).format(
|
||||
gst_document_link='<a href="https://docs.erpnext.com/docs/user/manual/en/regional/india/gst-setup">GST Documentation</a>'
|
||||
)
|
||||
|
||||
frappe.throw(msg)
|
||||
|
||||
doc.eligibility_for_itc = "ITC on Reverse Charge"
|
||||
|
||||
|
||||
def update_itc_availed_fields(doc, method):
|
||||
country = frappe.get_cached_value("Company", doc.company, "country")
|
||||
|
||||
if country != "India":
|
||||
return
|
||||
|
||||
# Initialize values
|
||||
doc.itc_integrated_tax = doc.itc_state_tax = doc.itc_central_tax = doc.itc_cess_amount = 0
|
||||
gst_accounts = get_gst_accounts(doc.company, only_non_reverse_charge=1)
|
||||
|
||||
for tax in doc.get("taxes"):
|
||||
if tax.account_head in gst_accounts.get("igst_account", []):
|
||||
doc.itc_integrated_tax += flt(tax.base_tax_amount_after_discount_amount)
|
||||
if tax.account_head in gst_accounts.get("sgst_account", []):
|
||||
doc.itc_state_tax += flt(tax.base_tax_amount_after_discount_amount)
|
||||
if tax.account_head in gst_accounts.get("cgst_account", []):
|
||||
doc.itc_central_tax += flt(tax.base_tax_amount_after_discount_amount)
|
||||
if tax.account_head in gst_accounts.get("cess_account", []):
|
||||
doc.itc_cess_amount += flt(tax.base_tax_amount_after_discount_amount)
|
||||
|
||||
|
||||
def update_place_of_supply(doc, method):
|
||||
country = frappe.get_cached_value("Company", doc.company, "country")
|
||||
if country != "India":
|
||||
return
|
||||
|
||||
address = frappe.db.get_value(
|
||||
"Address", doc.get("customer_address"), ["gst_state", "gst_state_number"], as_dict=1
|
||||
)
|
||||
if address and address.gst_state and address.gst_state_number:
|
||||
doc.place_of_supply = cstr(address.gst_state_number) + "-" + cstr(address.gst_state)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_regional_round_off_accounts(company, account_list):
|
||||
country = frappe.get_cached_value("Company", company, "country")
|
||||
|
||||
if country != "India":
|
||||
return
|
||||
|
||||
if isinstance(account_list, str):
|
||||
account_list = json.loads(account_list)
|
||||
|
||||
if not frappe.db.get_single_value("GST Settings", "round_off_gst_values"):
|
||||
return
|
||||
|
||||
gst_accounts = get_gst_accounts(company)
|
||||
|
||||
gst_account_list = []
|
||||
for account in ["cgst_account", "sgst_account", "igst_account"]:
|
||||
if account in gst_accounts:
|
||||
gst_account_list += gst_accounts.get(account)
|
||||
|
||||
account_list.extend(gst_account_list)
|
||||
|
||||
return account_list
|
||||
|
||||
|
||||
def update_taxable_values(doc, method):
|
||||
country = frappe.get_cached_value("Company", doc.company, "country")
|
||||
|
||||
if country != "India":
|
||||
return
|
||||
|
||||
gst_accounts = get_gst_accounts(doc.company)
|
||||
|
||||
# Only considering sgst account to avoid inflating taxable value
|
||||
gst_account_list = (
|
||||
gst_accounts.get("sgst_account", [])
|
||||
+ gst_accounts.get("sgst_account", [])
|
||||
+ gst_accounts.get("igst_account", [])
|
||||
)
|
||||
|
||||
additional_taxes = 0
|
||||
total_charges = 0
|
||||
item_count = 0
|
||||
considered_rows = []
|
||||
|
||||
for tax in doc.get("taxes"):
|
||||
prev_row_id = cint(tax.row_id) - 1
|
||||
if tax.account_head in gst_account_list and prev_row_id not in considered_rows:
|
||||
if tax.charge_type == "On Previous Row Amount":
|
||||
additional_taxes += doc.get("taxes")[prev_row_id].tax_amount_after_discount_amount
|
||||
considered_rows.append(prev_row_id)
|
||||
if tax.charge_type == "On Previous Row Total":
|
||||
additional_taxes += doc.get("taxes")[prev_row_id].base_total - doc.base_net_total
|
||||
considered_rows.append(prev_row_id)
|
||||
|
||||
for item in doc.get("items"):
|
||||
proportionate_value = item.base_net_amount if doc.base_net_total else item.qty
|
||||
total_value = doc.base_net_total if doc.base_net_total else doc.total_qty
|
||||
|
||||
applicable_charges = flt(
|
||||
flt(
|
||||
proportionate_value * (flt(additional_taxes) / flt(total_value)),
|
||||
item.precision("taxable_value"),
|
||||
)
|
||||
)
|
||||
item.taxable_value = applicable_charges + proportionate_value
|
||||
total_charges += applicable_charges
|
||||
item_count += 1
|
||||
|
||||
if total_charges != additional_taxes:
|
||||
diff = additional_taxes - total_charges
|
||||
doc.get("items")[item_count - 1].taxable_value += diff
|
||||
|
||||
|
||||
def get_depreciation_amount(asset, depreciable_value, row):
|
||||
if row.depreciation_method in ("Straight Line", "Manual"):
|
||||
# if the Depreciation Schedule is being prepared for the first time
|
||||
if not asset.flags.increase_in_asset_life:
|
||||
depreciation_amount = (
|
||||
flt(asset.gross_purchase_amount) - flt(row.expected_value_after_useful_life)
|
||||
) / flt(row.total_number_of_depreciations)
|
||||
|
||||
# if the Depreciation Schedule is being modified after Asset Repair
|
||||
else:
|
||||
depreciation_amount = (
|
||||
flt(row.value_after_depreciation) - flt(row.expected_value_after_useful_life)
|
||||
) / (date_diff(asset.to_date, asset.available_for_use_date) / 365)
|
||||
|
||||
else:
|
||||
rate_of_depreciation = row.rate_of_depreciation
|
||||
# if its the first depreciation
|
||||
if depreciable_value == asset.gross_purchase_amount:
|
||||
if row.finance_book and frappe.db.get_value("Finance Book", row.finance_book, "for_income_tax"):
|
||||
# as per IT act, if the asset is purchased in the 2nd half of fiscal year, then rate is divided by 2
|
||||
diff = date_diff(row.depreciation_start_date, asset.available_for_use_date)
|
||||
if diff <= 180:
|
||||
rate_of_depreciation = rate_of_depreciation / 2
|
||||
frappe.msgprint(
|
||||
_(
|
||||
"As per IT Act, the rate of depreciation for the first depreciation entry is reduced by 50%."
|
||||
)
|
||||
)
|
||||
|
||||
depreciation_amount = flt(depreciable_value * (flt(rate_of_depreciation) / 100))
|
||||
|
||||
return depreciation_amount
|
||||
|
||||
|
||||
def set_item_tax_from_hsn_code(item):
|
||||
if not item.taxes and item.gst_hsn_code:
|
||||
hsn_doc = frappe.get_doc("GST HSN Code", item.gst_hsn_code)
|
||||
|
||||
for tax in hsn_doc.taxes:
|
||||
item.append(
|
||||
"taxes",
|
||||
{
|
||||
"item_tax_template": tax.item_tax_template,
|
||||
"tax_category": tax.tax_category,
|
||||
"valid_from": tax.valid_from,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def delete_gst_settings_for_company(doc, method):
|
||||
if doc.country != "India":
|
||||
return
|
||||
|
||||
gst_settings = frappe.get_doc("GST Settings")
|
||||
records_to_delete = []
|
||||
|
||||
for d in reversed(gst_settings.get("gst_accounts")):
|
||||
if d.company == doc.name:
|
||||
records_to_delete.append(d)
|
||||
|
||||
for d in records_to_delete:
|
||||
gst_settings.remove(d)
|
||||
|
||||
gst_settings.save()
|
||||
@@ -1,22 +0,0 @@
|
||||
{
|
||||
"align_labels_right": 0,
|
||||
"creation": "2017-07-04 16:26:21.120187",
|
||||
"custom_format": 0,
|
||||
"disabled": 1,
|
||||
"doc_type": "Sales Invoice",
|
||||
"docstatus": 0,
|
||||
"doctype": "Print Format",
|
||||
"font": "Default",
|
||||
"format_data": "[{\"fieldname\": \"print_heading_template\", \"fieldtype\": \"Custom HTML\", \"options\": \"<div class=\\\"print-heading\\\">\\n\\t<h2>\\n\\t\\tTAX INVOICE<br>\\n\\t\\t<small>{{ doc.name }}</small>\\n\\t</h2>\\n</div>\\n<h2 class=\\\"text-center\\\">\\n\\t{% if doc.invoice_copy -%}\\n\\t\\t<small>{{ doc.invoice_copy }}</small>\\n\\t{% endif -%}\\n</h2>\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"company\", \"label\": \"Company\"}, {\"print_hide\": 0, \"fieldname\": \"company_address_display\", \"label\": \"Company Address\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"posting_date\", \"label\": \"Date\"}, {\"print_hide\": 0, \"fieldname\": \"due_date\", \"label\": \"Payment Due Date\"}, {\"print_hide\": 0, \"fieldname\": \"reverse_charge\", \"label\": \"Reverse Charge\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"label\": \"Custom HTML\", \"fieldname\": \"_custom_html\", \"options\": \"<hr>\", \"fieldtype\": \"HTML\"}, {\"fieldtype\": \"Section Break\", \"label\": \"Address\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"customer_name\", \"label\": \"Customer Name\"}, {\"print_hide\": 0, \"fieldname\": \"address_display\", \"label\": \"Address\"}, {\"print_hide\": 0, \"fieldname\": \"contact_display\", \"label\": \"Contact\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"shipping_address\", \"label\": \"Shipping Address\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"visible_columns\": [{\"print_hide\": 0, \"fieldname\": \"item_code\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"description\", \"print_width\": \"200px\"}, {\"print_hide\": 0, \"fieldname\": \"gst_hsn_code\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"serial_no\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"qty\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"uom\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"rate\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"amount\", \"print_width\": \"\"}], \"print_hide\": 0, \"fieldname\": \"items\", \"label\": \"Items\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"total\", \"label\": \"Total\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"visible_columns\": [{\"print_hide\": 0, \"fieldname\": \"description\", \"print_width\": \"300px\"}], \"print_hide\": 0, \"fieldname\": \"taxes\", \"label\": \"Sales Taxes and Charges\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"grand_total\", \"label\": \"Grand Total\"}, {\"print_hide\": 0, \"fieldname\": \"rounded_total\", \"label\": \"Rounded Total\"}, {\"print_hide\": 0, \"fieldname\": \"in_words\", \"label\": \"In Words\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"other_charges_calculation\", \"align\": \"left\", \"label\": \"Tax Breakup\"}, {\"fieldtype\": \"Section Break\", \"label\": \"Terms\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"terms\", \"label\": \"Terms and Conditions Details\"}]",
|
||||
"idx": 0,
|
||||
"line_breaks": 0,
|
||||
"modified": "2017-12-15 11:57:25.477278",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Regional",
|
||||
"name": "GST Tax Invoice",
|
||||
"owner": "Administrator",
|
||||
"print_format_builder": 1,
|
||||
"print_format_type": "Jinja",
|
||||
"show_section_headings": 0,
|
||||
"standard": "Yes"
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
/* eslint-disable */
|
||||
|
||||
frappe.query_reports["E-Invoice Summary"] = {
|
||||
"filters": [
|
||||
{
|
||||
"fieldtype": "Link",
|
||||
"options": "Company",
|
||||
"reqd": 1,
|
||||
"fieldname": "company",
|
||||
"label": __("Company"),
|
||||
"default": frappe.defaults.get_user_default("Company"),
|
||||
},
|
||||
{
|
||||
"fieldtype": "Link",
|
||||
"options": "Customer",
|
||||
"fieldname": "customer",
|
||||
"label": __("Customer")
|
||||
},
|
||||
{
|
||||
"fieldtype": "Date",
|
||||
"reqd": 1,
|
||||
"fieldname": "from_date",
|
||||
"label": __("From Date"),
|
||||
"default": frappe.datetime.add_months(frappe.datetime.get_today(), -1),
|
||||
},
|
||||
{
|
||||
"fieldtype": "Date",
|
||||
"reqd": 1,
|
||||
"fieldname": "to_date",
|
||||
"label": __("To Date"),
|
||||
"default": frappe.datetime.get_today(),
|
||||
},
|
||||
{
|
||||
"fieldtype": "Select",
|
||||
"fieldname": "status",
|
||||
"label": __("Status"),
|
||||
"options": "\nPending\nGenerated\nCancelled\nFailed"
|
||||
}
|
||||
],
|
||||
|
||||
"formatter": function (value, row, column, data, default_formatter) {
|
||||
value = default_formatter(value, row, column, data);
|
||||
|
||||
if (column.fieldname == "einvoice_status" && value) {
|
||||
if (value == 'Pending') value = `<span class="bold" style="color: var(--text-on-orange)">${value}</span>`;
|
||||
else if (value == 'Generated') value = `<span class="bold" style="color: var(--text-on-green)">${value}</span>`;
|
||||
else if (value == 'Cancelled') value = `<span class="bold" style="color: var(--text-on-red)">${value}</span>`;
|
||||
else if (value == 'Failed') value = `<span class="bold" style="color: var(--text-on-red)">${value}</span>`;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
};
|
||||
@@ -1,28 +0,0 @@
|
||||
{
|
||||
"add_total_row": 0,
|
||||
"columns": [],
|
||||
"creation": "2021-03-12 11:23:37.312294",
|
||||
"disable_prepared_report": 0,
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"filters": [],
|
||||
"idx": 0,
|
||||
"is_standard": "Yes",
|
||||
"json": "{}",
|
||||
"letter_head": "Logo",
|
||||
"modified": "2021-03-13 12:36:48.689413",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Regional",
|
||||
"name": "E-Invoice Summary",
|
||||
"owner": "Administrator",
|
||||
"prepared_report": 0,
|
||||
"ref_doctype": "Sales Invoice",
|
||||
"report_name": "E-Invoice Summary",
|
||||
"report_type": "Script Report",
|
||||
"roles": [
|
||||
{
|
||||
"role": "Administrator"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
|
||||
|
||||
def execute(filters=None):
|
||||
validate_filters(filters)
|
||||
|
||||
columns = get_columns()
|
||||
data = get_data(filters)
|
||||
|
||||
return columns, data
|
||||
|
||||
|
||||
def validate_filters(filters=None):
|
||||
if filters is None:
|
||||
filters = {}
|
||||
filters = frappe._dict(filters)
|
||||
|
||||
if not filters.company:
|
||||
frappe.throw(
|
||||
_("{} is mandatory for generating E-Invoice Summary Report").format(_("Company")),
|
||||
title=_("Invalid Filter"),
|
||||
)
|
||||
if filters.company:
|
||||
# validate if company has e-invoicing enabled
|
||||
pass
|
||||
if not filters.from_date or not filters.to_date:
|
||||
frappe.throw(
|
||||
_("From Date & To Date is mandatory for generating E-Invoice Summary Report"),
|
||||
title=_("Invalid Filter"),
|
||||
)
|
||||
if filters.from_date > filters.to_date:
|
||||
frappe.throw(_("From Date must be before To Date"), title=_("Invalid Filter"))
|
||||
|
||||
|
||||
def get_data(filters=None):
|
||||
if filters is None:
|
||||
filters = {}
|
||||
query_filters = {
|
||||
"posting_date": ["between", [filters.from_date, filters.to_date]],
|
||||
"einvoice_status": ["is", "set"],
|
||||
"company": filters.company,
|
||||
}
|
||||
if filters.customer:
|
||||
query_filters["customer"] = filters.customer
|
||||
if filters.status:
|
||||
query_filters["einvoice_status"] = filters.status
|
||||
|
||||
data = frappe.get_all(
|
||||
"Sales Invoice", filters=query_filters, fields=[d.get("fieldname") for d in get_columns()]
|
||||
)
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def get_columns():
|
||||
return [
|
||||
{"fieldtype": "Date", "fieldname": "posting_date", "label": _("Posting Date"), "width": 0},
|
||||
{
|
||||
"fieldtype": "Link",
|
||||
"fieldname": "name",
|
||||
"label": _("Sales Invoice"),
|
||||
"options": "Sales Invoice",
|
||||
"width": 140,
|
||||
},
|
||||
{"fieldtype": "Data", "fieldname": "einvoice_status", "label": _("Status"), "width": 100},
|
||||
{"fieldtype": "Link", "fieldname": "customer", "options": "Customer", "label": _("Customer")},
|
||||
{"fieldtype": "Check", "fieldname": "is_return", "label": _("Is Return"), "width": 85},
|
||||
{"fieldtype": "Data", "fieldname": "ack_no", "label": "Ack. No.", "width": 145},
|
||||
{"fieldtype": "Data", "fieldname": "ack_date", "label": "Ack. Date", "width": 165},
|
||||
{"fieldtype": "Data", "fieldname": "irn", "label": _("IRN No."), "width": 250},
|
||||
{
|
||||
"fieldtype": "Currency",
|
||||
"options": "Company:company:default_currency",
|
||||
"fieldname": "base_grand_total",
|
||||
"label": _("Grand Total"),
|
||||
"width": 120,
|
||||
},
|
||||
]
|
||||
@@ -1,33 +0,0 @@
|
||||
// Copyright (c) 2016, FinByz Tech Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
/* eslint-disable */
|
||||
|
||||
frappe.query_reports["Eway Bill"] = {
|
||||
"filters": [
|
||||
{
|
||||
'fieldname': 'delivery_note',
|
||||
'label': __("Delivery Note"),
|
||||
'fieldtype': 'Link',
|
||||
'options': 'Delivery Note'
|
||||
},
|
||||
{
|
||||
'fieldname': 'posting_date',
|
||||
'label': __("Date"),
|
||||
'fieldtype': 'DateRange',
|
||||
'default': [frappe.datetime.nowdate(), frappe.datetime.nowdate()]
|
||||
},
|
||||
{
|
||||
'fieldname': 'customer',
|
||||
'label': __("Customer"),
|
||||
'fieldtype': 'Link',
|
||||
'options': 'Customer'
|
||||
},
|
||||
{
|
||||
"fieldname":"company",
|
||||
"label": __("Company"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Company",
|
||||
"default": frappe.defaults.get_user_default("Company")
|
||||
},
|
||||
]
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
{
|
||||
"add_total_row": 0,
|
||||
"apply_user_permissions": 1,
|
||||
"creation": "2018-07-13 19:59:18.922829",
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"idx": 0,
|
||||
"is_standard": "Yes",
|
||||
"modified": "2018-07-19 12:08:07.400295",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Regional",
|
||||
"name": "Eway Bill",
|
||||
"owner": "Administrator",
|
||||
"ref_doctype": "Delivery Note",
|
||||
"report_name": "Eway Bill",
|
||||
"report_type": "Script Report",
|
||||
"roles": [
|
||||
{
|
||||
"role": "Stock User"
|
||||
},
|
||||
{
|
||||
"role": "Stock Manager"
|
||||
},
|
||||
{
|
||||
"role": "Sales User"
|
||||
},
|
||||
{
|
||||
"role": "Accounts User"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,257 +0,0 @@
|
||||
# Copyright (c) 2013, FinByz Tech Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
|
||||
import json
|
||||
import re
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import nowdate
|
||||
|
||||
|
||||
def execute(filters=None):
|
||||
if not filters:
|
||||
filters.setdefault("posting_date", [nowdate(), nowdate()])
|
||||
columns, data = [], []
|
||||
columns = get_columns()
|
||||
data = get_data(filters)
|
||||
return columns, data
|
||||
|
||||
|
||||
def get_data(filters):
|
||||
|
||||
conditions = get_conditions(filters)
|
||||
|
||||
data = frappe.db.sql(
|
||||
"""
|
||||
SELECT
|
||||
dn.name as dn_id, dn.posting_date, dn.company, dn.company_gstin, dn.customer, dn.customer_gstin, dni.item_code, dni.item_name, dni.description, dni.gst_hsn_code, dni.uom, dni.qty, dni.amount, dn.mode_of_transport, dn.distance, dn.transporter_name, dn.gst_transporter_id, dn.lr_no, dn.lr_date, dn.vehicle_no, dn.gst_vehicle_type, dn.company_address, dn.shipping_address_name
|
||||
FROM
|
||||
`tabDelivery Note` AS dn join `tabDelivery Note Item` AS dni on (dni.parent = dn.name)
|
||||
WHERE
|
||||
dn.docstatus < 2
|
||||
%s """
|
||||
% conditions,
|
||||
as_dict=1,
|
||||
)
|
||||
|
||||
unit = {
|
||||
"Bag": "BAGS",
|
||||
"Bottle": "BOTTLES",
|
||||
"Kg": "KILOGRAMS",
|
||||
"Liter": "LITERS",
|
||||
"Meter": "METERS",
|
||||
"Nos": "NUMBERS",
|
||||
"PKT": "PACKS",
|
||||
"Roll": "ROLLS",
|
||||
"Set": "SETS",
|
||||
}
|
||||
|
||||
# Regular expression set to remove all the special characters
|
||||
special_characters = r"[$%^*()+\\[\]{};':\"\\|<>.?]"
|
||||
|
||||
for row in data:
|
||||
set_defaults(row)
|
||||
set_taxes(row, filters)
|
||||
set_address_details(row, special_characters)
|
||||
|
||||
# Eway Bill accepts date as dd/mm/yyyy and not dd-mm-yyyy
|
||||
row.posting_date = "/".join(str(row.posting_date).replace("-", "/").split("/")[::-1])
|
||||
row.lr_date = "/".join(str(row.lr_date).replace("-", "/").split("/")[::-1])
|
||||
|
||||
if row.gst_vehicle_type == "Over Dimensional Cargo (ODC)":
|
||||
row.gst_vehicle_type = "ODC"
|
||||
|
||||
row.item_name = re.sub(special_characters, " ", row.item_name)
|
||||
row.description = row.item_name
|
||||
|
||||
row.uom = unit.get(row.uom, row.uom)
|
||||
|
||||
# For removing special charactes and numbers from customer.
|
||||
row.customer = re.sub(special_characters[:-1] + "&0-9" + "]", "", row.customer)
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def get_conditions(filters):
|
||||
|
||||
conditions = ""
|
||||
|
||||
conditions += filters.get("company") and " AND dn.company = '%s' " % filters.get("company") or ""
|
||||
conditions += (
|
||||
filters.get("posting_date")
|
||||
and " AND dn.posting_date >= '%s' AND dn.posting_date <= '%s' "
|
||||
% (filters.get("posting_date")[0], filters.get("posting_date")[1])
|
||||
or ""
|
||||
)
|
||||
conditions += (
|
||||
filters.get("delivery_note") and " AND dn.name = '%s' " % filters.get("delivery_note") or ""
|
||||
)
|
||||
conditions += (
|
||||
filters.get("customer")
|
||||
and " AND dn.customer = '%s' " % filters.get("customer").replace("'", "'")
|
||||
or ""
|
||||
)
|
||||
|
||||
return conditions
|
||||
|
||||
|
||||
def set_defaults(row):
|
||||
row.setdefault("supply_type", "Outward")
|
||||
row.setdefault("sub_type", "Supply")
|
||||
row.setdefault("doc_type", "Delivery Challan")
|
||||
|
||||
|
||||
def set_address_details(row, special_characters):
|
||||
|
||||
if row.get("company_address"):
|
||||
address_line1, address_line2, city, pincode, state = frappe.db.get_value(
|
||||
"Address",
|
||||
row.get("company_address"),
|
||||
["address_line1", "address_line2", "city", "pincode", "state"],
|
||||
)
|
||||
|
||||
row.update({"from_address_1": re.sub(special_characters, "", address_line1 or "")})
|
||||
row.update({"from_address_2": re.sub(special_characters, "", address_line2 or "")})
|
||||
row.update({"from_place": city and city.upper() or ""})
|
||||
row.update({"from_pin_code": pincode and pincode.replace(" ", "") or ""})
|
||||
row.update({"from_state": state and state.upper() or ""})
|
||||
row.update({"dispatch_state": row.from_state})
|
||||
|
||||
if row.get("shipping_address_name"):
|
||||
address_line1, address_line2, city, pincode, state = frappe.db.get_value(
|
||||
"Address",
|
||||
row.get("shipping_address_name"),
|
||||
["address_line1", "address_line2", "city", "pincode", "state"],
|
||||
)
|
||||
|
||||
row.update({"to_address_1": re.sub(special_characters, "", address_line1 or "")})
|
||||
row.update({"to_address_2": re.sub(special_characters, "", address_line2 or "")})
|
||||
row.update({"to_place": city and city.upper() or ""})
|
||||
row.update({"to_pin_code": pincode and pincode.replace(" ", "") or ""})
|
||||
row.update({"to_state": state and state.upper() or ""})
|
||||
row.update({"ship_to_state": row.to_state})
|
||||
|
||||
|
||||
def set_taxes(row, filters):
|
||||
taxes = frappe.get_all(
|
||||
"Sales Taxes and Charges",
|
||||
filters={"parent": row.dn_id},
|
||||
fields=("item_wise_tax_detail", "account_head"),
|
||||
)
|
||||
|
||||
account_list = ["cgst_account", "sgst_account", "igst_account", "cess_account"]
|
||||
taxes_list = frappe.get_all(
|
||||
"GST Account",
|
||||
filters={"parent": "GST Settings", "company": filters.company},
|
||||
fields=account_list,
|
||||
)
|
||||
|
||||
if not taxes_list:
|
||||
frappe.throw(_("Please set GST Accounts in GST Settings"))
|
||||
|
||||
item_tax_rate = {}
|
||||
|
||||
for tax in taxes:
|
||||
item_wise_tax = json.loads(tax.item_wise_tax_detail)
|
||||
item_tax_rate[tax.account_head] = item_wise_tax.get(row.item_code)
|
||||
|
||||
tax_rate = []
|
||||
|
||||
tax = taxes_list[0]
|
||||
for key in account_list:
|
||||
if tax[key] not in item_tax_rate.keys():
|
||||
item_tax_rate[tax[key]] = [0.0, 0.0]
|
||||
|
||||
tax_rate.append(str(item_tax_rate[tax[key]][0]))
|
||||
row.update({key[:5] + "amount": round(item_tax_rate.get(tax[key], 0.0)[1], 2)})
|
||||
item_tax_rate.pop(tax[key])
|
||||
|
||||
row.amount = float(row.amount) + sum(i[1] for i in item_tax_rate.values())
|
||||
row.update({"tax_rate": "+".join(tax_rate)})
|
||||
|
||||
|
||||
def get_columns():
|
||||
columns = [
|
||||
{"fieldname": "supply_type", "label": _("Supply Type"), "fieldtype": "Data", "width": 100},
|
||||
{"fieldname": "sub_type", "label": _("Sub Type"), "fieldtype": "Data", "width": 100},
|
||||
{"fieldname": "doc_type", "label": _("Doc Type"), "fieldtype": "Data", "width": 100},
|
||||
{
|
||||
"fieldname": "dn_id",
|
||||
"label": _("Doc Name"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Delivery Note",
|
||||
"width": 140,
|
||||
},
|
||||
{"fieldname": "posting_date", "label": _("Doc Date"), "fieldtype": "Data", "width": 100},
|
||||
{
|
||||
"fieldname": "company",
|
||||
"label": _("From Party Name"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Company",
|
||||
"width": 120,
|
||||
},
|
||||
{"fieldname": "company_gstin", "label": _("From GSTIN"), "fieldtype": "Data", "width": 100},
|
||||
{"fieldname": "from_address_1", "label": _("From Address 1"), "fieldtype": "Data", "width": 120},
|
||||
{"fieldname": "from_address_2", "label": _("From Address 2"), "fieldtype": "Data", "width": 120},
|
||||
{"fieldname": "from_place", "label": _("From Place"), "fieldtype": "Data", "width": 80},
|
||||
{"fieldname": "from_pin_code", "label": _("From Pin Code"), "fieldtype": "Data", "width": 80},
|
||||
{"fieldname": "from_state", "label": _("From State"), "fieldtype": "Data", "width": 80},
|
||||
{"fieldname": "dispatch_state", "label": _("Dispatch State"), "fieldtype": "Data", "width": 100},
|
||||
{"fieldname": "customer", "label": _("To Party Name"), "fieldtype": "Data", "width": 120},
|
||||
{"fieldname": "customer_gstin", "label": _("To GSTIN"), "fieldtype": "Data", "width": 120},
|
||||
{"fieldname": "to_address_1", "label": _("To Address 1"), "fieldtype": "Data", "width": 120},
|
||||
{"fieldname": "to_address_2", "label": _("To Address 2"), "fieldtype": "Data", "width": 120},
|
||||
{"fieldname": "to_place", "label": _("To Place"), "fieldtype": "Data", "width": 80},
|
||||
{"fieldname": "to_pin_code", "label": _("To Pin Code"), "fieldtype": "Data", "width": 80},
|
||||
{"fieldname": "to_state", "label": _("To State"), "fieldtype": "Data", "width": 80},
|
||||
{"fieldname": "ship_to_state", "label": _("Ship To State"), "fieldtype": "Data", "width": 100},
|
||||
{
|
||||
"fieldname": "item_name",
|
||||
"label": _("Product"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Item",
|
||||
"width": 120,
|
||||
},
|
||||
{"fieldname": "description", "label": _("Description"), "fieldtype": "Data", "width": 100},
|
||||
{"fieldname": "gst_hsn_code", "label": _("HSN"), "fieldtype": "Data", "width": 120},
|
||||
{"fieldname": "uom", "label": _("Unit"), "fieldtype": "Data", "width": 100},
|
||||
{"fieldname": "qty", "label": _("Qty"), "fieldtype": "Float", "width": 100},
|
||||
{"fieldname": "amount", "label": _("Accessable Value"), "fieldtype": "Float", "width": 120},
|
||||
{"fieldname": "tax_rate", "label": _("Tax Rate"), "fieldtype": "Data", "width": 100},
|
||||
{"fieldname": "cgst_amount", "label": _("CGST Amount"), "fieldtype": "Data", "width": 100},
|
||||
{"fieldname": "sgst_amount", "label": _("SGST Amount"), "fieldtype": "Data", "width": 100},
|
||||
{"fieldname": "igst_amount", "label": _("IGST Amount"), "fieldtype": "Data", "width": 100},
|
||||
{"fieldname": "cess_amount", "label": _("CESS Amount"), "fieldtype": "Data", "width": 100},
|
||||
{
|
||||
"fieldname": "mode_of_transport",
|
||||
"label": _("Mode of Transport"),
|
||||
"fieldtype": "Data",
|
||||
"width": 100,
|
||||
},
|
||||
{"fieldname": "distance", "label": _("Distance"), "fieldtype": "Data", "width": 100},
|
||||
{
|
||||
"fieldname": "transporter_name",
|
||||
"label": _("Transporter Name"),
|
||||
"fieldtype": "Data",
|
||||
"width": 120,
|
||||
},
|
||||
{
|
||||
"fieldname": "gst_transporter_id",
|
||||
"label": _("Transporter ID"),
|
||||
"fieldtype": "Data",
|
||||
"width": 100,
|
||||
},
|
||||
{"fieldname": "lr_no", "label": _("Transport Receipt No"), "fieldtype": "Data", "width": 120},
|
||||
{
|
||||
"fieldname": "lr_date",
|
||||
"label": _("Transport Receipt Date"),
|
||||
"fieldtype": "Data",
|
||||
"width": 120,
|
||||
},
|
||||
{"fieldname": "vehicle_no", "label": _("Vehicle No"), "fieldtype": "Data", "width": 100},
|
||||
{"fieldname": "gst_vehicle_type", "label": _("Vehicle Type"), "fieldtype": "Data", "width": 100},
|
||||
]
|
||||
|
||||
return columns
|
||||
@@ -1,7 +0,0 @@
|
||||
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
/* eslint-disable */
|
||||
|
||||
{% include "erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.js" %}
|
||||
|
||||
frappe.query_reports["GST Itemised Purchase Register"] = frappe.query_reports["Item-wise Purchase Register"]
|
||||
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"add_total_row": 1,
|
||||
"apply_user_permissions": 1,
|
||||
"creation": "2017-06-22 15:25:04.101930",
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"idx": 0,
|
||||
"is_standard": "Yes",
|
||||
"modified": "2017-08-04 15:39:41.671851",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Regional",
|
||||
"name": "GST Itemised Purchase Register",
|
||||
"owner": "Administrator",
|
||||
"ref_doctype": "Purchase Invoice",
|
||||
"report_name": "GST Itemised Purchase Register",
|
||||
"report_type": "Script Report",
|
||||
"roles": []
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
|
||||
from erpnext.accounts.report.item_wise_purchase_register.item_wise_purchase_register import (
|
||||
_execute,
|
||||
)
|
||||
|
||||
|
||||
def execute(filters=None):
|
||||
return _execute(
|
||||
filters,
|
||||
additional_table_columns=[
|
||||
dict(fieldtype="Data", label="Supplier GSTIN", fieldname="supplier_gstin", width=120),
|
||||
dict(fieldtype="Data", label="Company GSTIN", fieldname="company_gstin", width=120),
|
||||
dict(fieldtype="Data", label="Reverse Charge", fieldname="reverse_charge", width=120),
|
||||
dict(fieldtype="Data", label="GST Category", fieldname="gst_category", width=120),
|
||||
dict(fieldtype="Data", label="Export Type", fieldname="export_type", width=120),
|
||||
dict(fieldtype="Data", label="E-Commerce GSTIN", fieldname="ecommerce_gstin", width=130),
|
||||
dict(fieldtype="Data", label="HSN Code", fieldname="gst_hsn_code", width=120),
|
||||
dict(fieldtype="Data", label="Supplier Invoice No", fieldname="bill_no", width=120),
|
||||
dict(fieldtype="Date", label="Supplier Invoice Date", fieldname="bill_date", width=100),
|
||||
],
|
||||
additional_query_columns=[
|
||||
"supplier_gstin",
|
||||
"company_gstin",
|
||||
"reverse_charge",
|
||||
"gst_category",
|
||||
"export_type",
|
||||
"ecommerce_gstin",
|
||||
"gst_hsn_code",
|
||||
"bill_no",
|
||||
"bill_date",
|
||||
],
|
||||
)
|
||||
@@ -1,33 +0,0 @@
|
||||
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
/* eslint-disable */
|
||||
|
||||
{% include "erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.js" %}
|
||||
{% include "erpnext/regional/report/india_gst_common/india_gst_common.js" %}
|
||||
|
||||
let filters = frappe.query_reports["Item-wise Sales Register"]["filters"];
|
||||
|
||||
// Add GSTIN filter
|
||||
filters = filters.concat({
|
||||
"fieldname":"company_gstin",
|
||||
"label": __("Company GSTIN"),
|
||||
"fieldtype": "Select",
|
||||
"placeholder":"Company GSTIN",
|
||||
"options": [""],
|
||||
"width": "80"
|
||||
}, {
|
||||
"fieldname":"invoice_type",
|
||||
"label": __("Invoice Type"),
|
||||
"fieldtype": "Select",
|
||||
"placeholder":"Invoice Type",
|
||||
"options": ["", "Regular", "SEZ", "Export", "Deemed Export"]
|
||||
});
|
||||
|
||||
// Handle company on change
|
||||
for (var i = 0; i < filters.length; ++i) {
|
||||
if (filters[i].fieldname === 'company') {
|
||||
filters[i].on_change = fetch_gstins;
|
||||
}
|
||||
}
|
||||
|
||||
frappe.query_reports["GST Itemised Sales Register"] = { "filters": filters, "onload": fetch_gstins };
|
||||
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"add_total_row": 1,
|
||||
"apply_user_permissions": 1,
|
||||
"creation": "2017-06-22 15:24:43.083217",
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"idx": 0,
|
||||
"is_standard": "Yes",
|
||||
"modified": "2017-08-04 15:39:46.998093",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Regional",
|
||||
"name": "GST Itemised Sales Register",
|
||||
"owner": "Administrator",
|
||||
"ref_doctype": "Sales Invoice",
|
||||
"report_name": "GST Itemised Sales Register",
|
||||
"report_type": "Script Report",
|
||||
"roles": []
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
|
||||
from erpnext.accounts.report.item_wise_sales_register.item_wise_sales_register import _execute
|
||||
|
||||
|
||||
def execute(filters=None):
|
||||
return _execute(
|
||||
filters,
|
||||
additional_table_columns=[
|
||||
dict(fieldtype="Data", label="Customer GSTIN", fieldname="customer_gstin", width=120),
|
||||
dict(
|
||||
fieldtype="Data", label="Billing Address GSTIN", fieldname="billing_address_gstin", width=140
|
||||
),
|
||||
dict(fieldtype="Data", label="Company GSTIN", fieldname="company_gstin", width=120),
|
||||
dict(fieldtype="Data", label="Place of Supply", fieldname="place_of_supply", width=120),
|
||||
dict(fieldtype="Data", label="Reverse Charge", fieldname="reverse_charge", width=120),
|
||||
dict(fieldtype="Data", label="GST Category", fieldname="gst_category", width=120),
|
||||
dict(fieldtype="Data", label="Export Type", fieldname="export_type", width=120),
|
||||
dict(fieldtype="Data", label="E-Commerce GSTIN", fieldname="ecommerce_gstin", width=130),
|
||||
dict(fieldtype="Data", label="HSN Code", fieldname="gst_hsn_code", width=120),
|
||||
],
|
||||
additional_query_columns=[
|
||||
"customer_gstin",
|
||||
"billing_address_gstin",
|
||||
"company_gstin",
|
||||
"place_of_supply",
|
||||
"reverse_charge",
|
||||
"gst_category",
|
||||
"export_type",
|
||||
"ecommerce_gstin",
|
||||
"gst_hsn_code",
|
||||
],
|
||||
)
|
||||
@@ -1,7 +0,0 @@
|
||||
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
/* eslint-disable */
|
||||
|
||||
{% include "erpnext/accounts/report/purchase_register/purchase_register.js" %}
|
||||
|
||||
frappe.query_reports["GST Purchase Register"] = frappe.query_reports["Purchase Register"]
|
||||
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"add_total_row": 1,
|
||||
"apply_user_permissions": 1,
|
||||
"creation": "2017-06-22 11:06:40.836073",
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"idx": 0,
|
||||
"is_standard": "Yes",
|
||||
"modified": "2017-08-04 15:39:53.123814",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Regional",
|
||||
"name": "GST Purchase Register",
|
||||
"owner": "Administrator",
|
||||
"ref_doctype": "Purchase Invoice",
|
||||
"report_name": "GST Purchase Register",
|
||||
"report_type": "Script Report",
|
||||
"roles": []
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
|
||||
from erpnext.accounts.report.purchase_register.purchase_register import _execute
|
||||
|
||||
|
||||
def execute(filters=None):
|
||||
return _execute(
|
||||
filters,
|
||||
additional_table_columns=[
|
||||
dict(fieldtype="Data", label="Supplier GSTIN", fieldname="supplier_gstin", width=120),
|
||||
dict(fieldtype="Data", label="Company GSTIN", fieldname="company_gstin", width=120),
|
||||
dict(fieldtype="Data", label="Reverse Charge", fieldname="reverse_charge", width=120),
|
||||
dict(fieldtype="Data", label="GST Category", fieldname="gst_category", width=120),
|
||||
dict(fieldtype="Data", label="Export Type", fieldname="export_type", width=120),
|
||||
dict(fieldtype="Data", label="E-Commerce GSTIN", fieldname="ecommerce_gstin", width=130),
|
||||
],
|
||||
additional_query_columns=[
|
||||
"supplier_gstin",
|
||||
"company_gstin",
|
||||
"reverse_charge",
|
||||
"gst_category",
|
||||
"export_type",
|
||||
"ecommerce_gstin",
|
||||
],
|
||||
)
|
||||
@@ -1,7 +0,0 @@
|
||||
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
/* eslint-disable */
|
||||
|
||||
{% include "erpnext/accounts/report/sales_register/sales_register.js" %}
|
||||
|
||||
frappe.query_reports["GST Sales Register"] = frappe.query_reports["Sales Register"]
|
||||
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"add_total_row": 1,
|
||||
"apply_user_permissions": 1,
|
||||
"creation": "2017-06-21 16:44:41.621260",
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"idx": 0,
|
||||
"is_standard": "Yes",
|
||||
"modified": "2017-08-04 15:39:33.401936",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Regional",
|
||||
"name": "GST Sales Register",
|
||||
"owner": "Administrator",
|
||||
"ref_doctype": "Sales Invoice",
|
||||
"report_name": "GST Sales Register",
|
||||
"report_type": "Script Report",
|
||||
"roles": []
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
|
||||
from erpnext.accounts.report.sales_register.sales_register import _execute
|
||||
|
||||
|
||||
def execute(filters=None):
|
||||
return _execute(
|
||||
filters,
|
||||
additional_table_columns=[
|
||||
dict(fieldtype="Data", label="Customer GSTIN", fieldname="customer_gstin", width=120),
|
||||
dict(
|
||||
fieldtype="Data", label="Billing Address GSTIN", fieldname="billing_address_gstin", width=140
|
||||
),
|
||||
dict(fieldtype="Data", label="Company GSTIN", fieldname="company_gstin", width=120),
|
||||
dict(fieldtype="Data", label="Place of Supply", fieldname="place_of_supply", width=120),
|
||||
dict(fieldtype="Data", label="Reverse Charge", fieldname="reverse_charge", width=120),
|
||||
dict(fieldtype="Data", label="GST Category", fieldname="gst_category", width=120),
|
||||
dict(fieldtype="Data", label="Export Type", fieldname="export_type", width=120),
|
||||
dict(fieldtype="Data", label="E-Commerce GSTIN", fieldname="ecommerce_gstin", width=130),
|
||||
],
|
||||
additional_query_columns=[
|
||||
"customer_gstin",
|
||||
"billing_address_gstin",
|
||||
"company_gstin",
|
||||
"place_of_supply",
|
||||
"reverse_charge",
|
||||
"gst_category",
|
||||
"export_type",
|
||||
"ecommerce_gstin",
|
||||
],
|
||||
)
|
||||
@@ -1,105 +0,0 @@
|
||||
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
// License: GNU General Public License v3. See license.txt
|
||||
|
||||
frappe.query_reports["GSTR-1"] = {
|
||||
"filters": [
|
||||
{
|
||||
"fieldname": "company",
|
||||
"label": __("Company"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Company",
|
||||
"reqd": 1,
|
||||
"default": frappe.defaults.get_user_default("Company")
|
||||
},
|
||||
{
|
||||
"fieldname": "company_address",
|
||||
"label": __("Address"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Address",
|
||||
"get_query": function () {
|
||||
let company = frappe.query_report.get_filter_value('company');
|
||||
if (company) {
|
||||
return {
|
||||
"query": 'frappe.contacts.doctype.address.address.address_query',
|
||||
"filters": { link_doctype: 'Company', link_name: company }
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"fieldname": "company_gstin",
|
||||
"label": __("Company GSTIN"),
|
||||
"fieldtype": "Select"
|
||||
},
|
||||
{
|
||||
"fieldname": "from_date",
|
||||
"label": __("From Date"),
|
||||
"fieldtype": "Date",
|
||||
"reqd": 1,
|
||||
"default": frappe.datetime.add_months(frappe.datetime.get_today(), -3),
|
||||
"width": "80"
|
||||
},
|
||||
{
|
||||
"fieldname": "to_date",
|
||||
"label": __("To Date"),
|
||||
"fieldtype": "Date",
|
||||
"reqd": 1,
|
||||
"default": frappe.datetime.get_today()
|
||||
},
|
||||
{
|
||||
"fieldname": "type_of_business",
|
||||
"label": __("Type of Business"),
|
||||
"fieldtype": "Select",
|
||||
"reqd": 1,
|
||||
"options": [
|
||||
{ "value": "B2B", "label": __("B2B Invoices - 4A, 4B, 4C, 6B, 6C") },
|
||||
{ "value": "B2C Large", "label": __("B2C(Large) Invoices - 5A, 5B") },
|
||||
{ "value": "B2C Small", "label": __("B2C(Small) Invoices - 7") },
|
||||
{ "value": "CDNR-REG", "label": __("Credit/Debit Notes (Registered) - 9B") },
|
||||
{ "value": "CDNR-UNREG", "label": __("Credit/Debit Notes (Unregistered) - 9B") },
|
||||
{ "value": "EXPORT", "label": __("Export Invoice - 6A") },
|
||||
{ "value": "Advances", "label": __("Tax Liability (Advances Received) - 11A(1), 11A(2)") },
|
||||
{ "value": "NIL Rated", "label": __("NIL RATED/EXEMPTED Invoices") }
|
||||
],
|
||||
"default": "B2B"
|
||||
}
|
||||
],
|
||||
onload: function (report) {
|
||||
let filters = report.get_values();
|
||||
|
||||
frappe.call({
|
||||
method: 'erpnext.regional.report.gstr_1.gstr_1.get_company_gstins',
|
||||
args: {
|
||||
company: filters.company
|
||||
},
|
||||
callback: function(r) {
|
||||
frappe.query_report.page.fields_dict.company_gstin.df.options = r.message;
|
||||
frappe.query_report.page.fields_dict.company_gstin.refresh();
|
||||
}
|
||||
});
|
||||
|
||||
report.page.add_inner_button(__("Download as JSON"), function () {
|
||||
let filters = report.get_values();
|
||||
|
||||
frappe.call({
|
||||
method: 'erpnext.regional.report.gstr_1.gstr_1.get_json',
|
||||
args: {
|
||||
data: report.data,
|
||||
report_name: report.report_name,
|
||||
filters: filters
|
||||
},
|
||||
callback: function(r) {
|
||||
if (r.message) {
|
||||
const args = {
|
||||
cmd: 'erpnext.regional.report.gstr_1.gstr_1.download_json_file',
|
||||
data: r.message.data,
|
||||
report_name: r.message.report_name,
|
||||
report_type: r.message.report_type
|
||||
};
|
||||
open_url_post(frappe.request.url, args);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
{
|
||||
"add_total_row": 1,
|
||||
"creation": "2018-01-02 15:54:41.424225",
|
||||
"disable_prepared_report": 0,
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"idx": 0,
|
||||
"is_standard": "Yes",
|
||||
"modified": "2019-09-03 19:33:59.769385",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Regional",
|
||||
"name": "GSTR-1",
|
||||
"owner": "Administrator",
|
||||
"prepared_report": 0,
|
||||
"ref_doctype": "GL Entry",
|
||||
"report_name": "GSTR-1",
|
||||
"report_type": "Script Report",
|
||||
"roles": []
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,39 +0,0 @@
|
||||
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
/* eslint-disable */
|
||||
|
||||
frappe.query_reports["GSTR-2"] = {
|
||||
"filters": [
|
||||
{
|
||||
"fieldname":"company",
|
||||
"label": __("Company"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Company",
|
||||
"reqd": 1,
|
||||
"default": frappe.defaults.get_user_default("Company")
|
||||
},
|
||||
{
|
||||
"fieldname":"from_date",
|
||||
"label": __("From Date"),
|
||||
"fieldtype": "Date",
|
||||
"reqd": 1,
|
||||
"default": frappe.datetime.add_months(frappe.datetime.get_today(), -3),
|
||||
"width": "80"
|
||||
},
|
||||
{
|
||||
"fieldname":"to_date",
|
||||
"label": __("To Date"),
|
||||
"fieldtype": "Date",
|
||||
"reqd": 1,
|
||||
"default": frappe.datetime.get_today()
|
||||
},
|
||||
{
|
||||
"fieldname":"type_of_business",
|
||||
"label": __("Type of Business"),
|
||||
"fieldtype": "Select",
|
||||
"reqd": 1,
|
||||
"options": ["B2B","CDNR"],
|
||||
"default": "B2B"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"add_total_row": 0,
|
||||
"apply_user_permissions": 1,
|
||||
"creation": "2018-01-29 12:59:55.650445",
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"idx": 0,
|
||||
"is_standard": "Yes",
|
||||
"modified": "2018-09-03 12:59:55.650445",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Regional",
|
||||
"name": "GSTR-2",
|
||||
"owner": "Administrator",
|
||||
"ref_doctype": "GL Entry",
|
||||
"report_name": "GSTR-2",
|
||||
"report_type": "Script Report",
|
||||
"roles": []
|
||||
}
|
||||
@@ -1,240 +0,0 @@
|
||||
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
|
||||
from datetime import date
|
||||
|
||||
import frappe
|
||||
|
||||
from erpnext.regional.report.gstr_1.gstr_1 import Gstr1Report
|
||||
|
||||
|
||||
def execute(filters=None):
|
||||
return Gstr2Report(filters).run()
|
||||
|
||||
|
||||
class Gstr2Report(Gstr1Report):
|
||||
def __init__(self, filters=None):
|
||||
self.filters = frappe._dict(filters or {})
|
||||
self.columns = []
|
||||
self.data = []
|
||||
self.doctype = "Purchase Invoice"
|
||||
self.tax_doctype = "Purchase Taxes and Charges"
|
||||
self.select_columns = """
|
||||
name as invoice_number,
|
||||
supplier_name,
|
||||
posting_date,
|
||||
base_grand_total,
|
||||
base_rounded_total,
|
||||
supplier_gstin,
|
||||
place_of_supply,
|
||||
ecommerce_gstin,
|
||||
reverse_charge,
|
||||
gst_category,
|
||||
return_against,
|
||||
is_return,
|
||||
gst_category,
|
||||
export_type,
|
||||
reason_for_issuing_document,
|
||||
eligibility_for_itc,
|
||||
itc_integrated_tax,
|
||||
itc_central_tax,
|
||||
itc_state_tax,
|
||||
itc_cess_amount
|
||||
"""
|
||||
|
||||
def get_data(self):
|
||||
self.get_igst_invoices()
|
||||
for inv, items_based_on_rate in self.items_based_on_tax_rate.items():
|
||||
invoice_details = self.invoices.get(inv)
|
||||
for rate, items in items_based_on_rate.items():
|
||||
if rate or invoice_details.get("gst_category") == "Registered Composition":
|
||||
if inv not in self.igst_invoices:
|
||||
rate = rate / 2
|
||||
row, taxable_value = self.get_row_data_for_invoice(inv, invoice_details, rate, items)
|
||||
tax_amount = taxable_value * rate / 100
|
||||
row += [0, tax_amount, tax_amount]
|
||||
else:
|
||||
row, taxable_value = self.get_row_data_for_invoice(inv, invoice_details, rate, items)
|
||||
tax_amount = taxable_value * rate / 100
|
||||
row += [tax_amount, 0, 0]
|
||||
|
||||
row += [
|
||||
self.invoice_cess.get(inv),
|
||||
invoice_details.get("eligibility_for_itc"),
|
||||
invoice_details.get("itc_integrated_tax"),
|
||||
invoice_details.get("itc_central_tax"),
|
||||
invoice_details.get("itc_state_tax"),
|
||||
invoice_details.get("itc_cess_amount"),
|
||||
]
|
||||
if self.filters.get("type_of_business") == "CDNR":
|
||||
row.append("Y" if invoice_details.posting_date <= date(2017, 7, 1) else "N")
|
||||
row.append("C" if invoice_details.return_against else "R")
|
||||
|
||||
self.data.append(row)
|
||||
|
||||
def get_igst_invoices(self):
|
||||
self.igst_invoices = []
|
||||
for d in self.tax_details:
|
||||
is_igst = True if d[1] in self.gst_accounts.igst_account else False
|
||||
if is_igst and d[0] not in self.igst_invoices:
|
||||
self.igst_invoices.append(d[0])
|
||||
|
||||
def get_conditions(self):
|
||||
conditions = ""
|
||||
|
||||
for opts in (
|
||||
("company", " and company=%(company)s"),
|
||||
("from_date", " and posting_date>=%(from_date)s"),
|
||||
("to_date", " and posting_date<=%(to_date)s"),
|
||||
):
|
||||
if self.filters.get(opts[0]):
|
||||
conditions += opts[1]
|
||||
|
||||
if self.filters.get("type_of_business") == "B2B":
|
||||
conditions += "and ifnull(gst_category, '') in ('Registered Regular', 'Deemed Export', 'SEZ', 'Registered Composition') and is_return != 1 "
|
||||
|
||||
elif self.filters.get("type_of_business") == "CDNR":
|
||||
conditions += """ and is_return = 1 """
|
||||
|
||||
return conditions
|
||||
|
||||
def get_columns(self):
|
||||
self.tax_columns = [
|
||||
{"fieldname": "rate", "label": "Rate", "fieldtype": "Int", "width": 60},
|
||||
{"fieldname": "taxable_value", "label": "Taxable Value", "fieldtype": "Currency", "width": 100},
|
||||
{
|
||||
"fieldname": "integrated_tax_paid",
|
||||
"label": "Integrated Tax Paid",
|
||||
"fieldtype": "Currency",
|
||||
"width": 100,
|
||||
},
|
||||
{
|
||||
"fieldname": "central_tax_paid",
|
||||
"label": "Central Tax Paid",
|
||||
"fieldtype": "Currency",
|
||||
"width": 100,
|
||||
},
|
||||
{
|
||||
"fieldname": "state_tax_paid",
|
||||
"label": "State/UT Tax Paid",
|
||||
"fieldtype": "Currency",
|
||||
"width": 100,
|
||||
},
|
||||
{"fieldname": "cess_amount", "label": "Cess Paid", "fieldtype": "Currency", "width": 100},
|
||||
{
|
||||
"fieldname": "eligibility_for_itc",
|
||||
"label": "Eligibility For ITC",
|
||||
"fieldtype": "Data",
|
||||
"width": 100,
|
||||
},
|
||||
{
|
||||
"fieldname": "itc_integrated_tax",
|
||||
"label": "Availed ITC Integrated Tax",
|
||||
"fieldtype": "Currency",
|
||||
"width": 100,
|
||||
},
|
||||
{
|
||||
"fieldname": "itc_central_tax",
|
||||
"label": "Availed ITC Central Tax",
|
||||
"fieldtype": "Currency",
|
||||
"width": 100,
|
||||
},
|
||||
{
|
||||
"fieldname": "itc_state_tax",
|
||||
"label": "Availed ITC State/UT Tax",
|
||||
"fieldtype": "Currency",
|
||||
"width": 100,
|
||||
},
|
||||
{
|
||||
"fieldname": "itc_cess_amount",
|
||||
"label": "Availed ITC Cess ",
|
||||
"fieldtype": "Currency",
|
||||
"width": 100,
|
||||
},
|
||||
]
|
||||
self.other_columns = []
|
||||
|
||||
if self.filters.get("type_of_business") == "B2B":
|
||||
self.invoice_columns = [
|
||||
{
|
||||
"fieldname": "supplier_gstin",
|
||||
"label": "GSTIN of Supplier",
|
||||
"fieldtype": "Data",
|
||||
"width": 120,
|
||||
},
|
||||
{
|
||||
"fieldname": "invoice_number",
|
||||
"label": "Invoice Number",
|
||||
"fieldtype": "Link",
|
||||
"options": "Purchase Invoice",
|
||||
"width": 120,
|
||||
},
|
||||
{"fieldname": "posting_date", "label": "Invoice date", "fieldtype": "Date", "width": 120},
|
||||
{
|
||||
"fieldname": "invoice_value",
|
||||
"label": "Invoice Value",
|
||||
"fieldtype": "Currency",
|
||||
"width": 120,
|
||||
},
|
||||
{
|
||||
"fieldname": "place_of_supply",
|
||||
"label": "Place of Supply",
|
||||
"fieldtype": "Data",
|
||||
"width": 120,
|
||||
},
|
||||
{"fieldname": "reverse_charge", "label": "Reverse Charge", "fieldtype": "Data", "width": 80},
|
||||
{"fieldname": "gst_category", "label": "Invoice Type", "fieldtype": "Data", "width": 80},
|
||||
]
|
||||
elif self.filters.get("type_of_business") == "CDNR":
|
||||
self.invoice_columns = [
|
||||
{
|
||||
"fieldname": "supplier_gstin",
|
||||
"label": "GSTIN of Supplier",
|
||||
"fieldtype": "Data",
|
||||
"width": 120,
|
||||
},
|
||||
{
|
||||
"fieldname": "invoice_number",
|
||||
"label": "Note/Refund Voucher Number",
|
||||
"fieldtype": "Link",
|
||||
"options": "Purchase Invoice",
|
||||
},
|
||||
{
|
||||
"fieldname": "posting_date",
|
||||
"label": "Note/Refund Voucher date",
|
||||
"fieldtype": "Date",
|
||||
"width": 120,
|
||||
},
|
||||
{
|
||||
"fieldname": "return_against",
|
||||
"label": "Invoice/Advance Payment Voucher Number",
|
||||
"fieldtype": "Link",
|
||||
"options": "Purchase Invoice",
|
||||
"width": 120,
|
||||
},
|
||||
{
|
||||
"fieldname": "posting_date",
|
||||
"label": "Invoice/Advance Payment Voucher date",
|
||||
"fieldtype": "Date",
|
||||
"width": 120,
|
||||
},
|
||||
{
|
||||
"fieldname": "reason_for_issuing_document",
|
||||
"label": "Reason For Issuing document",
|
||||
"fieldtype": "Data",
|
||||
"width": 120,
|
||||
},
|
||||
{"fieldname": "supply_type", "label": "Supply Type", "fieldtype": "Data", "width": 120},
|
||||
{
|
||||
"fieldname": "invoice_value",
|
||||
"label": "Invoice Value",
|
||||
"fieldtype": "Currency",
|
||||
"width": 120,
|
||||
},
|
||||
]
|
||||
self.other_columns = [
|
||||
{"fieldname": "pre_gst", "label": "PRE GST", "fieldtype": "Data", "width": 50},
|
||||
{"fieldname": "document_type", "label": "Document Type", "fieldtype": "Data", "width": 50},
|
||||
]
|
||||
self.columns = self.invoice_columns + self.tax_columns + self.other_columns
|
||||
@@ -1,73 +0,0 @@
|
||||
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
/* eslint-disable */
|
||||
|
||||
{% include "erpnext/regional/report/india_gst_common/india_gst_common.js" %}
|
||||
|
||||
frappe.query_reports["HSN-wise-summary of outward supplies"] = {
|
||||
"filters": [
|
||||
{
|
||||
"fieldname":"company",
|
||||
"label": __("Company"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Company",
|
||||
"reqd": 1,
|
||||
"default": frappe.defaults.get_user_default("Company"),
|
||||
"on_change": fetch_gstins
|
||||
},
|
||||
{
|
||||
"fieldname":"gst_hsn_code",
|
||||
"label": __("HSN/SAC"),
|
||||
"fieldtype": "Link",
|
||||
"options": "GST HSN Code",
|
||||
"width": "80"
|
||||
},
|
||||
{
|
||||
"fieldname":"company_gstin",
|
||||
"label": __("Company GSTIN"),
|
||||
"fieldtype": "Select",
|
||||
"placeholder":"Company GSTIN",
|
||||
"options": [""],
|
||||
"width": "80"
|
||||
},
|
||||
{
|
||||
"fieldname":"from_date",
|
||||
"label": __("From Date"),
|
||||
"fieldtype": "Date",
|
||||
"width": "80"
|
||||
},
|
||||
{
|
||||
"fieldname":"to_date",
|
||||
"label": __("To Date"),
|
||||
"fieldtype": "Date",
|
||||
"width": "80"
|
||||
},
|
||||
|
||||
],
|
||||
onload: (report) => {
|
||||
fetch_gstins(report);
|
||||
|
||||
report.page.add_inner_button(__("Download JSON"), function () {
|
||||
var filters = report.get_values();
|
||||
|
||||
frappe.call({
|
||||
method: 'erpnext.regional.report.hsn_wise_summary_of_outward_supplies.hsn_wise_summary_of_outward_supplies.get_json',
|
||||
args: {
|
||||
data: report.data,
|
||||
report_name: report.report_name,
|
||||
filters: filters
|
||||
},
|
||||
callback: function(r) {
|
||||
if (r.message) {
|
||||
const args = {
|
||||
cmd: 'erpnext.regional.report.hsn_wise_summary_of_outward_supplies.hsn_wise_summary_of_outward_supplies.download_json_file',
|
||||
data: r.message.data,
|
||||
report_name: r.message.report_name
|
||||
};
|
||||
open_url_post(frappe.request.url, args);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"add_total_row": 0,
|
||||
"creation": "2018-04-26 10:49:29.159400",
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"idx": 0,
|
||||
"is_standard": "Yes",
|
||||
"modified": "2019-09-03 12:59:38.603649",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Regional",
|
||||
"name": "HSN-wise-summary of outward supplies",
|
||||
"owner": "Administrator",
|
||||
"ref_doctype": "Sales Invoice",
|
||||
"report_name": "HSN-wise-summary of outward supplies",
|
||||
"report_type": "Script Report",
|
||||
"roles": []
|
||||
}
|
||||
@@ -1,306 +0,0 @@
|
||||
# Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
|
||||
import json
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.model.meta import get_field_precision
|
||||
from frappe.utils import cstr, flt, getdate
|
||||
|
||||
import erpnext
|
||||
from erpnext.regional.india.utils import get_gst_accounts
|
||||
from erpnext.regional.report.gstr_1.gstr_1 import get_company_gstin_number
|
||||
|
||||
|
||||
def execute(filters=None):
|
||||
return _execute(filters)
|
||||
|
||||
|
||||
def _execute(filters=None):
|
||||
if not filters:
|
||||
filters = {}
|
||||
columns = get_columns()
|
||||
|
||||
company_currency = erpnext.get_company_currency(filters.company)
|
||||
item_list = get_items(filters)
|
||||
if item_list:
|
||||
itemised_tax, tax_columns = get_tax_accounts(item_list, columns, company_currency)
|
||||
|
||||
data = []
|
||||
added_item = []
|
||||
for d in item_list:
|
||||
if (d.parent, d.item_code) not in added_item:
|
||||
row = [d.gst_hsn_code, d.description, d.stock_uom, d.stock_qty, d.tax_rate]
|
||||
total_tax = 0
|
||||
for tax in tax_columns:
|
||||
item_tax = itemised_tax.get((d.parent, d.item_code), {}).get(tax, {})
|
||||
total_tax += flt(item_tax.get("tax_amount", 0))
|
||||
|
||||
row += [d.base_net_amount + total_tax]
|
||||
row += [d.base_net_amount]
|
||||
for tax in tax_columns:
|
||||
item_tax = itemised_tax.get((d.parent, d.item_code), {}).get(tax, {})
|
||||
row += [item_tax.get("tax_amount", 0)]
|
||||
data.append(row)
|
||||
added_item.append((d.parent, d.item_code))
|
||||
if data:
|
||||
data = get_merged_data(columns, data) # merge same hsn code data
|
||||
return columns, data
|
||||
|
||||
|
||||
def get_columns():
|
||||
columns = [
|
||||
{
|
||||
"fieldname": "gst_hsn_code",
|
||||
"label": _("HSN/SAC"),
|
||||
"fieldtype": "Link",
|
||||
"options": "GST HSN Code",
|
||||
"width": 100,
|
||||
},
|
||||
{"fieldname": "description", "label": _("Description"), "fieldtype": "Data", "width": 300},
|
||||
{"fieldname": "stock_uom", "label": _("Stock UOM"), "fieldtype": "Data", "width": 100},
|
||||
{"fieldname": "stock_qty", "label": _("Stock Qty"), "fieldtype": "Float", "width": 90},
|
||||
{"fieldname": "tax_rate", "label": _("Tax Rate"), "fieldtype": "Data", "width": 90},
|
||||
{"fieldname": "total_amount", "label": _("Total Amount"), "fieldtype": "Currency", "width": 120},
|
||||
{
|
||||
"fieldname": "taxable_amount",
|
||||
"label": _("Total Taxable Amount"),
|
||||
"fieldtype": "Currency",
|
||||
"width": 170,
|
||||
},
|
||||
]
|
||||
|
||||
return columns
|
||||
|
||||
|
||||
def get_conditions(filters):
|
||||
conditions = ""
|
||||
|
||||
for opts in (
|
||||
("company", " and company=%(company)s"),
|
||||
("gst_hsn_code", " and gst_hsn_code=%(gst_hsn_code)s"),
|
||||
("company_gstin", " and company_gstin=%(company_gstin)s"),
|
||||
("from_date", " and posting_date >= %(from_date)s"),
|
||||
("to_date", " and posting_date <= %(to_date)s"),
|
||||
):
|
||||
if filters.get(opts[0]):
|
||||
conditions += opts[1]
|
||||
|
||||
return conditions
|
||||
|
||||
|
||||
def get_items(filters):
|
||||
conditions = get_conditions(filters)
|
||||
match_conditions = frappe.build_match_conditions("Sales Invoice")
|
||||
if match_conditions:
|
||||
match_conditions = " and {0} ".format(match_conditions)
|
||||
|
||||
items = frappe.db.sql(
|
||||
"""
|
||||
select
|
||||
`tabSales Invoice Item`.gst_hsn_code,
|
||||
`tabSales Invoice Item`.stock_uom,
|
||||
sum(`tabSales Invoice Item`.stock_qty) as stock_qty,
|
||||
sum(`tabSales Invoice Item`.base_net_amount) as base_net_amount,
|
||||
sum(`tabSales Invoice Item`.base_price_list_rate) as base_price_list_rate,
|
||||
`tabSales Invoice Item`.parent,
|
||||
`tabSales Invoice Item`.item_code,
|
||||
`tabGST HSN Code`.description,
|
||||
json_extract(`tabSales Taxes and Charges`.item_wise_tax_detail,
|
||||
concat('$."' , `tabSales Invoice Item`.item_code, '"[0]')) * count(distinct `tabSales Taxes and Charges`.name) as tax_rate
|
||||
from
|
||||
`tabSales Invoice`,
|
||||
`tabSales Invoice Item`,
|
||||
`tabGST HSN Code`,
|
||||
`tabSales Taxes and Charges`
|
||||
where
|
||||
`tabSales Invoice`.name = `tabSales Invoice Item`.parent
|
||||
and `tabSales Taxes and Charges`.parent = `tabSales Invoice`.name
|
||||
and `tabSales Invoice`.docstatus = 1
|
||||
and `tabSales Invoice Item`.gst_hsn_code is not NULL
|
||||
and `tabSales Invoice Item`.gst_hsn_code = `tabGST HSN Code`.name %s %s
|
||||
group by
|
||||
`tabSales Invoice Item`.parent,
|
||||
`tabSales Invoice Item`.item_code
|
||||
"""
|
||||
% (conditions, match_conditions),
|
||||
filters,
|
||||
as_dict=1,
|
||||
)
|
||||
|
||||
return items
|
||||
|
||||
|
||||
def get_tax_accounts(
|
||||
item_list,
|
||||
columns,
|
||||
company_currency,
|
||||
doctype="Sales Invoice",
|
||||
tax_doctype="Sales Taxes and Charges",
|
||||
):
|
||||
item_row_map = {}
|
||||
tax_columns = []
|
||||
invoice_item_row = {}
|
||||
itemised_tax = {}
|
||||
conditions = ""
|
||||
|
||||
tax_amount_precision = (
|
||||
get_field_precision(
|
||||
frappe.get_meta(tax_doctype).get_field("tax_amount"), currency=company_currency
|
||||
)
|
||||
or 2
|
||||
)
|
||||
|
||||
for d in item_list:
|
||||
invoice_item_row.setdefault(d.parent, []).append(d)
|
||||
item_row_map.setdefault(d.parent, {}).setdefault(d.item_code or d.item_name, []).append(d)
|
||||
|
||||
tax_details = frappe.db.sql(
|
||||
"""
|
||||
select
|
||||
parent, account_head, item_wise_tax_detail,
|
||||
base_tax_amount_after_discount_amount
|
||||
from `tab%s`
|
||||
where
|
||||
parenttype = %s and docstatus = 1
|
||||
and (description is not null and description != '')
|
||||
and parent in (%s)
|
||||
%s
|
||||
order by description
|
||||
"""
|
||||
% (tax_doctype, "%s", ", ".join(["%s"] * len(invoice_item_row)), conditions),
|
||||
tuple([doctype] + list(invoice_item_row)),
|
||||
)
|
||||
|
||||
for parent, account_head, item_wise_tax_detail, tax_amount in tax_details:
|
||||
|
||||
if account_head not in tax_columns and tax_amount:
|
||||
# as description is text editor earlier and markup can break the column convention in reports
|
||||
tax_columns.append(account_head)
|
||||
|
||||
if item_wise_tax_detail:
|
||||
try:
|
||||
item_wise_tax_detail = json.loads(item_wise_tax_detail)
|
||||
|
||||
for item_code, tax_data in item_wise_tax_detail.items():
|
||||
if not frappe.db.get_value("Item", item_code, "gst_hsn_code"):
|
||||
continue
|
||||
itemised_tax.setdefault(item_code, frappe._dict())
|
||||
if isinstance(tax_data, list):
|
||||
tax_amount = tax_data[1]
|
||||
else:
|
||||
tax_amount = 0
|
||||
|
||||
for d in item_row_map.get(parent, {}).get(item_code, []):
|
||||
item_tax_amount = tax_amount
|
||||
if item_tax_amount:
|
||||
itemised_tax.setdefault((parent, item_code), {})[account_head] = frappe._dict(
|
||||
{"tax_amount": flt(item_tax_amount, tax_amount_precision)}
|
||||
)
|
||||
except ValueError:
|
||||
continue
|
||||
|
||||
tax_columns.sort()
|
||||
for account_head in tax_columns:
|
||||
columns.append(
|
||||
{
|
||||
"label": account_head,
|
||||
"fieldname": frappe.scrub(account_head),
|
||||
"fieldtype": "Float",
|
||||
"width": 110,
|
||||
}
|
||||
)
|
||||
|
||||
return itemised_tax, tax_columns
|
||||
|
||||
|
||||
def get_merged_data(columns, data):
|
||||
merged_hsn_dict = {} # to group same hsn under one key and perform row addition
|
||||
result = []
|
||||
|
||||
for row in data:
|
||||
key = row[0] + "-" + row[2] + "-" + str(row[4])
|
||||
merged_hsn_dict.setdefault(key, {})
|
||||
for i, d in enumerate(columns):
|
||||
if d["fieldtype"] not in ("Int", "Float", "Currency"):
|
||||
merged_hsn_dict[key][d["fieldname"]] = row[i]
|
||||
else:
|
||||
if merged_hsn_dict.get(key, {}).get(d["fieldname"], ""):
|
||||
merged_hsn_dict[key][d["fieldname"]] += row[i]
|
||||
else:
|
||||
merged_hsn_dict[key][d["fieldname"]] = row[i]
|
||||
|
||||
for key, value in merged_hsn_dict.items():
|
||||
result.append(value)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_json(filters, report_name, data):
|
||||
filters = json.loads(filters)
|
||||
report_data = json.loads(data)
|
||||
gstin = filters.get("company_gstin") or get_company_gstin_number(filters["company"])
|
||||
|
||||
if not filters.get("from_date") or not filters.get("to_date"):
|
||||
frappe.throw(_("Please enter From Date and To Date to generate JSON"))
|
||||
|
||||
fp = "%02d%s" % (getdate(filters["to_date"]).month, getdate(filters["to_date"]).year)
|
||||
|
||||
gst_json = {"version": "GST3.0.3", "hash": "hash", "gstin": gstin, "fp": fp}
|
||||
|
||||
gst_json["hsn"] = {"data": get_hsn_wise_json_data(filters, report_data)}
|
||||
|
||||
return {"report_name": report_name, "data": gst_json}
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def download_json_file():
|
||||
"""download json content in a file"""
|
||||
data = frappe._dict(frappe.local.form_dict)
|
||||
frappe.response["filename"] = frappe.scrub("{0}".format(data["report_name"])) + ".json"
|
||||
frappe.response["filecontent"] = data["data"]
|
||||
frappe.response["content_type"] = "application/json"
|
||||
frappe.response["type"] = "download"
|
||||
|
||||
|
||||
def get_hsn_wise_json_data(filters, report_data):
|
||||
|
||||
filters = frappe._dict(filters)
|
||||
gst_accounts = get_gst_accounts(filters.company)
|
||||
data = []
|
||||
count = 1
|
||||
|
||||
for hsn in report_data:
|
||||
row = {
|
||||
"num": count,
|
||||
"hsn_sc": hsn.get("gst_hsn_code"),
|
||||
"desc": hsn.get("description"),
|
||||
"uqc": hsn.get("stock_uom").upper(),
|
||||
"qty": hsn.get("stock_qty"),
|
||||
"rt": flt(hsn.get("tax_rate"), 2),
|
||||
"txval": flt(hsn.get("taxable_amount", 2)),
|
||||
"iamt": 0.0,
|
||||
"camt": 0.0,
|
||||
"samt": 0.0,
|
||||
"csamt": 0.0,
|
||||
}
|
||||
|
||||
for account in gst_accounts.get("igst_account"):
|
||||
row["iamt"] += flt(hsn.get(frappe.scrub(cstr(account)), 0.0), 2)
|
||||
|
||||
for account in gst_accounts.get("cgst_account"):
|
||||
row["camt"] += flt(hsn.get(frappe.scrub(cstr(account)), 0.0), 2)
|
||||
|
||||
for account in gst_accounts.get("sgst_account"):
|
||||
row["samt"] += flt(hsn.get(frappe.scrub(cstr(account)), 0.0), 2)
|
||||
|
||||
for account in gst_accounts.get("cess_account"):
|
||||
row["csamt"] += flt(hsn.get(frappe.scrub(cstr(account)), 0.0), 2)
|
||||
|
||||
data.append(row)
|
||||
count += 1
|
||||
|
||||
return data
|
||||
@@ -1,102 +0,0 @@
|
||||
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
import frappe
|
||||
|
||||
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
||||
from erpnext.regional.doctype.gstr_3b_report.test_gstr_3b_report import (
|
||||
make_company as setup_company,
|
||||
)
|
||||
from erpnext.regional.doctype.gstr_3b_report.test_gstr_3b_report import (
|
||||
make_customers as setup_customers,
|
||||
)
|
||||
from erpnext.regional.doctype.gstr_3b_report.test_gstr_3b_report import (
|
||||
set_account_heads as setup_gst_settings,
|
||||
)
|
||||
from erpnext.regional.report.hsn_wise_summary_of_outward_supplies.hsn_wise_summary_of_outward_supplies import (
|
||||
execute as run_report,
|
||||
)
|
||||
from erpnext.stock.doctype.item.test_item import make_item
|
||||
|
||||
|
||||
class TestHSNWiseSummaryReport(TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
setup_company()
|
||||
setup_customers()
|
||||
setup_gst_settings()
|
||||
make_item("Golf Car", properties={"gst_hsn_code": "999900"})
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
frappe.db.rollback()
|
||||
|
||||
def test_hsn_summary_for_invoice_with_duplicate_items(self):
|
||||
si = create_sales_invoice(
|
||||
company="_Test Company GST",
|
||||
customer="_Test GST Customer",
|
||||
currency="INR",
|
||||
warehouse="Finished Goods - _GST",
|
||||
debit_to="Debtors - _GST",
|
||||
income_account="Sales - _GST",
|
||||
expense_account="Cost of Goods Sold - _GST",
|
||||
cost_center="Main - _GST",
|
||||
do_not_save=1,
|
||||
)
|
||||
|
||||
si.items = []
|
||||
si.append(
|
||||
"items",
|
||||
{
|
||||
"item_code": "Golf Car",
|
||||
"gst_hsn_code": "999900",
|
||||
"qty": "1",
|
||||
"rate": "120",
|
||||
"cost_center": "Main - _GST",
|
||||
},
|
||||
)
|
||||
si.append(
|
||||
"items",
|
||||
{
|
||||
"item_code": "Golf Car",
|
||||
"gst_hsn_code": "999900",
|
||||
"qty": "1",
|
||||
"rate": "140",
|
||||
"cost_center": "Main - _GST",
|
||||
},
|
||||
)
|
||||
si.append(
|
||||
"taxes",
|
||||
{
|
||||
"charge_type": "On Net Total",
|
||||
"account_head": "Output Tax IGST - _GST",
|
||||
"cost_center": "Main - _GST",
|
||||
"description": "IGST @ 18.0",
|
||||
"rate": 18,
|
||||
},
|
||||
)
|
||||
si.posting_date = "2020-11-17"
|
||||
si.submit()
|
||||
si.reload()
|
||||
|
||||
[columns, data] = run_report(
|
||||
filters=frappe._dict(
|
||||
{
|
||||
"company": "_Test Company GST",
|
||||
"gst_hsn_code": "999900",
|
||||
"company_gstin": si.company_gstin,
|
||||
"from_date": si.posting_date,
|
||||
"to_date": si.posting_date,
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
filtered_rows = list(filter(lambda row: row["gst_hsn_code"] == "999900", data))
|
||||
self.assertTrue(filtered_rows)
|
||||
|
||||
hsn_row = filtered_rows[0]
|
||||
self.assertEquals(hsn_row["stock_qty"], 2.0)
|
||||
self.assertEquals(hsn_row["total_amount"], 306.8)
|
||||
@@ -1,21 +0,0 @@
|
||||
function fetch_gstins(report) {
|
||||
var company_gstins = report.get_filter('company_gstin');
|
||||
var company = report.get_filter_value('company');
|
||||
if (company) {
|
||||
frappe.call({
|
||||
method:'erpnext.regional.india.utils.get_gstins_for_company',
|
||||
async: false,
|
||||
args: {
|
||||
company: company
|
||||
},
|
||||
callback: function(r) {
|
||||
r.message.unshift("");
|
||||
company_gstins.df.options = r.message;
|
||||
company_gstins.refresh();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
company_gstins.df.options = [""];
|
||||
company_gstins.refresh();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user