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

# ERPNext - web based ERP (http://erpnext.com)
# For license information, please see license.txt

from __future__ import unicode_literals
import frappe, unittest, erpnext
from frappe.utils import flt
from erpnext.stock.doctype.material_request.material_request import raise_production_orders

class TestMaterialRequest(unittest.TestCase):
	def setUp(self):
		erpnext.set_perpetual_inventory(0)

	def test_make_purchase_order(self):
		from erpnext.stock.doctype.material_request.material_request import make_purchase_order

		mr = frappe.copy_doc(test_records[0]).insert()

		self.assertRaises(frappe.ValidationError, make_purchase_order,
			mr.name)

		mr = frappe.get_doc("Material Request", mr.name)
		mr.submit()
		po = make_purchase_order(mr.name)

		self.assertEquals(po.doctype, "Purchase Order")
		self.assertEquals(len(po.get("items")), len(mr.get("items")))

	def test_make_supplier_quotation(self):
		from erpnext.stock.doctype.material_request.material_request import make_supplier_quotation

		mr = frappe.copy_doc(test_records[0]).insert()

		self.assertRaises(frappe.ValidationError, make_supplier_quotation, mr.name)

		mr = frappe.get_doc("Material Request", mr.name)
		mr.submit()
		sq = make_supplier_quotation(mr.name)

		self.assertEquals(sq.doctype, "Supplier Quotation")
		self.assertEquals(len(sq.get("items")), len(mr.get("items")))


	def test_make_stock_entry(self):
		from erpnext.stock.doctype.material_request.material_request import make_stock_entry

		mr = frappe.copy_doc(test_records[0]).insert()

		self.assertRaises(frappe.ValidationError, make_stock_entry,
			mr.name)

		mr = frappe.get_doc("Material Request", mr.name)
		mr.material_request_type = "Material Transfer"
		mr.submit()
		se = make_stock_entry(mr.name)

		self.assertEquals(se.doctype, "Stock Entry")
		self.assertEquals(len(se.get("items")), len(mr.get("items")))

	def _insert_stock_entry(self, qty1, qty2, warehouse = None ):
		se = frappe.get_doc({
				"company": "_Test Company",
				"doctype": "Stock Entry",
				"posting_date": "2013-03-01",
				"posting_time": "00:00:00",
				"purpose": "Material Receipt",
				"items": [
					{
						"conversion_factor": 1.0,
						"doctype": "Stock Entry Detail",
						"item_code": "_Test Item Home Desktop 100",
						"parentfield": "items",
						"basic_rate": 100,
						"qty": qty1,
						"stock_uom": "_Test UOM 1",
						"transfer_qty": qty1,
						"uom": "_Test UOM 1",
						"t_warehouse": warehouse or "_Test Warehouse 1 - _TC",
						"cost_center": "_Test Cost Center - _TC"
					},
					{
						"conversion_factor": 1.0,
						"doctype": "Stock Entry Detail",
						"item_code": "_Test Item Home Desktop 200",
						"parentfield": "items",
						"basic_rate": 100,
						"qty": qty2,
						"stock_uom": "_Test UOM 1",
						"transfer_qty": qty2,
						"uom": "_Test UOM 1",
						"t_warehouse": warehouse or "_Test Warehouse 1 - _TC",
						"cost_center": "_Test Cost Center - _TC"
					}
				]
			})
		se.insert()
		se.submit()

	def test_cannot_stop_cancelled_material_request(self):
		mr = frappe.copy_doc(test_records[0])
		mr.insert()
		mr.submit()

		mr.load_from_db()
		mr.cancel()
		self.assertRaises(frappe.ValidationError, mr.update_status, 'Stopped')

	def test_mr_changes_from_stopped_to_pending_after_reopen(self):
		mr = frappe.copy_doc(test_records[0])
		mr.insert()
		mr.submit()
		self.assertEqual('Pending', mr.status)

		mr.update_status('Stopped')
		self.assertEqual('Stopped', mr.status)

		mr.update_status('Submitted')
		self.assertEqual('Pending', mr.status)

	def test_cannot_submit_cancelled_mr(self):
		mr = frappe.copy_doc(test_records[0])
		mr.insert()
		mr.submit()
		mr.load_from_db()
		mr.cancel()
		self.assertRaises(frappe.ValidationError, mr.submit)

	def test_mr_changes_from_pending_to_cancelled_after_cancel(self):
		mr = frappe.copy_doc(test_records[0])
		mr.insert()
		mr.submit()
		mr.cancel()
		self.assertEqual('Cancelled', mr.status)

	def test_cannot_change_cancelled_mr(self):
		mr = frappe.copy_doc(test_records[0])
		mr.insert()
		mr.submit()
		mr.load_from_db()
		mr.cancel()

		self.assertRaises(frappe.InvalidStatusError, mr.update_status, 'Draft')
		self.assertRaises(frappe.InvalidStatusError, mr.update_status, 'Stopped')
		self.assertRaises(frappe.InvalidStatusError, mr.update_status, 'Ordered')
		self.assertRaises(frappe.InvalidStatusError, mr.update_status, 'Issued')
		self.assertRaises(frappe.InvalidStatusError, mr.update_status, 'Transferred')
		self.assertRaises(frappe.InvalidStatusError, mr.update_status, 'Pending')

	def test_cannot_submit_deleted_material_request(self):
		mr = frappe.copy_doc(test_records[0])
		mr.insert()
		mr.delete()

		self.assertRaises(frappe.ValidationError, mr.submit)

	def test_cannot_delete_submitted_mr(self):
		mr = frappe.copy_doc(test_records[0])
		mr.insert()
		mr.submit()

		self.assertRaises(frappe.ValidationError, mr.delete)

	def test_stopped_mr_changes_to_pending_after_reopen(self):
		mr = frappe.copy_doc(test_records[0])
		mr.insert()
		mr.submit()
		mr.load_from_db()

		mr.update_status('Stopped')
		mr.update_status('Submitted')
		self.assertEqual(mr.status, 'Pending')

	def test_pending_mr_changes_to_stopped_after_stop(self):
		mr = frappe.copy_doc(test_records[0])
		mr.insert()
		mr.submit()
		mr.load_from_db()

		mr.update_status('Stopped')
		self.assertEqual(mr.status, 'Stopped')

	def test_cannot_stop_unsubmitted_mr(self):
		mr = frappe.copy_doc(test_records[0])
		mr.insert()
		self.assertRaises(frappe.InvalidStatusError, mr.update_status, 'Stopped')

	def test_completed_qty_for_purchase(self):
		existing_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC")
		existing_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC")

		# submit material request of type Purchase
		mr = frappe.copy_doc(test_records[0])
		mr.insert()
		mr.submit()

		# check if per complete is None
		mr.load_from_db()
		self.assertEquals(mr.per_ordered, 0)
		self.assertEquals(mr.get("items")[0].ordered_qty, 0)
		self.assertEquals(mr.get("items")[1].ordered_qty, 0)

		# map a purchase order
		from erpnext.stock.doctype.material_request.material_request import make_purchase_order
		po_doc = make_purchase_order(mr.name)
		po_doc.supplier = "_Test Supplier"
		po_doc.transaction_date = "2013-07-07"
		po_doc.get("items")[0].qty = 27.0
		po_doc.get("items")[1].qty = 1.5
		po_doc.get("items")[0].schedule_date = "2013-07-09"
		po_doc.get("items")[1].schedule_date = "2013-07-09"


		# check for stopped status of Material Request
		po = frappe.copy_doc(po_doc)
		po.insert()
		po.load_from_db()
		mr.update_status('Stopped')
		self.assertRaises(frappe.InvalidStatusError, po.submit)
		frappe.db.set(po, "docstatus", 1)
		self.assertRaises(frappe.InvalidStatusError, po.cancel)

		# resubmit and check for per complete
		mr.load_from_db()
		mr.update_status('Submitted')
		po = frappe.copy_doc(po_doc)
		po.insert()
		po.submit()

		# check if per complete is as expected
		mr.load_from_db()
		self.assertEquals(mr.per_ordered, 50)
		self.assertEquals(mr.get("items")[0].ordered_qty, 27.0)
		self.assertEquals(mr.get("items")[1].ordered_qty, 1.5)

		current_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC")
		current_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC")

		self.assertEquals(current_requested_qty_item1, existing_requested_qty_item1 + 27.0)
		self.assertEquals(current_requested_qty_item2, existing_requested_qty_item2 + 1.5)

		po.cancel()
		# check if per complete is as expected
		mr.load_from_db()
		self.assertEquals(mr.per_ordered, 0)
		self.assertEquals(mr.get("items")[0].ordered_qty, 0)
		self.assertEquals(mr.get("items")[1].ordered_qty, 0)

		current_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC")
		current_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC")

		self.assertEquals(current_requested_qty_item1, existing_requested_qty_item1 + 54.0)
		self.assertEquals(current_requested_qty_item2, existing_requested_qty_item2 + 3.0)

	def test_completed_qty_for_transfer(self):
		existing_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC")
		existing_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC")

		# submit material request of type Purchase
		mr = frappe.copy_doc(test_records[0])
		mr.material_request_type = "Material Transfer"
		mr.insert()
		mr.submit()

		# check if per complete is None
		mr.load_from_db()
		self.assertEquals(mr.per_ordered, 0)
		self.assertEquals(mr.get("items")[0].ordered_qty, 0)
		self.assertEquals(mr.get("items")[1].ordered_qty, 0)

		current_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC")
		current_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC")

		self.assertEquals(current_requested_qty_item1, existing_requested_qty_item1 + 54.0)
		self.assertEquals(current_requested_qty_item2, existing_requested_qty_item2 + 3.0)

		from erpnext.stock.doctype.material_request.material_request import make_stock_entry

		# map a stock entry
		se_doc = make_stock_entry(mr.name)
		se_doc.update({
			"posting_date": "2013-03-01",
			"posting_time": "01:00",
			"fiscal_year": "_Test Fiscal Year 2013",
		})
		se_doc.get("items")[0].update({
			"qty": 27.0,
			"transfer_qty": 27.0,
			"s_warehouse": "_Test Warehouse 1 - _TC",
			"basic_rate": 1.0
		})
		se_doc.get("items")[1].update({
			"qty": 1.5,
			"transfer_qty": 1.5,
			"s_warehouse": "_Test Warehouse 1 - _TC",
			"basic_rate": 1.0
		})

		# make available the qty in _Test Warehouse 1 before transfer
		self._insert_stock_entry(27.0, 1.5)

		# check for stopped status of Material Request
		se = frappe.copy_doc(se_doc)
		se.insert()
		mr.update_status('Stopped')
		self.assertRaises(frappe.InvalidStatusError, se.submit)

		mr.update_status('Submitted')

		se.flags.ignore_validate_update_after_submit = True
		se.submit()
		mr.update_status('Stopped')
		self.assertRaises(frappe.InvalidStatusError, se.cancel)

		mr.update_status('Submitted')
		se = frappe.copy_doc(se_doc)
		se.insert()
		se.submit()

		# check if per complete is as expected
		mr.load_from_db()
		self.assertEquals(mr.per_ordered, 50)
		self.assertEquals(mr.get("items")[0].ordered_qty, 27.0)
		self.assertEquals(mr.get("items")[1].ordered_qty, 1.5)

		current_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC")
		current_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC")

		self.assertEquals(current_requested_qty_item1, existing_requested_qty_item1 + 27.0)
		self.assertEquals(current_requested_qty_item2, existing_requested_qty_item2 + 1.5)

		# check if per complete is as expected for Stock Entry cancelled
		se.cancel()
		mr.load_from_db()
		self.assertEquals(mr.per_ordered, 0)
		self.assertEquals(mr.get("items")[0].ordered_qty, 0)
		self.assertEquals(mr.get("items")[1].ordered_qty, 0)

		current_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC")
		current_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC")

		self.assertEquals(current_requested_qty_item1, existing_requested_qty_item1 + 54.0)
		self.assertEquals(current_requested_qty_item2, existing_requested_qty_item2 + 3.0)

	def test_completed_qty_for_over_transfer(self):
		existing_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC")
		existing_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC")

		# submit material request of type Purchase
		mr = frappe.copy_doc(test_records[0])
		mr.material_request_type = "Material Transfer"
		mr.insert()
		mr.submit()

		# check if per complete is None
		mr.load_from_db()
		self.assertEquals(mr.per_ordered, 0)
		self.assertEquals(mr.get("items")[0].ordered_qty, 0)
		self.assertEquals(mr.get("items")[1].ordered_qty, 0)

		# map a stock entry
		from erpnext.stock.doctype.material_request.material_request import make_stock_entry

		se_doc = make_stock_entry(mr.name)
		se_doc.update({
			"posting_date": "2013-03-01",
			"posting_time": "00:00",
			"fiscal_year": "_Test Fiscal Year 2013",
		})
		se_doc.get("items")[0].update({
			"qty": 54.0,
			"transfer_qty": 54.0,
			"s_warehouse": "_Test Warehouse 1 - _TC",
			"basic_rate": 1.0
		})
		se_doc.get("items")[1].update({
			"qty": 3.0,
			"transfer_qty": 3.0,
			"s_warehouse": "_Test Warehouse 1 - _TC",
			"basic_rate": 1.0
		})

		# make available the qty in _Test Warehouse 1 before transfer
		self._insert_stock_entry(60.0, 3.0)

		# check for stopped status of Material Request
		se = frappe.copy_doc(se_doc)
		se.insert()
		mr.update_status('Stopped')
		self.assertRaises(frappe.InvalidStatusError, se.submit)
		self.assertRaises(frappe.InvalidStatusError, se.cancel)

		mr.update_status('Submitted')
		se = frappe.copy_doc(se_doc)
		se.insert()
		se.submit()

		# check if per complete is as expected
		mr.load_from_db()

		self.assertEquals(mr.per_ordered, 100)
		self.assertEquals(mr.get("items")[0].ordered_qty, 54.0)
		self.assertEquals(mr.get("items")[1].ordered_qty, 3.0)

		current_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC")
		current_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC")

		self.assertEquals(current_requested_qty_item1, existing_requested_qty_item1)
		self.assertEquals(current_requested_qty_item2, existing_requested_qty_item2)

		# check if per complete is as expected for Stock Entry cancelled
		se.cancel()
		mr.load_from_db()
		self.assertEquals(mr.per_ordered, 0)
		self.assertEquals(mr.get("items")[0].ordered_qty, 0)
		self.assertEquals(mr.get("items")[1].ordered_qty, 0)

		current_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC")
		current_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC")

		self.assertEquals(current_requested_qty_item1, existing_requested_qty_item1 + 54.0)
		self.assertEquals(current_requested_qty_item2, existing_requested_qty_item2 + 3.0)

	def test_incorrect_mapping_of_stock_entry(self):
		# submit material request of type Transfer
		mr = frappe.copy_doc(test_records[0])
		mr.material_request_type = "Material Transfer"
		mr.insert()
		mr.submit()

		# map a stock entry
		from erpnext.stock.doctype.material_request.material_request import make_stock_entry

		se_doc = make_stock_entry(mr.name)
		se_doc.update({
			"posting_date": "2013-03-01",
			"posting_time": "00:00",
			"fiscal_year": "_Test Fiscal Year 2013",
		})
		se_doc.get("items")[0].update({
			"qty": 60.0,
			"transfer_qty": 60.0,
			"s_warehouse": "_Test Warehouse - _TC",
			"t_warehouse": "_Test Warehouse 1 - _TC",
			"basic_rate": 1.0
		})
		se_doc.get("items")[1].update({
			"qty": 3.0,
			"transfer_qty": 3.0,
			"s_warehouse": "_Test Warehouse 1 - _TC",
			"basic_rate": 1.0
		})

		# check for stopped status of Material Request
		se = frappe.copy_doc(se_doc)
		self.assertRaises(frappe.MappingMismatchError, se.insert)

		# submit material request of type Transfer
		mr = frappe.copy_doc(test_records[0])
		mr.material_request_type = "Material Issue"
		mr.insert()
		mr.submit()

		# map a stock entry
		from erpnext.stock.doctype.material_request.material_request import make_stock_entry
		se_doc = make_stock_entry(mr.name)
		self.assertEquals(se_doc.get("items")[0].s_warehouse, "_Test Warehouse - _TC")

	def test_warehouse_company_validation(self):
		from erpnext.stock.utils import InvalidWarehouseCompany
		mr = frappe.copy_doc(test_records[0])
		mr.company = "_Test Company 1"
		self.assertRaises(InvalidWarehouseCompany, mr.insert)

	def _get_requested_qty(self, item_code, warehouse):
		return flt(frappe.db.get_value("Bin", {"item_code": item_code, "warehouse": warehouse}, "indented_qty"))

	def test_make_stock_entry_for_material_issue(self):
		from erpnext.stock.doctype.material_request.material_request import make_stock_entry

		mr = frappe.copy_doc(test_records[0]).insert()

		self.assertRaises(frappe.ValidationError, make_stock_entry,
			mr.name)

		mr = frappe.get_doc("Material Request", mr.name)
		mr.material_request_type = "Material Issue"
		mr.submit()
		se = make_stock_entry(mr.name)

		self.assertEquals(se.doctype, "Stock Entry")
		self.assertEquals(len(se.get("items")), len(mr.get("items")))

	def test_completed_qty_for_issue(self):
		def _get_requested_qty():
			return flt(frappe.db.get_value("Bin", {"item_code": "_Test Item Home Desktop 100",
				"warehouse": "_Test Warehouse - _TC"}, "indented_qty"))

		from erpnext.stock.doctype.material_request.material_request import make_stock_entry

		existing_requested_qty = _get_requested_qty()

		mr = frappe.copy_doc(test_records[0])
		mr.material_request_type = "Material Issue"
		mr.submit()

		#testing bin value after material request is submitted
		self.assertEquals(_get_requested_qty(), existing_requested_qty + 54.0)

		# receive items to allow issue
		self._insert_stock_entry(60, 6, "_Test Warehouse - _TC")

		# make stock entry against MR

		se_doc = make_stock_entry(mr.name)
		se_doc.fiscal_year = "_Test Fiscal Year 2014"
		se_doc.get("items")[0].qty = 54.0
		se_doc.insert()
		se_doc.submit()

		# check if per complete is as expected
		mr.load_from_db()
		self.assertEquals(mr.get("items")[0].ordered_qty, 54.0)
		self.assertEquals(mr.get("items")[1].ordered_qty, 3.0)

		#testing bin requested qty after issuing stock against material request
		self.assertEquals(_get_requested_qty(), existing_requested_qty)
		
	def test_material_request_type_manufacture(self):
		mr = frappe.copy_doc(test_records[1]).insert()
		mr = frappe.get_doc("Material Request", mr.name)
		mr.submit()
		completed_qty = mr.items[0].ordered_qty
		requested_qty = frappe.db.sql("""select indented_qty from `tabBin` where \
			item_code= %s and warehouse= %s """, (mr.items[0].item_code, mr.items[0].warehouse))[0][0]

		prod_order = raise_production_orders(mr.name)
		po = frappe.get_doc("Production Order", prod_order[0])
		po.wip_warehouse = "_Test Warehouse 1 - _TC"
		po.submit()
		
		mr = frappe.get_doc("Material Request", mr.name)
		self.assertEquals(completed_qty + po.qty, mr.items[0].ordered_qty)

		new_requested_qty = frappe.db.sql("""select indented_qty from `tabBin` where \
			item_code= %s and warehouse= %s """, (mr.items[0].item_code, mr.items[0].warehouse))[0][0]
		
		self.assertEquals(requested_qty - po.qty, new_requested_qty)
		
		po.cancel()

		mr = frappe.get_doc("Material Request", mr.name)
		self.assertEquals(completed_qty, mr.items[0].ordered_qty)
		
		new_requested_qty = frappe.db.sql("""select indented_qty from `tabBin` where \
			item_code= %s and warehouse= %s """, (mr.items[0].item_code, mr.items[0].warehouse))[0][0]
		self.assertEquals(requested_qty, new_requested_qty)

test_dependencies = ["Currency Exchange"]
test_records = frappe.get_test_records('Material Request')
