# Copyright (c) 2018, InfluxFramework LLC
# See license.txt

import unittest

import influxframework
from influxframework.utils import today

from influxerp.accounts.utils import get_fiscal_year

test_dependencies = ["Supplier Group", "Customer Group"]


class TestTaxWithholdingCategory(unittest.TestCase):
	@classmethod
	def setUpClass(self):
		# create relevant supplier, etc
		create_records()
		create_tax_with_holding_category()

	def tearDown(self):
		cancel_invoices()

	def test_cumulative_threshold_tds(self):
		influxframework.db.set_value(
			"Supplier", "Test TDS Supplier", "tax_withholding_category", "Cumulative Threshold TDS"
		)
		invoices = []

		# create invoices for lower than single threshold tax rate
		for _ in range(2):
			pi = create_purchase_invoice(supplier="Test TDS Supplier")
			pi.submit()
			invoices.append(pi)

		# create another invoice whose total when added to previously created invoice,
		# surpasses cumulative threshhold
		pi = create_purchase_invoice(supplier="Test TDS Supplier")
		pi.submit()

		# assert equal tax deduction on total invoice amount uptil now
		self.assertEqual(pi.taxes_and_charges_deducted, 3000)
		self.assertEqual(pi.grand_total, 7000)
		invoices.append(pi)

		# TDS is already deducted, so from onward system will deduct the TDS on every invoice
		pi = create_purchase_invoice(supplier="Test TDS Supplier", rate=5000)
		pi.submit()

		# assert equal tax deduction on total invoice amount uptil now
		self.assertEqual(pi.taxes_and_charges_deducted, 500)
		invoices.append(pi)

		# delete invoices to avoid clashing
		for d in reversed(invoices):
			d.cancel()

	def test_single_threshold_tds(self):
		invoices = []
		influxframework.db.set_value(
			"Supplier", "Test TDS Supplier1", "tax_withholding_category", "Single Threshold TDS"
		)
		pi = create_purchase_invoice(supplier="Test TDS Supplier1", rate=20000)
		pi.submit()
		invoices.append(pi)

		self.assertEqual(pi.taxes_and_charges_deducted, 2000)
		self.assertEqual(pi.grand_total, 18000)

		# check gl entry for the purchase invoice
		gl_entries = influxframework.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, 18000)
			elif d.account == pi.items[0].get("expense_account"):
				self.assertEqual(d.debit, 20000)
			elif d.account == pi.taxes[0].get("account_head"):
				self.assertEqual(d.credit, 2000)
			else:
				raise ValueError("Account head does not match.")

		pi = create_purchase_invoice(supplier="Test TDS Supplier1")
		pi.submit()
		invoices.append(pi)

		# TDS amount is 1000 because in previous invoices it's already deducted
		self.assertEqual(pi.taxes_and_charges_deducted, 1000)

		# delete invoices to avoid clashing
		for d in reversed(invoices):
			d.cancel()

	def test_tax_withholding_category_checks(self):
		invoices = []
		influxframework.db.set_value(
			"Supplier", "Test TDS Supplier3", "tax_withholding_category", "New TDS Category"
		)

		# First Invoice with no tds check
		pi = create_purchase_invoice(supplier="Test TDS Supplier3", rate=20000, do_not_save=True)
		pi.apply_tds = 0
		pi.save()
		pi.submit()
		invoices.append(pi)

		# Second Invoice will apply TDS checked
		pi1 = create_purchase_invoice(supplier="Test TDS Supplier3", rate=20000)
		pi1.submit()
		invoices.append(pi1)

		# Cumulative threshold is 30000
		# Threshold calculation should be on both the invoices
		# TDS should be applied only on 1000
		self.assertEqual(pi1.taxes[0].tax_amount, 1000)

		for d in reversed(invoices):
			d.cancel()

	def test_cumulative_threshold_tcs(self):
		influxframework.db.set_value(
			"Customer", "Test TCS Customer", "tax_withholding_category", "Cumulative Threshold TCS"
		)
		invoices = []

		# create invoices for lower than single threshold tax rate
		for _ in range(2):
			si = create_sales_invoice(customer="Test TCS Customer")
			si.submit()
			invoices.append(si)

		# create another invoice whose total when added to previously created invoice,
		# surpasses cumulative threshhold
		si = create_sales_invoice(customer="Test TCS Customer", rate=12000)
		si.submit()

		# assert tax collection on total invoice amount created until now
		tcs_charged = sum([d.base_tax_amount for d in si.taxes if d.account_head == "TCS - _TC"])
		self.assertEqual(tcs_charged, 200)
		self.assertEqual(si.grand_total, 12200)
		invoices.append(si)

		# TCS is already collected once, so going forward system will collect TCS on every invoice
		si = create_sales_invoice(customer="Test TCS Customer", rate=5000)
		si.submit()

		tcs_charged = sum(d.base_tax_amount for d in si.taxes if d.account_head == "TCS - _TC")
		self.assertEqual(tcs_charged, 500)
		invoices.append(si)

		# cancel invoices to avoid clashing
		for d in reversed(invoices):
			d.cancel()

	def test_tds_calculation_on_net_total(self):
		influxframework.db.set_value(
			"Supplier", "Test TDS Supplier4", "tax_withholding_category", "Cumulative Threshold TDS"
		)
		invoices = []

		pi = create_purchase_invoice(supplier="Test TDS Supplier4", rate=20000, do_not_save=True)
		pi.append(
			"taxes",
			{
				"category": "Total",
				"charge_type": "Actual",
				"account_head": "_Test Account VAT - _TC",
				"cost_center": "Main - _TC",
				"tax_amount": 1000,
				"description": "Test",
				"add_deduct_tax": "Add",
			},
		)
		pi.save()
		pi.submit()
		invoices.append(pi)

		# Second Invoice will apply TDS checked
		pi1 = create_purchase_invoice(supplier="Test TDS Supplier4", rate=20000)
		pi1.submit()
		invoices.append(pi1)

		self.assertEqual(pi1.taxes[0].tax_amount, 4000)

		# cancel invoices to avoid clashing
		for d in reversed(invoices):
			d.cancel()

	def test_multi_category_single_supplier(self):
		influxframework.db.set_value(
			"Supplier", "Test TDS Supplier5", "tax_withholding_category", "Test Service Category"
		)
		invoices = []

		pi = create_purchase_invoice(supplier="Test TDS Supplier5", rate=500, do_not_save=True)
		pi.tax_withholding_category = "Test Service Category"
		pi.save()
		pi.submit()
		invoices.append(pi)

		# Second Invoice will apply TDS checked
		pi1 = create_purchase_invoice(supplier="Test TDS Supplier5", rate=2500, do_not_save=True)
		pi1.tax_withholding_category = "Test Goods Category"
		pi1.save()
		pi1.submit()
		invoices.append(pi1)

		self.assertEqual(pi1.taxes[0].tax_amount, 250)

		# cancel invoices to avoid clashing
		for d in reversed(invoices):
			d.cancel()

	def test_tax_withholding_category_voucher_display(self):
		influxframework.db.set_value(
			"Supplier", "Test TDS Supplier6", "tax_withholding_category", "Test Multi Invoice Category"
		)
		invoices = []

		pi = create_purchase_invoice(supplier="Test TDS Supplier6", rate=4000, do_not_save=True)
		pi.apply_tds = 1
		pi.tax_withholding_category = "Test Multi Invoice Category"
		pi.save()
		pi.submit()
		invoices.append(pi)

		pi1 = create_purchase_invoice(supplier="Test TDS Supplier6", rate=2000, do_not_save=True)
		pi1.apply_tds = 1
		pi1.is_return = 1
		pi1.items[0].qty = -1
		pi1.tax_withholding_category = "Test Multi Invoice Category"
		pi1.save()
		pi1.submit()
		invoices.append(pi1)

		pi2 = create_purchase_invoice(supplier="Test TDS Supplier6", rate=9000, do_not_save=True)
		pi2.apply_tds = 1
		pi2.tax_withholding_category = "Test Multi Invoice Category"
		pi2.save()
		pi2.submit()
		invoices.append(pi2)

		pi2.load_from_db()

		self.assertTrue(pi2.taxes[0].tax_amount, 1100)

		self.assertTrue(pi2.tax_withheld_vouchers[0].voucher_name == pi1.name)
		self.assertTrue(pi2.tax_withheld_vouchers[0].taxable_amount == pi1.net_total)
		self.assertTrue(pi2.tax_withheld_vouchers[1].voucher_name == pi.name)
		self.assertTrue(pi2.tax_withheld_vouchers[1].taxable_amount == pi.net_total)

		# cancel invoices to avoid clashing
		for d in reversed(invoices):
			d.cancel()


def cancel_invoices():
	purchase_invoices = influxframework.get_all(
		"Purchase Invoice",
		{
			"supplier": ["in", ["Test TDS Supplier", "Test TDS Supplier1", "Test TDS Supplier2"]],
			"docstatus": 1,
		},
		pluck="name",
	)

	sales_invoices = influxframework.get_all(
		"Sales Invoice", {"customer": "Test TCS Customer", "docstatus": 1}, pluck="name"
	)

	for d in purchase_invoices:
		influxframework.get_doc("Purchase Invoice", d).cancel()

	for d in sales_invoices:
		influxframework.get_doc("Sales Invoice", d).cancel()


def create_purchase_invoice(**args):
	# return sales invoice doc object
	item = influxframework.db.get_value("Item", {"item_name": "TDS Item"}, "name")

	args = influxframework._dict(args)
	pi = influxframework.get_doc(
		{
			"doctype": "Purchase Invoice",
			"posting_date": today(),
			"apply_tds": 0 if args.do_not_apply_tds else 1,
			"supplier": args.supplier,
			"company": "_Test Company",
			"taxes_and_charges": "",
			"currency": "INR",
			"credit_to": "Creditors - _TC",
			"taxes": [],
			"items": [
				{
					"doctype": "Purchase Invoice Item",
					"item_code": item,
					"qty": args.qty or 1,
					"rate": args.rate or 10000,
					"cost_center": "Main - _TC",
					"expense_account": "Stock Received But Not Billed - _TC",
				}
			],
		}
	)

	pi.save()
	return pi


def create_sales_invoice(**args):
	# return sales invoice doc object
	item = influxframework.db.get_value("Item", {"item_name": "TCS Item"}, "name")

	args = influxframework._dict(args)
	si = influxframework.get_doc(
		{
			"doctype": "Sales Invoice",
			"posting_date": today(),
			"customer": args.customer,
			"company": "_Test Company",
			"taxes_and_charges": "",
			"currency": "INR",
			"debit_to": "Debtors - _TC",
			"taxes": [],
			"items": [
				{
					"doctype": "Sales Invoice Item",
					"item_code": item,
					"qty": args.qty or 1,
					"rate": args.rate or 10000,
					"cost_center": "Main - _TC",
					"expense_account": "Cost of Goods Sold - _TC",
					"warehouse": args.warehouse or "_Test Warehouse - _TC",
				}
			],
		}
	)

	si.save()
	return si


def create_records():
	# create a new suppliers
	for name in [
		"Test TDS Supplier",
		"Test TDS Supplier1",
		"Test TDS Supplier2",
		"Test TDS Supplier3",
		"Test TDS Supplier4",
		"Test TDS Supplier5",
		"Test TDS Supplier6",
	]:
		if influxframework.db.exists("Supplier", name):
			continue

		influxframework.get_doc(
			{
				"supplier_group": "_Test Supplier Group",
				"supplier_name": name,
				"doctype": "Supplier",
			}
		).insert()

	for name in ["Test TCS Customer"]:
		if influxframework.db.exists("Customer", name):
			continue

		influxframework.get_doc(
			{"customer_group": "_Test Customer Group", "customer_name": name, "doctype": "Customer"}
		).insert()

	# create item
	if not influxframework.db.exists("Item", "TDS Item"):
		influxframework.get_doc(
			{
				"doctype": "Item",
				"item_code": "TDS Item",
				"item_name": "TDS Item",
				"item_group": "All Item Groups",
				"is_stock_item": 0,
			}
		).insert()

	if not influxframework.db.exists("Item", "TCS Item"):
		influxframework.get_doc(
			{
				"doctype": "Item",
				"item_code": "TCS Item",
				"item_name": "TCS Item",
				"item_group": "All Item Groups",
				"is_stock_item": 1,
			}
		).insert()

	# create tds account
	if not influxframework.db.exists("Account", "TDS - _TC"):
		influxframework.get_doc(
			{
				"doctype": "Account",
				"company": "_Test Company",
				"account_name": "TDS",
				"parent_account": "Tax Assets - _TC",
				"report_type": "Balance Sheet",
				"root_type": "Asset",
			}
		).insert()

	# create tcs account
	if not influxframework.db.exists("Account", "TCS - _TC"):
		influxframework.get_doc(
			{
				"doctype": "Account",
				"company": "_Test Company",
				"account_name": "TCS",
				"parent_account": "Duties and Taxes - _TC",
				"report_type": "Balance Sheet",
				"root_type": "Liability",
			}
		).insert()


def create_tax_with_holding_category():
	fiscal_year = get_fiscal_year(today(), company="_Test Company")
	# Cumulative threshold
	if not influxframework.db.exists("Tax Withholding Category", "Cumulative Threshold TDS"):
		influxframework.get_doc(
			{
				"doctype": "Tax Withholding Category",
				"name": "Cumulative Threshold TDS",
				"category_name": "10% TDS",
				"rates": [
					{
						"from_date": fiscal_year[1],
						"to_date": fiscal_year[2],
						"tax_withholding_rate": 10,
						"single_threshold": 0,
						"cumulative_threshold": 30000.00,
					}
				],
				"accounts": [{"company": "_Test Company", "account": "TDS - _TC"}],
			}
		).insert()

	if not influxframework.db.exists("Tax Withholding Category", "Cumulative Threshold TCS"):
		influxframework.get_doc(
			{
				"doctype": "Tax Withholding Category",
				"name": "Cumulative Threshold TCS",
				"category_name": "10% TCS",
				"rates": [
					{
						"from_date": fiscal_year[1],
						"to_date": fiscal_year[2],
						"tax_withholding_rate": 10,
						"single_threshold": 0,
						"cumulative_threshold": 30000.00,
					}
				],
				"accounts": [{"company": "_Test Company", "account": "TCS - _TC"}],
			}
		).insert()

	# Single thresold
	if not influxframework.db.exists("Tax Withholding Category", "Single Threshold TDS"):
		influxframework.get_doc(
			{
				"doctype": "Tax Withholding Category",
				"name": "Single Threshold TDS",
				"category_name": "10% TDS",
				"rates": [
					{
						"from_date": fiscal_year[1],
						"to_date": fiscal_year[2],
						"tax_withholding_rate": 10,
						"single_threshold": 20000.00,
						"cumulative_threshold": 0,
					}
				],
				"accounts": [{"company": "_Test Company", "account": "TDS - _TC"}],
			}
		).insert()

	if not influxframework.db.exists("Tax Withholding Category", "New TDS Category"):
		influxframework.get_doc(
			{
				"doctype": "Tax Withholding Category",
				"name": "New TDS Category",
				"category_name": "New TDS Category",
				"round_off_tax_amount": 1,
				"consider_party_ledger_amount": 1,
				"tax_on_excess_amount": 1,
				"rates": [
					{
						"from_date": fiscal_year[1],
						"to_date": fiscal_year[2],
						"tax_withholding_rate": 10,
						"single_threshold": 0,
						"cumulative_threshold": 30000,
					}
				],
				"accounts": [{"company": "_Test Company", "account": "TDS - _TC"}],
			}
		).insert()

	if not influxframework.db.exists("Tax Withholding Category", "Test Service Category"):
		influxframework.get_doc(
			{
				"doctype": "Tax Withholding Category",
				"name": "Test Service Category",
				"category_name": "Test Service Category",
				"rates": [
					{
						"from_date": fiscal_year[1],
						"to_date": fiscal_year[2],
						"tax_withholding_rate": 10,
						"single_threshold": 2000,
						"cumulative_threshold": 2000,
					}
				],
				"accounts": [{"company": "_Test Company", "account": "TDS - _TC"}],
			}
		).insert()

	if not influxframework.db.exists("Tax Withholding Category", "Test Goods Category"):
		influxframework.get_doc(
			{
				"doctype": "Tax Withholding Category",
				"name": "Test Goods Category",
				"category_name": "Test Goods Category",
				"rates": [
					{
						"from_date": fiscal_year[1],
						"to_date": fiscal_year[2],
						"tax_withholding_rate": 10,
						"single_threshold": 2000,
						"cumulative_threshold": 2000,
					}
				],
				"accounts": [{"company": "_Test Company", "account": "TDS - _TC"}],
			}
		).insert()

	if not influxframework.db.exists("Tax Withholding Category", "Test Multi Invoice Category"):
		influxframework.get_doc(
			{
				"doctype": "Tax Withholding Category",
				"name": "Test Multi Invoice Category",
				"category_name": "Test Multi Invoice Category",
				"rates": [
					{
						"from_date": fiscal_year[1],
						"to_date": fiscal_year[2],
						"tax_withholding_rate": 10,
						"single_threshold": 5000,
						"cumulative_threshold": 10000,
					}
				],
				"accounts": [{"company": "_Test Company", "account": "TDS - _TC"}],
			}
		).insert()
