# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt


import frappe
from frappe.test_runner import make_test_records
from frappe.utils import nowdate

from erpnext.accounts.party import get_due_date
from erpnext.controllers.website_list_for_contact import get_customers_suppliers
from erpnext.exceptions import PartyDisabled

test_dependencies = ["Payment Term", "Payment Terms Template"]
test_records = frappe.get_test_records("Supplier")

from frappe.tests.utils import FrappeTestCase


class TestSupplier(FrappeTestCase):
	def test_get_supplier_group_details(self):
		doc = frappe.new_doc("Supplier Group")
		doc.supplier_group_name = "_Testing Supplier Group"
		doc.payment_terms = "_Test Payment Term Template 3"
		doc.accounts = []
		test_account_details = {
			"company": "_Test Company",
			"account": "Creditors - _TC",
		}
		doc.append("accounts", test_account_details)
		doc.save()
		s_doc = frappe.new_doc("Supplier")
		s_doc.supplier_name = "Testing Supplier"
		s_doc.supplier_group = "_Testing Supplier Group"
		s_doc.payment_terms = ""
		s_doc.accounts = []
		s_doc.insert()
		s_doc.get_supplier_group_details()
		self.assertEqual(s_doc.payment_terms, "_Test Payment Term Template 3")
		self.assertEqual(s_doc.accounts[0].company, "_Test Company")
		self.assertEqual(s_doc.accounts[0].account, "Creditors - _TC")
		s_doc.delete()
		doc.delete()

	def test_supplier_default_payment_terms(self):
		# Payment Term based on Days after invoice date
		frappe.db.set_value(
			"Supplier", "_Test Supplier With Template 1", "payment_terms", "_Test Payment Term Template 3"
		)

		due_date = get_due_date("2016-01-22", "Supplier", "_Test Supplier With Template 1")
		self.assertEqual(due_date, "2016-02-21")

		due_date = get_due_date("2017-01-22", "Supplier", "_Test Supplier With Template 1")
		self.assertEqual(due_date, "2017-02-21")

		# Payment Term based on last day of month
		frappe.db.set_value(
			"Supplier", "_Test Supplier With Template 1", "payment_terms", "_Test Payment Term Template 1"
		)

		due_date = get_due_date("2016-01-22", "Supplier", "_Test Supplier With Template 1")
		self.assertEqual(due_date, "2016-02-29")

		due_date = get_due_date("2017-01-22", "Supplier", "_Test Supplier With Template 1")
		self.assertEqual(due_date, "2017-02-28")

		frappe.db.set_value("Supplier", "_Test Supplier With Template 1", "payment_terms", "")

		# Set credit limit for the supplier group instead of supplier and evaluate the due date
		frappe.db.set_value(
			"Supplier Group", "_Test Supplier Group", "payment_terms", "_Test Payment Term Template 3"
		)

		due_date = get_due_date("2016-01-22", "Supplier", "_Test Supplier With Template 1")
		self.assertEqual(due_date, "2016-02-21")

		# Payment terms for Supplier Group instead of supplier and evaluate the due date
		frappe.db.set_value(
			"Supplier Group", "_Test Supplier Group", "payment_terms", "_Test Payment Term Template 1"
		)

		# Leap year
		due_date = get_due_date("2016-01-22", "Supplier", "_Test Supplier With Template 1")
		self.assertEqual(due_date, "2016-02-29")
		# # Non Leap year
		due_date = get_due_date("2017-01-22", "Supplier", "_Test Supplier With Template 1")
		self.assertEqual(due_date, "2017-02-28")

		# Supplier with no default Payment Terms Template
		frappe.db.set_value("Supplier Group", "_Test Supplier Group", "payment_terms", "")
		frappe.db.set_value("Supplier", "_Test Supplier", "payment_terms", "")

		due_date = get_due_date("2016-01-22", "Supplier", "_Test Supplier")
		self.assertEqual(due_date, "2016-01-22")
		# # Non Leap year
		due_date = get_due_date("2017-01-22", "Supplier", "_Test Supplier")
		self.assertEqual(due_date, "2017-01-22")

	def test_supplier_disabled(self):
		make_test_records("Item")

		frappe.db.set_value("Supplier", "_Test Supplier", "disabled", 1)

		from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order

		po = create_purchase_order(do_not_save=True)

		self.assertRaises(PartyDisabled, po.save)

		frappe.db.set_value("Supplier", "_Test Supplier", "disabled", 0)

		po.save()

	def test_disabled_internal_supplier_does_not_block_new_one(self):
		def make_internal_supplier():
			supplier = frappe.new_doc("Supplier")
			supplier.update(
				{
					"supplier_name": frappe.generate_hash(),
					"supplier_group": "Services",
					"is_internal_supplier": 1,
					"represents_company": "_Test Company 7",
					"companies": [{"company": "_Test Company 7"}],
				}
			)
			return supplier.insert()

		old_supplier = make_internal_supplier()
		self.assertRaises(frappe.ValidationError, make_internal_supplier)

		old_supplier.disabled = 1
		old_supplier.save()
		make_internal_supplier()
		old_supplier.save()

	def test_primary_contact_must_be_linked_to_supplier(self):
		other_supplier = create_supplier()
		contact = frappe.get_doc(
			{
				"doctype": "Contact",
				"first_name": frappe.generate_hash(),
				"links": [{"link_doctype": "Supplier", "link_name": other_supplier.name}],
			}
		).insert()

		supplier = create_supplier()
		supplier.supplier_primary_contact = contact.name
		self.assertRaises(frappe.ValidationError, supplier.save)

		contact.append("links", {"link_doctype": "Supplier", "link_name": supplier.name})
		contact.save()
		supplier.reload()
		supplier.supplier_primary_contact = contact.name
		supplier.save()

	def test_party_account_must_be_payable(self):
		self.assertRaises(frappe.ValidationError, create_supplier, party_account="Debtors - _TC")
		create_supplier(party_account="Creditors - _TC")

	def test_hold_all_blocks_receipts_and_quotations(self):
		from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchase_return
		from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt

		supplier = create_supplier()
		receipt = make_purchase_receipt(supplier=supplier.name)
		supplier.update({"on_hold": 1, "hold_type": "All"})
		supplier.save()

		quotation = {"company": "_Test Company", "transaction_date": nowdate()}
		item = {"item_code": "_Test Item", "qty": 1, "rate": 100, "warehouse": "_Test Warehouse - _TC"}
		rfq_item = {**item, "uom": "_Test UOM", "conversion_factor": 1, "schedule_date": nowdate()}
		documents = [
			lambda: make_purchase_receipt(supplier=supplier.name),
			frappe.get_doc(
				{"doctype": "Supplier Quotation", "supplier": supplier.name, "items": [item], **quotation}
			).insert,
			frappe.get_doc(
				{
					"doctype": "Request for Quotation",
					"message_for_supplier": "Please quote",
					"suppliers": [{"supplier": supplier.name}],
					"items": [rfq_item],
					**quotation,
				}
			).insert,
		]
		for make_document in documents:
			self.assertRaisesRegex(frappe.ValidationError, "is blocked", make_document)

		make_purchase_return(receipt.name).submit()

	def test_hold_payments_blocks_bank_and_cash_journal_entries(self):
		from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry

		supplier = create_supplier()
		supplier.update({"on_hold": 1, "hold_type": "Payments"})
		supplier.save()

		def make_supplier_entry(contra_account, amount=100):
			journal_entry = make_journal_entry("Creditors - _TC", contra_account, amount, save=False)
			journal_entry.accounts[0].update({"party_type": "Supplier", "party": supplier.name})
			return journal_entry

		self.assertRaisesRegex(
			frappe.ValidationError, "is blocked", make_supplier_entry("_Test Bank - _TC").insert
		)
		make_supplier_entry("_Test Account Cost for Goods Sold - _TC").insert()
		make_supplier_entry("_Test Bank - _TC", amount=-100).insert()

		negative_bank_debit = make_supplier_entry("_Test Bank - _TC")
		negative_bank_debit.accounts[1].update(
			{"credit_in_account_currency": 0, "debit_in_account_currency": -100}
		)
		self.assertRaisesRegex(frappe.ValidationError, "is blocked", negative_bank_debit.insert)

		refund_with_other_payment = make_supplier_entry("_Test Bank - _TC")
		refund_with_other_payment.accounts[0].update({"party": create_supplier().name})
		refund_with_other_payment.accounts[1].credit_in_account_currency = 70
		refund_with_other_payment.append(
			"accounts",
			{
				"account": "Creditors - _TC",
				"party_type": "Supplier",
				"party": supplier.name,
				"cost_center": "_Test Cost Center - _TC",
				"credit_in_account_currency": 30,
			},
		)
		refund_with_other_payment.insert()

	def test_hold_payments_ignores_is_return_on_payment_entry(self):
		from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
		from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order

		supplier = create_supplier()
		payment_entry = get_payment_entry(
			"Purchase Order",
			create_purchase_order(supplier=supplier.name).name,
			bank_account="_Test Bank - _TC",
		)
		supplier.update({"on_hold": 1, "hold_type": "Payments"})
		supplier.save()

		payment_entry.update(
			{"references": [], "reference_no": "1", "reference_date": nowdate(), "is_return": 1}
		)
		self.assertRaisesRegex(frappe.ValidationError, "is blocked", payment_entry.insert)

	def test_supplier_country(self):
		# Test that country field exists in Supplier DocType
		supplier = frappe.get_doc("Supplier", "_Test Supplier with Country")
		self.assertTrue("country" in supplier.as_dict())

		# Test if test supplier field record is 'Greece'
		self.assertEqual(supplier.country, "Greece")

		# Test update Supplier instance country value
		supplier = frappe.get_doc("Supplier", "_Test Supplier")
		supplier.country = "Greece"
		supplier.save()
		self.assertEqual(supplier.country, "Greece")

	def test_party_details_tax_category(self):
		from erpnext.accounts.party import _get_party_details

		frappe.delete_doc_if_exists("Address", "_Test Address With Tax Category-Billing")

		# Tax Category without Address
		details = _get_party_details("_Test Supplier With Tax Category", party_type="Supplier")
		self.assertEqual(details.tax_category, "_Test Tax Category 1")

		address = frappe.get_doc(
			dict(
				doctype="Address",
				address_title="_Test Address With Tax Category",
				tax_category="_Test Tax Category 2",
				address_type="Billing",
				address_line1="Station Road",
				city="_Test City",
				country="India",
				links=[dict(link_doctype="Supplier", link_name="_Test Supplier With Tax Category")],
			)
		).insert()

		# Tax Category with Address
		details = _get_party_details("_Test Supplier With Tax Category", party_type="Supplier")
		self.assertEqual(details.tax_category, "_Test Tax Category 2")

		# Rollback
		address.delete()


def create_supplier(**args):
	args = frappe._dict(args)

	if not args.supplier_name:
		args.supplier_name = frappe.generate_hash()

	if frappe.db.exists("Supplier", args.supplier_name):
		return frappe.get_doc("Supplier", args.supplier_name)

	doc = frappe.get_doc(
		{
			"doctype": "Supplier",
			"supplier_name": args.supplier_name,
			"default_currency": args.default_currency,
			"supplier_type": args.supplier_type or "Company",
			"tax_withholding_category": args.tax_withholding_category,
		}
	)
	if not args.without_supplier_group:
		doc.supplier_group = args.supplier_group or "Services"

	if args.get("party_account"):
		doc.append(
			"accounts",
			{
				"company": frappe.db.get_value("Account", args.get("party_account"), "company"),
				"account": args.get("party_account"),
			},
		)

	doc.insert()

	return doc


class TestSupplierPortal(FrappeTestCase):
	def test_portal_user_can_access_supplier_data(self):
		supplier = create_supplier()

		user = frappe.generate_hash() + "@example.com"
		frappe.new_doc(
			"User",
			first_name="Supplier Portal User",
			email=user,
			send_welcome_email=False,
		).insert()

		supplier.append("portal_users", {"user": user})
		supplier.save()

		with self.set_user(user):
			_, suppliers = get_customers_suppliers("Purchase Order", user)

		self.assertIn(supplier.name, suppliers)

	def test_portal_user_contact_link(self):
		user_email = frappe.generate_hash() + "@example.com"
		user = frappe.new_doc("User")
		user.email = user_email
		user.first_name = "Test Portal Contact User"
		user.send_welcome_email = False
		user.insert(ignore_permissions=True)

		contact = frappe.new_doc("Contact")
		contact.first_name = "Test Portal Contact User"
		contact.add_email(user_email, is_primary=1)
		contact.links = []
		contact.insert(ignore_permissions=True)

		supplier = create_supplier()
		supplier.append("portal_users", {"user": user.name})
		supplier.save()

		contact.reload()
		self.assertTrue(contact.has_link("Supplier", supplier.name))
