Ejemplo n.º 1
0
def validate_returned_items(doc):
	from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos

	valid_items = frappe._dict()

	select_fields = "item_code, sum(qty) as qty, rate" if doc.doctype=="Purchase Invoice" \
		else "item_code, sum(qty) as qty, rate, serial_no, batch_no"

	for d in frappe.db.sql("""select {0} from `tab{1} Item` where parent = %s
		group by item_code""".format(select_fields, doc.doctype), doc.return_against, as_dict=1):
			valid_items.setdefault(d.item_code, d)

	if doc.doctype in ("Delivery Note", "Sales Invoice"):
		for d in frappe.db.sql("""select item_code, sum(qty) as qty, serial_no, batch_no from `tabPacked Item`
			where parent = %s group by item_code""".format(doc.doctype), doc.return_against, as_dict=1):
				valid_items.setdefault(d.item_code, d)

	already_returned_items = get_already_returned_items(doc)

	# ( not mandatory when it is Purchase Invoice or a Sales Invoice without Update Stock )
	warehouse_mandatory = not (doc.doctype=="Purchase Invoice" or (doc.doctype=="Sales Invoice" and not doc.update_stock))

	items_returned = False
	for d in doc.get("items"):
		if flt(d.qty) < 0:
			if d.item_code not in valid_items:
				frappe.throw(_("Row # {0}: Returned Item {1} does not exists in {2} {3}")
					.format(d.idx, d.item_code, doc.doctype, doc.return_against))
			else:
				ref = valid_items.get(d.item_code, frappe._dict())
				already_returned_qty = flt(already_returned_items.get(d.item_code))
				max_return_qty = flt(ref.qty) - already_returned_qty

				if already_returned_qty >= ref.qty:
					frappe.throw(_("Item {0} has already been returned").format(d.item_code), StockOverReturnError)
				elif abs(d.qty) > max_return_qty:
					frappe.throw(_("Row # {0}: Cannot return more than {1} for Item {2}")
						.format(d.idx, ref.qty, d.item_code), StockOverReturnError)
				elif ref.batch_no and d.batch_no != ref.batch_no:
					frappe.throw(_("Row # {0}: Batch No must be same as {1} {2}")
						.format(d.idx, doc.doctype, doc.return_against))
				elif ref.serial_no:
					if not d.serial_no:
						frappe.throw(_("Row # {0}: Serial No is mandatory").format(d.idx))
					else:
						serial_nos = get_serial_nos(d.serial_no)
						ref_serial_nos = get_serial_nos(ref.serial_no)
						for s in serial_nos:
							if s not in ref_serial_nos:
								frappe.throw(_("Row # {0}: Serial No {1} does not match with {2} {3}")
									.format(d.idx, s, doc.doctype, doc.return_against))

				if warehouse_mandatory and not d.get("warehouse"):
					frappe.throw(_("Warehouse is mandatory"))

			items_returned = True

	if not items_returned:
		frappe.throw(_("Atleast one item should be entered with negative quantity in return document"))
Ejemplo n.º 2
0
	def test_serialized_partial_sales_invoice(self):
		se = make_serialized_item()
		serial_no = get_serial_nos(se.get("items")[0].serial_no)
		serial_no = '\n'.join(serial_no)

		dn = create_delivery_note(item_code="_Test Serialized Item With Series", qty=2, serial_no=serial_no)

		si = make_sales_invoice(dn.name)
		si.items[0].qty = 1
		si.submit()
		self.assertEqual(si.items[0].qty, 1)

		si = make_sales_invoice(dn.name)
		si.submit()
		self.assertEqual(si.items[0].qty, len(get_serial_nos(si.items[0].serial_no)))
Ejemplo n.º 3
0
	def update_rate_in_serial_no(self, purchase_receipt):
		for item in purchase_receipt.get("purchase_receipt_details"):
			if item.serial_no:
				serial_nos = get_serial_nos(item.serial_no)
				if serial_nos:
					frappe.db.sql("update `tabSerial No` set purchase_rate=%s where name in ({0})"
						.format(", ".join(["%s"]*len(serial_nos))), tuple([item.valuation_rate] + serial_nos))
Ejemplo n.º 4
0
    def test_serialize_status(self):
        from erpnext.stock.doctype.serial_no.serial_no import (
            SerialNoStatusError,
            get_serial_nos,
            SerialNoDuplicateError,
        )
        from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item

        se = make_serialized_item()
        serial_nos = get_serial_nos(se.get("items")[0].serial_no)

        sr = frappe.get_doc("Serial No", serial_nos[0])
        sr.status = "Not Available"
        sr.save()

        si = frappe.copy_doc(test_records[0])
        si.update_stock = 1
        si.get("items")[0].item_code = "_Test Serialized Item With Series"
        si.get("items")[0].qty = 1
        si.get("items")[0].serial_no = serial_nos[0]
        si.insert()

        self.assertRaises(SerialNoStatusError, si.submit)

        # hack! because stock ledger entires are already inserted and are not rolled back!
        self.assertRaises(SerialNoDuplicateError, si.cancel)
Ejemplo n.º 5
0
    def test_return_for_serialized_items(self):
        se = make_serialized_item()
        serial_no = get_serial_nos(se.get("items")[0].serial_no)[0]

        dn = create_delivery_note(item_code="_Test Serialized Item With Series", rate=500, serial_no=serial_no)

        self.check_serial_no_values(serial_no, {"warehouse": "", "delivery_document_no": dn.name})

        # return entry
        dn1 = create_delivery_note(
            item_code="_Test Serialized Item With Series",
            is_return=1,
            return_against=dn.name,
            qty=-1,
            rate=500,
            serial_no=serial_no,
        )

        self.check_serial_no_values(serial_no, {"warehouse": "_Test Warehouse - _TC", "delivery_document_no": ""})

        dn1.cancel()

        self.check_serial_no_values(serial_no, {"warehouse": "", "delivery_document_no": dn.name})

        dn.cancel()

        self.check_serial_no_values(
            serial_no,
            {"warehouse": "_Test Warehouse - _TC", "delivery_document_no": "", "purchase_document_no": se.name},
        )
Ejemplo n.º 6
0
	def set_missing_item_details(self, for_validate=False):
		"""set missing item values"""
		from erpnext.stock.get_item_details import get_item_details
		from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos

		if hasattr(self, "items"):
			parent_dict = {}
			for fieldname in self.meta.get_valid_columns():
				parent_dict[fieldname] = self.get(fieldname)

			if self.doctype in ["Quotation", "Sales Order", "Delivery Note", "Sales Invoice"]:
				document_type = "{} Item".format(self.doctype)
				parent_dict.update({"document_type": document_type})

			for item in self.get("items"):
				if item.get("item_code"):
					args = parent_dict.copy()
					args.update(item.as_dict())

					args["doctype"] = self.doctype
					args["name"] = self.name

					if not args.get("transaction_date"):
						args["transaction_date"] = args.get("posting_date")

					if self.get("is_subcontracted"):
						args["is_subcontracted"] = self.is_subcontracted
					ret = get_item_details(args)
					for fieldname, value in ret.items():
						if item.meta.get_field(fieldname) and value is not None:
							if (item.get(fieldname) is None or fieldname in force_item_fields):
								item.set(fieldname, value)

							elif fieldname in ['cost_center', 'conversion_factor'] and not item.get(fieldname):
								item.set(fieldname, value)

							elif fieldname == "serial_no":
								# Ensure that serial numbers are matched against Stock UOM
								item_conversion_factor = item.get("conversion_factor") or 1.0
								item_qty = abs(item.get("qty")) * item_conversion_factor

								if item_qty != len(get_serial_nos(item.get('serial_no'))):
									item.set(fieldname, value)

					if self.doctype in ["Purchase Invoice", "Sales Invoice"] and item.meta.get_field('is_fixed_asset'):
						item.set('is_fixed_asset', ret.get('is_fixed_asset', 0))

					if ret.get("pricing_rule"):
						# if user changed the discount percentage then set user's discount percentage ?
						item.set("pricing_rule", ret.get("pricing_rule"))
						item.set("discount_percentage", ret.get("discount_percentage"))
						if ret.get("pricing_rule_for") == "Rate":
							item.set("price_list_rate", ret.get("price_list_rate"))

						if item.price_list_rate:
							item.rate = flt(item.price_list_rate *
											(1.0 - (flt(item.discount_percentage) / 100.0)), item.precision("rate"))

			if self.doctype == "Purchase Invoice":
				self.set_expense_account(for_validate)
Ejemplo n.º 7
0
	def test_purchase_return_for_serialized_items(self):
		def _check_serial_no_values(serial_no, field_values):
			serial_no = frappe.get_doc("Serial No", serial_no)
			for field, value in field_values.items():
				self.assertEquals(cstr(serial_no.get(field)), value)

		from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos

		pr = make_purchase_receipt(item_code="_Test Serialized Item With Series", qty=1)

		serial_no = get_serial_nos(pr.get("items")[0].serial_no)[0]

		_check_serial_no_values(serial_no, {
			"warehouse": "_Test Warehouse - _TC",
			"purchase_document_no": pr.name
		})

		return_pr = make_purchase_receipt(item_code="_Test Serialized Item With Series", qty=-1,
			is_return=1, return_against=pr.name, serial_no=serial_no)

		_check_serial_no_values(serial_no, {
			"warehouse": "",
			"purchase_document_no": pr.name,
			"delivery_document_no": return_pr.name
		})
Ejemplo n.º 8
0
def get_ref_item_dict(valid_items, ref_item_row):
    from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos

    valid_items.setdefault(
        ref_item_row.item_code,
        frappe._dict({
            "qty": 0,
            "rejected_qty": 0,
            "received_qty": 0,
            "serial_no": [],
            "batch_no": []
        }))
    item_dict = valid_items[ref_item_row.item_code]
    item_dict["qty"] += ref_item_row.qty

    if ref_item_row.parenttype in ['Purchase Invoice', 'Purchase Receipt']:
        item_dict["received_qty"] += ref_item_row.received_qty
        item_dict["rejected_qty"] += ref_item_row.rejected_qty

    if ref_item_row.get("serial_no"):
        item_dict["serial_no"] += get_serial_nos(ref_item_row.serial_no)

    if ref_item_row.get("batch_no"):
        item_dict["batch_no"].append(ref_item_row.batch_no)

    return valid_items
Ejemplo n.º 9
0
	def test_serialized(self):
		from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
		from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos

		se = make_serialized_item()
		serial_nos = get_serial_nos(se.get("items")[0].serial_no)

		si = frappe.copy_doc(test_records[0])
		si.update_stock = 1
		si.get("items")[0].item_code = "_Test Serialized Item With Series"
		si.get("items")[0].qty = 1
		si.get("items")[0].serial_no = serial_nos[0]
		si.insert()
		si.submit()

		self.assertFalse(frappe.db.get_value("Serial No", serial_nos[0], "warehouse"))
		self.assertEquals(frappe.db.get_value("Serial No", serial_nos[0],
			"delivery_document_no"), si.name)
		self.assertEquals(frappe.db.get_value("Serial No", serial_nos[0], "sales_invoice"),
			si.name)

		# check if the serial number is already linked with any other Sales Invoice
		_si = frappe.copy_doc(si.as_dict())
		self.assertRaises(frappe.ValidationError, _si.insert)

		return si
Ejemplo n.º 10
0
def create_asset_movement(**args):
	args = frappe._dict(args)

	if not args.transaction_date:
		args.transaction_date = now()

	movement = frappe.new_doc("Asset Movement")
	movement.update({
		"asset": args.asset,
		"transaction_date": args.transaction_date,
		"target_location": args.target_location,
		"company": args.company,
		'purpose': args.purpose or 'Receipt',
		'serial_no': args.serial_no,
		'quantity': len(get_serial_nos(args.serial_no)) if args.serial_no else 1,
		'from_employee': "_T-Employee-00001" or args.from_employee,
		'to_employee': args.to_employee
	})

	if args.source_location:
		movement.update({
			'source_location': args.source_location
		})

	movement.insert()
	movement.submit()

	return movement
	def test_serialize_status(self):
		se = make_serialized_item()
		serial_no = get_serial_nos(se.get("items")[0].serial_no)[0]
		
		frappe.db.set_value("Serial No", serial_no, "status", "Not Available")

		dn = create_delivery_note(item_code="_Test Serialized Item With Series", 
			serial_no=serial_no, do_not_submit=True)

		self.assertRaises(SerialNoStatusError, dn.submit)
Ejemplo n.º 12
0
	def test_serialized_cancel(self):
		from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
		si = self.test_serialized()
		si.cancel()

		serial_nos = get_serial_nos(si.get("items")[0].serial_no)

		self.assertEquals(frappe.db.get_value("Serial No", serial_nos[0], "warehouse"), "_Test Warehouse - _TC")
		self.assertFalse(frappe.db.get_value("Serial No", serial_nos[0],
			"delivery_document_no"))
Ejemplo n.º 13
0
def validate_returned_items(doc):
	from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos

	valid_items = frappe._dict()

	select_fields = "item_code, qty, parenttype" if doc.doctype=="Purchase Invoice" \
		else "item_code, qty, serial_no, batch_no, parenttype"

	if doc.doctype in ['Purchase Invoice', 'Purchase Receipt']:
		select_fields += ",rejected_qty, received_qty"

	for d in frappe.db.sql("""select {0} from `tab{1} Item` where parent = %s"""
		.format(select_fields, doc.doctype), doc.return_against, as_dict=1):
			valid_items = get_ref_item_dict(valid_items, d)

	if doc.doctype in ("Delivery Note", "Sales Invoice"):
		for d in frappe.db.sql("""select item_code, qty, serial_no, batch_no from `tabPacked Item`
			where parent = %s""".format(doc.doctype), doc.return_against, as_dict=1):
				valid_items = get_ref_item_dict(valid_items, d)

	already_returned_items = get_already_returned_items(doc)

	# ( not mandatory when it is Purchase Invoice or a Sales Invoice without Update Stock )
	warehouse_mandatory = not (doc.doctype=="Purchase Invoice" or (doc.doctype=="Sales Invoice" and not doc.update_stock))

	items_returned = False
	for d in doc.get("items"):
		if flt(d.qty) < 0 or d.get('received_qty') < 0:
			if d.item_code not in valid_items:
				frappe.throw(_("Row # {0}: Returned Item {1} does not exists in {2} {3}")
					.format(d.idx, d.item_code, doc.doctype, doc.return_against))
			else:
				ref = valid_items.get(d.item_code, frappe._dict())
				validate_quantity(doc, d, ref, valid_items, already_returned_items)

				if ref.batch_no and d.batch_no not in ref.batch_no:
					frappe.throw(_("Row # {0}: Batch No must be same as {1} {2}")
						.format(d.idx, doc.doctype, doc.return_against))
				elif ref.serial_no:
					if not d.serial_no:
						frappe.throw(_("Row # {0}: Serial No is mandatory").format(d.idx))
					else:
						serial_nos = get_serial_nos(d.serial_no)
						for s in serial_nos:
							if s not in ref.serial_no:
								frappe.throw(_("Row # {0}: Serial No {1} does not match with {2} {3}")
									.format(d.idx, s, doc.doctype, doc.return_against))

				if warehouse_mandatory and not d.get("warehouse"):
					frappe.throw(_("Warehouse is mandatory"))

			items_returned = True

	if not items_returned:
		frappe.throw(_("Atleast one item should be entered with negative quantity in return document"))
	def test_serialized_cancel(self):
		from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
		dn = self.test_serialized()
		dn.cancel()

		serial_nos = get_serial_nos(dn.get("delivery_note_details")[0].serial_no)

		self.assertEquals(frappe.db.get_value("Serial No", serial_nos[0], "status"), "Available")
		self.assertEquals(frappe.db.get_value("Serial No", serial_nos[0], "warehouse"), "_Test Warehouse - _TC")
		self.assertFalse(frappe.db.get_value("Serial No", serial_nos[0],
			"delivery_document_no"))
Ejemplo n.º 15
0
    def test_serialized(self):
        se = make_serialized_item()
        serial_no = get_serial_nos(se.get("items")[0].serial_no)[0]

        dn = create_delivery_note(item_code="_Test Serialized Item With Series", serial_no=serial_no)

        self.check_serial_no_values(serial_no, {"warehouse": "", "delivery_document_no": dn.name})

        dn.cancel()

        self.check_serial_no_values(serial_no, {"warehouse": "_Test Warehouse - _TC", "delivery_document_no": ""})
Ejemplo n.º 16
0
	def validate_serial_against_delivery_note(self):
		"""
			validate if the serial numbers in Sales Invoice Items are same as in
			Delivery Note Item
		"""

		for item in self.items:
			if not item.delivery_note or not item.dn_detail:
				continue

			serial_nos = frappe.db.get_value("Delivery Note Item", item.dn_detail, "serial_no") or ""
			dn_serial_nos = set(get_serial_nos(serial_nos))

			serial_nos = item.serial_no or ""
			si_serial_nos = set(get_serial_nos(serial_nos))

			if si_serial_nos - dn_serial_nos:
				frappe.throw(_("Serial Numbers in row {0} does not match with Delivery Note".format(item.idx)))

			if item.serial_no and cint(item.qty) != len(si_serial_nos):
				frappe.throw(_("Row {0}: {1} Serial numbers required for Item {2}. You have provided {3}.".format(
					item.idx, item.qty, item.item_code, len(si_serial_nos))))
Ejemplo n.º 17
0
	def test_inter_company_transfer(self):
		se = make_serialized_item(target_warehouse="_Test Warehouse - _TC")
		serial_nos = get_serial_nos(se.get("items")[0].serial_no)

		create_delivery_note(item_code="_Test Serialized Item With Series", qty=1, serial_no=serial_nos[0])

		wh = create_warehouse("_Test Warehouse", company="_Test Company 1")
		make_purchase_receipt(item_code="_Test Serialized Item With Series", qty=1, serial_no=serial_nos[0],
			company="_Test Company 1", warehouse=wh)

		serial_no = frappe.db.get_value("Serial No", serial_nos[0], ["warehouse", "company"], as_dict=1)

		self.assertEqual(serial_no.warehouse, wh)
		self.assertEqual(serial_no.company, "_Test Company 1")
Ejemplo n.º 18
0
	def make_asset_movement(self, row):
		asset_movement = frappe.get_doc({
			'doctype': 'Asset Movement',
			'asset': row.asset,
			'target_location': row.asset_location,
			'purpose': 'Receipt',
			'serial_no': row.serial_no,
			'quantity': len(get_serial_nos(row.serial_no)),
			'company': self.company,
			'transaction_date': self.posting_date,
			'reference_doctype': self.doctype,
			'reference_name': self.name
		}).insert()

		return asset_movement.name
Ejemplo n.º 19
0
	def validate_asset(self):
		status, company = frappe.db.get_value("Asset", self.asset, ["status", "company"])
		if self.purpose == 'Transfer' and status in ("Draft", "Scrapped", "Sold"):
			frappe.throw(_("{0} asset cannot be transferred").format(status))

		if company != self.company:
			frappe.throw(_("Asset {0} does not belong to company {1}").format(self.asset, self.company))

		if self.serial_no and len(get_serial_nos(self.serial_no)) != self.quantity:
			frappe.throw(_("Number of serial nos and quantity must be the same"))

		if not(self.source_location or self.target_location or self.from_employee or self.to_employee):
			frappe.throw(_("Either location or employee must be required"))

		if (not self.serial_no and
			frappe.db.get_value('Serial No', {'asset': self.asset}, 'name')):
			frappe.throw(_("Serial no is required for the asset {0}").format(self.asset))
	def test_serialize_status(self):
		from erpnext.stock.doctype.serial_no.serial_no import SerialNoStatusError, get_serial_nos
		from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item

		se = make_serialized_item()
		serial_nos = get_serial_nos(se.get("mtn_details")[0].serial_no)

		sr = frappe.get_doc("Serial No", serial_nos[0])
		sr.status = "Not Available"
		sr.save()

		dn = frappe.copy_doc(test_records[0])
		dn.get("delivery_note_details")[0].item_code = "_Test Serialized Item With Series"
		dn.get("delivery_note_details")[0].qty = 1
		dn.get("delivery_note_details")[0].serial_no = serial_nos[0]
		dn.insert()

		self.assertRaises(SerialNoStatusError, dn.submit)
Ejemplo n.º 21
0
def get_ref_item_dict(valid_items, ref_item_row):
	from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
	
	valid_items.setdefault(ref_item_row.item_code, frappe._dict({
		"qty": 0,
		"serial_no": [],
		"batch_no": []
	}))
	item_dict = valid_items[ref_item_row.item_code]
	item_dict["qty"] += ref_item_row.qty
	
	if ref_item_row.get("serial_no"):
		item_dict["serial_no"] += get_serial_nos(ref_item_row.serial_no)
		
	if ref_item_row.get("batch_no"):
		item_dict["batch_no"].append(ref_item_row.batch_no)
		
	return valid_items
Ejemplo n.º 22
0
	def set_latest_location_in_asset(self):
		location, employee = '', ''
		cond = "1=1"

		args = {
			'asset': self.asset,
			'company': self.company
		}

		if self.serial_no:
			cond = "serial_no like %(txt)s"
			args.update({
				'txt': "%%%s%%" % self.serial_no
			})

		latest_movement_entry = frappe.db.sql("""select target_location, to_employee from `tabAsset Movement`
			where asset=%(asset)s and docstatus=1 and company=%(company)s and {0}
			order by transaction_date desc limit 1""".format(cond), args)

		if latest_movement_entry:
			location = latest_movement_entry[0][0]
			employee = latest_movement_entry[0][1]
		elif self.purpose in ['Transfer', 'Receipt']:
			movement_entry = frappe.db.sql("""select source_location, from_employee from `tabAsset Movement`
				where asset=%(asset)s and docstatus=2 and company=%(company)s and {0}
				order by transaction_date asc limit 1""".format(cond), args)
			if movement_entry:
				location = movement_entry[0][0]
				employee = movement_entry[0][1]

		if not self.serial_no:
			frappe.db.set_value("Asset", self.asset, "location", location)

		if not employee and self.purpose in ['Receipt', 'Transfer']:
			employee = self.to_employee

		if self.serial_no:
			for d in get_serial_nos(self.serial_no):
				if (location or (self.purpose == 'Issue' and self.source_location)):
					frappe.db.set_value('Serial No', d, 'location', location)

				if employee or self.docstatus==2 or self.purpose == 'Issue':
					frappe.db.set_value('Serial No', d, 'employee', employee)
	def test_serialize_status(self):
		from erpnext.stock.doctype.serial_no.serial_no import SerialNoStatusError, get_serial_nos
		from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item

		se = make_serialized_item()
		serial_nos = get_serial_nos(se.get("items")[0].serial_no)

		sr = frappe.get_doc("Serial No", serial_nos[0])
		sr.status = "Not Available"
		sr.save()

		si = frappe.copy_doc(test_records[0])
		si.update_stock = 1
		si.get("items")[0].item_code = "_Test Serialized Item With Series"
		si.get("items")[0].qty = 1
		si.get("items")[0].serial_no = serial_nos[0]
		si.insert()

		self.assertRaises(SerialNoStatusError, si.submit)
	def test_serialized(self):
		from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
		from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos

		se = make_serialized_item()
		serial_nos = get_serial_nos(se.get("mtn_details")[0].serial_no)

		dn = frappe.copy_doc(test_records[0])
		dn.get("delivery_note_details")[0].item_code = "_Test Serialized Item With Series"
		dn.get("delivery_note_details")[0].qty = 1
		dn.get("delivery_note_details")[0].serial_no = serial_nos[0]
		dn.insert()
		dn.submit()

		self.assertEquals(frappe.db.get_value("Serial No", serial_nos[0], "status"), "Delivered")
		self.assertFalse(frappe.db.get_value("Serial No", serial_nos[0], "warehouse"))
		self.assertEquals(frappe.db.get_value("Serial No", serial_nos[0],
			"delivery_document_no"), dn.name)

		return dn
Ejemplo n.º 25
0
	def test_serial_numbers_against_delivery_note(self):
		""" 
			check if the sales invoice item serial numbers and the delivery note items
			serial numbers are same
		"""
		from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
		from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
		from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_invoice
		from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos

		se = make_serialized_item()
		serial_nos = get_serial_nos(se.get("items")[0].serial_no)

		dn = create_delivery_note(item=se.get("items")[0].item_code, serial_no=serial_nos[0])
		dn.submit()

		si = make_sales_invoice(dn.name)
		si.save()

		self.assertEquals(si.get("items")[0].serial_no, dn.get("items")[0].serial_no)
Ejemplo n.º 26
0
	def test_serialized(self):
		from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
		from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos

		se = make_serialized_item()
		serial_nos = get_serial_nos(se.get("items")[0].serial_no)

		si = frappe.copy_doc(test_records[0])
		si.update_stock = 1
		si.get("items")[0].item_code = "_Test Serialized Item With Series"
		si.get("items")[0].qty = 1
		si.get("items")[0].serial_no = serial_nos[0]
		si.insert()
		si.submit()

		self.assertFalse(frappe.db.get_value("Serial No", serial_nos[0], "warehouse"))
		self.assertEquals(frappe.db.get_value("Serial No", serial_nos[0],
			"delivery_document_no"), si.name)

		return si
Ejemplo n.º 27
0
	def test_serialized(self):
		se = make_serialized_item()
		serial_no = get_serial_nos(se.get("items")[0].serial_no)[0]

		dn = create_delivery_note(item_code="_Test Serialized Item With Series", serial_no=serial_no)

		self.check_serial_no_values(serial_no, {
			"warehouse": "",
			"delivery_document_no": dn.name
		})

		si = make_sales_invoice(dn.name)
		si.insert(ignore_permissions=True)
		self.assertEqual(dn.items[0].serial_no, si.items[0].serial_no)

		dn.cancel()

		self.check_serial_no_values(serial_no, {
			"warehouse": "_Test Warehouse - _TC",
			"delivery_document_no": ""
		})
Ejemplo n.º 28
0
	def validate_location(self):
		if self.purpose in ['Transfer', 'Issue']:
			if not self.serial_no and not (self.from_employee or self.to_employee):
				self.source_location = frappe.db.get_value("Asset", self.asset, "location")

			if self.purpose == 'Issue' and not (self.source_location or self.from_employee):
				frappe.throw(_("Source Location is required for the asset {0}").format(self.asset))

			if self.serial_no and self.source_location:
				s_nos = get_serial_nos(self.serial_no)
				serial_nos = frappe.db.sql_list(""" select name from `tabSerial No` where location != '%s'
					and name in (%s)""" %(self.source_location, ','.join(['%s'] * len(s_nos))), tuple(s_nos))

				if serial_nos:
					frappe.throw(_("Serial nos {0} does not belongs to the location {1}").
						format(','.join(serial_nos), self.source_location))

		if self.source_location and self.source_location == self.target_location and self.purpose == 'Transfer':
			frappe.throw(_("Source and Target Location cannot be same"))

		if self.purpose == 'Receipt' and not (self.target_location or self.to_employee):
			frappe.throw(_("Target Location is required for the asset {0}").format(self.asset))
def validate_returned_items(doc):
    from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos

    valid_items = frappe._dict()

    select_fields = "item_code, sum(qty) as qty, rate" if doc.doctype=="Purchase Invoice" \
     else "item_code, sum(qty) as qty, rate, serial_no, batch_no"

    for d in frappe.db.sql("""select {0} from `tab{1} Item` where parent = %s
		group by item_code""".format(select_fields, doc.doctype),
                           doc.return_against,
                           as_dict=1):
        valid_items.setdefault(d.item_code, d)

    if doc.doctype in ("Delivery Note", "Sales Invoice"):
        for d in frappe.db.sql(
                """select item_code, sum(qty) as qty, serial_no, batch_no from `tabPacked Item`
			where parent = %s group by item_code""".format(doc.doctype),
                doc.return_against,
                as_dict=1):
            valid_items.setdefault(d.item_code, d)

    already_returned_items = get_already_returned_items(doc)

    items_returned = False
    for d in doc.get("items"):
        if flt(d.qty) < 0:
            if d.item_code not in valid_items:
                frappe.throw(
                    _("Row # {0}: Returned Item {1} does not exists in {2} {3}"
                      ).format(d.idx, d.item_code, doc.doctype,
                               doc.return_against))
            else:
                ref = valid_items.get(d.item_code, frappe._dict())
                already_returned_qty = flt(
                    already_returned_items.get(d.item_code))
                max_return_qty = flt(ref.qty) - already_returned_qty

                if already_returned_qty >= ref.qty:
                    frappe.throw(
                        _("Item {0} has already been returned").format(
                            d.item_code), StockOverReturnError)
                elif abs(d.qty) > max_return_qty:
                    frappe.throw(
                        _("Row # {0}: Cannot return more than {1} for Item {2}"
                          ).format(d.idx, ref.qty, d.item_code),
                        StockOverReturnError)
                elif ref.batch_no and d.batch_no != ref.batch_no:
                    frappe.throw(
                        _("Row # {0}: Batch No must be same as {1} {2}").
                        format(d.idx, doc.doctype, doc.return_against))
                elif ref.serial_no:
                    if not d.serial_no:
                        frappe.throw(
                            _("Row # {0}: Serial No is mandatory").format(
                                d.idx))
                    else:
                        serial_nos = get_serial_nos(d.serial_no)
                        ref_serial_nos = get_serial_nos(ref.serial_no)
                        for s in serial_nos:
                            if s not in ref_serial_nos:
                                frappe.throw(
                                    _("Row # {0}: Serial No {1} does not match with {2} {3}"
                                      ).format(d.idx, s, doc.doctype,
                                               doc.return_against))

            items_returned = True

    if not items_returned:
        frappe.throw(
            _("Atleast one item should be entered with negative quantity in return document"
              ))
Ejemplo n.º 30
0
def get_serial_nos_data(serial_nos):
    from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
    return get_serial_nos(serial_nos)
def validate_returned_items(doc):
	from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos

	valid_items = frappe._dict()

	select_fields = "item_code, qty, stock_qty, rate, parenttype, conversion_factor"
	if doc.doctype != 'Purchase Invoice':
		select_fields += ",serial_no, batch_no"

	if doc.doctype in ['Purchase Invoice', 'Purchase Receipt']:
		select_fields += ",rejected_qty, received_qty"

	for d in frappe.db.sql("""select {0} from `tab{1} Item` where parent = %s"""
		.format(select_fields, doc.doctype), doc.return_against, as_dict=1):
			valid_items = get_ref_item_dict(valid_items, d)

	if doc.doctype in ("Delivery Note", "Sales Invoice"):
		for d in frappe.db.sql("""select item_code, qty, serial_no, batch_no from `tabPacked Item`
			where parent = %s""".format(doc.doctype), doc.return_against, as_dict=1):
				valid_items = get_ref_item_dict(valid_items, d)

	already_returned_items = get_already_returned_items(doc)

	# ( not mandatory when it is Purchase Invoice or a Sales Invoice without Update Stock )
	warehouse_mandatory = not ((doc.doctype=="Purchase Invoice" or doc.doctype=="Sales Invoice") and not doc.update_stock)

	items_returned = False
	for d in doc.get("items"):
		if d.item_code and (flt(d.qty) < 0 or flt(d.get('received_qty')) < 0):
			if d.item_code not in valid_items:
				frappe.throw(_("Row # {0}: Returned Item {1} does not exists in {2} {3}")
					.format(d.idx, d.item_code, doc.doctype, doc.return_against))
			else:
				ref = valid_items.get(d.item_code, frappe._dict())
				validate_quantity(doc, d, ref, valid_items, already_returned_items)

				if ref.rate and doc.doctype in ("Delivery Note", "Sales Invoice") and flt(d.rate) > ref.rate:
					frappe.throw(_("Row # {0}: Rate cannot be greater than the rate used in {1} {2}")
						.format(d.idx, doc.doctype, doc.return_against))

				elif ref.batch_no and d.batch_no not in ref.batch_no:
					frappe.throw(_("Row # {0}: Batch No must be same as {1} {2}")
						.format(d.idx, doc.doctype, doc.return_against))

				elif ref.serial_no:
					if not d.serial_no:
						frappe.throw(_("Row # {0}: Serial No is mandatory").format(d.idx))
					else:
						serial_nos = get_serial_nos(d.serial_no)
						for s in serial_nos:
							if s not in ref.serial_no:
								frappe.throw(_("Row # {0}: Serial No {1} does not match with {2} {3}")
									.format(d.idx, s, doc.doctype, doc.return_against))

				if warehouse_mandatory and frappe.db.get_value("Item", d.item_code, "is_stock_item") \
					and not d.get("warehouse"):
						frappe.throw(_("Warehouse is mandatory"))

			items_returned = True

		elif d.item_name:
			items_returned = True

	if not items_returned:
		frappe.throw(_("Atleast one item should be entered with negative quantity in return document"))
Ejemplo n.º 32
0
    def test_stock_reco_for_serialized_item(self):
        set_perpetual_inventory()

        to_delete_records = []
        to_delete_serial_nos = []

        # Add new serial nos
        serial_item_code = "Stock-Reco-Serial-Item-1"
        serial_warehouse = "_Test Warehouse for Stock Reco1 - _TC"

        sr = create_stock_reconciliation(item_code=serial_item_code,
                                         warehouse=serial_warehouse,
                                         qty=5,
                                         rate=200)

        # print(sr.name)
        serial_nos = get_serial_nos(sr.items[0].serial_no)
        self.assertEqual(len(serial_nos), 5)

        args = {
            "item_code": serial_item_code,
            "warehouse": serial_warehouse,
            "posting_date": nowdate(),
            "posting_time": nowtime(),
            "serial_no": sr.items[0].serial_no
        }

        valuation_rate = get_incoming_rate(args)
        self.assertEqual(valuation_rate, 200)

        to_delete_records.append(sr.name)

        sr = create_stock_reconciliation(item_code=serial_item_code,
                                         warehouse=serial_warehouse,
                                         qty=5,
                                         rate=300,
                                         serial_no='\n'.join(serial_nos))

        # print(sr.name)
        serial_nos1 = get_serial_nos(sr.items[0].serial_no)
        self.assertEqual(len(serial_nos1), 5)

        args = {
            "item_code": serial_item_code,
            "warehouse": serial_warehouse,
            "posting_date": nowdate(),
            "posting_time": nowtime(),
            "serial_no": sr.items[0].serial_no
        }

        valuation_rate = get_incoming_rate(args)
        self.assertEqual(valuation_rate, 300)

        to_delete_records.append(sr.name)
        to_delete_records.reverse()

        for d in to_delete_records:
            stock_doc = frappe.get_doc("Stock Reconciliation", d)
            stock_doc.cancel()
            frappe.delete_doc("Stock Reconciliation", stock_doc.name)

        for d in serial_nos + serial_nos1:
            if frappe.db.exists("Serial No", d):
                frappe.delete_doc("Serial No", d)
Ejemplo n.º 33
0
 def update_serial(source, target, parent):
     serial_nos = get_serial_nos(target.serial_no)
     if len(serial_nos) == 1:
         target.serial_no = serial_nos[0]
     else:
         target.serial_no = ""
Ejemplo n.º 34
0
    def get_sle_for_serialized_items(self, row, sl_entries):
        from erpnext.stock.stock_ledger import get_previous_sle

        serial_nos = get_serial_nos(row.serial_no)

        # To issue existing serial nos
        if row.current_qty and (row.current_serial_no or row.batch_no):
            args = self.get_sle_for_items(row)
            args.update({
                'actual_qty': -1 * row.current_qty,
                'serial_no': row.current_serial_no,
                'batch_no': row.batch_no,
                'valuation_rate': row.current_valuation_rate
            })

            if row.current_serial_no:
                args.update({
                    'qty_after_transaction': 0,
                })

            sl_entries.append(args)

        qty_after_transaction = 0
        for serial_no in serial_nos:
            args = self.get_sle_for_items(row, [serial_no])

            previous_sle = get_previous_sle({
                "item_code": row.item_code,
                "posting_date": self.posting_date,
                "posting_time": self.posting_time,
                "serial_no": serial_no
            })

            if previous_sle and row.warehouse != previous_sle.get("warehouse"):
                # If serial no exists in different warehouse

                warehouse = previous_sle.get("warehouse", '') or row.warehouse

                if not qty_after_transaction:
                    qty_after_transaction = get_stock_balance(
                        row.item_code, warehouse, self.posting_date,
                        self.posting_time)

                qty_after_transaction -= 1

                new_args = args.copy()
                new_args.update({
                    'actual_qty':
                    -1,
                    'qty_after_transaction':
                    qty_after_transaction,
                    'warehouse':
                    warehouse,
                    'valuation_rate':
                    previous_sle.get("valuation_rate")
                })

                sl_entries.append(new_args)

        if row.qty:
            args = self.get_sle_for_items(row)

            args.update({
                'actual_qty': row.qty,
                'incoming_rate': row.valuation_rate,
                'valuation_rate': row.valuation_rate
            })

            sl_entries.append(args)

        if serial_nos == get_serial_nos(row.current_serial_no):
            # update valuation rate
            self.update_valuation_rate_for_serial_nos(row, serial_nos)
Ejemplo n.º 35
0
    def update_valuation_rate_for_serial_no(self):
        for d in self.items:
            if not d.serial_no: continue

            serial_nos = get_serial_nos(d.serial_no)
            self.update_valuation_rate_for_serial_nos(d, serial_nos)
Ejemplo n.º 36
0
    def test_return_non_consumed_materials(self):
        """
		- Set backflush based on Material Transfer
		- Create subcontracted PO for the item Subcontracted Item SA2.
		- Transfer the components from Stores to Supplier warehouse with serial nos.
		- Transfer extra qty of component for the subcontracted item Subcontracted Item SA2.
		- Create purchase receipt for full qty against the PO and change the qty of raw material.
		- After that return the non consumed material back to the store from supplier's warehouse.
		"""

        set_backflush_based_on("Material Transferred for Subcontract")
        items = [{
            "warehouse": "_Test Warehouse - _TC",
            "item_code": "Subcontracted Item SA2",
            "qty": 5,
            "rate": 100,
        }]
        rm_items = [{
            "item_code": "Subcontracted SRM Item 2",
            "qty": 6,
            "main_item_code": "Subcontracted Item SA2"
        }]

        itemwise_details = make_stock_in_entry(rm_items=rm_items)
        po = create_purchase_order(
            rm_items=items,
            is_subcontracted="Yes",
            supplier_warehouse="_Test Warehouse 1 - _TC")

        for d in rm_items:
            d["po_detail"] = po.items[0].name

        make_stock_transfer_entry(
            po_no=po.name,
            rm_items=rm_items,
            itemwise_details=copy.deepcopy(itemwise_details))

        pr1 = make_purchase_receipt(po.name)
        pr1.save()
        pr1.supplied_items[0].consumed_qty = 5
        pr1.supplied_items[0].serial_no = "\n".join(
            sorted(
                itemwise_details.get("Subcontracted SRM Item 2").get(
                    "serial_no")[0:5]))
        pr1.submit()

        for key, value in get_supplied_items(pr1).items():
            transferred_detais = itemwise_details.get(key)
            self.assertEqual(value.qty, 5)
            self.assertEqual(sorted(value.serial_no),
                             sorted(transferred_detais.get("serial_no")[0:5]))

        po.load_from_db()
        self.assertEqual(po.supplied_items[0].consumed_qty, 5)
        doc = get_materials_from_supplier(po.name,
                                          [d.name for d in po.supplied_items])
        self.assertEqual(doc.items[0].qty, 1)
        self.assertEqual(doc.items[0].s_warehouse, "_Test Warehouse 1 - _TC")
        self.assertEqual(doc.items[0].t_warehouse, "_Test Warehouse - _TC")
        self.assertEqual(
            get_serial_nos(doc.items[0].serial_no),
            itemwise_details.get(doc.items[0].item_code)["serial_no"][5:6],
        )
Ejemplo n.º 37
0
    def set_missing_item_details(self, for_validate=False):
        """set missing item values"""
        from erpnext.stock.get_item_details import get_item_details
        from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos

        if hasattr(self, "items"):
            parent_dict = {}
            for fieldname in self.meta.get_valid_columns():
                parent_dict[fieldname] = self.get(fieldname)

            if self.doctype in [
                    "Quotation", "Sales Order", "Delivery Note",
                    "Sales Invoice"
            ]:
                document_type = "{} Item".format(self.doctype)
                parent_dict.update({"document_type": document_type})

            for item in self.get("items"):
                if item.get("item_code"):
                    args = parent_dict.copy()
                    args.update(item.as_dict())

                    args["doctype"] = self.doctype
                    args["name"] = self.name

                    if not args.get("transaction_date"):
                        args["transaction_date"] = args.get("posting_date")

                    if self.get("is_subcontracted"):
                        args["is_subcontracted"] = self.is_subcontracted

                    ret = get_item_details(args)

                    for fieldname, value in ret.items():
                        if item.meta.get_field(
                                fieldname) and value is not None:
                            if (item.get(fieldname) is None
                                    or fieldname in force_item_fields):
                                item.set(fieldname, value)

                            elif fieldname in [
                                    'cost_center', 'conversion_factor'
                            ] and not item.get(fieldname):
                                item.set(fieldname, value)

                            elif fieldname == "serial_no":
                                stock_qty = item.get(
                                    "stock_qty") * -1 if item.get(
                                        "stock_qty") < 0 else item.get(
                                            "stock_qty")
                                if stock_qty != len(
                                        get_serial_nos(item.get('serial_no'))):
                                    item.set(fieldname, value)

                    if ret.get("pricing_rule"):
                        # if user changed the discount percentage then set user's discount percentage ?
                        item.set("discount_percentage",
                                 ret.get("discount_percentage"))
                        if ret.get("pricing_rule_for") == "Price":
                            item.set("pricing_list_rate",
                                     ret.get("pricing_list_rate"))

                        if item.price_list_rate:
                            item.rate = flt(
                                item.price_list_rate *
                                (1.0 -
                                 (flt(item.discount_percentage) / 100.0)),
                                item.precision("rate"))

            if self.doctype == "Purchase Invoice":
                self.set_expense_account(for_validate)
Ejemplo n.º 38
0
    def update_item(source_doc, target_doc, source_parent):
        target_doc.qty = -1 * source_doc.qty

        if source_doc.serial_no:
            returned_serial_nos = get_returned_serial_nos(
                source_doc, source_parent)
            serial_nos = list(
                set(get_serial_nos(source_doc.serial_no)) -
                set(returned_serial_nos))
            if serial_nos:
                target_doc.serial_no = '\n'.join(serial_nos)

        if doctype == "Purchase Receipt":
            returned_qty_map = get_returned_qty_map_for_row(
                source_doc.name, doctype)
            target_doc.received_qty = -1 * flt(source_doc.received_qty - (
                returned_qty_map.get('received_qty') or 0))
            target_doc.rejected_qty = -1 * flt(source_doc.rejected_qty - (
                returned_qty_map.get('rejected_qty') or 0))
            target_doc.qty = -1 * flt(source_doc.qty -
                                      (returned_qty_map.get('qty') or 0))

            target_doc.stock_qty = -1 * flt(source_doc.stock_qty - (
                returned_qty_map.get('stock_qty') or 0))
            target_doc.received_stock_qty = -1 * flt(
                source_doc.received_stock_qty -
                (returned_qty_map.get('received_stock_qty') or 0))

            target_doc.purchase_order = source_doc.purchase_order
            target_doc.purchase_order_item = source_doc.purchase_order_item
            target_doc.rejected_warehouse = source_doc.rejected_warehouse
            target_doc.purchase_receipt_item = source_doc.name

        elif doctype == "Purchase Invoice":
            returned_qty_map = get_returned_qty_map_for_row(
                source_doc.name, doctype)
            target_doc.received_qty = -1 * flt(source_doc.received_qty - (
                returned_qty_map.get('received_qty') or 0))
            target_doc.rejected_qty = -1 * flt(source_doc.rejected_qty - (
                returned_qty_map.get('rejected_qty') or 0))
            target_doc.qty = -1 * flt(source_doc.qty -
                                      (returned_qty_map.get('qty') or 0))

            target_doc.stock_qty = -1 * flt(source_doc.stock_qty - (
                returned_qty_map.get('stock_qty') or 0))
            target_doc.purchase_order = source_doc.purchase_order
            target_doc.purchase_receipt = source_doc.purchase_receipt
            target_doc.rejected_warehouse = source_doc.rejected_warehouse
            target_doc.po_detail = source_doc.po_detail
            target_doc.pr_detail = source_doc.pr_detail
            target_doc.purchase_invoice_item = source_doc.name
            target_doc.price_list_rate = 0

        elif doctype == "Delivery Note":
            returned_qty_map = get_returned_qty_map_for_row(
                source_doc.name, doctype)
            target_doc.qty = -1 * flt(source_doc.qty -
                                      (returned_qty_map.get('qty') or 0))
            target_doc.stock_qty = -1 * flt(source_doc.stock_qty - (
                returned_qty_map.get('stock_qty') or 0))

            target_doc.against_sales_order = source_doc.against_sales_order
            target_doc.against_sales_invoice = source_doc.against_sales_invoice
            target_doc.so_detail = source_doc.so_detail
            target_doc.si_detail = source_doc.si_detail
            target_doc.expense_account = source_doc.expense_account
            target_doc.dn_detail = source_doc.name
            if default_warehouse_for_sales_return:
                target_doc.warehouse = default_warehouse_for_sales_return
        elif doctype == "Sales Invoice" or doctype == "POS Invoice":
            returned_qty_map = get_returned_qty_map_for_row(
                source_doc.name, doctype)
            target_doc.qty = -1 * flt(source_doc.qty -
                                      (returned_qty_map.get('qty') or 0))
            target_doc.stock_qty = -1 * flt(source_doc.stock_qty - (
                returned_qty_map.get('stock_qty') or 0))

            target_doc.sales_order = source_doc.sales_order
            target_doc.delivery_note = source_doc.delivery_note
            target_doc.so_detail = source_doc.so_detail
            target_doc.dn_detail = source_doc.dn_detail
            target_doc.expense_account = source_doc.expense_account

            if doctype == "Sales Invoice":
                target_doc.sales_invoice_item = source_doc.name
            else:
                target_doc.pos_invoice_item = source_doc.name

            target_doc.price_list_rate = 0
            if default_warehouse_for_sales_return:
                target_doc.warehouse = default_warehouse_for_sales_return
Ejemplo n.º 39
0
	def set_serial_no_against_delivery_note(self):
		for item in self.items:
			if item.serial_no and item.delivery_note and \
				item.qty != len(get_serial_nos(item.serial_no)):
				item.serial_no = get_delivery_note_serial_no(item.item_code, item.qty, item.delivery_note)
Ejemplo n.º 40
0
 def set_serial_no_against_delivery_note(self):
     for item in self.items:
         if item.serial_no and item.delivery_note and \
          item.qty != len(get_serial_nos(item.serial_no)):
             item.serial_no = get_delivery_note_serial_no(
                 item.item_code, item.qty, item.delivery_note)
Ejemplo n.º 41
0
	def test_stock_reco_for_serial_and_batch_item_with_future_dependent_entry(self):
		"""
			Behaviour: 1) Create Stock Reconciliation, which will be the origin document
				of a new batch having a serial no
				2) Create a Stock Entry that adds a serial no to the same batch following this
					Stock Reconciliation
				3) Cancel Stock Reconciliation
				4) Cancel Stock Entry
			Expected Result: 3) Cancelling the Stock Reco throws a LinkExistsError since
				Stock Entry is dependent on the batch involved
				4) Serial No only in the Stock Entry is Inactive and Batch qty decreases
		"""
		from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
		from erpnext.stock.doctype.batch.batch import get_batch_qty

		set_perpetual_inventory()

		item = frappe.db.exists("Item", {'item_name': 'Batched and Serialised Item 1'})
		if not item:
			item = create_item("Batched and Serialised Item 1")
			item.has_batch_no = 1
			item.create_new_batch = 1
			item.has_serial_no = 1
			item.batch_number_series = "B-BATCH-.##"
			item.serial_no_series = "S-.####"
			item.save()
		else:
			item = frappe.get_doc("Item", {'item_name': 'Batched and Serialised Item 1'})

		warehouse = "_Test Warehouse for Stock Reco2 - _TC"

		stock_reco = create_stock_reconciliation(item_code=item.item_code,
			warehouse = warehouse, qty=1, rate=100)
		batch_no = stock_reco.items[0].batch_no
		serial_no = get_serial_nos(stock_reco.items[0].serial_no)[0]

		stock_entry = make_stock_entry(item_code=item.item_code, target=warehouse, qty=1, basic_rate=100,
			batch_no=batch_no)
		serial_no_2 = get_serial_nos(stock_entry.items[0].serial_no)[0]

		# Check Batch qty after 2 transactions
		batch_qty = get_batch_qty(batch_no, warehouse, item.item_code)
		self.assertEqual(batch_qty, 2)
		frappe.db.commit()

		# Cancelling Origin Document of Batch
		self.assertRaises(frappe.LinkExistsError, stock_reco.cancel)
		frappe.db.rollback()

		stock_entry.cancel()

		# Check Batch qty after cancellation
		batch_qty = get_batch_qty(batch_no, warehouse, item.item_code)
		self.assertEqual(batch_qty, 1)

		# Check if Serial No from Stock Reconcilation is intact
		self.assertEqual(frappe.db.get_value("Serial No", serial_no, "batch_no"), batch_no)
		self.assertEqual(frappe.db.get_value("Serial No", serial_no, "status"), "Active")

		# Check if Serial No from Stock Entry is Unlinked and Inactive
		self.assertEqual(frappe.db.get_value("Serial No", serial_no_2, "batch_no"), None)
		self.assertEqual(frappe.db.get_value("Serial No", serial_no_2, "status"), "Inactive")

		stock_reco.load_from_db()
		stock_reco.cancel()

		for sn in (serial_no, serial_no_2):
			if frappe.db.exists("Serial No", sn):
				frappe.delete_doc("Serial No", sn)