mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-26 16:34:46 +00:00
[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:
@@ -13,28 +13,47 @@ class LoyaltyProgram(Document):
|
|||||||
pass
|
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:
|
if not expiry_date:
|
||||||
expiry_date = today()
|
expiry_date = today()
|
||||||
|
|
||||||
args_list = [customer, loyalty_program, expiry_date, expiry_date]
|
|
||||||
condition = ''
|
condition = ''
|
||||||
if company:
|
if company:
|
||||||
condition = " and company=%s "
|
condition = " and company='%s' " % frappe.db.escape(company)
|
||||||
args_list.append(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,
|
loyalty_point_details = frappe.db.sql('''select sum(loyalty_points) as loyalty_points,
|
||||||
sum(purchase_amount) as total_spent from `tabLoyalty Point Entry`
|
sum(purchase_amount) as total_spent from `tabLoyalty Point Entry`
|
||||||
where customer=%s and loyalty_program=%s
|
where customer=%s and loyalty_program=%s and posting_date <= %s
|
||||||
and expiry_date>=%s and posting_date <= %s
|
|
||||||
{condition}
|
{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:
|
if loyalty_point_details:
|
||||||
return loyalty_point_details[0]
|
return loyalty_point_details[0]
|
||||||
else:
|
else:
|
||||||
return {"loyalty_points": 0, "total_spent": 0}
|
return {"loyalty_points": 0, "total_spent": 0}
|
||||||
|
|
||||||
@frappe.whitelist()
|
@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()
|
lp_details = frappe._dict()
|
||||||
|
|
||||||
if not loyalty_program:
|
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)
|
loyalty_program = frappe.get_doc("Loyalty Program", loyalty_program)
|
||||||
lp_details.update({"loyalty_program": loyalty_program.name})
|
lp_details.update({"loyalty_program": loyalty_program.name})
|
||||||
lp_details.update(loyalty_program.as_dict())
|
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
|
return lp_details
|
||||||
|
|
||||||
@frappe.whitelist()
|
@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"))
|
frappe.throw(_("The Loyalty Program isn't valid for the selected company"))
|
||||||
|
|
||||||
if loyalty_program and points_to_redeem:
|
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)
|
posting_date, ref_doc.company)
|
||||||
|
|
||||||
if points_to_redeem > loyalty_program_details.loyalty_points:
|
if points_to_redeem > loyalty_program_details.loyalty_points:
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from __future__ import unicode_literals
|
|||||||
import frappe
|
import frappe
|
||||||
import unittest
|
import unittest
|
||||||
from frappe.utils import today, cint, flt, getdate
|
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):
|
class TestLoyaltyProgram(unittest.TestCase):
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -15,6 +15,7 @@ class TestLoyaltyProgram(unittest.TestCase):
|
|||||||
create_records()
|
create_records()
|
||||||
|
|
||||||
def test_loyalty_points_earned_single_tier(self):
|
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
|
# create a new sales invoice
|
||||||
si_original = create_sales_invoice_record()
|
si_original = create_sales_invoice_record()
|
||||||
si_original.insert()
|
si_original.insert()
|
||||||
@@ -50,6 +51,7 @@ class TestLoyaltyProgram(unittest.TestCase):
|
|||||||
frappe.delete_doc('Sales Invoice', d.name)
|
frappe.delete_doc('Sales Invoice', d.name)
|
||||||
|
|
||||||
def test_loyalty_points_earned_multiple_tier(self):
|
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
|
# assign multiple tier program to the customer
|
||||||
customer = frappe.get_doc('Customer', {"customer_name": "Test Loyalty 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
|
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):
|
def test_cancel_sales_invoice(self):
|
||||||
''' cancelling the sales invoice should cancel the earned points'''
|
''' 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
|
# create a new sales invoice
|
||||||
si = create_sales_invoice_record()
|
si = create_sales_invoice_record()
|
||||||
si.insert()
|
si.insert()
|
||||||
@@ -106,6 +109,7 @@ class TestLoyaltyProgram(unittest.TestCase):
|
|||||||
self.assertEqual(True, (lpe is None))
|
self.assertEqual(True, (lpe is None))
|
||||||
|
|
||||||
def test_sales_invoice_return(self):
|
def test_sales_invoice_return(self):
|
||||||
|
frappe.db.set_value("Customer", "Test Loyalty Customer", "loyalty_program", "Test Single Loyalty")
|
||||||
# create a new sales invoice
|
# create a new sales invoice
|
||||||
si_original = create_sales_invoice_record(2)
|
si_original = create_sales_invoice_record(2)
|
||||||
si_original.conversion_rate = flt(1)
|
si_original.conversion_rate = flt(1)
|
||||||
@@ -149,8 +153,8 @@ def get_points_earned(self):
|
|||||||
""", self.name)
|
""", self.name)
|
||||||
return abs(flt(returned_amount[0][0])) if returned_amount else 0
|
return abs(flt(returned_amount[0][0])) if returned_amount else 0
|
||||||
|
|
||||||
lp_details = get_loyalty_program_details(self.customer, company=self.company,
|
lp_details = get_loyalty_program_details_with_points(self.customer, company=self.company,
|
||||||
loyalty_program=self.loyalty_program, expiry_date=self.posting_date)
|
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 \
|
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)):
|
(not lp_details.to_date or getdate(lp_details.to_date) >= getdate(self.posting_date)):
|
||||||
returned_amount = get_returned_amount()
|
returned_amount = get_returned_amount()
|
||||||
@@ -167,6 +171,7 @@ def create_sales_invoice_record(qty=1):
|
|||||||
"company": '_Test Company',
|
"company": '_Test Company',
|
||||||
"due_date": today(),
|
"due_date": today(),
|
||||||
"posting_date": today(),
|
"posting_date": today(),
|
||||||
|
"currency": "INR",
|
||||||
"taxes_and_charges": "",
|
"taxes_and_charges": "",
|
||||||
"debit_to": "Debtors - _TC",
|
"debit_to": "Debtors - _TC",
|
||||||
"taxes": [],
|
"taxes": [],
|
||||||
@@ -174,6 +179,7 @@ def create_sales_invoice_record(qty=1):
|
|||||||
'doctype': 'Sales Invoice Item',
|
'doctype': 'Sales Invoice Item',
|
||||||
'item_code': frappe.get_doc('Item', {'item_name': 'Loyal Item'}).name,
|
'item_code': frappe.get_doc('Item', {'item_name': 'Loyal Item'}).name,
|
||||||
'qty': qty,
|
'qty': qty,
|
||||||
|
"rate": 10000,
|
||||||
'income_account': 'Sales - _TC',
|
'income_account': 'Sales - _TC',
|
||||||
'cost_center': 'Main - _TC',
|
'cost_center': 'Main - _TC',
|
||||||
'expense_account': 'Cost of Goods Sold - _TC'
|
'expense_account': 'Cost of Goods Sold - _TC'
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import frappe, erpnext, json
|
|||||||
from frappe import _, scrub, ValidationError
|
from frappe import _, scrub, ValidationError
|
||||||
from frappe.utils import flt, comma_or, nowdate, getdate
|
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.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.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account
|
||||||
from erpnext.setup.utils import get_exchange_rate
|
from erpnext.setup.utils import get_exchange_rate
|
||||||
from erpnext.accounts.general_ledger import make_gl_entries
|
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
|
price_list: this.frm.doc.buying_price_list
|
||||||
}, function() {
|
}, function() {
|
||||||
me.apply_pricing_rule();
|
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) {
|
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) {
|
$.each(["warehouse", "rejected_warehouse"], function(i, field) {
|
||||||
frm.set_query(field, "items", function() {
|
frm.set_query(field, "items", function() {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -281,6 +281,39 @@
|
|||||||
"translatable": 0,
|
"translatable": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_in_quick_entry": 0,
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"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_bulk_edit": 0,
|
||||||
"allow_in_quick_entry": 0,
|
"allow_in_quick_entry": 0,
|
||||||
@@ -4606,5 +4639,6 @@
|
|||||||
"timeline_field": "supplier",
|
"timeline_field": "supplier",
|
||||||
"title_field": "title",
|
"title_field": "title",
|
||||||
"track_changes": 1,
|
"track_changes": 1,
|
||||||
"track_seen": 0
|
"track_seen": 0,
|
||||||
|
"track_views": 0
|
||||||
}
|
}
|
||||||
@@ -8,7 +8,7 @@ from frappe import _, throw
|
|||||||
import frappe.defaults
|
import frappe.defaults
|
||||||
|
|
||||||
from erpnext.controllers.buying_controller import BuyingController
|
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.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.doctype.purchase_receipt.purchase_receipt import update_billed_amount_based_on_po
|
||||||
from erpnext.stock import get_warehouse_account_map
|
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 six import iteritems
|
||||||
from erpnext.accounts.doctype.sales_invoice.sales_invoice import validate_inter_company_party, update_linked_invoice,\
|
from erpnext.accounts.doctype.sales_invoice.sales_invoice import validate_inter_company_party, update_linked_invoice,\
|
||||||
unlink_inter_company_invoice
|
unlink_inter_company_invoice
|
||||||
|
from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import get_party_tax_withholding_details
|
||||||
|
|
||||||
form_grid_templates = {
|
form_grid_templates = {
|
||||||
"items": "templates/form_grid/item_grid.html"
|
"items": "templates/form_grid/item_grid.html"
|
||||||
@@ -42,6 +43,10 @@ class PurchaseInvoice(BuyingController):
|
|||||||
'overflow_type': 'billing'
|
'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):
|
def before_save(self):
|
||||||
if not self.on_hold:
|
if not self.on_hold:
|
||||||
self.release_date = ''
|
self.release_date = ''
|
||||||
@@ -54,7 +59,10 @@ class PurchaseInvoice(BuyingController):
|
|||||||
self.is_opening = 'No'
|
self.is_opening = 'No'
|
||||||
|
|
||||||
self.validate_posting_time()
|
self.validate_posting_time()
|
||||||
|
|
||||||
|
# apply tax withholding only if checked and applicable
|
||||||
self.set_tax_withholding()
|
self.set_tax_withholding()
|
||||||
|
|
||||||
super(PurchaseInvoice, self).validate()
|
super(PurchaseInvoice, self).validate()
|
||||||
|
|
||||||
if not self.is_return:
|
if not self.is_return:
|
||||||
@@ -768,14 +776,18 @@ class PurchaseInvoice(BuyingController):
|
|||||||
self.db_set('release_date', None)
|
self.db_set('release_date', None)
|
||||||
|
|
||||||
def set_tax_withholding(self):
|
def set_tax_withholding(self):
|
||||||
tax_withholding_details = get_patry_tax_withholding_details(self)
|
if not self.apply_tds:
|
||||||
for tax_details in tax_withholding_details:
|
return
|
||||||
if flt(self.get("rounded_total") or self.grand_total) >= flt(tax_details['threshold']):
|
|
||||||
if self.taxes:
|
tax_withholding_details = get_party_tax_withholding_details(self)
|
||||||
if tax_details['tax']['description'] not in [tax.description for tax in self.taxes]:
|
accounts = []
|
||||||
self.append('taxes', tax_details['tax'])
|
for d in self.taxes:
|
||||||
else:
|
if d.account_head == tax_withholding_details.get("account_head"):
|
||||||
self.append('taxes', tax_details['tax'])
|
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()
|
@frappe.whitelist()
|
||||||
def make_debit_note(source_name, target_doc=None):
|
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.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.general_ledger import get_round_off_account_and_cost_center
|
||||||
from erpnext.accounts.doctype.loyalty_program.loyalty_program import \
|
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
|
from six import iteritems
|
||||||
|
|
||||||
@@ -973,12 +973,14 @@ class SalesInvoice(SellingController):
|
|||||||
|
|
||||||
# collection of the loyalty points, create the ledger entry for that.
|
# collection of the loyalty points, create the ledger entry for that.
|
||||||
def make_loyalty_point_entry(self):
|
def make_loyalty_point_entry(self):
|
||||||
lp_details = get_loyalty_program_details(self.customer, company=self.company,
|
returned_amount = self.get_returned_amount()
|
||||||
loyalty_program=self.loyalty_program, expiry_date=self.posting_date)
|
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 \
|
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)):
|
(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)
|
points_earned = cint(eligible_amount/lp_details.collection_factor)
|
||||||
doc = frappe.get_doc({
|
doc = frappe.get_doc({
|
||||||
"doctype": "Loyalty Point Entry",
|
"doctype": "Loyalty Point Entry",
|
||||||
@@ -994,7 +996,7 @@ class SalesInvoice(SellingController):
|
|||||||
})
|
})
|
||||||
doc.flags.ignore_permissions = 1
|
doc.flags.ignore_permissions = 1
|
||||||
doc.save()
|
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
|
# valdite the redemption and then delete the loyalty points earned on cancel of the invoice
|
||||||
def delete_loyalty_point_entry(self):
|
def delete_loyalty_point_entry(self):
|
||||||
@@ -1009,9 +1011,12 @@ class SalesInvoice(SellingController):
|
|||||||
else:
|
else:
|
||||||
frappe.db.sql('''delete from `tabLoyalty Point Entry` where sales_invoice=%s''', (self.name))
|
frappe.db.sql('''delete from `tabLoyalty Point Entry` where sales_invoice=%s''', (self.name))
|
||||||
# Set loyalty program
|
# Set loyalty program
|
||||||
lp_details = get_loyalty_program_details(self.customer, company=self.company,
|
self.set_loyalty_program_tier()
|
||||||
loyalty_program=self.loyalty_program, expiry_date=self.posting_date)
|
|
||||||
frappe.db.set_value("Customer", self.customer, "loyalty_program_tier", lp_details.tier_name)
|
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):
|
def get_returned_amount(self):
|
||||||
returned_amount = frappe.db.sql("""
|
returned_amount = frappe.db.sql("""
|
||||||
|
|||||||
@@ -15,12 +15,13 @@
|
|||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_in_quick_entry": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
"fieldname": "percent_of_tax_withheld",
|
"fieldname": "category_name",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Data",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
"ignore_xss_filter": 0,
|
"ignore_xss_filter": 0,
|
||||||
@@ -28,7 +29,7 @@
|
|||||||
"in_global_search": 0,
|
"in_global_search": 0,
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
"in_standard_filter": 0,
|
||||||
"label": "Percent of Tax Withheld",
|
"label": "Category Name",
|
||||||
"length": 0,
|
"length": 0,
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
@@ -38,6 +39,71 @@
|
|||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"remember_last_selected_value": 0,
|
"remember_last_selected_value": 0,
|
||||||
"report_hide": 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": "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_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,
|
"reqd": 1,
|
||||||
"search_index": 0,
|
"search_index": 0,
|
||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
@@ -46,129 +112,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_in_quick_entry": 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",
|
|
||||||
"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": "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,
|
|
||||||
"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,
|
|
||||||
"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,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
@@ -182,6 +126,7 @@
|
|||||||
"in_global_search": 0,
|
"in_global_search": 0,
|
||||||
"in_list_view": 0,
|
"in_list_view": 0,
|
||||||
"in_standard_filter": 0,
|
"in_standard_filter": 0,
|
||||||
|
"label": "Account Details",
|
||||||
"length": 0,
|
"length": 0,
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
@@ -199,6 +144,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_in_quick_entry": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
@@ -240,7 +186,7 @@
|
|||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 0,
|
"istable": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"modified": "2018-05-16 13:57:52.489773",
|
"modified": "2018-07-17 22:53:26.193179",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Tax Withholding Category",
|
"name": "Tax Withholding Category",
|
||||||
@@ -305,12 +251,13 @@
|
|||||||
"write": 1
|
"write": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"quick_entry": 1,
|
"quick_entry": 0,
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"read_only_onload": 0,
|
"read_only_onload": 0,
|
||||||
"show_name_in_global_search": 0,
|
"show_name_in_global_search": 0,
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"track_changes": 1,
|
"track_changes": 1,
|
||||||
"track_seen": 0
|
"track_seen": 0,
|
||||||
|
"track_views": 0
|
||||||
}
|
}
|
||||||
@@ -4,7 +4,115 @@
|
|||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
|
from frappe import _
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
from frappe.utils import flt
|
||||||
|
from erpnext.accounts.utils import get_fiscal_year
|
||||||
|
|
||||||
class TaxWithholdingCategory(Document):
|
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 frappe
|
||||||
import unittest
|
import unittest
|
||||||
|
from frappe.utils import today
|
||||||
|
|
||||||
|
test_dependencies = ["Supplier Group"]
|
||||||
|
|
||||||
class TestTaxWithholdingCategory(unittest.TestCase):
|
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_import": 0,
|
||||||
"allow_rename": 0,
|
"allow_rename": 0,
|
||||||
"beta": 0,
|
"beta": 0,
|
||||||
"creation": "2018-05-11 13:32:33.825307",
|
"creation": "2018-07-17 16:53:13.716665",
|
||||||
"custom": 0,
|
"custom": 0,
|
||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
@@ -18,8 +18,8 @@
|
|||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 2,
|
||||||
"fieldname": "tax_withholding_category",
|
"fieldname": "fiscal_year",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
@@ -28,10 +28,10 @@
|
|||||||
"in_global_search": 0,
|
"in_global_search": 0,
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
"in_standard_filter": 0,
|
||||||
"label": "Tax Withholding Category",
|
"label": "Fiscal Year",
|
||||||
"length": 0,
|
"length": 0,
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
"options": "Tax Withholding Category",
|
"options": "Fiscal Year",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"precision": "",
|
"precision": "",
|
||||||
"print_hide": 0,
|
"print_hide": 0,
|
||||||
@@ -39,7 +39,7 @@
|
|||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"remember_last_selected_value": 0,
|
"remember_last_selected_value": 0,
|
||||||
"report_hide": 0,
|
"report_hide": 0,
|
||||||
"reqd": 0,
|
"reqd": 1,
|
||||||
"search_index": 0,
|
"search_index": 0,
|
||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
"translatable": 0,
|
"translatable": 0,
|
||||||
@@ -51,41 +51,8 @@
|
|||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 2,
|
||||||
"fieldname": "valid_till",
|
"fieldname": "tax_withholding_rate",
|
||||||
"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",
|
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
@@ -94,7 +61,38 @@
|
|||||||
"in_global_search": 0,
|
"in_global_search": 0,
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
"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,
|
"length": 0,
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
@@ -116,17 +114,49 @@
|
|||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 3,
|
||||||
"fieldname": "certificate_received",
|
"fieldname": "single_threshold",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Currency",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
"ignore_xss_filter": 0,
|
"ignore_xss_filter": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
"in_global_search": 0,
|
"in_global_search": 0,
|
||||||
"in_list_view": 0,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
"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,
|
"length": 0,
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
@@ -153,10 +183,10 @@
|
|||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"modified": "2018-06-18 22:38:38.638721",
|
"modified": "2018-07-17 17:13:09.819580",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Buying",
|
"module": "Accounts",
|
||||||
"name": "Party Tax Withholding Config",
|
"name": "Tax Withholding Rate",
|
||||||
"name_case": "",
|
"name_case": "",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [],
|
"permissions": [],
|
||||||
@@ -6,5 +6,5 @@ from __future__ import unicode_literals
|
|||||||
import frappe
|
import frappe
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
class PartyTaxWithholdingConfig(Document):
|
class TaxWithholdingRate(Document):
|
||||||
pass
|
pass
|
||||||
@@ -65,6 +65,10 @@ def _get_party_details(party=None, account=None, party_type="Customer", company=
|
|||||||
"allocated_percentage": d.allocated_percentage or None
|
"allocated_percentage": d.allocated_percentage or None
|
||||||
} for d in party.get("sales_team")]
|
} 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
|
return out
|
||||||
|
|
||||||
def set_address_details(out, party, party_type, doctype=None, company=None):
|
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]
|
return out[0][0]
|
||||||
else:
|
else:
|
||||||
return ''
|
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
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -179,6 +179,39 @@
|
|||||||
"translatable": 0,
|
"translatable": 0,
|
||||||
"unique": 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_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_bulk_edit": 0,
|
||||||
"allow_in_quick_entry": 0,
|
"allow_in_quick_entry": 0,
|
||||||
@@ -1154,39 +1187,6 @@
|
|||||||
"translatable": 0,
|
"translatable": 0,
|
||||||
"unique": 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_bulk_edit": 0,
|
||||||
"allow_in_quick_entry": 0,
|
"allow_in_quick_entry": 0,
|
||||||
@@ -1363,7 +1363,7 @@
|
|||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 0,
|
"istable": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"modified": "2018-06-26 13:44:53.124637",
|
"modified": "2018-07-17 12:14:59.417939",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Buying",
|
"module": "Buying",
|
||||||
"name": "Supplier",
|
"name": "Supplier",
|
||||||
@@ -1512,5 +1512,6 @@
|
|||||||
"sort_order": "ASC",
|
"sort_order": "ASC",
|
||||||
"title_field": "supplier_name",
|
"title_field": "supplier_name",
|
||||||
"track_changes": 1,
|
"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,
|
args: args,
|
||||||
callback: function(r) {
|
callback: function(r) {
|
||||||
if(r.message) {
|
if(r.message) {
|
||||||
|
frm.supplier_tds = r.message.supplier_tds;
|
||||||
frm.updating_party_details = true;
|
frm.updating_party_details = true;
|
||||||
frappe.run_serially([
|
frappe.run_serially([
|
||||||
() => frm.set_value(r.message),
|
() => 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.custom.doctype.custom_field.custom_field import create_custom_fields
|
||||||
from frappe.permissions import add_permission
|
from frappe.permissions import add_permission
|
||||||
from erpnext.regional.india import states
|
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):
|
def setup(company=None, patch=True):
|
||||||
make_custom_fields()
|
make_custom_fields()
|
||||||
@@ -257,8 +259,10 @@ def make_fixtures(company=None):
|
|||||||
doc.insert()
|
doc.insert()
|
||||||
except frappe.NameError:
|
except frappe.NameError:
|
||||||
pass
|
pass
|
||||||
|
except frappe.DuplicateEntryError:
|
||||||
|
pass
|
||||||
|
|
||||||
# create tds fixtures
|
# create records for Tax Withholding Category
|
||||||
set_tax_withholding_category(company)
|
set_tax_withholding_category(company)
|
||||||
|
|
||||||
def set_salary_components(docs):
|
def set_salary_components(docs):
|
||||||
@@ -279,19 +283,24 @@ def set_tax_withholding_category(company):
|
|||||||
if company and tds_account:
|
if company and tds_account:
|
||||||
accounts = [dict(company=company, account=tds_account)]
|
accounts = [dict(company=company, account=tds_account)]
|
||||||
|
|
||||||
tds = frappe.get_doc({
|
fiscal_year = get_fiscal_year(today(), company=accounts[0].get('company'))[0]
|
||||||
'doctype': 'Tax Withholding Category', 'name': 'TDS',
|
docs = get_tds_details(accounts, fiscal_year)
|
||||||
'percent_of_tax_withheld': 10,'threshold': 150000, 'book_on_invoice': 1,
|
|
||||||
'withhold_cumulative_tax_amount': 0, 'accounts': accounts
|
|
||||||
})
|
|
||||||
|
|
||||||
try:
|
for d in docs:
|
||||||
tds.flags.ignore_permissions = True
|
try:
|
||||||
tds.insert()
|
doc = frappe.get_doc(d)
|
||||||
except frappe.DuplicateEntryError:
|
doc.flags.ignore_permissions = True
|
||||||
tds = frappe.get_doc("Tax Withholding Category", tds.get("name"))
|
doc.insert()
|
||||||
tds.append("accounts", accounts[0])
|
except frappe.DuplicateEntryError:
|
||||||
tds.save()
|
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):
|
def set_tds_account(docs, company):
|
||||||
abbr = frappe.get_value("Company", company, "abbr")
|
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
|
"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
|
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_ignore = ["Account", "Cost Center", "Payment Terms Template", "Salary Component"]
|
||||||
|
test_dependencies = ["Fiscal Year"]
|
||||||
test_records = frappe.get_test_records('Company')
|
test_records = frappe.get_test_records('Company')
|
||||||
|
|
||||||
class TestCompany(unittest.TestCase):
|
class TestCompany(unittest.TestCase):
|
||||||
|
|||||||
@@ -36,8 +36,8 @@ def get_context(context):
|
|||||||
# check for the loyalty program of the customer
|
# check for the loyalty program of the customer
|
||||||
customer_loyalty_program = frappe.db.get_value("Customer", context.doc.customer, "loyalty_program")
|
customer_loyalty_program = frappe.db.get_value("Customer", context.doc.customer, "loyalty_program")
|
||||||
if customer_loyalty_program:
|
if customer_loyalty_program:
|
||||||
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
|
||||||
loyalty_program_details = get_loyalty_program_details(context.doc.customer, customer_loyalty_program)
|
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"))
|
context.available_loyalty_points = int(loyalty_program_details.get("loyalty_points"))
|
||||||
|
|
||||||
def get_attachments(dt, dn):
|
def get_attachments(dt, dn):
|
||||||
|
|||||||
Reference in New Issue
Block a user