Merge branch 'develop' of https://github.com/niralisatapara/erpnext into develop
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
index e73d602..25b128b 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
@@ -1583,4 +1583,4 @@
"timeline_field": "supplier",
"title_field": "title",
"track_changes": 1
-}
\ No newline at end of file
+}
diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
index f901257..58e29f1 100644
--- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
@@ -1574,6 +1574,35 @@
self.assertTrue(return_pi.docstatus == 1)
+ def test_without_tds(self):
+ make_purchase_invoice_tds()
+
+ def test_total_tds(self):
+ supplier = create_supplier(
+ supplier_name="_Test TDS Advance Supplier",
+ tax_withholding_category="TDS - 194 - Dividends - Individual",
+ )
+ pi = make_purchase_invoice_tds(supplier= "_Test TDS Advance Supplier",total_tds = 1)
+
+ sum_tds = 0
+ for item in pi.items:
+ sum_tds += item.net_amount
+
+ self.assertEqual(pi.tax_withholding_net_total, sum_tds)
+ for tax in pi.taxes:
+ self.assertEqual(tax.tax_amount, pi.tax_withholding_net_total * 0.10)
+
+ def test_partial_tds(self):
+ pi = make_purchase_invoice_tds(supplier= "_Test TDS Advance Supplier",partial_tds = 1)
+
+ sum_tds = 0
+ for item in pi.items:
+ if item.apply_tds:
+ sum_tds += item.net_amount
+
+ self.assertEqual(pi.tax_withholding_net_total, sum_tds)
+ for tax in pi.taxes:
+ self.assertEqual(tax.tax_amount, pi.tax_withholding_net_total * 0.10)
def check_gl_entries(doc, voucher_no, expected_gle, posting_date):
gl_entries = frappe.db.sql(
@@ -1682,6 +1711,86 @@
pi.submit()
return pi
+def make_purchase_invoice_tds(**args):
+ pi = frappe.new_doc("Purchase Invoice")
+ args = frappe._dict(args)
+ pi.posting_date = args.posting_date or today()
+ if args.posting_time:
+ pi.posting_time = args.posting_time
+ if args.update_stock:
+ pi.update_stock = 1
+ if args.is_paid:
+ pi.is_paid = 1
+
+ if args.cash_bank_account:
+ pi.cash_bank_account = args.cash_bank_account
+
+ pi.company = args.company or "_Test Company"
+ pi.supplier = args.supplier or "_Test Supplier"
+ pi.currency = args.currency or "INR"
+ pi.conversion_rate = args.conversion_rate or 1
+ pi.is_return = args.is_return
+ pi.return_against = args.return_against
+ pi.is_subcontracted = args.is_subcontracted or 0
+ pi.supplier_warehouse = args.supplier_warehouse or "_Test Warehouse 1 - _TC"
+ pi.cost_center = args.parent_cost_center
+
+ if args.total_tds or args.partial_tds:
+ pi.apply_tds = 1
+
+ pi.extend(
+ "items",
+ [
+ {
+ "item_code": args.item or args.item_code or "_Test Item",
+ "warehouse": args.warehouse or "_Test Warehouse - _TC",
+ "qty": args.qty or 5,
+ "received_qty": args.received_qty or 0,
+ "rejected_qty": args.rejected_qty or 0,
+ "rate": args.rate or 5000,
+ "price_list_rate": args.price_list_rate or 5000,
+ "expense_account": args.expense_account or "_Test Account Cost for Goods Sold - _TC",
+ "discount_account": args.discount_account or None,
+ "discount_amount": args.discount_amount or 0,
+ "conversion_factor": 1.0,
+ "serial_no": args.serial_no,
+ "stock_uom": args.uom or "_Test UOM",
+ "cost_center": args.cost_center or "_Test Cost Center - _TC",
+ "project": args.project,
+ "rejected_warehouse": args.rejected_warehouse or "",
+ "rejected_serial_no": args.rejected_serial_no or "",
+ "asset_location": args.location or "",
+ "allow_zero_valuation_rate": args.get("allow_zero_valuation_rate") or 0,
+ "apply_tds": 1 if (args.total_tds or args.partial_tds) else 0
+ },
+ {
+ "item_code": args.item or args.item_code or "_Test Item",
+ "warehouse": args.warehouse or "_Test Warehouse - _TC",
+ "qty": args.qty or 5,
+ "received_qty": args.received_qty or 0,
+ "rejected_qty": args.rejected_qty or 0,
+ "rate": args.rate or 5000,
+ "price_list_rate": args.price_list_rate or 5000,
+ "expense_account": args.expense_account or "_Test Account Cost for Goods Sold - _TC",
+ "discount_account": args.discount_account or None,
+ "discount_amount": args.discount_amount or 0,
+ "conversion_factor": 1.0,
+ "serial_no": args.serial_no,
+ "stock_uom": args.uom or "_Test UOM",
+ "cost_center": args.cost_center or "_Test Cost Center - _TC",
+ "project": args.project,
+ "rejected_warehouse": args.rejected_warehouse or "",
+ "rejected_serial_no": args.rejected_serial_no or "",
+ "asset_location": args.location or "",
+ "allow_zero_valuation_rate": args.get("allow_zero_valuation_rate") or 0,
+ "apply_tds": 1 if (args.total_tds) else 0
+ },
+ ]
+ )
+
+ pi.save()
+ pi.submit()
+ return pi
def make_purchase_invoice_against_cost_center(**args):
pi = frappe.new_doc("Purchase Invoice")
@@ -1734,4 +1843,4 @@
return pi
-test_records = frappe.get_test_records("Purchase Invoice")
+test_records = frappe.get_test_records("Purchase Invoice")
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
index a8f6f80..5c1cb0d 100644
--- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
+++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
@@ -40,6 +40,7 @@
"discount_amount",
"base_rate_with_margin",
"sec_break2",
+ "apply_tds",
"rate",
"amount",
"item_tax_template",
@@ -868,6 +869,12 @@
"label": "Product Bundle",
"options": "Product Bundle",
"read_only": 1
+ },
+ {
+ "default": "1",
+ "fieldname": "apply_tds",
+ "fieldtype": "Check",
+ "label": "Apply TDS"
}
],
"idx": 1,
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 7eddd81..737338e 100644
--- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
+++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
@@ -61,6 +61,9 @@
def get_party_tax_withholding_details(inv, tax_withholding_category=None):
+ if inv.doctype == "Payment Entry":
+ inv.tax_withholding_net_total = inv.net_total
+
pan_no = ""
parties = []
party_type, party = get_party_details(inv)
@@ -242,7 +245,7 @@
if party_type == "Supplier":
ldc = get_lower_deduction_certificate(tax_details, pan_no)
if tax_deducted:
- net_total = inv.net_total
+ net_total = inv.tax_withholding_net_total
if ldc:
tax_amount = get_tds_amount_from_ldc(
ldc, parties, pan_no, tax_details, posting_date, net_total
@@ -392,7 +395,7 @@
tds_amount = 0
invoice_filters = {"name": ("in", vouchers), "docstatus": 1, "apply_tds": 1}
- field = "sum(net_total)"
+ field = "sum(tax_withholding_net_total)"
if cint(tax_details.consider_party_ledger_amount):
invoice_filters.pop("apply_tds", None)
@@ -415,12 +418,12 @@
)
supp_credit_amt += supp_jv_credit_amt
- supp_credit_amt += inv.net_total
+ supp_credit_amt += inv.tax_withholding_net_total
threshold = tax_details.get("threshold", 0)
cumulative_threshold = tax_details.get("cumulative_threshold", 0)
- if (threshold and inv.net_total >= threshold) or (
+ if (threshold and inv.tax_withholding_net_total >= threshold) or (
cumulative_threshold and supp_credit_amt >= cumulative_threshold
):
if (cumulative_threshold and supp_credit_amt >= cumulative_threshold) and cint(
@@ -440,7 +443,7 @@
ldc.valid_upto,
inv.get("posting_date") or inv.get("transaction_date"),
tax_deducted,
- inv.net_total,
+ inv.tax_withholding_net_total,
ldc.certificate_limit,
):
tds_amount = get_ltds_amount(supp_credit_amt, 0, ldc.certificate_limit, ldc.rate, tax_details)
@@ -523,7 +526,7 @@
limit_consumed = frappe.db.get_value(
"Purchase Invoice",
{"supplier": ("in", parties), "apply_tds": 1, "docstatus": 1},
- "sum(net_total)",
+ "sum(tax_withholding_net_total)",
)
if is_valid_certificate(
diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py
index b5836c9..a0bc6ba 100644
--- a/erpnext/controllers/taxes_and_totals.py
+++ b/erpnext/controllers/taxes_and_totals.py
@@ -58,12 +58,23 @@
self.initialize_taxes()
self.determine_exclusive_rate()
self.calculate_net_total()
+ self.calculate_tax_withholding_net_total()
self.calculate_taxes()
self.manipulate_grand_total_for_inclusive_tax()
self.calculate_totals()
self._cleanup()
self.calculate_total_net_weight()
+ def calculate_tax_withholding_net_total(self):
+ if hasattr(self.doc, "tax_withholding_net_total"):
+
+ sum_net_amount = 0
+ for item in self.doc.get("items"):
+ if hasattr(item, "apply_tds") and item.apply_tds:
+ sum_net_amount += item.net_amount
+
+ self.doc.tax_withholding_net_total = sum_net_amount
+
def validate_item_tax_template(self):
for item in self.doc.get("items"):
if item.item_code and item.get("item_tax_template"):