TDS Monthly report & TDS Computation Summary report (#15204)

* tds computation report added

* tds computation logic added

* minor changes to commonify code

* tds payable monthly report added

* initial logic for monthly tds calculation added

* added field for supplier type and pan

* changes related to pan and entity type

* date filter fix and cleanup
This commit is contained in:
Zarrar 2018-09-07 13:27:10 +05:30 committed by Nabin Hait
parent eef5cda1a9
commit 525a005b78
10 changed files with 604 additions and 7 deletions

View File

@ -102,17 +102,25 @@ def get_tds_amount(ref_doc, tax_details, fiscal_year_details):
return tds_amount
def get_advance_vouchers(supplier, fiscal_year):
def get_advance_vouchers(supplier, fiscal_year=None, company=None, from_date=None, to_date=None):
condition = "fiscal_year=%s" % fiscal_year
if from_date and to_date:
condition = "company=%s and posting_date between %s and %s" % (company, from_date, to_date)
return frappe.db.sql_list("""
select distinct voucher_no
from `tabGL Entry`
where party=%s and fiscal_year=%s and debit > 0
""", (supplier, fiscal_year))
where party=%s and %s and debit > 0
""", (supplier, condition))
def get_debit_note_amount(supplier, year_start_date, year_end_date, company=None):
condition = ""
if company:
condition = " and company=%s " % company
def get_debit_note_amount(supplier, year_start_date, year_end_date):
return flt(frappe.db.sql("""
select abs(sum(net_total))
from `tabPurchase Invoice`
where supplier=%s and is_return=1 and docstatus=1
where supplier=%s %s and is_return=1 and docstatus=1
and posting_date between %s and %s
""", (supplier, year_start_date, year_end_date)))
""", (supplier, condition, year_start_date, year_end_date)))

View File

@ -0,0 +1,43 @@
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
/* eslint-disable */
frappe.query_reports["TDS Computation Summary"] = {
"filters": [
{
"fieldname":"company",
"label": __("Company"),
"fieldtype": "Link",
"default": frappe.defaults.get_default('company')
},
{
"fieldname":"supplier",
"label": __("Supplier"),
"fieldtype": "Link",
"options": "Supplier",
"get_query": function() {
return {
"filters": {
"tax_withholding_category": ["!=",""],
}
}
}
},
{
"fieldname":"from_date",
"label": __("From Date"),
"fieldtype": "Date",
"default": frappe.datetime.add_months(frappe.datetime.get_today(), -1),
"reqd": 1,
"width": "60px"
},
{
"fieldname":"to_date",
"label": __("To Date"),
"fieldtype": "Date",
"default": frappe.datetime.get_today(),
"reqd": 1,
"width": "60px"
}
]
}

View File

@ -0,0 +1,33 @@
{
"add_total_row": 0,
"creation": "2018-08-21 11:25:00.551823",
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 0,
"is_standard": "Yes",
"letter_head": "Gadgets International",
"modified": "2018-08-21 11:25:00.551823",
"modified_by": "Administrator",
"module": "Accounts",
"name": "TDS Computation Summary",
"owner": "Administrator",
"prepared_report": 0,
"ref_doctype": "Purchase Invoice",
"report_name": "TDS Computation Summary",
"report_type": "Script Report",
"roles": [
{
"role": "Purchase User"
},
{
"role": "Accounts Manager"
},
{
"role": "Accounts User"
},
{
"role": "Auditor"
}
]
}

View File

@ -0,0 +1,135 @@
import frappe
from frappe import _
from frappe.utils import flt
from erpnext.accounts.utils import get_fiscal_year
from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category \
import get_advance_vouchers, get_debit_note_amount
def execute(filters=None):
validate_filters(filters)
columns = get_columns()
res = get_result(filters)
return columns, res
def validate_filters(filters):
''' Validate if dates are properly set and lie in the same fiscal year'''
if filters.from_date > filters.to_date:
frappe.throw(_("From Date must be before To Date"))
from_year = get_fiscal_year(filters.from_date)[0]
to_year = get_fiscal_year(filters.to_date)[0]
if from_year != to_year:
frappe.throw(_("From Date and To Date lie in different Fiscal Year"))
filters["fiscal_year"] = from_year
def get_result(filters):
# if no supplier selected, fetch data for all tds applicable supplier
# else fetch relevant data for selected supplier
pan = "pan" if frappe.db.has_column("Supplier", "pan") else "tax_id"
fields = ["name", pan+" as pan", "tax_withholding_category", "supplier_type"]
if filters.supplier:
filters.supplier = frappe.db.get_list('Supplier',
{"name": filters.supplier}, fields)
else:
filters.supplier = frappe.db.get_list('Supplier',
{"tax_withholding_category": ["!=", ""]}, fields)
out = []
for supplier in filters.supplier:
tds = frappe.get_doc("Tax Withholding Category", supplier.tax_withholding_category)
rate = [d.tax_withholding_rate for d in tds.rates if d.fiscal_year == filters.fiscal_year][0]
account = [d.account for d in tds.accounts if d.company == filters.company][0]
total_invoiced_amount, tds_deducted = get_invoice_and_tds_amount(supplier.name, account,
filters.company, filters.from_date, filters.to_date)
if total_invoiced_amount or tds_deducted:
out.append([supplier.pan, supplier.name, tds.name, supplier.supplier_type,
rate, total_invoiced_amount, tds_deducted])
return out
def get_invoice_and_tds_amount(supplier, account, company, from_date, to_date):
''' calculate total invoice amount and total tds deducted for given supplier '''
entries = frappe.db.sql("""
select voucher_no, credit
from `tabGL Entry`
where party in (%s) and credit > 0
and company=%s and posting_date between %s and %s
""", (supplier, company, from_date, to_date), as_dict=1)
supplier_credit_amount = flt(sum([d.credit for d in entries]))
vouchers = [d.voucher_no for d in entries]
vouchers += get_advance_vouchers(supplier, company=company,
from_date=from_date, to_date=to_date)
tds_deducted = 0
if vouchers:
tds_deducted = flt(frappe.db.sql("""
select sum(credit)
from `tabGL Entry`
where account=%s and posting_date between %s and %s
and company=%s and credit > 0 and voucher_no in ({0})
""".format(', '.join(["'%s'" % d for d in vouchers])),
(account, from_date, to_date, company))[0][0])
debit_note_amount = get_debit_note_amount(supplier, from_date, to_date, company=company)
total_invoiced_amount = supplier_credit_amount + tds_deducted - debit_note_amount
return total_invoiced_amount, tds_deducted
def get_columns():
columns = [
{
"label": _("PAN"),
"fieldname": "pan",
"fieldtype": "Data",
"width": 90
},
{
"label": _("Supplier"),
"options": "Supplier",
"fieldname": "supplier",
"fieldtype": "Link",
"width": 180
},
{
"label": _("Section Code"),
"options": "Tax Withholding Category",
"fieldname": "section_code",
"fieldtype": "Link",
"width": 180
},
{
"label": _("Entity Type"),
"fieldname": "entity_type",
"fieldtype": "Data",
"width": 180
},
{
"label": _("TDS Rate %"),
"fieldname": "tds_rate",
"fieldtype": "Float",
"width": 90
},
{
"label": _("Total Amount Credited"),
"fieldname": "total_amount_credited",
"fieldtype": "Float",
"width": 90
},
{
"label": _("Amount of TDS Deducted"),
"fieldname": "tds_deducted",
"fieldtype": "Float",
"width": 90
}
]
return columns

View File

@ -0,0 +1,89 @@
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
/* eslint-disable */
frappe.query_reports["TDS Payable Monthly"] = {
"filters": [
{
"fieldname":"company",
"label": __("Company"),
"fieldtype": "Link",
"default": frappe.defaults.get_default('company')
},
{
"fieldname":"supplier",
"label": __("Supplier"),
"fieldtype": "Link",
"options": "Supplier",
"get_query": function() {
return {
"filters": {
"tax_withholding_category": ["!=", ""],
}
}
},
on_change: function() {
frappe.query_report.set_filter_value("purchase_invoice", "");
frappe.query_report.refresh();
}
},
{
"fieldname":"purchase_invoice",
"label": __("Purchase Invoice"),
"fieldtype": "Link",
"options": "Purchase Invoice",
"get_query": function() {
return {
"filters": {
"name": ["in", frappe.query_report.invoices]
}
}
},
on_change: function() {
let supplier = frappe.query_report.get_filter_value('supplier');
if(!supplier) return; // return if no supplier selected
// filter invoices based on selected supplier
let invoices = [];
frappe.query_report.invoice_data.map(d => {
if(d.supplier==supplier)
invoices.push(d.name)
});
frappe.query_report.invoices = invoices;
frappe.query_report.refresh();
}
},
{
"fieldname":"from_date",
"label": __("From Date"),
"fieldtype": "Date",
"default": frappe.datetime.add_months(frappe.datetime.get_today(), -1),
"reqd": 1,
"width": "60px"
},
{
"fieldname":"to_date",
"label": __("To Date"),
"fieldtype": "Date",
"default": frappe.datetime.get_today(),
"reqd": 1,
"width": "60px"
}
],
onload: function(report) {
// fetch all tds applied invoices
frappe.call({
"method": "erpnext.accounts.report.tds_payable_monthly.tds_payable_monthly.get_tds_invoices",
callback: function(r) {
let invoices = [];
r.message.map(d => {
invoices.push(d.name);
});
report["invoice_data"] = r.message;
report["invoices"] = invoices;
}
});
}
}

View File

@ -0,0 +1,33 @@
{
"add_total_row": 0,
"creation": "2018-08-21 11:32:30.874923",
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 0,
"is_standard": "Yes",
"letter_head": "Gadgets International",
"modified": "2018-08-21 11:33:40.804532",
"modified_by": "Administrator",
"module": "Accounts",
"name": "TDS Payable Monthly",
"owner": "Administrator",
"prepared_report": 0,
"ref_doctype": "Purchase Invoice",
"report_name": "TDS Payable Monthly",
"report_type": "Script Report",
"roles": [
{
"role": "Purchase User"
},
{
"role": "Accounts Manager"
},
{
"role": "Accounts User"
},
{
"role": "Auditor"
}
]
}

View File

@ -0,0 +1,190 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe import _
from frappe.utils import getdate
def execute(filters=None):
filters["invoices"] = frappe.cache().hget("invoices", frappe.session.user)
validate_filters(filters)
set_filters(filters)
columns = get_columns()
res = get_result(filters)
return columns, res
def validate_filters(filters):
''' Validate if dates are properly set '''
if filters.from_date > filters.to_date:
frappe.throw(_("From Date must be before To Date"))
def set_filters(filters):
invoices = []
if not filters["invoices"]:
filters["invoices"] = get_tds_invoices()
if filters.supplier and filters.purchase_invoice:
for d in filters["invoices"]:
if d.name == filters.purchase_invoice and d.supplier == filters.supplier:
invoices.append(d)
elif filters.supplier and not filters.purchase_invoice:
for d in filters["invoices"]:
if d.supplier == filters.supplier:
invoices.append(d)
elif filters.purchase_invoice and not filters.supplier:
for d in filters["invoices"]:
if d.name == filters.purchase_invoice:
invoices.append(d)
filters["invoices"] = invoices if invoices else filters["invoices"]
def get_result(filters):
supplier_map, tds_docs = get_supplier_map(filters)
gle_map = get_gle_map(filters)
out = []
for d in gle_map:
tds_deducted, total_amount_credited = 0, 0
supplier = supplier_map[d]
tds_doc = tds_docs[supplier.tax_withholding_category]
account = [i.account for i in tds_doc.accounts if i.company == filters.company][0]
for k in gle_map[d]:
if k.party == supplier_map[d] and k.credit > 0:
total_amount_credited += k.credit
elif k.account == account and k.credit > 0:
tds_deducted = k.credit
total_amount_credited += k.credit
rate = [i.tax_withholding_rate for i in tds_doc.rates
if i.fiscal_year == gle_map[d][0].fiscal_year][0]
if getdate(filters.from_date) <= gle_map[d][0].posting_date \
and getdate(filters.to_date) >= gle_map[d][0].posting_date:
out.append([supplier.pan, supplier.name, tds_doc.name,
supplier.supplier_type, rate, total_amount_credited, tds_deducted,
gle_map[d][0].posting_date, "Purchase Invoice", d])
return out
def get_supplier_map(filters):
# create a supplier_map of the form {"purchase_invoice": {supplier_name, pan, tds_name}}
# pre-fetch all distinct applicable tds docs
supplier_map, tds_docs = {}, {}
pan = "pan" if frappe.db.has_column("Supplier", "pan") else "tax_id"
supplier_detail = frappe.db.get_all('Supplier',
{"name": ["in", [d.supplier for d in filters["invoices"]]]},
["tax_withholding_category", "name", pan+" as pan", "supplier_type"])
for d in filters["invoices"]:
supplier_map[d.get("name")] = [k for k in supplier_detail
if k.name == d.get("supplier")][0]
for d in supplier_detail:
if d.get("tax_withholding_category") not in tds_docs:
tds_docs[d.get("tax_withholding_category")] = \
frappe.get_doc("Tax Withholding Category", d.get("tax_withholding_category"))
return supplier_map, tds_docs
def get_gle_map(filters):
# create gle_map of the form
# {"purchase_invoice": list of dict of all gle created for this invoice}
gle_map = {}
gle = frappe.db.get_all('GL Entry',\
{"voucher_no": ["in", [d.get("name") for d in filters["invoices"]]]},
["fiscal_year", "credit", "debit", "account", "voucher_no", "posting_date"])
for d in gle:
if not d.voucher_no in gle_map:
gle_map[d.voucher_no] = [d]
else:
gle_map[d.voucher_no].append(d)
return gle_map
def get_columns():
pan = "pan" if frappe.db.has_column("Supplier", "pan") else "tax_id"
columns = [
{
"label": _(frappe.unscrub(pan)),
"fieldname": pan,
"fieldtype": "Data",
"width": 90
},
{
"label": _("Supplier"),
"options": "Supplier",
"fieldname": "supplier",
"fieldtype": "Link",
"width": 180
},
{
"label": _("Section Code"),
"options": "Tax Withholding Category",
"fieldname": "section_code",
"fieldtype": "Link",
"width": 180
},
{
"label": _("Entity Type"),
"fieldname": "entity_type",
"fieldtype": "Data",
"width": 180
},
{
"label": _("TDS Rate %"),
"fieldname": "tds_rate",
"fieldtype": "Float",
"width": 90
},
{
"label": _("Total Amount Credited"),
"fieldname": "total_amount_credited",
"fieldtype": "Float",
"width": 90
},
{
"label": _("Amount of TDS Deducted"),
"fieldname": "tds_deducted",
"fieldtype": "Float",
"width": 90
},
{
"label": _("Date of Transaction"),
"fieldname": "transaction_date",
"fieldtype": "Date",
"width": 90
},
{
"label": _("Transaction Type"),
"fieldname": "transaction_type",
"width": 90
},
{
"label": _("Reference No."),
"fieldname": "ref_no",
"fieldtype": "Dynamic Link",
"options": "transaction_type",
"width": 90
}
]
return columns
@frappe.whitelist()
def get_tds_invoices():
# fetch tds applicable supplier and fetch invoices for these suppliers
suppliers = [d.name for d in frappe.db.get_list("Supplier",
{"tax_withholding_category": ["!=", ""]}, ["name"])]
invoices = frappe.db.get_list("Purchase Invoice",
{"supplier": ["in", suppliers]}, ["name", "supplier"])
frappe.cache().hset("invoices", frappe.session.user, invoices)
return invoices

View File

@ -377,6 +377,72 @@
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "Company",
"fieldname": "supplier_type",
"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": "Supplier Type",
"length": 0,
"no_copy": 0,
"options": "Company\nIndividual",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"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": "pan",
"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": "PAN",
"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,
@ -1364,7 +1430,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-08-29 06:25:52.313864",
"modified": "2018-09-07 08:48:57.719713",
"modified_by": "Administrator",
"module": "Buying",
"name": "Supplier",