diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py index 0dc4ecf7ef..9a2c095516 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -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))) \ No newline at end of file + """, (supplier, condition, year_start_date, year_end_date))) diff --git a/erpnext/accounts/report/tds_computation_summary/__init__.py b/erpnext/accounts/report/tds_computation_summary/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.js b/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.js new file mode 100644 index 0000000000..74669c4e63 --- /dev/null +++ b/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.js @@ -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" + } + ] +} diff --git a/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.json b/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.json new file mode 100644 index 0000000000..6082ed2e95 --- /dev/null +++ b/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.json @@ -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" + } + ] +} \ No newline at end of file diff --git a/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.py b/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.py new file mode 100644 index 0000000000..391287bd6c --- /dev/null +++ b/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.py @@ -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 diff --git a/erpnext/accounts/report/tds_payable_monthly/__init__.py b/erpnext/accounts/report/tds_payable_monthly/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.js b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.js new file mode 100644 index 0000000000..232d0537a3 --- /dev/null +++ b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.js @@ -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; + } + }); + } +} diff --git a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.json b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.json new file mode 100644 index 0000000000..6a83272ae8 --- /dev/null +++ b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.json @@ -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" + } + ] +} \ No newline at end of file diff --git a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py new file mode 100644 index 0000000000..0e6f0a2f63 --- /dev/null +++ b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py @@ -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 diff --git a/erpnext/buying/doctype/supplier/supplier.json b/erpnext/buying/doctype/supplier/supplier.json index 0cadc346d0..5b095b0c05 100644 --- a/erpnext/buying/doctype/supplier/supplier.json +++ b/erpnext/buying/doctype/supplier/supplier.json @@ -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",