diff --git a/erpnext/accounts/doctype/loyalty_program/loyalty_program.py b/erpnext/accounts/doctype/loyalty_program/loyalty_program.py index 96eda7fce23..d840304795f 100644 --- a/erpnext/accounts/doctype/loyalty_program/loyalty_program.py +++ b/erpnext/accounts/doctype/loyalty_program/loyalty_program.py @@ -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: diff --git a/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py b/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py index 87a0cb8c43b..99553b4db19 100644 --- a/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py +++ b/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py @@ -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' diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index c981d0898a5..456acbabbe4 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -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 diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index 9a9fe20a6d4..14f7891bc1a 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -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 { diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index 6eb629cf568..60ccf1cbde6 100755 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -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 } \ No newline at end of file diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index c469a0244e6..f343165de1a 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -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): diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 2c4125f1fbb..f559fc01799 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -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(""" diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.json b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.json index 8edaf0187e4..f9160e281da 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.json +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.json @@ -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 } \ No newline at end of file diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py index 90c7d50a5ab..0dc4ecf7ef4 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -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 \ No newline at end of file + 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))) \ No newline at end of file diff --git a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py index 7d990119d15..20e1746e36a 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py @@ -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() \ No newline at end of file diff --git a/erpnext/buying/doctype/party_tax_withholding_config/__init__.py b/erpnext/accounts/doctype/tax_withholding_rate/__init__.py similarity index 100% rename from erpnext/buying/doctype/party_tax_withholding_config/__init__.py rename to erpnext/accounts/doctype/tax_withholding_rate/__init__.py diff --git a/erpnext/buying/doctype/party_tax_withholding_config/party_tax_withholding_config.json b/erpnext/accounts/doctype/tax_withholding_rate/tax_withholding_rate.json similarity index 71% rename from erpnext/buying/doctype/party_tax_withholding_config/party_tax_withholding_config.json rename to erpnext/accounts/doctype/tax_withholding_rate/tax_withholding_rate.json index d2583e5dcd5..1e8194af6e4 100644 --- a/erpnext/buying/doctype/party_tax_withholding_config/party_tax_withholding_config.json +++ b/erpnext/accounts/doctype/tax_withholding_rate/tax_withholding_rate.json @@ -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": [], diff --git a/erpnext/buying/doctype/party_tax_withholding_config/party_tax_withholding_config.py b/erpnext/accounts/doctype/tax_withholding_rate/tax_withholding_rate.py similarity index 85% rename from erpnext/buying/doctype/party_tax_withholding_config/party_tax_withholding_config.py rename to erpnext/accounts/doctype/tax_withholding_rate/tax_withholding_rate.py index bec7e83f237..6e32abec390 100644 --- a/erpnext/buying/doctype/party_tax_withholding_config/party_tax_withholding_config.py +++ b/erpnext/accounts/doctype/tax_withholding_rate/tax_withholding_rate.py @@ -6,5 +6,5 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document -class PartyTaxWithholdingConfig(Document): +class TaxWithholdingRate(Document): pass diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index d7fe12374e6..cc90c748ccc 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -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 - } diff --git a/erpnext/buying/doctype/supplier/supplier.json b/erpnext/buying/doctype/supplier/supplier.json index 1d342cd1a65..45901dcd0ce 100644 --- a/erpnext/buying/doctype/supplier/supplier.json +++ b/erpnext/buying/doctype/supplier/supplier.json @@ -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 } \ No newline at end of file diff --git a/erpnext/public/js/utils/party.js b/erpnext/public/js/utils/party.js index 378a8033bb6..3a5a0624e8b 100644 --- a/erpnext/public/js/utils/party.js +++ b/erpnext/public/js/utils/party.js @@ -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), diff --git a/erpnext/regional/india/setup.py b/erpnext/regional/india/setup.py index 7bb09e981c1..b16affa9058 100644 --- a/erpnext/regional/india/setup.py +++ b/erpnext/regional/india/setup.py @@ -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}]) + ] diff --git a/erpnext/setup/doctype/company/test_company.py b/erpnext/setup/doctype/company/test_company.py index 4ad8af85e93..c3260ab9060 100644 --- a/erpnext/setup/doctype/company/test_company.py +++ b/erpnext/setup/doctype/company/test_company.py @@ -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): diff --git a/erpnext/templates/pages/order.py b/erpnext/templates/pages/order.py index 70bd702ba81..34985d94ea7 100644 --- a/erpnext/templates/pages/order.py +++ b/erpnext/templates/pages/order.py @@ -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):