[Enhancement] Tax Withholding Category (#15064)
* add single, cumulative threshold, remove checkboxes * remove tds child table from supplier & add tds link field * add description field in tax withholding category * add tax withholding data for indian setup, some fixes * add a checkbox for tax withholding in purchase invoice * remove supplier's child table for tds * enable tds field if supplier has tds set inits master * move rates data to child table - adding fiscal year support * change bootstrap data according to child table config of tds * show category name in list view * loyalty program fixes * moved tax calculation to tax_withholding.py - calculation for tds amount for cumulative threshold from gl entry * add fiscal year dependency in company test * minor loyalty program fix * minor tier calculation fix * minor handling duplicate exception * toggle apply_tds according to supplier, code rectify * minor fixes for loyalty program * test case for single and cumulative threshold * codacy fix
This commit is contained in:
parent
e45ec661ea
commit
7f8024c516
@ -13,28 +13,47 @@ class LoyaltyProgram(Document):
|
||||
pass
|
||||
|
||||
|
||||
def get_loyalty_details(customer, loyalty_program, expiry_date=None, company=None):
|
||||
def get_loyalty_details(customer, loyalty_program, expiry_date=None, company=None, include_expired_entry=False):
|
||||
if not expiry_date:
|
||||
expiry_date = today()
|
||||
|
||||
args_list = [customer, loyalty_program, expiry_date, expiry_date]
|
||||
condition = ''
|
||||
if company:
|
||||
condition = " and company=%s "
|
||||
args_list.append(company)
|
||||
condition = " and company='%s' " % frappe.db.escape(company)
|
||||
if not include_expired_entry:
|
||||
condition += " and expiry_date>='%s' " % expiry_date
|
||||
|
||||
loyalty_point_details = frappe.db.sql('''select sum(loyalty_points) as loyalty_points,
|
||||
sum(purchase_amount) as total_spent from `tabLoyalty Point Entry`
|
||||
where customer=%s and loyalty_program=%s
|
||||
and expiry_date>=%s and posting_date <= %s
|
||||
where customer=%s and loyalty_program=%s and posting_date <= %s
|
||||
{condition}
|
||||
group by customer'''.format(condition=condition), tuple(args_list), as_dict=1)
|
||||
group by customer'''.format(condition=condition),
|
||||
(customer, loyalty_program, expiry_date), as_dict=1)
|
||||
|
||||
if loyalty_point_details:
|
||||
return loyalty_point_details[0]
|
||||
else:
|
||||
return {"loyalty_points": 0, "total_spent": 0}
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_loyalty_program_details(customer, loyalty_program=None, expiry_date=None, company=None, silent=False):
|
||||
def get_loyalty_program_details_with_points(customer, loyalty_program=None, expiry_date=None, company=None, silent=False, include_expired_entry=False, current_transaction_amount=0):
|
||||
lp_details = get_loyalty_program_details(customer, loyalty_program, company=company, silent=silent)
|
||||
loyalty_program = frappe.get_doc("Loyalty Program", loyalty_program)
|
||||
lp_details.update(get_loyalty_details(customer, loyalty_program.name, expiry_date, company, include_expired_entry))
|
||||
|
||||
tier_spent_level = sorted([d.as_dict() for d in loyalty_program.collection_rules],
|
||||
key=lambda rule:rule.min_spent, reverse=True)
|
||||
for i, d in enumerate(tier_spent_level):
|
||||
if i==0 or (lp_details.total_spent+current_transaction_amount) <= d.min_spent:
|
||||
lp_details.tier_name = d.tier_name
|
||||
lp_details.collection_factor = d.collection_factor
|
||||
else:
|
||||
break
|
||||
|
||||
return lp_details
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_loyalty_program_details(customer, loyalty_program=None, expiry_date=None, company=None, silent=False, include_expired_entry=False):
|
||||
lp_details = frappe._dict()
|
||||
|
||||
if not loyalty_program:
|
||||
@ -51,17 +70,6 @@ def get_loyalty_program_details(customer, loyalty_program=None, expiry_date=None
|
||||
loyalty_program = frappe.get_doc("Loyalty Program", loyalty_program)
|
||||
lp_details.update({"loyalty_program": loyalty_program.name})
|
||||
lp_details.update(loyalty_program.as_dict())
|
||||
|
||||
lp_details.update(get_loyalty_details(customer, loyalty_program.name, expiry_date, company))
|
||||
|
||||
tier_spent_level = sorted([d.as_dict() for d in loyalty_program.collection_rules],
|
||||
key=lambda rule:rule.min_spent, reverse=True)
|
||||
for i, d in enumerate(tier_spent_level):
|
||||
if i == 0 or lp_details.total_spent < d.min_spent:
|
||||
lp_details.tier_name = d.tier_name
|
||||
lp_details.collection_factor = d.collection_factor
|
||||
else:
|
||||
break
|
||||
return lp_details
|
||||
|
||||
@frappe.whitelist()
|
||||
@ -95,7 +103,7 @@ def validate_loyalty_points(ref_doc, points_to_redeem):
|
||||
frappe.throw(_("The Loyalty Program isn't valid for the selected company"))
|
||||
|
||||
if loyalty_program and points_to_redeem:
|
||||
loyalty_program_details = get_loyalty_program_details(ref_doc.customer, loyalty_program,
|
||||
loyalty_program_details = get_loyalty_program_details_with_points(ref_doc.customer, loyalty_program,
|
||||
posting_date, ref_doc.company)
|
||||
|
||||
if points_to_redeem > loyalty_program_details.loyalty_points:
|
||||
|
@ -6,7 +6,7 @@ from __future__ import unicode_literals
|
||||
import frappe
|
||||
import unittest
|
||||
from frappe.utils import today, cint, flt, getdate
|
||||
from erpnext.accounts.doctype.loyalty_program.loyalty_program import get_loyalty_program_details
|
||||
from erpnext.accounts.doctype.loyalty_program.loyalty_program import get_loyalty_program_details_with_points
|
||||
|
||||
class TestLoyaltyProgram(unittest.TestCase):
|
||||
@classmethod
|
||||
@ -15,6 +15,7 @@ class TestLoyaltyProgram(unittest.TestCase):
|
||||
create_records()
|
||||
|
||||
def test_loyalty_points_earned_single_tier(self):
|
||||
frappe.db.set_value("Customer", "Test Loyalty Customer", "loyalty_program", "Test Single Loyalty")
|
||||
# create a new sales invoice
|
||||
si_original = create_sales_invoice_record()
|
||||
si_original.insert()
|
||||
@ -50,6 +51,7 @@ class TestLoyaltyProgram(unittest.TestCase):
|
||||
frappe.delete_doc('Sales Invoice', d.name)
|
||||
|
||||
def test_loyalty_points_earned_multiple_tier(self):
|
||||
frappe.db.set_value("Customer", "Test Loyalty Customer", "loyalty_program", "Test Multiple Loyalty")
|
||||
# assign multiple tier program to the customer
|
||||
customer = frappe.get_doc('Customer', {"customer_name": "Test Loyalty Customer"})
|
||||
customer.loyalty_program = frappe.get_doc('Loyalty Program', {'loyalty_program_name': 'Test Multiple Loyalty'}).name
|
||||
@ -92,6 +94,7 @@ class TestLoyaltyProgram(unittest.TestCase):
|
||||
|
||||
def test_cancel_sales_invoice(self):
|
||||
''' cancelling the sales invoice should cancel the earned points'''
|
||||
frappe.db.set_value("Customer", "Test Loyalty Customer", "loyalty_program", "Test Single Loyalty")
|
||||
# create a new sales invoice
|
||||
si = create_sales_invoice_record()
|
||||
si.insert()
|
||||
@ -106,6 +109,7 @@ class TestLoyaltyProgram(unittest.TestCase):
|
||||
self.assertEqual(True, (lpe is None))
|
||||
|
||||
def test_sales_invoice_return(self):
|
||||
frappe.db.set_value("Customer", "Test Loyalty Customer", "loyalty_program", "Test Single Loyalty")
|
||||
# create a new sales invoice
|
||||
si_original = create_sales_invoice_record(2)
|
||||
si_original.conversion_rate = flt(1)
|
||||
@ -149,8 +153,8 @@ def get_points_earned(self):
|
||||
""", self.name)
|
||||
return abs(flt(returned_amount[0][0])) if returned_amount else 0
|
||||
|
||||
lp_details = get_loyalty_program_details(self.customer, company=self.company,
|
||||
loyalty_program=self.loyalty_program, expiry_date=self.posting_date)
|
||||
lp_details = get_loyalty_program_details_with_points(self.customer, company=self.company,
|
||||
loyalty_program=self.loyalty_program, expiry_date=self.posting_date, include_expired_entry=True)
|
||||
if lp_details and getdate(lp_details.from_date) <= getdate(self.posting_date) and \
|
||||
(not lp_details.to_date or getdate(lp_details.to_date) >= getdate(self.posting_date)):
|
||||
returned_amount = get_returned_amount()
|
||||
@ -167,6 +171,7 @@ def create_sales_invoice_record(qty=1):
|
||||
"company": '_Test Company',
|
||||
"due_date": today(),
|
||||
"posting_date": today(),
|
||||
"currency": "INR",
|
||||
"taxes_and_charges": "",
|
||||
"debit_to": "Debtors - _TC",
|
||||
"taxes": [],
|
||||
@ -174,6 +179,7 @@ def create_sales_invoice_record(qty=1):
|
||||
'doctype': 'Sales Invoice Item',
|
||||
'item_code': frappe.get_doc('Item', {'item_name': 'Loyal Item'}).name,
|
||||
'qty': qty,
|
||||
"rate": 10000,
|
||||
'income_account': 'Sales - _TC',
|
||||
'cost_center': 'Main - _TC',
|
||||
'expense_account': 'Cost of Goods Sold - _TC'
|
||||
|
@ -7,7 +7,7 @@ import frappe, erpnext, json
|
||||
from frappe import _, scrub, ValidationError
|
||||
from frappe.utils import flt, comma_or, nowdate, getdate
|
||||
from erpnext.accounts.utils import get_outstanding_invoices, get_account_currency, get_balance_on
|
||||
from erpnext.accounts.party import get_party_account, get_patry_tax_withholding_details
|
||||
from erpnext.accounts.party import get_party_account
|
||||
from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account
|
||||
from erpnext.setup.utils import get_exchange_rate
|
||||
from erpnext.accounts.general_ledger import make_gl_entries
|
||||
|
@ -242,6 +242,9 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
|
||||
price_list: this.frm.doc.buying_price_list
|
||||
}, function() {
|
||||
me.apply_pricing_rule();
|
||||
|
||||
me.frm.doc.apply_tds = me.frm.supplier_tds ? 1 : 0;
|
||||
me.frm.set_df_property("apply_tds", "read_only", me.frm.supplier_tds ? 0 : 1);
|
||||
})
|
||||
},
|
||||
|
||||
@ -493,6 +496,10 @@ frappe.ui.form.on("Purchase Invoice", {
|
||||
},
|
||||
|
||||
onload: function(frm) {
|
||||
if(frm.doc.__onload && !frm.doc.__onload.supplier_tds) {
|
||||
me.frm.set_df_property("apply_tds", "read_only", 1);
|
||||
}
|
||||
|
||||
$.each(["warehouse", "rejected_warehouse"], function(i, field) {
|
||||
frm.set_query(field, "items", function() {
|
||||
return {
|
||||
|
@ -288,6 +288,39 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "",
|
||||
"fieldname": "apply_tds",
|
||||
"fieldtype": "Check",
|
||||
"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": "Apply Tax Withholding Amount",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"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": "column_break1",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
@ -4606,5 +4639,6 @@
|
||||
"timeline_field": "supplier",
|
||||
"title_field": "title",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0
|
||||
"track_seen": 0,
|
||||
"track_views": 0
|
||||
}
|
@ -8,7 +8,7 @@ from frappe import _, throw
|
||||
import frappe.defaults
|
||||
|
||||
from erpnext.controllers.buying_controller import BuyingController
|
||||
from erpnext.accounts.party import get_party_account, get_due_date, get_patry_tax_withholding_details
|
||||
from erpnext.accounts.party import get_party_account, get_due_date
|
||||
from erpnext.accounts.utils import get_account_currency, get_fiscal_year
|
||||
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import update_billed_amount_based_on_po
|
||||
from erpnext.stock import get_warehouse_account_map
|
||||
@ -21,6 +21,7 @@ from frappe.model.mapper import get_mapped_doc
|
||||
from six import iteritems
|
||||
from erpnext.accounts.doctype.sales_invoice.sales_invoice import validate_inter_company_party, update_linked_invoice,\
|
||||
unlink_inter_company_invoice
|
||||
from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import get_party_tax_withholding_details
|
||||
|
||||
form_grid_templates = {
|
||||
"items": "templates/form_grid/item_grid.html"
|
||||
@ -42,6 +43,10 @@ class PurchaseInvoice(BuyingController):
|
||||
'overflow_type': 'billing'
|
||||
}]
|
||||
|
||||
def onload(self):
|
||||
supplier_tds = frappe.db.get_value("Supplier", self.supplier, "tax_withholding_category")
|
||||
self.set_onload("supplier_tds", supplier_tds)
|
||||
|
||||
def before_save(self):
|
||||
if not self.on_hold:
|
||||
self.release_date = ''
|
||||
@ -54,7 +59,10 @@ class PurchaseInvoice(BuyingController):
|
||||
self.is_opening = 'No'
|
||||
|
||||
self.validate_posting_time()
|
||||
|
||||
# apply tax withholding only if checked and applicable
|
||||
self.set_tax_withholding()
|
||||
|
||||
super(PurchaseInvoice, self).validate()
|
||||
|
||||
if not self.is_return:
|
||||
@ -768,14 +776,18 @@ class PurchaseInvoice(BuyingController):
|
||||
self.db_set('release_date', None)
|
||||
|
||||
def set_tax_withholding(self):
|
||||
tax_withholding_details = get_patry_tax_withholding_details(self)
|
||||
for tax_details in tax_withholding_details:
|
||||
if flt(self.get("rounded_total") or self.grand_total) >= flt(tax_details['threshold']):
|
||||
if self.taxes:
|
||||
if tax_details['tax']['description'] not in [tax.description for tax in self.taxes]:
|
||||
self.append('taxes', tax_details['tax'])
|
||||
else:
|
||||
self.append('taxes', tax_details['tax'])
|
||||
if not self.apply_tds:
|
||||
return
|
||||
|
||||
tax_withholding_details = get_party_tax_withholding_details(self)
|
||||
accounts = []
|
||||
for d in self.taxes:
|
||||
if d.account_head == tax_withholding_details.get("account_head"):
|
||||
d.update(tax_withholding_details)
|
||||
accounts.append(d.account_head)
|
||||
|
||||
if not accounts or tax_withholding_details.get("account_head") not in accounts:
|
||||
self.append("taxes", tax_withholding_details)
|
||||
|
||||
@frappe.whitelist()
|
||||
def make_debit_note(source_name, target_doc=None):
|
||||
|
@ -22,7 +22,7 @@ from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos, get_delive
|
||||
from erpnext.setup.doctype.company.company import update_company_current_month_sales
|
||||
from erpnext.accounts.general_ledger import get_round_off_account_and_cost_center
|
||||
from erpnext.accounts.doctype.loyalty_program.loyalty_program import \
|
||||
get_loyalty_program_details, get_loyalty_details, validate_loyalty_points
|
||||
get_loyalty_program_details_with_points, get_loyalty_details, validate_loyalty_points
|
||||
|
||||
from six import iteritems
|
||||
|
||||
@ -973,12 +973,14 @@ class SalesInvoice(SellingController):
|
||||
|
||||
# collection of the loyalty points, create the ledger entry for that.
|
||||
def make_loyalty_point_entry(self):
|
||||
lp_details = get_loyalty_program_details(self.customer, company=self.company,
|
||||
loyalty_program=self.loyalty_program, expiry_date=self.posting_date)
|
||||
returned_amount = self.get_returned_amount()
|
||||
current_amount = flt(self.grand_total) - cint(self.loyalty_amount)
|
||||
eligible_amount = current_amount - returned_amount
|
||||
lp_details = get_loyalty_program_details_with_points(self.customer, company=self.company,
|
||||
current_transaction_amount=current_amount, loyalty_program=self.loyalty_program,
|
||||
expiry_date=self.posting_date, include_expired_entry=True)
|
||||
if lp_details and getdate(lp_details.from_date) <= getdate(self.posting_date) and \
|
||||
(not lp_details.to_date or getdate(lp_details.to_date) >= getdate(self.posting_date)):
|
||||
returned_amount = self.get_returned_amount()
|
||||
eligible_amount = flt(self.grand_total) - cint(self.loyalty_amount) - returned_amount
|
||||
points_earned = cint(eligible_amount/lp_details.collection_factor)
|
||||
doc = frappe.get_doc({
|
||||
"doctype": "Loyalty Point Entry",
|
||||
@ -994,7 +996,7 @@ class SalesInvoice(SellingController):
|
||||
})
|
||||
doc.flags.ignore_permissions = 1
|
||||
doc.save()
|
||||
frappe.db.set_value("Customer", self.customer, "loyalty_program_tier", lp_details.tier_name)
|
||||
self.set_loyalty_program_tier()
|
||||
|
||||
# valdite the redemption and then delete the loyalty points earned on cancel of the invoice
|
||||
def delete_loyalty_point_entry(self):
|
||||
@ -1009,9 +1011,12 @@ class SalesInvoice(SellingController):
|
||||
else:
|
||||
frappe.db.sql('''delete from `tabLoyalty Point Entry` where sales_invoice=%s''', (self.name))
|
||||
# Set loyalty program
|
||||
lp_details = get_loyalty_program_details(self.customer, company=self.company,
|
||||
loyalty_program=self.loyalty_program, expiry_date=self.posting_date)
|
||||
frappe.db.set_value("Customer", self.customer, "loyalty_program_tier", lp_details.tier_name)
|
||||
self.set_loyalty_program_tier()
|
||||
|
||||
def set_loyalty_program_tier(self):
|
||||
lp_details = get_loyalty_program_details_with_points(self.customer, company=self.company,
|
||||
loyalty_program=self.loyalty_program, include_expired_entry=True)
|
||||
frappe.db.set_value("Customer", self.customer, "loyalty_program_tier", lp_details.tier_name)
|
||||
|
||||
def get_returned_amount(self):
|
||||
returned_amount = frappe.db.sql("""
|
||||
|
@ -15,51 +15,21 @@
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "percent_of_tax_withheld",
|
||||
"fieldtype": "Float",
|
||||
"fieldname": "category_name",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Percent of Tax Withheld",
|
||||
"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": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "threshold",
|
||||
"fieldtype": "Currency",
|
||||
"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": "Threshold",
|
||||
"label": "Category Name",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
@ -77,100 +47,74 @@
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "column_break_3",
|
||||
"fieldtype": "Column Break",
|
||||
"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,
|
||||
"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,
|
||||
"fieldname": "section_break_8",
|
||||
"fieldtype": "Section Break",
|
||||
"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": "Tax Withholding Rates",
|
||||
"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_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "book_on_invoice",
|
||||
"fieldtype": "Check",
|
||||
"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": "Book on Invoice",
|
||||
"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,
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "rates",
|
||||
"fieldtype": "Table",
|
||||
"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": "Rates",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Tax Withholding Rate",
|
||||
"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_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "withhold_cumulative_tax_amount",
|
||||
"fieldtype": "Check",
|
||||
"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": "Withhold Cumulative Tax Amount On First Invoice After Threshold",
|
||||
"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_on_submit": 0,
|
||||
"bold": 0,
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "section_break_7",
|
||||
@ -182,6 +126,7 @@
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Account Details",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
@ -199,6 +144,7 @@
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -240,7 +186,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-05-16 13:57:52.489773",
|
||||
"modified": "2018-07-17 22:53:26.193179",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Tax Withholding Category",
|
||||
@ -305,12 +251,13 @@
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"quick_entry": 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_seen": 0,
|
||||
"track_views": 0
|
||||
}
|
@ -4,7 +4,115 @@
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import flt
|
||||
from erpnext.accounts.utils import get_fiscal_year
|
||||
|
||||
class TaxWithholdingCategory(Document):
|
||||
pass
|
||||
pass
|
||||
|
||||
def get_party_tax_withholding_details(ref_doc):
|
||||
tax_withholding_category = frappe.db.get_value('Supplier', ref_doc.supplier, 'tax_withholding_category')
|
||||
if not tax_withholding_category:
|
||||
return
|
||||
|
||||
fy = get_fiscal_year(ref_doc.posting_date, company=ref_doc.company)
|
||||
tax_details = get_tax_withholding_details(tax_withholding_category, fy[0], ref_doc.company)
|
||||
tds_amount = get_tds_amount(ref_doc, tax_details, fy)
|
||||
tax_row = get_tax_row(tax_details, tds_amount)
|
||||
return tax_row
|
||||
|
||||
def get_tax_withholding_details(tax_withholding_category, fiscal_year, company):
|
||||
tax_withholding = frappe.get_doc("Tax Withholding Category", tax_withholding_category)
|
||||
|
||||
tax_rate_detail = get_tax_withholding_rates(tax_withholding, fiscal_year)
|
||||
|
||||
for account_detail in tax_withholding.accounts:
|
||||
if company == account_detail.company:
|
||||
return frappe._dict({
|
||||
"account_head": account_detail.account,
|
||||
"rate": tax_rate_detail.tax_withholding_rate,
|
||||
"threshold": tax_rate_detail.single_threshold,
|
||||
"cumulative_threshold": tax_rate_detail.cumulative_threshold,
|
||||
"description": tax_withholding.category_name
|
||||
})
|
||||
|
||||
def get_tax_withholding_rates(tax_withholding, fiscal_year):
|
||||
# returns the row that matches with the fiscal year from posting date
|
||||
for rate in tax_withholding.rates:
|
||||
if rate.fiscal_year == fiscal_year:
|
||||
return rate
|
||||
|
||||
frappe.throw(_("No Tax Withholding data found for the current Fiscal Year."))
|
||||
|
||||
def get_tax_row(tax_details, tds_amount):
|
||||
return {
|
||||
"category": "Total",
|
||||
"add_deduct_tax": "Deduct",
|
||||
"charge_type": "Actual",
|
||||
"account_head": tax_details.account_head,
|
||||
"description": tax_details.description,
|
||||
"tax_amount": tds_amount
|
||||
}
|
||||
|
||||
def get_tds_amount(ref_doc, tax_details, fiscal_year_details):
|
||||
fiscal_year, year_start_date, year_end_date = fiscal_year_details
|
||||
tds_amount = 0
|
||||
|
||||
def _get_tds():
|
||||
tds_amount = 0
|
||||
if not tax_details.threshold or ref_doc.net_total >= tax_details.threshold:
|
||||
tds_amount = ref_doc.net_total * tax_details.rate / 100
|
||||
return tds_amount
|
||||
|
||||
if tax_details.cumulative_threshold:
|
||||
entries = frappe.db.sql("""
|
||||
select voucher_no, credit
|
||||
from `tabGL Entry`
|
||||
where party=%s and fiscal_year=%s and credit > 0
|
||||
""", (ref_doc.supplier, fiscal_year), 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(ref_doc.supplier, fiscal_year)
|
||||
|
||||
tds_deducted = 0
|
||||
if vouchers:
|
||||
tds_deducted = flt(frappe.db.sql("""
|
||||
select sum(credit)
|
||||
from `tabGL Entry`
|
||||
where account=%s and fiscal_year=%s and credit > 0
|
||||
and voucher_no in ({0})
|
||||
""".format(', '.join(["'%s'" % d for d in vouchers])),
|
||||
(tax_details.account_head, fiscal_year))[0][0])
|
||||
|
||||
debit_note_amount = get_debit_note_amount(ref_doc.supplier, year_start_date, year_end_date)
|
||||
|
||||
total_invoiced_amount = supplier_credit_amount + tds_deducted \
|
||||
+ flt(ref_doc.net_total) - debit_note_amount
|
||||
if total_invoiced_amount >= tax_details.cumulative_threshold:
|
||||
total_applicable_tds = total_invoiced_amount * tax_details.rate / 100
|
||||
tds_amount = min(total_applicable_tds - tds_deducted, ref_doc.net_total)
|
||||
else:
|
||||
tds_amount = _get_tds()
|
||||
else:
|
||||
tds_amount = _get_tds()
|
||||
|
||||
return tds_amount
|
||||
|
||||
def get_advance_vouchers(supplier, fiscal_year):
|
||||
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))
|
||||
|
||||
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
|
||||
and posting_date between %s and %s
|
||||
""", (supplier, year_start_date, year_end_date)))
|
@ -5,6 +5,107 @@ from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
import unittest
|
||||
from frappe.utils import today
|
||||
|
||||
test_dependencies = ["Supplier Group"]
|
||||
|
||||
class TestTaxWithholdingCategory(unittest.TestCase):
|
||||
pass
|
||||
@classmethod
|
||||
def setUpClass(self):
|
||||
# create relevant supplier, etc
|
||||
create_records()
|
||||
|
||||
def test_single_threshold_tds(self):
|
||||
frappe.db.set_value("Supplier", "Test TDS Supplier", "tax_withholding_category", "TDS - 194D - Individual")
|
||||
pi = create_purchase_invoice()
|
||||
pi.submit()
|
||||
|
||||
self.assertEqual(pi.taxes_and_charges_deducted, 800)
|
||||
self.assertEqual(pi.grand_total, 15200)
|
||||
|
||||
# check gl entry for the purchase invoice
|
||||
gl_entries = frappe.db.get_all('GL Entry', filters={'voucher_no': pi.name}, fields=["*"])
|
||||
self.assertEqual(len(gl_entries), 3)
|
||||
for d in gl_entries:
|
||||
if d.account == pi.credit_to:
|
||||
self.assertEqual(d.credit, 15200)
|
||||
elif d.account == pi.items[0].get("expense_account"):
|
||||
self.assertEqual(d.debit, 16000)
|
||||
elif d.account == pi.taxes[0].get("account_head"):
|
||||
self.assertEqual(d.credit, 800)
|
||||
else:
|
||||
raise ValueError("Account head does not match.")
|
||||
|
||||
# delete purchase invoice to avoid it interefering in other tests
|
||||
pi.cancel()
|
||||
frappe.delete_doc('Purchase Invoice', pi.name)
|
||||
|
||||
def test_cumulative_threshold_tds(self):
|
||||
frappe.db.set_value("Supplier", "Test TDS Supplier", "tax_withholding_category", "TDS - 194C - Individual")
|
||||
invoices = []
|
||||
|
||||
# create invoices for lower than single threshold tax rate
|
||||
for _ in xrange(6):
|
||||
pi = create_purchase_invoice()
|
||||
pi.submit()
|
||||
invoices.append(pi)
|
||||
|
||||
# create another invoice whose total when added to previously created invoice,
|
||||
# surpasses cumulative threshhold
|
||||
pi = create_purchase_invoice()
|
||||
pi.submit()
|
||||
|
||||
# assert equal tax deduction on total invoice amount uptil now
|
||||
self.assertEqual(pi.taxes_and_charges_deducted, 1120)
|
||||
self.assertEqual(pi.grand_total, 14880)
|
||||
invoices.append(pi)
|
||||
|
||||
# delete invoices to avoid clashing
|
||||
for d in invoices:
|
||||
d.cancel()
|
||||
frappe.delete_doc("Purchase Invoice", d.name)
|
||||
|
||||
def create_purchase_invoice(qty=1):
|
||||
# return sales invoice doc object
|
||||
item = frappe.get_doc('Item', {'item_name': 'TDS Item'})
|
||||
pi = frappe.get_doc({
|
||||
"doctype": "Purchase Invoice",
|
||||
"posting_date": today(),
|
||||
"apply_tds": 1,
|
||||
"supplier": frappe.get_doc('Supplier', {"supplier_name": "Test TDS Supplier"}).name,
|
||||
"company": '_Test Company',
|
||||
"taxes_and_charges": "",
|
||||
"currency": "INR",
|
||||
"credit_to": "Creditors - _TC",
|
||||
"taxes": [],
|
||||
"items": [{
|
||||
'doctype': 'Purchase Invoice Item',
|
||||
'item_code': item.name,
|
||||
'qty': qty,
|
||||
'rate': 16000,
|
||||
'cost_center': 'Main - _TC',
|
||||
'expense_account': 'Stock Received But Not Billed - _TC'
|
||||
}]
|
||||
})
|
||||
|
||||
pi.save()
|
||||
return pi
|
||||
|
||||
def create_records():
|
||||
# create a new supplier
|
||||
frappe.get_doc({
|
||||
"supplier_group": "_Test Supplier Group",
|
||||
"supplier_name": "Test TDS Supplier",
|
||||
"doctype": "Supplier",
|
||||
"tax_withholding_category": "TDS - 194D - Individual"
|
||||
}).insert()
|
||||
|
||||
# create an item
|
||||
frappe.get_doc({
|
||||
"doctype": "Item",
|
||||
"item_code": "TDS Item",
|
||||
"item_name": "TDS Item",
|
||||
"item_group": "All Item Groups",
|
||||
"company": "_Test Company",
|
||||
"is_stock_item": 0,
|
||||
}).insert()
|
@ -4,7 +4,7 @@
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"beta": 0,
|
||||
"creation": "2018-05-11 13:32:33.825307",
|
||||
"creation": "2018-07-17 16:53:13.716665",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
@ -18,8 +18,8 @@
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "tax_withholding_category",
|
||||
"columns": 2,
|
||||
"fieldname": "fiscal_year",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
@ -28,10 +28,10 @@
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Tax Withholding Category",
|
||||
"label": "Fiscal Year",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Tax Withholding Category",
|
||||
"options": "Fiscal Year",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
@ -39,7 +39,7 @@
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
@ -51,41 +51,8 @@
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "valid_till",
|
||||
"fieldtype": "Date",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Valid Till",
|
||||
"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,
|
||||
"fetch_from": "tax_withholding_category.percent_of_tax_withheld",
|
||||
"fieldname": "applicable_percent",
|
||||
"columns": 2,
|
||||
"fieldname": "tax_withholding_rate",
|
||||
"fieldtype": "Float",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
@ -94,7 +61,38 @@
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Applicable Percent",
|
||||
"label": "Tax Withholding Rate",
|
||||
"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": 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": "column_break_3",
|
||||
"fieldtype": "Column Break",
|
||||
"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,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
@ -116,17 +114,49 @@
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "certificate_received",
|
||||
"fieldtype": "Check",
|
||||
"columns": 3,
|
||||
"fieldname": "single_threshold",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Certificate Received",
|
||||
"label": "Single Transaction Threshold",
|
||||
"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": 3,
|
||||
"fieldname": "cumulative_threshold",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Cumulative Transaction Threshold",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
@ -153,10 +183,10 @@
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-06-18 22:38:38.638721",
|
||||
"modified": "2018-07-17 17:13:09.819580",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Party Tax Withholding Config",
|
||||
"module": "Accounts",
|
||||
"name": "Tax Withholding Rate",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
@ -6,5 +6,5 @@ from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
class PartyTaxWithholdingConfig(Document):
|
||||
class TaxWithholdingRate(Document):
|
||||
pass
|
@ -65,6 +65,10 @@ def _get_party_details(party=None, account=None, party_type="Customer", company=
|
||||
"allocated_percentage": d.allocated_percentage or None
|
||||
} for d in party.get("sales_team")]
|
||||
|
||||
# supplier tax withholding category
|
||||
if party_type == "Supplier" and party:
|
||||
out["supplier_tds"] = frappe.get_value(party_type, party.name, "tax_withholding_category")
|
||||
|
||||
return out
|
||||
|
||||
def set_address_details(out, party, party_type, doctype=None, company=None):
|
||||
@ -507,57 +511,3 @@ def get_party_shipping_address(doctype, name):
|
||||
return out[0][0]
|
||||
else:
|
||||
return ''
|
||||
|
||||
def get_patry_tax_withholding_details(ref_doc):
|
||||
supplier = frappe.get_doc("Supplier", ref_doc.supplier)
|
||||
tax_withholding_details = []
|
||||
for tax in supplier.tax_withholding_config:
|
||||
tax_mapper = get_tax_mapper()
|
||||
|
||||
set_tax_withholding_details(tax_mapper, ref_doc, tax_withholding_category=tax.tax_withholding_category)
|
||||
|
||||
if tax.valid_till and date_diff(tax.valid_till, ref_doc.posting_date) > 0:
|
||||
tax_mapper.update({
|
||||
"rate": tax.applicable_percent
|
||||
})
|
||||
|
||||
prepare_tax_withholding_details(tax_mapper, tax_withholding_details)
|
||||
|
||||
return tax_withholding_details
|
||||
|
||||
def prepare_tax_withholding_details(tax_mapper, tax_withholding_details):
|
||||
if tax_mapper.get('account_head'):
|
||||
|
||||
tax_withholding_details.append({
|
||||
"threshold": tax_mapper['threshold'],
|
||||
"tax": tax_mapper
|
||||
})
|
||||
|
||||
del tax_mapper['threshold']
|
||||
|
||||
def set_tax_withholding_details(tax_mapper, ref_doc, tax_withholding_category=None, use_default=0):
|
||||
if tax_withholding_category:
|
||||
tax_withholding = frappe.get_doc("Tax Withholding Category", tax_withholding_category)
|
||||
|
||||
if tax_withholding.book_on_invoice and ref_doc.doctype=='Purchase Invoice' \
|
||||
or ref_doc.doctype in ('Payment Entry', 'Journal Entry'):
|
||||
|
||||
for account_detail in tax_withholding.accounts:
|
||||
if ref_doc.company == account_detail.company:
|
||||
tax_mapper.update({
|
||||
"account_head": account_detail.account,
|
||||
"rate": tax_withholding.percent_of_tax_withheld,
|
||||
"threshold": tax_withholding.threshold,
|
||||
"description": tax_withholding.name
|
||||
})
|
||||
|
||||
def get_tax_mapper():
|
||||
return {
|
||||
"category": "Total",
|
||||
"add_deduct_tax": "Deduct",
|
||||
"charge_type": "On Net Total",
|
||||
"rate": 0,
|
||||
"description": '',
|
||||
"account_head": '',
|
||||
"threshold": 0.0
|
||||
}
|
||||
|
@ -186,6 +186,39 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "tax_withholding_category",
|
||||
"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": "Tax Withholding Category",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Tax Withholding Category",
|
||||
"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,
|
||||
"default": "0",
|
||||
"fieldname": "is_internal_supplier",
|
||||
"fieldtype": "Check",
|
||||
@ -1154,39 +1187,6 @@
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "tax_withholding_config",
|
||||
"fieldtype": "Table",
|
||||
"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": "Tax Withholding Account",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Party Tax Withholding Config",
|
||||
"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,
|
||||
@ -1363,7 +1363,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-06-26 13:44:53.124637",
|
||||
"modified": "2018-07-17 12:14:59.417939",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Supplier",
|
||||
@ -1512,5 +1512,6 @@
|
||||
"sort_order": "ASC",
|
||||
"title_field": "supplier_name",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0
|
||||
"track_seen": 0,
|
||||
"track_views": 0
|
||||
}
|
@ -42,6 +42,7 @@ erpnext.utils.get_party_details = function(frm, method, args, callback) {
|
||||
args: args,
|
||||
callback: function(r) {
|
||||
if(r.message) {
|
||||
frm.supplier_tds = r.message.supplier_tds;
|
||||
frm.updating_party_details = true;
|
||||
frappe.run_serially([
|
||||
() => frm.set_value(r.message),
|
||||
|
@ -7,6 +7,8 @@ import frappe, os, json
|
||||
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
|
||||
from frappe.permissions import add_permission
|
||||
from erpnext.regional.india import states
|
||||
from erpnext.accounts.utils import get_fiscal_year
|
||||
from frappe.utils import today
|
||||
|
||||
def setup(company=None, patch=True):
|
||||
make_custom_fields()
|
||||
@ -257,8 +259,10 @@ def make_fixtures(company=None):
|
||||
doc.insert()
|
||||
except frappe.NameError:
|
||||
pass
|
||||
except frappe.DuplicateEntryError:
|
||||
pass
|
||||
|
||||
# create tds fixtures
|
||||
# create records for Tax Withholding Category
|
||||
set_tax_withholding_category(company)
|
||||
|
||||
def set_salary_components(docs):
|
||||
@ -279,19 +283,24 @@ def set_tax_withholding_category(company):
|
||||
if company and tds_account:
|
||||
accounts = [dict(company=company, account=tds_account)]
|
||||
|
||||
tds = frappe.get_doc({
|
||||
'doctype': 'Tax Withholding Category', 'name': 'TDS',
|
||||
'percent_of_tax_withheld': 10,'threshold': 150000, 'book_on_invoice': 1,
|
||||
'withhold_cumulative_tax_amount': 0, 'accounts': accounts
|
||||
})
|
||||
fiscal_year = get_fiscal_year(today(), company=accounts[0].get('company'))[0]
|
||||
docs = get_tds_details(accounts, fiscal_year)
|
||||
|
||||
try:
|
||||
tds.flags.ignore_permissions = True
|
||||
tds.insert()
|
||||
except frappe.DuplicateEntryError:
|
||||
tds = frappe.get_doc("Tax Withholding Category", tds.get("name"))
|
||||
tds.append("accounts", accounts[0])
|
||||
tds.save()
|
||||
for d in docs:
|
||||
try:
|
||||
doc = frappe.get_doc(d)
|
||||
doc.flags.ignore_permissions = True
|
||||
doc.insert()
|
||||
except frappe.DuplicateEntryError:
|
||||
doc = frappe.get_doc("Tax Withholding Category", d.get("name"))
|
||||
doc.append("accounts", accounts[0])
|
||||
|
||||
# if fiscal year don't match with any of the already entered data, append rate row
|
||||
fy_exist = [k for k in doc.get('rates') if k.get('fiscal_year')==fiscal_year]
|
||||
if not fy_exist:
|
||||
doc.append("rates", d.get('rates')[0])
|
||||
|
||||
doc.save()
|
||||
|
||||
def set_tds_account(docs, company):
|
||||
abbr = frappe.get_value("Company", company, "abbr")
|
||||
@ -301,3 +310,148 @@ def set_tds_account(docs, company):
|
||||
"parent_account": "Duties and Taxes - {0}".format(abbr), "company": company
|
||||
}
|
||||
])
|
||||
|
||||
def get_tds_details(accounts, fiscal_year):
|
||||
# bootstrap default tax withholding sections
|
||||
return [
|
||||
dict(name="TDS - 194C - Company",
|
||||
category_name="Payment to Contractors (Single / Aggregate)",
|
||||
doctype="Tax Withholding Category", accounts=accounts,
|
||||
rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 2,
|
||||
"single_threshold": 30000, "cumulative_threshold": 100000}]),
|
||||
dict(name="TDS - 194C - Individual",
|
||||
category_name="Payment to Contractors (Single / Aggregate)",
|
||||
doctype="Tax Withholding Category", accounts=accounts,
|
||||
rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 1,
|
||||
"single_threshold": 30000, "cumulative_threshold": 100000}]),
|
||||
dict(name="TDS - 194C - No PAN / Invalid PAN",
|
||||
category_name="Payment to Contractors (Single / Aggregate)",
|
||||
doctype="Tax Withholding Category", accounts=accounts,
|
||||
rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 20,
|
||||
"single_threshold": 30000, "cumulative_threshold": 100000}]),
|
||||
dict(name="TDS - 194D - Company",
|
||||
category_name="Insurance Commission",
|
||||
doctype="Tax Withholding Category", accounts=accounts,
|
||||
rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 5,
|
||||
"single_threshold": 15000, "cumulative_threshold": 0}]),
|
||||
dict(name="TDS - 194D - Company Assessee",
|
||||
category_name="Insurance Commission",
|
||||
doctype="Tax Withholding Category", accounts=accounts,
|
||||
rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 10,
|
||||
"single_threshold": 15000, "cumulative_threshold": 0}]),
|
||||
dict(name="TDS - 194D - Individual",
|
||||
category_name="Insurance Commission",
|
||||
doctype="Tax Withholding Category", accounts=accounts,
|
||||
rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 5,
|
||||
"single_threshold": 15000, "cumulative_threshold": 0}]),
|
||||
dict(name="TDS - 194D - No PAN / Invalid PAN",
|
||||
category_name="Insurance Commission",
|
||||
doctype="Tax Withholding Category", accounts=accounts,
|
||||
rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 20,
|
||||
"single_threshold": 15000, "cumulative_threshold": 0}]),
|
||||
dict(name="TDS - 194DA - Company",
|
||||
category_name="Non-exempt payments made under a life insurance policy",
|
||||
doctype="Tax Withholding Category", accounts=accounts,
|
||||
rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 1,
|
||||
"single_threshold": 100000, "cumulative_threshold": 0}]),
|
||||
dict(name="TDS - 194DA - Individual",
|
||||
category_name="Non-exempt payments made under a life insurance policy",
|
||||
doctype="Tax Withholding Category", accounts=accounts,
|
||||
rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 1,
|
||||
"single_threshold": 100000, "cumulative_threshold": 0}]),
|
||||
dict(name="TDS - 194DA - No PAN / Invalid PAN",
|
||||
category_name="Non-exempt payments made under a life insurance policy",
|
||||
doctype="Tax Withholding Category", accounts=accounts,
|
||||
rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 20,
|
||||
"single_threshold": 100000, "cumulative_threshold": 0}]),
|
||||
dict(name="TDS - 194H - Company",
|
||||
category_name="Commission / Brokerage",
|
||||
doctype="Tax Withholding Category", accounts=accounts,
|
||||
rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 5,
|
||||
"single_threshold": 15000, "cumulative_threshold": 0}]),
|
||||
dict(name="TDS - 194H - Individual",
|
||||
category_name="Commission / Brokerage",
|
||||
doctype="Tax Withholding Category", accounts=accounts,
|
||||
rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 5,
|
||||
"single_threshold": 15000, "cumulative_threshold": 0}]),
|
||||
dict(name="TDS - 194H - No PAN / Invalid PAN",
|
||||
category_name="Commission / Brokerage",
|
||||
doctype="Tax Withholding Category", accounts=accounts,
|
||||
rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 20,
|
||||
"single_threshold": 15000, "cumulative_threshold": 0}]),
|
||||
dict(name="TDS - 194I - Rent - Company",
|
||||
category_name="Rent",
|
||||
doctype="Tax Withholding Category", accounts=accounts,
|
||||
rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 10,
|
||||
"single_threshold": 180000, "cumulative_threshold": 0}]),
|
||||
dict(name="TDS - 194I - Rent - Individual",
|
||||
category_name="Rent",
|
||||
doctype="Tax Withholding Category", accounts=accounts,
|
||||
rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 10,
|
||||
"single_threshold": 180000, "cumulative_threshold": 0}]),
|
||||
dict(name="TDS - 194I - Rent - No PAN / Invalid PAN",
|
||||
category_name="Rent",
|
||||
doctype="Tax Withholding Category", accounts=accounts,
|
||||
rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 20,
|
||||
"single_threshold": 180000, "cumulative_threshold": 0}]),
|
||||
dict(name="TDS - 194I - Rent/Machinery - Company",
|
||||
category_name="Rent-Plant / Machinery",
|
||||
doctype="Tax Withholding Category", accounts=accounts,
|
||||
rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 2,
|
||||
"single_threshold": 180000, "cumulative_threshold": 0}]),
|
||||
dict(name="TDS - 194I - Rent/Machinery - Individual",
|
||||
category_name="Rent-Plant / Machinery",
|
||||
doctype="Tax Withholding Category", accounts=accounts,
|
||||
rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 2,
|
||||
"single_threshold": 180000, "cumulative_threshold": 0}]),
|
||||
dict(name="TDS - 194I - Rent/Machinery - No PAN / Invalid PAN",
|
||||
category_name="Rent-Plant / Machinery",
|
||||
doctype="Tax Withholding Category", accounts=accounts,
|
||||
rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 20,
|
||||
"single_threshold": 180000, "cumulative_threshold": 0}]),
|
||||
dict(name="TDS - 194J - Professional Fees - Company",
|
||||
category_name="Professional Fees",
|
||||
doctype="Tax Withholding Category", accounts=accounts,
|
||||
rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 10,
|
||||
"single_threshold": 30000, "cumulative_threshold": 0}]),
|
||||
dict(name="TDS - 194J - Professional Fees - Individual",
|
||||
category_name="Professional Fees",
|
||||
doctype="Tax Withholding Category", accounts=accounts,
|
||||
rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 10,
|
||||
"single_threshold": 30000, "cumulative_threshold": 0}]),
|
||||
dict(name="TDS - 194J - Professional Fees - No PAN / Invalid PAN",
|
||||
category_name="Professional Fees",
|
||||
doctype="Tax Withholding Category", accounts=accounts,
|
||||
rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 20,
|
||||
"single_threshold": 30000, "cumulative_threshold": 0}]),
|
||||
dict(name="TDS - 194J - Director Fees - Company",
|
||||
category_name="Director Fees",
|
||||
doctype="Tax Withholding Category", accounts=accounts,
|
||||
rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 10,
|
||||
"single_threshold": 0, "cumulative_threshold": 0}]),
|
||||
dict(name="TDS - 194J - Director Fees - Individual",
|
||||
category_name="Director Fees",
|
||||
doctype="Tax Withholding Category", accounts=accounts,
|
||||
rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 10,
|
||||
"single_threshold": 0, "cumulative_threshold": 0}]),
|
||||
dict(name="TDS - 194J - Director Fees - No PAN / Invalid PAN",
|
||||
category_name="Director Fees",
|
||||
doctype="Tax Withholding Category", accounts=accounts,
|
||||
rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 20,
|
||||
"single_threshold": 0, "cumulative_threshold": 0}]),
|
||||
dict(name="TDS - 194 - Dividends - Company",
|
||||
category_name="Dividends",
|
||||
doctype="Tax Withholding Category", accounts=accounts,
|
||||
rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 10,
|
||||
"single_threshold": 2500, "cumulative_threshold": 0}]),
|
||||
dict(name="TDS - 194 - Dividends - Individual",
|
||||
category_name="Dividends",
|
||||
doctype="Tax Withholding Category", accounts=accounts,
|
||||
rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 10,
|
||||
"single_threshold": 2500, "cumulative_threshold": 0}]),
|
||||
dict(name="TDS - 194 - Dividends - No PAN / Invalid PAN",
|
||||
category_name="Dividends",
|
||||
doctype="Tax Withholding Category", accounts=accounts,
|
||||
rates=[{"fiscal_year": fiscal_year, "tax_withholding_rate": 20,
|
||||
"single_threshold": 2500, "cumulative_threshold": 0}])
|
||||
]
|
||||
|
@ -8,6 +8,7 @@ from frappe.utils import random_string
|
||||
from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import get_charts_for_country
|
||||
|
||||
test_ignore = ["Account", "Cost Center", "Payment Terms Template", "Salary Component"]
|
||||
test_dependencies = ["Fiscal Year"]
|
||||
test_records = frappe.get_test_records('Company')
|
||||
|
||||
class TestCompany(unittest.TestCase):
|
||||
|
@ -36,8 +36,8 @@ def get_context(context):
|
||||
# check for the loyalty program of the customer
|
||||
customer_loyalty_program = frappe.db.get_value("Customer", context.doc.customer, "loyalty_program")
|
||||
if customer_loyalty_program:
|
||||
from erpnext.accounts.doctype.loyalty_program.loyalty_program import get_loyalty_program_details
|
||||
loyalty_program_details = get_loyalty_program_details(context.doc.customer, customer_loyalty_program)
|
||||
from erpnext.accounts.doctype.loyalty_program.loyalty_program import get_loyalty_program_details_with_points
|
||||
loyalty_program_details = get_loyalty_program_details_with_points(context.doc.customer, customer_loyalty_program)
|
||||
context.available_loyalty_points = int(loyalty_program_details.get("loyalty_points"))
|
||||
|
||||
def get_attachments(dt, dn):
|
||||
|
Loading…
x
Reference in New Issue
Block a user