brotherton-erpnext/erpnext/regional/report/gstr_1/gstr_1.py

497 lines
14 KiB
Python
Raw Normal View History

2018-01-10 12:18:03 +00:00
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe, json
from frappe import _
from frappe.utils import flt, formatdate
from datetime import date
from six import iteritems
2018-01-10 12:18:03 +00:00
def execute(filters=None):
2018-01-12 06:29:59 +00:00
return Gstr1Report(filters).run()
class Gstr1Report(object):
def __init__(self, filters=None):
self.filters = frappe._dict(filters or {})
2018-02-09 10:00:55 +00:00
self.columns = []
self.data = []
2018-02-05 10:39:51 +00:00
self.doctype = "Sales Invoice"
self.tax_doctype = "Sales Taxes and Charges"
self.select_columns = """
name as invoice_number,
customer_name,
posting_date,
base_grand_total,
base_rounded_total,
COALESCE(NULLIF(customer_gstin,''), NULLIF(billing_address_gstin, '')) as customer_gstin,
2018-02-05 10:39:51 +00:00
place_of_supply,
ecommerce_gstin,
reverse_charge,
invoice_type,
return_against,
is_return,
invoice_type,
export_type,
port_code,
shipping_bill_number,
shipping_bill_date,
reason_for_issuing_document
"""
2018-01-12 06:29:59 +00:00
self.customer_type = "Company" if self.filters.get("type_of_business") == "B2B" else "Individual"
2018-01-12 06:29:59 +00:00
def run(self):
self.get_columns()
self.get_gst_accounts()
self.get_invoice_data()
2018-02-09 10:00:55 +00:00
if self.invoices:
self.get_invoice_items()
self.get_items_based_on_tax_rate()
self.invoice_fields = [d["fieldname"] for d in self.invoice_columns]
self.get_data()
2018-01-12 06:29:59 +00:00
2018-02-05 10:39:51 +00:00
return self.columns, self.data
2018-01-12 06:29:59 +00:00
2018-02-05 10:39:51 +00:00
def get_data(self):
2018-01-12 06:29:59 +00:00
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():
2018-02-05 10:39:51 +00:00
row, taxable_value = self.get_row_data_for_invoice(inv, invoice_details, rate, items)
2018-01-12 06:29:59 +00:00
if self.filters.get("type_of_business") == "B2C Small":
row.append("E" if invoice_details.ecommerce_gstin else "OE")
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")
2018-01-12 06:29:59 +00:00
self.data.append(row)
2018-02-05 10:39:51 +00:00
def get_row_data_for_invoice(self, invoice, invoice_details, tax_rate, items):
row = []
for fieldname in self.invoice_fields:
if self.filters.get("type_of_business") == "CDNR" and fieldname == "invoice_value":
row.append(abs(invoice_details.base_rounded_total) or abs(invoice_details.base_grand_total))
elif fieldname == "invoice_value":
row.append(invoice_details.base_rounded_total or invoice_details.base_grand_total)
elif fieldname in ('posting_date', 'shipping_bill_date'):
row.append(formatdate(invoice_details.get(fieldname), 'dd-MMM-YY'))
elif fieldname == "export_type":
export_type = "WPAY" if invoice_details.get(fieldname)=="With Payment of Tax" else "WOPAY"
row.append(export_type)
2018-02-05 10:39:51 +00:00
else:
row.append(invoice_details.get(fieldname))
taxable_value = sum([abs(net_amount)
for item_code, net_amount in self.invoice_items.get(invoice).items() if item_code in items])
row += [tax_rate or 0, taxable_value]
2018-02-05 10:39:51 +00:00
return row, taxable_value
2018-01-12 06:29:59 +00:00
def get_invoice_data(self):
self.invoices = frappe._dict()
conditions = self.get_conditions()
invoice_data = frappe.db.sql("""
select
2018-02-05 10:39:51 +00:00
{select_columns}
from `tab{doctype}`
where docstatus = 1 {where_conditions}
2018-01-12 06:29:59 +00:00
order by posting_date desc
2018-02-05 10:39:51 +00:00
""".format(select_columns=self.select_columns, doctype=self.doctype,
where_conditions=conditions), self.filters, as_dict=1)
2018-01-12 06:29:59 +00:00
for d in invoice_data:
self.invoices.setdefault(d.invoice_number, d)
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"),
("company_address", " and company_address=%(company_address)s")):
2018-01-12 06:29:59 +00:00
if self.filters.get(opts[0]):
conditions += opts[1]
customers = frappe.get_all("Customer", filters={"customer_type": self.customer_type})
if self.filters.get("type_of_business") == "B2B":
2018-03-27 08:45:36 +00:00
conditions += """ and ifnull(invoice_type, '') != 'Export' and is_return != 1
and customer in ('{0}')""".format("', '".join([frappe.db.escape(c.name) for c in customers]))
2018-01-12 06:29:59 +00:00
if self.filters.get("type_of_business") in ("B2C Large", "B2C Small"):
b2c_limit = frappe.db.get_single_value('GSt Settings', 'b2c_limit')
if not b2c_limit:
frappe.throw(_("Please set B2C Limit in GST Settings."))
2018-01-12 06:29:59 +00:00
if self.filters.get("type_of_business") == "B2C Large":
conditions += """ and SUBSTR(place_of_supply, 1, 2) != SUBSTR(company_gstin, 1, 2)
and grand_total > {0} and is_return != 1 and customer in ('{1}')""".\
format(flt(b2c_limit), "', '".join([frappe.db.escape(c.name) for c in customers]))
2018-01-12 06:29:59 +00:00
elif self.filters.get("type_of_business") == "B2C Small":
conditions += """ and (
SUBSTR(place_of_supply, 1, 2) = SUBSTR(company_gstin, 1, 2)
or grand_total <= {0}) and is_return != 1 and customer in ('{1}')""".\
format(flt(b2c_limit), "', '".join([frappe.db.escape(c.name) for c in customers]))
elif self.filters.get("type_of_business") == "CDNR":
conditions += """ and is_return = 1 """
2018-01-12 06:29:59 +00:00
elif self.filters.get("type_of_business") == "EXPORT":
conditions += """ and is_return !=1 and invoice_type = 'Export' """
2018-01-12 06:29:59 +00:00
return conditions
def get_invoice_items(self):
self.invoice_items = frappe._dict()
items = frappe.db.sql("""
select item_code, parent, base_net_amount
2018-02-05 10:39:51 +00:00
from `tab%s Item`
2018-01-12 06:29:59 +00:00
where parent in (%s)
2018-02-05 10:39:51 +00:00
""" % (self.doctype, ', '.join(['%s']*len(self.invoices))), tuple(self.invoices), as_dict=1)
2018-01-12 06:29:59 +00:00
for d in items:
if d.item_code not in self.invoice_items.get(d.parent, {}):
self.invoice_items.setdefault(d.parent, {}).setdefault(d.item_code,
sum(i.get('base_net_amount', 0) for i in items
if i.item_code == d.item_code and i.parent == d.parent))
2018-01-12 06:29:59 +00:00
def get_items_based_on_tax_rate(self):
2018-02-05 10:39:51 +00:00
self.tax_details = frappe.db.sql("""
2018-01-12 06:29:59 +00:00
select
parent, account_head, item_wise_tax_detail, base_tax_amount_after_discount_amount
2018-02-05 10:39:51 +00:00
from `tab%s`
2018-01-12 06:29:59 +00:00
where
2018-02-05 10:39:51 +00:00
parenttype = %s and docstatus = 1
2018-01-12 06:29:59 +00:00
and parent in (%s)
order by account_head
2018-02-05 10:39:51 +00:00
""" % (self.tax_doctype, '%s', ', '.join(['%s']*len(self.invoices.keys()))),
2018-04-16 09:09:37 +00:00
tuple([self.doctype] + list(self.invoices.keys())))
2018-01-12 06:29:59 +00:00
self.items_based_on_tax_rate = {}
self.invoice_cess = frappe._dict()
unidentified_gst_accounts = []
2018-02-05 10:39:51 +00:00
for parent, account, item_wise_tax_detail, tax_amount in self.tax_details:
2018-01-12 06:29:59 +00:00
if account in self.gst_accounts.cess_account:
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.gst_accounts.cgst_account \
or account in self.gst_accounts.sgst_account:
cgst_or_sgst = True
if not (cgst_or_sgst or account in self.gst_accounts.igst_account):
if "gst" in account.lower() and account not in unidentified_gst_accounts:
unidentified_gst_accounts.append(account)
continue
for item_code, tax_amounts in item_wise_tax_detail.items():
tax_rate = tax_amounts[0]
if cgst_or_sgst:
tax_rate *= 2
2018-02-05 10:39:51 +00:00
rate_based_dict = self.items_based_on_tax_rate\
.setdefault(parent, {}).setdefault(tax_rate, [])
2018-01-12 06:29:59 +00:00
if item_code not in rate_based_dict:
rate_based_dict.append(item_code)
except ValueError:
continue
2018-01-12 06:29:59 +00:00
if unidentified_gst_accounts:
frappe.msgprint(_("Following accounts might be selected in GST Settings:")
+ "<br>" + "<br>".join(unidentified_gst_accounts), alert=True)
# Build itemised tax for export invoices where tax table is blank
for invoice, items in iteritems(self.invoice_items):
if invoice not in self.items_based_on_tax_rate \
and frappe.db.get_value(self.doctype, invoice, "export_type") == "Without Payment of Tax":
self.items_based_on_tax_rate.setdefault(invoice, {}).setdefault(0, items.keys())
2018-01-12 06:29:59 +00:00
def get_gst_accounts(self):
self.gst_accounts = frappe._dict()
2018-08-30 10:46:35 +00:00
gst_settings_accounts = frappe.get_all("GST Account",
2018-01-12 06:29:59 +00:00
filters={"parent": "GST Settings", "company": self.filters.company},
fields=["cgst_account", "sgst_account", "igst_account", "cess_account"])
2018-01-12 06:29:59 +00:00
if not gst_settings_accounts:
frappe.throw(_("Please set GST Accounts in GST Settings"))
2018-01-10 12:18:03 +00:00
2018-01-12 06:29:59 +00:00
for d in gst_settings_accounts:
for acc, val in d.items():
self.gst_accounts.setdefault(acc, []).append(val)
2018-01-10 12:18:03 +00:00
2018-01-12 06:29:59 +00:00
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
}
]
self.other_columns = []
2018-01-12 06:29:59 +00:00
if self.filters.get("type_of_business") == "B2B":
self.invoice_columns = [
{
"fieldname": "customer_gstin",
"label": "GSTIN/UIN of Recipient",
"fieldtype": "Data",
"width": 150
2018-01-12 06:29:59 +00:00
},
{
"fieldname": "customer_name",
"label": "Receiver Name",
"fieldtype": "Data",
"width":100
2018-01-12 06:29:59 +00:00
},
{
"fieldname": "invoice_number",
"label": "Invoice Number",
"fieldtype": "Link",
"options": "Sales Invoice",
"width":100
2018-01-12 06:29:59 +00:00
},
{
"fieldname": "posting_date",
"label": "Invoice date",
"fieldtype": "Data",
"width":80
2018-01-12 06:29:59 +00:00
},
{
"fieldname": "invoice_value",
"label": "Invoice Value",
"fieldtype": "Currency",
"width":100
2018-01-12 06:29:59 +00:00
},
{
"fieldname": "place_of_supply",
"label": "Place Of Supply",
"fieldtype": "Data",
"width":100
2018-01-12 06:29:59 +00:00
},
{
"fieldname": "reverse_charge",
"label": "Reverse Charge",
"fieldtype": "Data"
},
{
"fieldname": "invoice_type",
"label": "Invoice Type",
"fieldtype": "Data"
},
{
"fieldname": "ecommerce_gstin",
"label": "E-Commerce GSTIN",
"fieldtype": "Data",
"width":120
2018-01-12 06:29:59 +00:00
}
]
self.other_columns = [
{
"fieldname": "cess_amount",
"label": "Cess Amount",
"fieldtype": "Currency",
"width": 100
}
]
2018-01-12 06:29:59 +00:00
elif self.filters.get("type_of_business") == "B2C Large":
self.invoice_columns = [
{
"fieldname": "invoice_number",
"label": "Invoice Number",
"fieldtype": "Link",
"options": "Sales Invoice",
"width": 120
},
{
"fieldname": "posting_date",
"label": "Invoice date",
"fieldtype": "Data",
2018-01-12 06:29:59 +00:00
"width": 100
},
{
"fieldname": "invoice_value",
"label": "Invoice Value",
"fieldtype": "Currency",
"width": 100
},
{
"fieldname": "place_of_supply",
"label": "Place Of Supply",
2018-01-12 06:29:59 +00:00
"fieldtype": "Data",
"width": 120
},
{
"fieldname": "ecommerce_gstin",
"label": "E-Commerce GSTIN",
"fieldtype": "Data",
"width": 130
}
]
self.other_columns = [
{
"fieldname": "cess_amount",
"label": "Cess Amount",
"fieldtype": "Currency",
"width": 100
}
]
elif self.filters.get("type_of_business") == "CDNR":
self.invoice_columns = [
{
"fieldname": "customer_gstin",
"label": "GSTIN/UIN of Recipient",
"fieldtype": "Data",
"width": 150
},
{
"fieldname": "customer_name",
"label": "Receiver Name",
"fieldtype": "Data",
"width": 120
},
{
"fieldname": "return_against",
"label": "Invoice/Advance Receipt Number",
"fieldtype": "Link",
"options": "Sales Invoice",
"width": 120
},
{
"fieldname": "posting_date",
"label": "Invoice/Advance Receipt date",
"fieldtype": "Data",
"width": 120
},
{
"fieldname": "invoice_number",
"label": "Invoice/Advance Receipt Number",
"fieldtype": "Link",
"options": "Sales Invoice",
"width":120
},
{
"fieldname": "reason_for_issuing_document",
"label": "Reason For Issuing document",
"fieldtype": "Data",
"width": 140
},
{
"fieldname": "place_of_supply",
"label": "Place Of Supply",
"fieldtype": "Data",
"width": 120
},
{
"fieldname": "invoice_value",
"label": "Invoice Value",
"fieldtype": "Currency",
"width": 120
}
]
self.other_columns = [
{
"fieldname": "cess_amount",
"label": "Cess Amount",
"fieldtype": "Currency",
"width": 100
},
{
"fieldname": "pre_gst",
"label": "PRE GST",
"fieldtype": "Data",
"width": 80
},
{
"fieldname": "document_type",
"label": "Document Type",
"fieldtype": "Data",
"width": 80
}
]
2018-01-12 06:29:59 +00:00
elif self.filters.get("type_of_business") == "B2C Small":
self.invoice_columns = [
{
"fieldname": "place_of_supply",
"label": "Place Of Supply",
2018-01-12 06:29:59 +00:00
"fieldtype": "Data",
"width": 120
},
{
"fieldname": "ecommerce_gstin",
"label": "E-Commerce GSTIN",
"fieldtype": "Data",
"width": 130
}
]
self.other_columns = [
{
"fieldname": "cess_amount",
"label": "Cess Amount",
"fieldtype": "Currency",
"width": 100
},
2018-01-12 06:29:59 +00:00
{
"fieldname": "type",
"label": "Type",
"fieldtype": "Data",
"width": 50
}
]
elif self.filters.get("type_of_business") == "EXPORT":
self.invoice_columns = [
{
"fieldname": "export_type",
"label": "Export Type",
"fieldtype": "Data",
"width":120
},
{
"fieldname": "invoice_number",
"label": "Invoice Number",
"fieldtype": "Link",
"options": "Sales Invoice",
"width":120
},
{
"fieldname": "posting_date",
"label": "Invoice date",
"fieldtype": "Data",
"width": 120
},
{
"fieldname": "invoice_value",
"label": "Invoice Value",
"fieldtype": "Currency",
"width": 120
},
{
"fieldname": "port_code",
"label": "Port Code",
"fieldtype": "Data",
"width": 120
},
{
"fieldname": "shipping_bill_number",
"label": "Shipping Bill Number",
"fieldtype": "Data",
"width": 120
},
{
"fieldname": "shipping_bill_date",
"label": "Shipping Bill Date",
"fieldtype": "Data",
"width": 120
}
]
2018-02-15 11:54:15 +00:00
self.columns = self.invoice_columns + self.tax_columns + self.other_columns