Merge branch 'develop' into hr-separation

This commit is contained in:
Rucha Mahabal
2022-07-07 13:46:54 +05:30
192 changed files with 517 additions and 73374 deletions

View File

@@ -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) {
// }
});

View File

@@ -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"
}

View File

@@ -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

View File

@@ -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

View File

@@ -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>`])
);
}
});

View File

@@ -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
}

View File

@@ -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."))

View File

@@ -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

View File

@@ -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
}

View File

@@ -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

View File

@@ -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'));
}
}
});
}
);
});
}
}
});

View File

@@ -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
}

View File

@@ -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()

View File

@@ -1,8 +0,0 @@
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
import unittest
class TestGSTHSNCode(unittest.TestCase):
pass

View File

@@ -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
}
};
});
}
});

View File

@@ -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
}

View File

@@ -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

View File

@@ -1,8 +0,0 @@
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
import unittest
class TestGSTSettings(unittest.TestCase):
pass

View File

@@ -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")}}: &nbsp {{ data.gstin }}</h5>
<h5>{{__("Period")}}: &nbsp {{ data.ret_period }}</h5>
</div>
<h5>3.1&nbsp&nbsp{{__("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&nbsp&nbsp{{__("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. &nbsp {{__("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>&nbsp (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>&nbsp (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>&nbsp (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>&nbsp (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>&nbsp (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>&nbsp (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>&nbsp (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>&nbsp (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>&nbsp (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. &nbsp&nbsp {{__("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>

View File

@@ -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
}
};
});
},
});

View File

@@ -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
}

View File

@@ -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"

View File

@@ -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
}
]
}
}

View File

@@ -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()

View File

@@ -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()}

View File

@@ -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}"
}}

View File

@@ -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}"
}}
}}

View File

@@ -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"
]
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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);
}
}
});
}
});
}

View File

@@ -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")

View File

@@ -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()

View File

@@ -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"
}

View File

@@ -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;
}
};

View File

@@ -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"
}
]
}

View File

@@ -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,
},
]

View File

@@ -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")
},
]
}

View File

@@ -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"
}
]
}

View File

@@ -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

View File

@@ -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"]

View File

@@ -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": []
}

View File

@@ -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",
],
)

View File

@@ -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 };

View File

@@ -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": []
}

View File

@@ -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",
],
)

View File

@@ -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"]

View File

@@ -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": []
}

View File

@@ -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",
],
)

View File

@@ -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"]

View File

@@ -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": []
}

View File

@@ -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",
],
)

View File

@@ -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);
}
}
});
});
}
}

View File

@@ -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

View File

@@ -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"
}
]
}

View File

@@ -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": []
}

View File

@@ -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

View File

@@ -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);
}
}
});
});
}
};

View File

@@ -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": []
}

View File

@@ -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

View File

@@ -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)

View File

@@ -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();
}
}