feat: GSTR3B Report JSON creation and Print Format (#16595)

* feat: Created doctype for GSTR3B report and added boilerplate code

* feat: Updated gst_fields and patches for gst_category

* feat: Functions for calculating itc amount

* fix: Patched eligibility_for_itc_field

* fix: Updated set_category for gst

* fix: Function for setting iter_state supplies

* fix: Changed route to regional module, minor fix in inster_state_supply grouping and fixes in print format

* fix(style): Added missing semicolon and removed unused imports

* fix: Patch field only if column is available

* fix: Make custom fields only for india sepecific company

* fix: Add intro to gstr3b report

* fix: Updated patch in patches.txt

* fix: Update patches.txt

* fix: Update patch to set GST Category

* fix: Add fields for nil rated and non gst in item master

* fix: Added logic for nil rated and non gst inward flow

* fix: Initial test case for GSTR3B Report

* fix: Codacy fixes

* fix: Test Case fixes

* fix: Add link for gstr_3b_report in accounting module

* fix: Updated report template

* fix: Changes in GSTR3B Report doctype

* fix: Added function to get missing field invoices

* fix: Added more test cases

* fix: Item not found error in test case

* fix: Key error in state numbers

* fix: Changes in GSTR3b Doctype

* fix: Changed functions to method

* fix: Minor fix in patch

* fix: Add gst_ctegory in GST Reports

* fix: Minor fixes in patch and itc_mapping

* fix: Query to patch itc field

* fix: Patch registered customers and fix for multiple gst accounts

* fix: Test case

* fix: Total taxable calculation logic fix and template enhancement

* fix: Calculate txval seperately

* fix: itc amount calculation fix and patch improvement

* fix: Updated test_cases for itc calculation

* fix: Missing field query

* fix: Multiple minor fixes inreport

* fix: Added transalations in GSTR3B-Form

* fix: Use double underscore for translation

* fix: GST fields ordering fix

* fix: Print form precision fix and get_period function fix
This commit is contained in:
Deepesh Garg 2019-03-21 20:47:47 +05:30 committed by Faris Ansari
parent 3a05b3501c
commit 22b61607c6
16 changed files with 1590 additions and 42 deletions

View File

@ -563,6 +563,10 @@ def get_data():
"name": "GSTR-2",
"is_query_report": True
},
{
"type": "doctype",
"name": "GSTR 3B Report",
},
{
"type": "report",
"name": "GST Sales Register",

View File

@ -583,6 +583,7 @@ erpnext.patches.v11_0.renamed_from_to_fields_in_project
erpnext.patches.v11_0.add_permissions_in_gst_settings
erpnext.patches.v11_1.setup_guardian_role
execute:frappe.delete_doc('DocType', 'Notification Control')
erpnext.patches.v12_0.set_gst_category
erpnext.patches.v11_0.remove_barcodes_field_from_copy_fields_to_variants
erpnext.patches.v12_0.set_task_status
erpnext.patches.v11_0.make_italian_localization_fields # 01-03-2019

View File

@ -0,0 +1,50 @@
import frappe
from erpnext.regional.india.setup import make_custom_fields
def execute():
company = frappe.get_all('Company', filters = {'country': 'India'})
if not company:
return
make_custom_fields()
for doctype in ['Sales Invoice', 'Purchase Invoice']:
has_column = frappe.db.has_column(doctype,'invoice_type')
if has_column:
update_map = {
'Regular': 'Registered Regular',
'Export': 'Overseas',
'SEZ': 'SEZ',
'Deemed Export': 'Deemed Export',
}
for old, new in update_map.items():
frappe.db.sql("UPDATE `tab{doctype}` SET gst_category = %s where invoice_type = %s".format(doctype=doctype), (new, old)) #nosec
frappe.delete_doc('Custom Field', 'Sales Invoice-invoice_type')
frappe.delete_doc('Custom Field', 'Purchase Invoice-invoice_type')
itc_update_map = {
"ineligible": "Ineligible",
"input service": "Input Service Distributor",
"capital goods": "Import Of Capital Goods",
"input": "All Other ITC"
}
has_gst_fields = frappe.db.has_column('Purchase Invoice','eligibility_for_itc')
if has_gst_fields:
for old, new in itc_update_map.items():
frappe.db.sql("UPDATE `tabPurchase Invoice` SET eligibility_for_itc = %s where eligibility_for_itc = %s ", (new, old))
for doctype in ["Customer", "Supplier"]:
frappe.db.sql(""" UPDATE `tab{doctype}` t1, `tabAddress` t2, `tabDynamic Link` t3 SET t1.gst_category = "Registered Regular"
where t3.link_name = t1.name and t3.parent = t2.name and t2.gstin IS NOT NULL and t2.gstin != '' """.format(doctype=doctype)) #nosec
frappe.db.sql(""" UPDATE `tab{doctype}` t1, `tabAddress` t2, `tabDynamic Link` t3 SET t1.gst_category = "Overseas"
where t3.link_name = t1.name and t3.parent = t2.name and t2.country != 'India' """.format(doctype=doctype)) #nosec

View File

@ -0,0 +1,297 @@
<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>{{__("Suppliies made to Composition Taxable 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 UIN holders")}}</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>
</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 op 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></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>&nbsp (2) {{__("Others")}}</td>
<td></td>
<td></td>
<td></td>
<td></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

@ -0,0 +1,59 @@
// 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){
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:[]
});
});
}
});
});
}
},
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

@ -0,0 +1,259 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "format:GSTR3B-{month}-{year}-{company_address}",
"beta": 0,
"creation": "2019-02-04 11:35:55.964639",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "company",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Company",
"length": 0,
"no_copy": 0,
"options": "Company",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "company_address",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Company Address",
"length": 0,
"no_copy": 0,
"options": "Address",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "year",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Year",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "month",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Month",
"length": 0,
"no_copy": 0,
"options": "January\nFebruary\nMarch\nApril\nMay\nJune\nJuly\nAugust\nSeptember\nOctober\nNovember\nDecember",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "json_output",
"fieldtype": "Code",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "JSON Output",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "missing_field_invoices",
"fieldtype": "Small Text",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Invoices with no Place Of Supply",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2019-03-04 10:04:44.767655",
"modified_by": "Administrator",
"module": "Regional",
"name": "GSTR 3B Report",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
}
],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0,
"track_views": 0
}

View File

@ -0,0 +1,459 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
import json
from six import iteritems
from frappe.utils import flt, getdate
from erpnext.regional.india import state_numbers
class GSTR3BReport(Document):
def before_save(self):
self.get_data()
def get_data(self):
self.report_dict = {
"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": 1,
"camt": 1,
"samt": 1,
"csamt": 1
},
{
"samt": 0,
"csamt": 0,
"ty": "OTH",
"camt": 0,
"iamt": 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
}
]
}
}
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()
outward_supply_tax_amounts = self.get_tax_amounts("Sales Invoice")
inward_supply_tax_amounts = self.get_tax_amounts("Purchase Invoice", reverse_charge="Y")
itc_details = self.get_itc_details()
inter_state_supplies = self.get_inter_state_supplies(self.gst_details.get("gst_state"))
inward_nil_exempt = self.get_inward_nil_exempt(self.gst_details.get("gst_state"))
self.prepare_data("Sales Invoice", outward_supply_tax_amounts, "sup_details", "osup_det", ["Registered Regular"])
self.prepare_data("Sales Invoice", outward_supply_tax_amounts, "sup_details", "osup_zero", ["SEZ", "Deemed Export", "Overseas"])
self.prepare_data("Purchase Invoice", inward_supply_tax_amounts, "sup_details", "isup_rev", ["Registered Regular"], reverse_charge="Y")
self.report_dict["sup_details"]["osup_nil_exmp"]["txval"] = flt(self.get_nil_rated_supply_value(), 2)
self.set_itc_details(itc_details)
self.set_inter_state_supply(inter_state_supplies)
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_type_map = {
'IMPG': 'Import Of Capital Goods',
'IMPS': 'Import Of Service',
'ISD': 'Input Service Distributor',
'OTH': 'All Other ITC'
}
net_itc = self.report_dict["itc_elg"]["itc_net"]
for d in self.report_dict["itc_elg"]["itc_avl"]:
if d["ty"] == 'ISRC':
reverse_charge = "Y"
else:
reverse_charge = "N"
for account_head in self.account_heads:
d["iamt"] = flt(itc_details.get((itc_type_map.get(d["ty"]), reverse_charge, account_head.get('igst_account')), {}).get("amount"), 2)
net_itc["iamt"] += flt(d["iamt"], 2)
d["camt"] = flt(itc_details.get((itc_type_map.get(d["ty"]), reverse_charge, account_head.get('cgst_account')), {}).get("amount"), 2)
net_itc["camt"] += flt(d["camt"], 2)
d["samt"] = flt(itc_details.get((itc_type_map.get(d["ty"]), reverse_charge, account_head.get('sgst_account')), {}).get("amount"), 2)
net_itc["samt"] += flt(d["samt"], 2)
d["csamt"] = flt(itc_details.get((itc_type_map.get(d["ty"]), reverse_charge, account_head.get('cess_account')), {}).get("amount"), 2)
net_itc["csamt"] += flt(d["csamt"], 2)
for account_head in self.account_heads:
self.report_dict["itc_elg"]["itc_inelg"][1]["iamt"] = flt(itc_details.get(("Ineligible", "N", account_head.get("igst_account")), {}).get("amount"), 2)
self.report_dict["itc_elg"]["itc_inelg"][1]["camt"] = flt(itc_details.get(("Ineligible", "N", account_head.get("cgst_account")), {}).get("amount"), 2)
self.report_dict["itc_elg"]["itc_inelg"][1]["samt"] = flt(itc_details.get(("Ineligible", "N", account_head.get("sgst_account")), {}).get("amount"), 2)
self.report_dict["itc_elg"]["itc_inelg"][1]["csamt"] = flt(itc_details.get(("Ineligible", "N", account_head.get("cess_account")), {}).get("amount"), 2)
def prepare_data(self, doctype, tax_details, supply_type, supply_category, gst_category_list, reverse_charge="N"):
account_map = {
'sgst_account': 'samt',
'cess_account': 'csamt',
'cgst_account': 'camt',
'igst_account': 'iamt'
}
txval = 0
total_taxable_value = self.get_total_taxable_value(doctype, reverse_charge)
for gst_category in gst_category_list:
txval += total_taxable_value.get(gst_category,0)
for account_head in self.account_heads:
for account_type, account_name in iteritems(account_head):
if account_map.get(account_type) in self.report_dict.get(supply_type).get(supply_category):
self.report_dict[supply_type][supply_category][account_map.get(account_type)] += \
flt(tax_details.get((account_name, gst_category), {}).get("amount"), 2)
for k, v in iteritems(account_map):
txval -= self.report_dict.get(supply_type, {}).get(supply_category, {}).get(v, 0)
self.report_dict[supply_type][supply_category]["txval"] = flt(txval, 2)
def set_inter_state_supply(self, inter_state_supply):
for d in inter_state_supply.get("Unregistered", []):
self.report_dict["inter_sup"]["unreg_details"].append(d)
for d in inter_state_supply.get("Registered Composition", []):
self.report_dict["inter_sup"]["comp_details"].append(d)
for d in inter_state_supply.get("UIN Holders", []):
self.report_dict["inter_sup"]["uin_details"].append(d)
def get_total_taxable_value(self, doctype, reverse_charge):
return frappe._dict(frappe.db.sql("""
select gst_category, sum(base_grand_total) as total
from `tab{doctype}`
where docstatus = 1 and month(posting_date) = %s
and year(posting_date) = %s and reverse_charge = %s
and company = %s and company_gstin = %s
group by gst_category
""" #nosec
.format(doctype = doctype), (self.month_no, self.year, reverse_charge, self.company, self.gst_details.get("gstin"))))
def get_itc_details(self, reverse_charge='N'):
itc_amount = frappe.db.sql("""
select s.gst_category, sum(t.tax_amount) as tax_amount, t.account_head, s.eligibility_for_itc, s.reverse_charge
from `tabPurchase Invoice` s , `tabPurchase Taxes and Charges` t
where s.docstatus = 1 and t.parent = s.name and s.reverse_charge = %s
and month(s.posting_date) = %s and year(s.posting_date) = %s and s.company = %s
and s.company_gstin = %s
group by t.account_head, s.gst_category, s.eligibility_for_itc
""",
(reverse_charge, self.month_no, self.year, self.company, self.gst_details.get("gstin")), as_dict=1)
itc_details = {}
for d in itc_amount:
itc_details.setdefault((d.eligibility_for_itc, d.reverse_charge, d.account_head),{
"amount": d.tax_amount
})
return itc_details
def get_nil_rated_supply_value(self):
return frappe.db.sql("""
select sum(i.base_amount) as total from
`tabSales Invoice Item` i, `tabSales Invoice` s
where s.docstatus = 1 and i.parent = s.name and i.is_nil_exempt = 1
and month(s.posting_date) = %s and year(s.posting_date) = %s
and s.company = %s and s.company_gstin = %s""",
(self.month_no, self.year, self.company, self.gst_details.get("gstin")), as_dict=1)[0].total
def get_inter_state_supplies(self, state):
inter_state_supply = frappe.db.sql(""" select sum(s.grand_total) as total, t.tax_amount, a.gst_state, s.gst_category
from `tabSales Invoice` s, `tabSales Taxes and Charges` t, `tabAddress` a
where t.parent = s.name and s.customer_address = a.name and
s.docstatus = 1 and month(s.posting_date) = %s and year(s.posting_date) = %s and
a.gst_state <> %s and s.company = %s and s.company_gstin = %s and
s.gst_category in ('Unregistered', 'Registered Composition', 'UIN Holders')
group by s.gst_category, a.state""", (self.month_no, self.year, state, self.company, self.gst_details.get("gstin")), as_dict=1)
inter_state_supply_details = {}
for d in inter_state_supply:
inter_state_supply_details.setdefault(
d.gst_category, []
)
inter_state_supply_details[d.gst_category].append({
"pos": get_state_code(d.gst_state),
"txval": d.total,
"iamt": d.tax_amount
})
return inter_state_supply_details
def get_inward_nil_exempt(self, state):
inward_nil_exempt = frappe.db.sql(""" select a.gst_state, sum(i.base_amount) as base_amount,
i.is_nil_exempt, i.is_non_gst from `tabPurchase Invoice` p , `tabPurchase Invoice Item` i, `tabAddress` a
where p.docstatus = 1 and p.name = i.parent and p.supplier_address = a.name
and i.is_nil_exempt = 1 or i.is_non_gst = 1 and
month(p.posting_date) = %s and year(p.posting_date) = %s and p.company = %s and p.company_gstin = %s
group by a.gst_state """, (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
}
}
for d in inward_nil_exempt:
if d.is_nil_exempt == 1 and state == d.gst_state:
inward_nil_exempt_details["gst"]["intra"] += d.base_amount
elif d.is_nil_exempt == 1 and state != d.gst_state:
inward_nil_exempt_details["gst"]["inter"] += d.base_amount
elif d.is_non_gst == 1 and state == d.gst_state:
inward_nil_exempt_details["non_gst"]["inter"] += d.base_amount
elif d.is_non_gst == 1 and state != d.gst_state:
inward_nil_exempt_details["non_gst"]["intra"] += d.base_amount
return inward_nil_exempt_details
def get_tax_amounts(self, doctype, reverse_charge="N"):
if doctype == "Sales Invoice":
tax_template = 'Sales Taxes and Charges'
elif doctype == "Purchase Invoice":
tax_template = 'Purchase Taxes and Charges'
tax_amounts = frappe.db.sql("""
select s.gst_category, sum(t.tax_amount) as tax_amount, t.account_head
from `tab{doctype}` s , `tab{template}` t
where s.docstatus = 1 and t.parent = s.name and s.reverse_charge = %s
and month(s.posting_date) = %s and year(s.posting_date) = %s and s.company = %s
and s.company_gstin = %s
group by t.account_head, s.gst_category
""" #nosec
.format(doctype=doctype, template=tax_template),
(reverse_charge, self.month_no, self.year, self.company, self.gst_details.get("gstin")), as_dict=1)
tax_details = {}
for d in tax_amounts:
tax_details.setdefault(
(d.account_head,d.gst_category),{
"amount": d.get("tax_amount"),
}
)
return tax_details
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_heads = frappe.get_all("GST Account",
fields=["cgst_account", "sgst_account", "igst_account", "cess_account"],
filters={
"company":self.company
})
if account_heads:
return account_heads
else:
frappe.throw("Please set account heads in GST Settings for Compnay {0}".format(self.company))
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 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_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

@ -0,0 +1,380 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
import frappe
import unittest
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
from erpnext.stock.doctype.item.test_item import make_item
import json
class TestGSTR3BReport(unittest.TestCase):
def test_gstr_3b_report(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'")
make_company()
make_item("Milk", properties = {"is_nil_exempt": 1, "standard_rate": 0.000000})
set_account_heads()
make_customers()
make_suppliers()
make_sales_invoice()
create_purchase_invoices()
if frappe.db.exists("GSTR 3B Report", "GSTR3B-March-2019-_Test Address-Billing"):
report = frappe.get_doc("GSTR 3B Report", "GSTR3B-March-2019-_Test Address-Billing")
report.save()
else:
report = frappe.get_doc({
"doctype": "GSTR 3B Report",
"company": "_Test Company GST",
"company_address": "_Test Address-Billing",
"year": "2019",
"month": "March"
}).insert()
output = json.loads(report.json_output)
self.assertEqual(output["sup_details"]["osup_det"]["iamt"], 18),
self.assertEqual(output["sup_details"]["osup_zero"]["iamt"], 18),
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]["inter"], 250)
self.assertEqual(output["itc_elg"]["itc_avl"][4]["iamt"], 45)
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',
posting_date = '2019-03-10',
do_not_save=1
)
si.append("taxes", {
"charge_type": "On Net Total",
"account_head": "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',
posting_date = '2019-03-10',
do_not_save=1
)
si1.append("taxes", {
"charge_type": "On Net Total",
"account_head": "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',
posting_date = '2019-03-10',
do_not_save=1
)
si2.append("taxes", {
"charge_type": "On Net Total",
"account_head": "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',
posting_date = '2019-03-10',
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',
posting_date = '2019-03-10',
do_not_save=1,
)
pi.eligibility_for_itc = "All Other ITC"
pi.append("taxes", {
"charge_type": "On Net Total",
"account_head": "IGST - _GST",
"cost_center": "Main - _GST",
"description": "IGST @ 18.0",
"rate": 18
})
pi.submit()
pi1 = make_purchase_invoice(
company="_Test Company GST",
supplier = '_Test Registered Supplier',
currency = 'INR',
warehouse = 'Finished Goods - _GST',
cost_center = 'Main - _GST',
posting_date = '2019-03-10',
item = "Milk",
do_not_save=1
)
pi1.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.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-Billing'):
address = frappe.get_doc({
"address_line1": "_Test Address Line 1",
"address_title": "_Test Address",
"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": "CGST - _GST",
"sgst_account": "SGST - _GST",
"igst_account": "IGST - _GST",
})
gst_settings.save()

View File

@ -94,21 +94,39 @@ def make_custom_fields(update=True):
hsn_sac_field = dict(fieldname='gst_hsn_code', label='HSN/SAC',
fieldtype='Data', fetch_from='item_code.gst_hsn_code', insert_after='description',
allow_on_submit=1, print_hide=1)
invoice_gst_fields = [
nil_rated_exempt = dict(fieldname='is_nil_exempt', label='Is nil rated or exempted',
fieldtype='Check', fetch_from='item_code.is_nil_exempt', insert_after='gst_hsn_code',
print_hide=1)
is_non_gst = dict(fieldname='is_non_gst', label='Is Non GST',
fieldtype='Check', fetch_from='item_code.is_non_gst', insert_after='is_nil_exempt',
print_hide=1)
purchase_invoice_gst_category = [
dict(fieldname='gst_section', label='GST Details', fieldtype='Section Break',
insert_after='language', print_hide=1, collapsible=1),
dict(fieldname='gst_category', label='GST Category',
fieldtype='Data', insert_after='gst_section', print_hide=1,
fetch_from='supplier.gst_category')
]
sales_invoice_gst_category = [
dict(fieldname='gst_section', label='GST Details', fieldtype='Section Break',
insert_after='language', print_hide=1, collapsible=1),
dict(fieldname='gst_category', label='GST Category',
fieldtype='Data', insert_after='gst_section', print_hide=1,
fetch_from='customer.gst_category')
]
invoice_gst_fields = [
dict(fieldname='invoice_copy', label='Invoice Copy',
fieldtype='Select', insert_after='gst_section', print_hide=1, allow_on_submit=1,
fieldtype='Select', insert_after='gst_category', print_hide=1, allow_on_submit=1,
options='Original for Recipient\nDuplicate for Transporter\nDuplicate for Supplier\nTriplicate for Supplier'),
dict(fieldname='reverse_charge', label='Reverse Charge',
fieldtype='Select', insert_after='invoice_copy', print_hide=1,
options='Y\nN', default='N'),
dict(fieldname='invoice_type', label='Invoice Type',
fieldtype='Select', insert_after='invoice_copy', print_hide=1,
options='Regular\nSEZ\nExport\nDeemed Export', default='Regular'),
dict(fieldname='export_type', label='Export Type',
fieldtype='Select', insert_after='invoice_type', print_hide=1,
depends_on='eval:in_list(["SEZ", "Export", "Deemed Export"], doc.invoice_type)',
fieldtype='Select', insert_after='reverse_charge', print_hide=1,
depends_on='eval:in_list(["SEZ", "Overseas", "Deemed Export"], doc.gst_category)',
options='\nWith Payment of Tax\nWithout Payment of Tax'),
dict(fieldname='ecommerce_gstin', label='E-commerce GSTIN',
fieldtype='Data', insert_after='export_type', print_hide=1),
@ -134,7 +152,7 @@ def make_custom_fields(update=True):
purchase_invoice_itc_fields = [
dict(fieldname='eligibility_for_itc', label='Eligibility For ITC',
fieldtype='Select', insert_after='reason_for_issuing_document', print_hide=1,
options='input\ninput service\ncapital goods\nineligible', default="ineligible"),
options='Input Service Distributor\nImport Of Service\nImport Of Capital Goods\nIneligible\nAll Other ITC', default="All Other ITC"),
dict(fieldname='itc_integrated_tax', label='Availed ITC Integrated Tax',
fieldtype='Data', insert_after='eligibility_for_itc', print_hide=1),
dict(fieldname='itc_central_tax', label='Availed ITC Central Tax',
@ -163,13 +181,13 @@ def make_custom_fields(update=True):
sales_invoice_shipping_fields = [
dict(fieldname='port_code', label='Port Code',
fieldtype='Data', insert_after='reason_for_issuing_document', print_hide=1,
depends_on="eval:doc.invoice_type=='Export' "),
depends_on="eval:doc.gst_category=='Overseas' "),
dict(fieldname='shipping_bill_number', label=' Shipping Bill Number',
fieldtype='Data', insert_after='port_code', print_hide=1,
depends_on="eval:doc.invoice_type=='Export' "),
depends_on="eval:doc.gst_category=='Overseas' "),
dict(fieldname='shipping_bill_date', label='Shipping Bill Date',
fieldtype='Date', insert_after='shipping_bill_number', print_hide=1,
depends_on="eval:doc.invoice_type=='Export' ")
depends_on="eval:doc.gst_category=='Overseas' "),
]
inter_state_gst_field = [
@ -223,26 +241,30 @@ def make_custom_fields(update=True):
dict(fieldname='gst_state_number', label='GST State Number',
fieldtype='Data', insert_after='gst_state', read_only=1),
],
'Purchase Invoice': invoice_gst_fields + purchase_invoice_gst_fields + purchase_invoice_itc_fields,
'Purchase Invoice': purchase_invoice_gst_category + invoice_gst_fields + purchase_invoice_itc_fields + purchase_invoice_gst_fields,
'Purchase Order': purchase_invoice_gst_fields,
'Purchase Receipt': purchase_invoice_gst_fields,
'Sales Invoice': invoice_gst_fields + sales_invoice_gst_fields + sales_invoice_shipping_fields,
'Delivery Note': sales_invoice_gst_fields + ewaybill_fields + sales_invoice_shipping_fields,
'Sales Invoice': sales_invoice_gst_category + invoice_gst_fields + sales_invoice_shipping_fields + sales_invoice_gst_fields,
'Delivery Note': sales_invoice_gst_fields + ewaybill_fields,
'Sales Order': sales_invoice_gst_fields,
'Sales Taxes and Charges Template': inter_state_gst_field,
'Purchase Taxes and Charges Template': inter_state_gst_field,
'Item': [
dict(fieldname='gst_hsn_code', label='HSN/SAC',
fieldtype='Link', options='GST HSN Code', insert_after='item_group'),
dict(fieldname='is_nil_exempt', label='Is nil rated or exempted',
fieldtype='Check', insert_after='gst_hsn_code'),
dict(fieldname='is_non_gst', label='Is Non GST ',
fieldtype='Check', insert_after='is_nil_exempt')
],
'Quotation Item': [hsn_sac_field],
'Supplier Quotation Item': [hsn_sac_field],
'Sales Order Item': [hsn_sac_field],
'Delivery Note Item': [hsn_sac_field],
'Sales Invoice Item': [hsn_sac_field],
'Purchase Order Item': [hsn_sac_field],
'Purchase Receipt Item': [hsn_sac_field],
'Purchase Invoice Item': [hsn_sac_field],
'Quotation Item': [hsn_sac_field, nil_rated_exempt, is_non_gst],
'Supplier Quotation Item': [hsn_sac_field, nil_rated_exempt, is_non_gst],
'Sales Order Item': [hsn_sac_field, nil_rated_exempt, is_non_gst],
'Delivery Note Item': [hsn_sac_field, nil_rated_exempt, is_non_gst],
'Sales Invoice Item': [hsn_sac_field, nil_rated_exempt, is_non_gst],
'Purchase Order Item': [hsn_sac_field, nil_rated_exempt, is_non_gst],
'Purchase Receipt Item': [hsn_sac_field, nil_rated_exempt, is_non_gst],
'Purchase Invoice Item': [hsn_sac_field, nil_rated_exempt, is_non_gst],
'Employee': [
dict(fieldname='ifsc_code', label='IFSC Code',
fieldtype='Data', insert_after='bank_ac_no', print_hide=1,
@ -301,11 +323,28 @@ def make_custom_fields(update=True):
'fieldtype': 'Data',
'insert_after': 'supplier_type',
'depends_on': 'eval:doc.is_transporter'
},
{
'fieldname': 'gst_category',
'label': 'GST Category',
'fieldtype': 'Select',
'insert_after': 'gst_transporter_id',
'options': 'Registered Regular\nRegistered Composition\nUnregistered\nSEZ\nOverseas\nUIN Holders',
'default': 'Unregistered'
}
],
'Customer': [
{
'fieldname': 'gst_category',
'label': 'GST Category',
'fieldtype': 'Select',
'insert_after': 'customer_type',
'options': 'Registered Regular\nRegistered Composition\nUnregistered\nSEZ\nOverseas\nConsumer\nDeemed Export\nUIN Holders',
'default': 'Unregistered'
}
]
}
create_custom_fields(custom_fields, ignore_validate = frappe.flags.in_patch, update=update)
create_custom_fields(custom_fields, update=update)
def make_fixtures(company=None):
docs = []

View File

@ -10,7 +10,7 @@ def execute(filters=None):
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='Invoice Type', fieldname="invoice_type", 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="hsn_code", width=120),
@ -20,7 +20,7 @@ def execute(filters=None):
'supplier_gstin',
'company_gstin',
'reverse_charge',
'invoice_type',
'gst_category',
'export_type',
'ecommerce_gstin',
'gst_hsn_code',

View File

@ -12,7 +12,7 @@ def execute(filters=None):
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='Invoice Type', fieldname="invoice_type", 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="hsn_code", width=120)
@ -22,7 +22,7 @@ def execute(filters=None):
'company_gstin',
'place_of_supply',
'reverse_charge',
'invoice_type',
'gst_category',
'export_type',
'ecommerce_gstin',
'gst_hsn_code'

View File

@ -10,14 +10,14 @@ def execute(filters=None):
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='Invoice Type', fieldname="invoice_type", 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',
'invoice_type',
'gst_category',
'export_type',
'ecommerce_gstin'
])

View File

@ -12,7 +12,7 @@ def execute(filters=None):
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='Invoice Type', fieldname="invoice_type", 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=[
@ -21,7 +21,7 @@ def execute(filters=None):
'company_gstin',
'place_of_supply',
'reverse_charge',
'invoice_type',
'gst_category',
'export_type',
'ecommerce_gstin'
])

View File

@ -28,10 +28,10 @@ class Gstr1Report(object):
place_of_supply,
ecommerce_gstin,
reverse_charge,
invoice_type,
gst_category,
return_against,
is_return,
invoice_type,
gst_category,
export_type,
port_code,
shipping_bill_number,
@ -116,7 +116,7 @@ class Gstr1Report(object):
customers = frappe.get_all("Customer", filters={"customer_type": self.customer_type})
if self.filters.get("type_of_business") == "B2B":
conditions += """ and ifnull(invoice_type, '') != 'Export' and is_return != 1
conditions += """ and ifnull(gst_category, '') != 'Overseas' and is_return != 1
and customer in ({0})""".format(", ".join([frappe.db.escape(c.name) for c in customers]))
if self.filters.get("type_of_business") in ("B2C Large", "B2C Small"):
@ -138,7 +138,7 @@ class Gstr1Report(object):
conditions += """ and is_return = 1 """
elif self.filters.get("type_of_business") == "EXPORT":
conditions += """ and is_return !=1 and invoice_type = 'Export' """
conditions += """ and is_return !=1 and gst_category = 'Overseas' """
return conditions
def get_invoice_items(self):
@ -283,8 +283,8 @@ class Gstr1Report(object):
"fieldtype": "Data"
},
{
"fieldname": "invoice_type",
"label": "Invoice Type",
"fieldname": "gst_category",
"label": "GST Category",
"fieldtype": "Data"
},
{

View File

@ -26,10 +26,10 @@ class Gstr2Report(Gstr1Report):
place_of_supply,
ecommerce_gstin,
reverse_charge,
invoice_type,
gst_category,
return_against,
is_return,
invoice_type,
gst_category,
export_type,
reason_for_issuing_document,
eligibility_for_itc,
@ -82,7 +82,7 @@ class Gstr2Report(Gstr1Report):
conditions += opts[1]
if self.filters.get("type_of_business") == "B2B":
conditions += "and ifnull(invoice_type, '') != 'Export' and is_return != 1 "
conditions += "and ifnull(gst_category, '') != 'Overseas' and is_return != 1 "
elif self.filters.get("type_of_business") == "CDNR":
conditions += """ and is_return = 1 """
@ -200,7 +200,7 @@ class Gstr2Report(Gstr1Report):
"width": 80
},
{
"fieldname": "invoice_type",
"fieldname": "gst_category",
"label": "Invoice Type",
"fieldtype": "Data",
"width": 80