def get_stock_balance_for(
    item_code,
    warehouse,
    posting_date,
    posting_time,
    batch_no=None,
    with_valuation_rate=True,
):
    frappe.has_permission("Backported Stock Reconciliation",
                          "write",
                          throw=True)

    item_dict = frappe.db.get_value("Item",
                                    item_code,
                                    ["has_serial_no", "has_batch_no"],
                                    as_dict=1)

    serial_nos = ""
    if item_dict.get("has_serial_no"):
        qty, rate, serial_nos = _get_qty_rate_for_serial_nos(
            item_code, warehouse, posting_date, posting_time, item_dict)
    else:
        qty, rate = get_stock_balance(
            item_code,
            warehouse,
            posting_date,
            posting_time,
            with_valuation_rate=with_valuation_rate,
        )

    if item_dict.get("has_batch_no"):
        qty = get_batch_qty(batch_no, warehouse) or 0
        print(get_batch_qty(batch_no, warehouse))

    return {"qty": qty, "rate": rate, "serial_nos": serial_nos}
Exemplo n.º 2
0
	def test_retain_sample(self):
		from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
		from erpnext.stock.doctype.batch.batch import get_batch_qty

		create_warehouse("Test Warehouse for Sample Retention")
		frappe.db.set_value("Stock Settings", None, "sample_retention_warehouse", "Test Warehouse for Sample Retention - _TC")

		test_item_code = "Retain Sample Item"
		if not frappe.db.exists('Item', test_item_code):
			item = frappe.new_doc("Item")
			item.item_code = test_item_code
			item.item_name = "Retain Sample Item"
			item.description = "Retain Sample Item"
			item.item_group = "All Item Groups"
			item.is_stock_item = 1
			item.has_batch_no = 1
			item.create_new_batch = 1
			item.retain_sample = 1
			item.sample_quantity = 4
			item.save()

		receipt_entry = frappe.new_doc("Stock Entry")
		receipt_entry.company = "_Test Company"
		receipt_entry.purpose = "Material Receipt"
		receipt_entry.append("items", {
			"item_code": test_item_code,
			"t_warehouse": "_Test Warehouse - _TC",
			"qty": 40,
			"basic_rate": 12,
			"cost_center": "_Test Cost Center - _TC",
			"sample_quantity": 4
		})
		receipt_entry.set_stock_entry_type()
		receipt_entry.insert()
		receipt_entry.submit()

		retention_data = move_sample_to_retention_warehouse(receipt_entry.company, receipt_entry.get("items"))
		retention_entry = frappe.new_doc("Stock Entry")
		retention_entry.company = retention_data.company
		retention_entry.purpose = retention_data.purpose
		retention_entry.append("items", {
			"item_code": test_item_code,
			"t_warehouse": "Test Warehouse for Sample Retention - _TC",
			"s_warehouse": "_Test Warehouse - _TC",
			"qty": 4,
			"basic_rate": 12,
			"cost_center": "_Test Cost Center - _TC",
			"batch_no": receipt_entry.get("items")[0].batch_no
		})
		retention_entry.set_stock_entry_type()
		retention_entry.insert()
		retention_entry.submit()

		qty_in_usable_warehouse = get_batch_qty(receipt_entry.get("items")[0].batch_no, "_Test Warehouse - _TC", "_Test Item")
		qty_in_retention_warehouse = get_batch_qty(receipt_entry.get("items")[0].batch_no, "Test Warehouse for Sample Retention - _TC", "_Test Item")

		self.assertEqual(qty_in_usable_warehouse, 36)
		self.assertEqual(qty_in_retention_warehouse, 4)
Exemplo n.º 3
0
	def test_batch_split(self):
		'''Test batch splitting'''
		receipt = self.test_purchase_receipt()
		from erpnext.stock.doctype.batch.batch import split_batch

		new_batch = split_batch(receipt.items[0].batch_no, 'ITEM-BATCH-1', receipt.items[0].warehouse, 22)

		self.assertEquals(get_batch_qty(receipt.items[0].batch_no, receipt.items[0].warehouse), 78)
		self.assertEquals(get_batch_qty(new_batch, receipt.items[0].warehouse), 22)
Exemplo n.º 4
0
	def test_batch_split(self):
		'''Test batch splitting'''
		receipt = self.test_purchase_receipt()
		from erpnext.stock.doctype.batch.batch import split_batch

		new_batch = split_batch(receipt.items[0].batch_no, 'ITEM-BATCH-1', receipt.items[0].warehouse, 22)

		self.assertEqual(get_batch_qty(receipt.items[0].batch_no, receipt.items[0].warehouse), 78)
		self.assertEqual(get_batch_qty(new_batch, receipt.items[0].warehouse), 22)
Exemplo n.º 5
0
	def test_serial_batch_item_qty_deduction(self):
		"""
		Behaviour: Create 2 Stock Entries, both adding Serial Nos to same batch
		Expected: 1) Cancelling first Stock Entry (origin transaction of created batch)
		should throw a LinkExistsError
		2) Cancelling second Stock Entry should make Serial Nos that are, linked to mentioned batch
		and in that transaction only, Inactive.
		"""
		from erpnext.stock.doctype.batch.batch import get_batch_qty

		item = frappe.db.exists("Item", {"item_name": "Batched and Serialised Item"})
		if not item:
			item = create_item("Batched and Serialised Item")
			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"})

		se1 = make_stock_entry(
			item_code=item.item_code, target="_Test Warehouse - _TC", qty=1, basic_rate=100
		)
		batch_no = se1.items[0].batch_no
		serial_no1 = get_serial_nos(se1.items[0].serial_no)[0]

		# Check Source (Origin) Document of Batch
		self.assertEqual(frappe.db.get_value("Batch", batch_no, "reference_name"), se1.name)

		se2 = make_stock_entry(
			item_code=item.item_code,
			target="_Test Warehouse - _TC",
			qty=1,
			basic_rate=100,
			batch_no=batch_no,
		)
		serial_no2 = get_serial_nos(se2.items[0].serial_no)[0]

		batch_qty = get_batch_qty(batch_no, "_Test Warehouse - _TC", item.item_code)
		self.assertEqual(batch_qty, 2)

		se2.cancel()

		# Check decrease in Batch Qty
		batch_qty = get_batch_qty(batch_no, "_Test Warehouse - _TC", item.item_code)
		self.assertEqual(batch_qty, 1)

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

		# Check if Serial No from Stock Entry 2 is Unlinked and Inactive
		self.assertEqual(frappe.db.get_value("Serial No", serial_no2, "batch_no"), None)
		self.assertEqual(frappe.db.get_value("Serial No", serial_no2, "status"), "Inactive")
Exemplo n.º 6
0
	def test_get_batch_qty(self):
		'''Test getting batch quantities by batch_numbers, item_code or warehouse'''
		self.make_batch_item('ITEM-BATCH-2')
		self.make_new_batch_and_entry('ITEM-BATCH-2', 'batch a', '_Test Warehouse - _TC')
		self.make_new_batch_and_entry('ITEM-BATCH-2', 'batch b', '_Test Warehouse - _TC')

		self.assertEquals(get_batch_qty(item_code = 'ITEM-BATCH-2', warehouse = '_Test Warehouse - _TC'),
			[{'batch_no': u'batch a', 'qty': 90.0}, {'batch_no': u'batch b', 'qty': 90.0}])

		self.assertEquals(get_batch_qty('batch a', '_Test Warehouse - _TC'), 90)
Exemplo n.º 7
0
	def test_get_batch_qty(self):
		'''Test getting batch quantities by batch_numbers, item_code or warehouse'''
		self.make_batch_item('ITEM-BATCH-2')
		self.make_new_batch_and_entry('ITEM-BATCH-2', 'batch a', '_Test Warehouse - _TC')
		self.make_new_batch_and_entry('ITEM-BATCH-2', 'batch b', '_Test Warehouse - _TC')

		self.assertEqual(get_batch_qty(item_code = 'ITEM-BATCH-2', warehouse = '_Test Warehouse - _TC'),
			[{'batch_no': u'batch a', 'qty': 90.0}, {'batch_no': u'batch b', 'qty': 90.0}])

		self.assertEqual(get_batch_qty('batch a', '_Test Warehouse - _TC'), 90)
Exemplo n.º 8
0
	def test_retain_sample(self):
		from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
		from erpnext.stock.doctype.batch.batch import get_batch_qty
		
		create_warehouse("Test Warehouse for Sample Retention")
		frappe.db.set_value("Stock Settings", None, "sample_retention_warehouse", "Test Warehouse for Sample Retention - _TC")
		
		item = frappe.new_doc("Item")
		item.item_code = "Retain Sample Item"
		item.item_name = "Retain Sample Item"
		item.description = "Retain Sample Item"
		item.item_group = "All Item Groups"
		item.is_stock_item = 1
		item.has_batch_no = 1
		item.create_new_batch = 1
		item.retain_sample = 1
		item.sample_quantity = 4
		item.save()

		receipt_entry = frappe.new_doc("Stock Entry")
		receipt_entry.company = "_Test Company"
		receipt_entry.purpose = "Material Receipt"
		receipt_entry.append("items", {
			"item_code": item.item_code,
			"t_warehouse": "_Test Warehouse - _TC",
			"qty": 40,
			"basic_rate": 12,
			"cost_center": "_Test Cost Center - _TC",
			"sample_quantity": 4
		})
		receipt_entry.insert()
		receipt_entry.submit()

		retention_data = move_sample_to_retention_warehouse(receipt_entry.company, receipt_entry.get("items"))
		retention_entry = frappe.new_doc("Stock Entry")
		retention_entry.company = retention_data.company
		retention_entry.purpose = retention_data.purpose
		retention_entry.append("items", {
			"item_code": item.item_code,
			"t_warehouse": "Test Warehouse for Sample Retention - _TC",
			"s_warehouse": "_Test Warehouse - _TC",
			"qty": 4,
			"basic_rate": 12,
			"cost_center": "_Test Cost Center - _TC",
			"batch_no": receipt_entry.get("items")[0].batch_no
		})
		retention_entry.insert()
		retention_entry.submit()

		qty_in_usable_warehouse = get_batch_qty(receipt_entry.get("items")[0].batch_no, "_Test Warehouse - _TC", "_Test Item")
		qty_in_retention_warehouse = get_batch_qty(receipt_entry.get("items")[0].batch_no, "Test Warehouse for Sample Retention - _TC", "_Test Item")
		
		self.assertEquals(qty_in_usable_warehouse, 36)
		self.assertEquals(qty_in_retention_warehouse, 4)
Exemplo n.º 9
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 Entry
		Expected Result: 3) Serial No only in the Stock Entry is Inactive and Batch qty decreases
		"""
		from erpnext.stock.doctype.batch.batch import get_batch_qty
		from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry

		item = create_item("_TestBatchSerialItemDependentReco")
		item.has_batch_no = 1
		item.create_new_batch = 1
		item.has_serial_no = 1
		item.batch_number_series = "TBSD-BATCH-.##"
		item.serial_no_series = "TBSD-.####"
		item.save()

		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
		reco_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)

		# Cancel latest stock document
		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", reco_serial_no, "batch_no"), batch_no)
		self.assertEqual(frappe.db.get_value("Serial No", reco_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.cancel()
Exemplo n.º 10
0
def adjust_qty_for_expired_items(item_code, stock_qty, warehouse):
	batches = frappe.get_all('Batch', filters=[{'item': item_code}], fields=['expiry_date', 'name'])
	expired_batches = get_expired_batches(batches)
	stock_qty = [list(item) for item in stock_qty]

	for batch in expired_batches:
		if warehouse:
			stock_qty[0][0] = max(0, stock_qty[0][0] - get_batch_qty(batch, warehouse))
		else:
			stock_qty[0][0] = max(0, stock_qty[0][0] - qty_from_all_warehouses(get_batch_qty(batch)))

		if not stock_qty[0][0]:
			break

	return stock_qty
Exemplo n.º 11
0
def update_bactch_stock_status(self, cdt):
    if self.doctype == "Purchase Receipt":
        for item in self.items:
            if "Strip-MS" in str(item.item_code):
                msg = get_batch_qty(item.batch_no)
                total_batch_qty = 0
                for x in msg:
                    total_batch_qty +=int(x.qty)
                if msg:
                    batch_msg = frappe.get_doc("Batch",item.batch_no)
                    batch_msg.batch_stock_status = "Available"
                    batch_msg.save() 
                else:
                    batch_msg = frappe.get_doc("Batch",item.batch_no)
                    batch_msg.batch_stock_status = "Empty"
                    batch_msg.save()
                
    elif self.doctype == "Delivery Note":  
        for item in self.items:
            if "Strip-MS" in str(item.item_code):
                msg = get_batch_qty(item.batch_no)
                total_batch_qty = 0
                for x in msg:
                    total_batch_qty +=int(x.qty)
                if total_batch_qty>0:
                    batch_msg = frappe.get_doc("Batch",item.batch_no)
                    batch_msg.batch_stock_status = "Available"
                    batch_msg.save() 
                else:
                    batch_msg = frappe.get_doc("Batch",item.batch_no)
                    batch_msg.batch_stock_status = "Empty"
                    batch_msg.save()
                
    else:
        for item in self.items:
            if "Strip-MS" in str(item.item_code):
                msg = get_batch_qty(item.batch_no)
                total_batch_qty = 0
                for x in msg:
                    total_batch_qty +=int(x.qty)
                if total_batch_qty>0:
                    batch_msg = frappe.get_doc("Batch",item.batch_no)
                    batch_msg.batch_stock_status = "Available"
                    batch_msg.save() 
                else:
                    batch_msg = frappe.get_doc("Batch",item.batch_no)
                    batch_msg.batch_stock_status = "Empty"
                    batch_msg.save()
Exemplo n.º 12
0
	def validate_pos_reserved_batch_qty(self, item):
		filters = {"item_code": item.item_code, "warehouse": item.warehouse, "batch_no": item.batch_no}

		available_batch_qty = get_batch_qty(item.batch_no, item.warehouse, item.item_code)
		reserved_batch_qty = get_pos_reserved_batch_qty(filters)

		bold_item_name = frappe.bold(item.item_name)
		bold_extra_batch_qty_needed = frappe.bold(
			abs(available_batch_qty - reserved_batch_qty - item.qty)
		)
		bold_invalid_batch_no = frappe.bold(item.batch_no)

		if (available_batch_qty - reserved_batch_qty) == 0:
			frappe.throw(
				_(
					"Row #{}: Batch No. {} of item {} has no stock available. Please select valid batch no."
				).format(item.idx, bold_invalid_batch_no, bold_item_name),
				title=_("Item Unavailable"),
			)
		elif (available_batch_qty - reserved_batch_qty - item.qty) < 0:
			frappe.throw(
				_(
					"Row #{}: Batch No. {} of item {} has less than required stock available, {} more required"
				).format(
					item.idx, bold_invalid_batch_no, bold_item_name, bold_extra_batch_qty_needed
				),
				title=_("Item Unavailable"),
			)
Exemplo n.º 13
0
	def test_delivery_note(self):
		'''Test automatic batch selection for outgoing items'''
		batch_qty = 15
		receipt = self.test_purchase_receipt(batch_qty)

		delivery_note = frappe.get_doc(dict(
			doctype = 'Delivery Note',
			customer = '_Test Customer',
			company = receipt.company,
			items = [
				dict(
					item_code = 'ITEM-BATCH-1',
					qty = batch_qty,
					rate = 10,
					warehouse = receipt.items[0].warehouse
				)
			]
		)).insert()
		delivery_note.submit()

		# shipped with same batch
		self.assertEquals(delivery_note.items[0].batch_no, receipt.items[0].batch_no)

		# balance is 0
		self.assertEquals(get_batch_qty(receipt.items[0].batch_no,
			receipt.items[0].warehouse), 0)
Exemplo n.º 14
0
	def test_stock_entry_outgoing(self):
		'''Test automatic batch selection for outgoing stock entry'''

		batch_qty = 16
		receipt = self.test_purchase_receipt(batch_qty)

		stock_entry = frappe.get_doc(dict(
			doctype = 'Stock Entry',
			purpose = 'Material Issue',
			company = receipt.company,
			items = [
				dict(
					item_code = 'ITEM-BATCH-1',
					qty = batch_qty,
					s_warehouse = receipt.items[0].warehouse,
				)
			]
		)).insert()
		stock_entry.submit()

		# assert same batch is selected
		self.assertEqual(stock_entry.items[0].batch_no, receipt.items[0].batch_no)

		# balance is 0
		self.assertEquals(get_batch_qty(receipt.items[0].batch_no,
			receipt.items[0].warehouse), 0)
Exemplo n.º 15
0
	def test_delivery_note(self):
		'''Test automatic batch selection for outgoing items'''
		batch_qty = 15
		receipt = self.test_purchase_receipt(batch_qty)

		delivery_note = frappe.get_doc(dict(
			doctype = 'Delivery Note',
			customer = '_Test Customer',
			company = receipt.company,
			items = [
				dict(
					item_code = 'ITEM-BATCH-1',
					qty = batch_qty,
					rate = 10,
					warehouse = receipt.items[0].warehouse
				)
			]
		)).insert()
		delivery_note.submit()

		# shipped with same batch
		self.assertEquals(delivery_note.items[0].batch_no, receipt.items[0].batch_no)

		# balance is 0
		self.assertEquals(get_batch_qty(receipt.items[0].batch_no,
			receipt.items[0].warehouse), 0)
Exemplo n.º 16
0
def get_transfer_items(work_order, items):
    from erpnext.stock.doctype.batch.batch import get_batch_qty

    if isinstance(items, basestring):
        items = json.loads(items)

    se_data = frappe.get_list("Stock Entry",
                              filters={
                                  "purpose":
                                  "Material Transfer for Manufacture",
                                  "work_order": work_order,
                                  "docstatus": 1
                              })

    items_list = []

    for se in se_data:
        doc = frappe.get_doc("Stock Entry", se.name)
        items_list += [
            row for row in doc.items
            if get_batch_qty(batch_no=row.batch_no,
                             warehouse=row.t_warehouse,
                             item_code=row.item_code)
        ]

    items_list += items[-1:]

    return items_list
Exemplo n.º 17
0
def update_batch_expired_patch():
	batch_items = frappe.db.sql ("""SELECT name, item, expiry_date, expiry_start_day FROM `tabBatch`
		where expiry_status IN ('Expired')""", as_dict=1)

	for d in batch_items:
		if formatdate(d.expiry_date) == "06-09-2017":
			from erpnext.stock.doctype.batch.batch import get_batch_qty
			batch_qty = get_batch_qty(d.name, "DM Arcadia - MS", None)
			#frappe.throw(_("batch qty is {0}").format(batch_qty))
			batch_doc = frappe.get_doc("Batch", d.name)
			#days_to_expiry = frappe.utils.date_diff(d.expiry_date, nowdate())
			#batch_doc.days_to_expiry = days_to_expiry

			if flt(batch_qty) > 0:
				_data = frappe.db.sql ("""SELECT data from `tabVersion` where docname=%s and ref_doctype='Batch' order by modified desc limit 1""", d.name, as_dict=0)
				#frappe.throw(_("{0}").format(_data))
				_data = json.loads(_data[0][0])
				_changes = _data.get("changed")
				#frappe.throw(_("{0}").format(_changes))

				if _changes:
					print(d.name)
					batch_doc.days_to_expiry = _changes[2][1]
					batch_doc.expiry_status	= _changes[0][1]
					batch_doc.expiry_date = get_datetime(formatdate(_changes[1][1]))
				

					batch_doc.save()
Exemplo n.º 18
0
def get_stock_balance_for(item_code,
                          warehouse,
                          posting_date,
                          posting_time,
                          batch_no=None,
                          with_valuation_rate=True):
    frappe.has_permission("Stock Reconciliation", "write", throw=True)

    item_dict = frappe.db.get_value("Item",
                                    item_code,
                                    ["has_serial_no", "has_batch_no"],
                                    as_dict=1)

    serial_nos = ""
    with_serial_no = True if item_dict.get("has_serial_no") else False
    data = get_stock_balance(item_code,
                             warehouse,
                             posting_date,
                             posting_time,
                             with_valuation_rate=with_valuation_rate,
                             with_serial_no=with_serial_no)

    if with_serial_no:
        qty, rate, serial_nos = data
    else:
        qty, rate = data

    if item_dict.get("has_batch_no"):
        qty = get_batch_qty(batch_no, warehouse) or 0

    return {'qty': qty, 'rate': rate, 'serial_nos': serial_nos}
Exemplo n.º 19
0
def update_batch_expired_date_daily():
	batch_items = frappe.db.sql ("""SELECT name, item, expiry_date, expiry_start_day FROM `tabBatch`
		where expiry_date <> '' and days_to_expiry >= 0 and expiry_status NOT IN ('Not Set','Expired')""", as_dict=1)

	for d in batch_items:
		if d.expiry_date:
			from erpnext.stock.doctype.batch.batch import get_batch_qty
			batch_qty = get_batch_qty(d.name, "DM Arcadia - MS", None)

			batch_doc = frappe.get_doc("Batch", d.name)
			days_to_expiry = frappe.utils.date_diff(d.expiry_date, nowdate())
			batch_doc.days_to_expiry = days_to_expiry
			
			
			if batch_qty and flt(batch_qty) <= 0:
				batch_doc.expiry_date = nowdate()
				batch_doc.expiry_status	= "Expired"
				batch_doc.days_to_expiry = 0
			elif int(days_to_expiry) <= int(d.expiry_start_day) and int(days_to_expiry) > 30:
				batch_doc.expiry_status	= "Expired Soon"
			elif int(days_to_expiry) <= 30 and int(days_to_expiry) > 0:
				batch_doc.expiry_status	= "Expired Very Soon"
			elif int(days_to_expiry) <= 0:
				batch_doc.expiry_status	= "Expired"
			else:
				batch_doc.expiry_status	= "Open"

			batch_doc.save()
Exemplo n.º 20
0
    def test_stock_entry_incoming(self):
        """Test batch creation via Stock Entry (Work Order)"""

        self.make_batch_item("ITEM-BATCH-1")

        stock_entry = frappe.get_doc(
            dict(
                doctype="Stock Entry",
                purpose="Material Receipt",
                company="_Test Company",
                items=[
                    dict(
                        item_code="ITEM-BATCH-1",
                        qty=90,
                        t_warehouse="_Test Warehouse - _TC",
                        cost_center="Main - _TC",
                        rate=10,
                    )
                ],
            ))

        stock_entry.set_stock_entry_type()
        stock_entry.insert()
        stock_entry.submit()

        self.assertTrue(stock_entry.items[0].batch_no)
        self.assertEqual(
            get_batch_qty(stock_entry.items[0].batch_no,
                          stock_entry.items[0].t_warehouse), 90)
Exemplo n.º 21
0
def validate_sample_quantity(item_code, sample_quantity, qty, batch_no=None):
    if cint(qty) < cint(sample_quantity):
        frappe.throw(
            _("Sample quantity {0} cannot be more than received quantity {1}").
            format(sample_quantity, qty))
    retention_warehouse = frappe.db.get_single_value(
        'Stock Settings', 'sample_retention_warehouse')
    retainted_qty = 0
    if batch_no:
        retainted_qty = get_batch_qty(batch_no, retention_warehouse, item_code)
    max_retain_qty = frappe.get_value('Item', item_code, 'sample_quantity')
    if retainted_qty >= max_retain_qty:
        frappe.msgprint(_(
            "Maximum Samples - {0} have already been retained for Batch {1} and Item {2} in Batch {3}."
        ).format(retainted_qty, batch_no, item_code, batch_no),
                        alert=True)
        sample_quantity = 0
    qty_diff = max_retain_qty - retainted_qty
    if cint(sample_quantity) > cint(qty_diff):
        frappe.msgprint(_(
            "Maximum Samples - {0} can be retained for Batch {1} and Item {2}."
        ).format(max_retain_qty, batch_no, item_code),
                        alert=True)
        sample_quantity = qty_diff
    return sample_quantity
Exemplo n.º 22
0
	def test_stock_entry_outgoing(self):
		'''Test automatic batch selection for outgoing stock entry'''

		batch_qty = 16
		receipt = self.test_purchase_receipt(batch_qty)

		stock_entry = frappe.get_doc(dict(
			doctype = 'Stock Entry',
			purpose = 'Material Issue',
			company = receipt.company,
			items = [
				dict(
					item_code = 'ITEM-BATCH-1',
					qty = batch_qty,
					s_warehouse = receipt.items[0].warehouse,
				)
			]
		)).insert()
		stock_entry.submit()

		# assert same batch is selected
		self.assertEqual(stock_entry.items[0].batch_no, receipt.items[0].batch_no)

		# balance is 0
		self.assertEquals(get_batch_qty(receipt.items[0].batch_no,
			receipt.items[0].warehouse), 0)
Exemplo n.º 23
0
    def test_stock_entry_incoming(self):
        '''Test batch creation via Stock Entry (Work Order)'''

        self.make_batch_item('ITEM-BATCH-1')

        stock_entry = frappe.get_doc(
            dict(doctype='Stock Entry',
                 purpose='Material Receipt',
                 company='_Test Company',
                 items=[
                     dict(item_code='ITEM-BATCH-1',
                          qty=90,
                          t_warehouse='_Test Warehouse - _TC',
                          cost_center='Main - _TC',
                          rate=10)
                 ]))

        stock_entry.set_stock_entry_type()
        stock_entry.insert()
        stock_entry.submit()

        self.assertTrue(stock_entry.items[0].batch_no)
        self.assertEqual(
            get_batch_qty(stock_entry.items[0].batch_no,
                          stock_entry.items[0].t_warehouse), 90)
Exemplo n.º 24
0
def get_items_details(pos_profile, items_data):
    pos_profile = json.loads(pos_profile)
    items_data = json.loads(items_data)
    warehouse = pos_profile.get("warehouse")
    result = []

    if len(items_data) > 0:
        for item in items_data:
            item_code = item.get("item_code")
            item_stock_qty = get_stock_availability(item_code, warehouse)
            has_batch_no, has_serial_no = frappe.get_value(
                "Item", item_code, ["has_batch_no", "has_serial_no"])

            uoms = frappe.get_all("UOM Conversion Detail",
                                  filters={"parent": item_code},
                                  fields=["uom", "conversion_factor"])

            serial_no_data = frappe.get_all('Serial No',
                                            filters={
                                                "item_code": item_code,
                                                "status": "Active"
                                            },
                                            fields=["name as serial_no"])

            batch_no_data = []
            from erpnext.stock.doctype.batch.batch import get_batch_qty
            batch_list = get_batch_qty(warehouse=warehouse,
                                       item_code=item_code)
            for batch in batch_list:
                if batch.qty > 0 and batch.batch_no:
                    batch_doc = frappe.get_doc("Batch", batch.batch_no)
                    if (str(batch_doc.expiry_date) > str(nowdate())
                            or batch_doc.expiry_date
                            in ["", None]) and batch_doc.disabled == 0:
                        batch_no_data.append({
                            "batch_no":
                            batch.batch_no,
                            "batch_qty":
                            batch.qty,
                            "expiry_date":
                            batch_doc.expiry_date,
                            "btach_price":
                            batch_doc.posa_btach_price,
                        })

            row = {}
            row.update(item)
            row.update({
                'item_uoms': uoms or [],
                'serial_no_data': serial_no_data or [],
                'batch_no_data': batch_no_data or [],
                'actual_qty': item_stock_qty or 0,
                'has_batch_no': has_batch_no,
                'has_serial_no': has_serial_no,
            })

            result.append(row)

    return result
Exemplo n.º 25
0
    def test_get_batch_qty(self):
        """Test getting batch quantities by batch_numbers, item_code or warehouse"""
        self.make_batch_item("ITEM-BATCH-2")
        self.make_new_batch_and_entry("ITEM-BATCH-2", "batch a",
                                      "_Test Warehouse - _TC")
        self.make_new_batch_and_entry("ITEM-BATCH-2", "batch b",
                                      "_Test Warehouse - _TC")

        self.assertEqual(
            get_batch_qty(item_code="ITEM-BATCH-2",
                          warehouse="_Test Warehouse - _TC"),
            [{
                "batch_no": "batch a",
                "qty": 90.0
            }, {
                "batch_no": "batch b",
                "qty": 90.0
            }],
        )

        self.assertEqual(get_batch_qty("batch a", "_Test Warehouse - _TC"), 90)
Exemplo n.º 26
0
def validate_batch_actual_qty(self):
	from erpnext.stock.doctype.batch.batch import get_batch_qty

	for d in self.get("purchase_receipts"):
		doc = frappe.get_doc(d.receipt_document_type, d.receipt_document)

		for row in doc.items:
			if row.batch_no:
				batch_qty = get_batch_qty(row.batch_no, row.warehouse)

				if batch_qty < row.stock_qty:
					frappe.throw(_("The batch <b>{0}</b> does not have sufficient quantity for item <b>{1}</b> in row {2}.".format(row.batch_no, row.item_code, d.idx)))
def batch_qty(batch_no, s_warehouse):
    batch_doc = frappe.get_doc('Batch', batch_no)
    required_item = batch_doc.item
    s_batch_qty = get_batch_qty(batch_no, s_warehouse)
    strip_bin = get_bin(required_item, s_warehouse)
    #s_reserved_qty = strip_bin.reserved_qty_for_production
    s_reserved_qty = batch_doc.allocated_quantity
    available_batch_qty = s_batch_qty - s_reserved_qty
    return {
        'available_qty': available_batch_qty,
        'required_item': required_item
    }
Exemplo n.º 28
0
def get_stock_balance_for(
    item_code: str,
    warehouse: str,
    posting_date: str,
    posting_time: str,
    batch_no: Optional[str] = None,
    with_valuation_rate: bool = True,
):
    frappe.has_permission("Stock Reconciliation", "write", throw=True)

    item_dict = frappe.get_cached_value("Item",
                                        item_code,
                                        ["has_serial_no", "has_batch_no"],
                                        as_dict=1)

    if not item_dict:
        # In cases of data upload to Items table
        msg = _("Item {} does not exist.").format(item_code)
        frappe.throw(msg, title=_("Missing"))

    serial_nos = None
    has_serial_no = bool(item_dict.get("has_serial_no"))
    has_batch_no = bool(item_dict.get("has_batch_no"))

    if not batch_no and has_batch_no:
        # Not enough information to fetch data
        return {"qty": 0, "rate": 0, "serial_nos": None}

    # TODO: fetch only selected batch's values
    data = get_stock_balance(
        item_code,
        warehouse,
        posting_date,
        posting_time,
        with_valuation_rate=with_valuation_rate,
        with_serial_no=has_serial_no,
    )

    if has_serial_no:
        qty, rate, serial_nos = data
    else:
        qty, rate = data

    if item_dict.get("has_batch_no"):
        qty = (get_batch_qty(batch_no,
                             warehouse,
                             posting_date=posting_date,
                             posting_time=posting_time) or 0)

    return {"qty": qty, "rate": rate, "serial_nos": serial_nos}
def get_stock_balance_for(item_code,
                          warehouse,
                          posting_date,
                          posting_time,
                          batch_no=None,
                          with_valuation_rate=True):
    frappe.has_permission("Stock Reconciliation", "write", throw=True)

    item_dict = frappe.db.get_value("Item",
                                    item_code,
                                    ["has_serial_no", "has_batch_no"],
                                    as_dict=1)

    serial_nos = ""
    if item_dict.get("has_serial_no"):
        qty, rate, serial_nos = get_qty_rate_for_serial_nos(
            item_code, warehouse, posting_date, posting_time, item_dict)
    else:
        qty, rate = get_stock_balance(item_code,
                                      warehouse,
                                      posting_date,
                                      posting_time,
                                      with_valuation_rate=with_valuation_rate)

    if item_dict.get("has_batch_no"):
        qty = get_batch_qty(batch_no, warehouse) or 0

    stock_val = frappe.db.sql(
        """SELECT sum(stock_value),sum(actual_qty) from tabBin where item_code='{}'"""
        .format(item_code))
    warehouse_val = frappe.db.sql(
        """SELECT stock_value,actual_qty from tabBin where item_code='{}' and warehouse = '{}' """
        .format(item_code, warehouse))

    valuation_rate = 0.00
    if stock_val:
        if stock_val[0][0] and stock_val[0][1]:
            valuation_rate = stock_val[0][0] / stock_val[0][1]

    if warehouse_val:
        if warehouse_val[0][0] and warehouse_val[0][1]:
            rate = warehouse_val[0][0] / warehouse_val[0][1]

    return {
        'qty': qty,
        'rate': rate,
        'serial_nos': serial_nos,
        'valuation_rate': valuation_rate
    }
Exemplo n.º 30
0
    def test_serial_batch_item_stock_entry(self):
        """
			Behaviour: 1) Submit Stock Entry (Receipt) with Serial & Batched Item
				2) Cancel same Stock Entry
			Expected Result: 1) Batch is created with Reference in Serial No
				2) Batch is deleted and Serial No is Inactive
		"""
        from erpnext.stock.doctype.batch.batch import get_batch_qty

        item = frappe.db.exists("Item",
                                {'item_name': 'Batched and Serialised Item'})
        if not item:
            item = create_item("Batched and Serialised Item")
            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'})

        se = make_stock_entry(item_code=item.item_code,
                              target="_Test Warehouse - _TC",
                              qty=1,
                              basic_rate=100)
        batch_no = se.items[0].batch_no
        serial_no = get_serial_nos(se.items[0].serial_no)[0]
        batch_qty = get_batch_qty(batch_no, "_Test Warehouse - _TC",
                                  item.item_code)

        batch_in_serial_no = frappe.db.get_value("Serial No", serial_no,
                                                 "batch_no")
        self.assertEqual(batch_in_serial_no, batch_no)

        self.assertEqual(batch_qty, 1)

        se.cancel()

        batch_in_serial_no = frappe.db.get_value("Serial No", serial_no,
                                                 "batch_no")
        self.assertEqual(batch_in_serial_no, None)

        self.assertEqual(frappe.db.get_value("Serial No", serial_no, "status"),
                         "Inactive")
        self.assertEqual(frappe.db.exists("Batch", batch_no), None)
Exemplo n.º 31
0
def validate_batch_item(sales_order, method):
	for item in sales_order.items:
		qty = item.stock_qty or item.transfer_qty or item.qty or 0
		has_batch_no = frappe.db.get_value('Item', item.item_code, 'has_batch_no')
		warehouse = item.warehouse

		if has_batch_no and warehouse and qty > 0:
			if not item.batch_no:
				return

			batch_qty = get_batch_qty(batch_no=item.batch_no, warehouse=warehouse)
			if flt(batch_qty, item.precision("qty")) < flt(qty, item.precision("qty")):
				frappe.throw(_("""
					Row #{0}: The batch {1} has only {2} qty. Either select a different
					batch that has more than {3} qty available, or split the row to sell
					from multiple batches.
				""").format(item.idx, item.batch_no, batch_qty, qty))
Exemplo n.º 32
0
    def update_bin_batch(self):
        coil_bin = get_bin(self.required_item, self.s_warehouse)
        if self.batch_qty != coil_bin.projected_qty:
            frappe.throw('Batch available quantity is: {0} kg'.format(
                coil_bin.projected_qty))

        update_projected_qty(self.required_item, self.s_warehouse, None, None,
                             self.allocate_quantity)
        update_projected_qty(self.required_item, self.wip_warehouse,
                             self.allocate_quantity, None,
                             self.allocate_quantity)

        batch = frappe.get_doc('Batch', self.batch_no)
        s_batch_qty = get_batch_qty(self.batch_no, self.s_warehouse)
        s_reserved_qty = coil_bin.reserved_qty_for_production
        available_batch_qty = s_batch_qty - s_reserved_qty
        if available_batch_qty == 0:
            batch.db_set('batch_stock_status', 'Empty')
Exemplo n.º 33
0
def validate_sample_quantity(item_code, sample_quantity, qty, batch_no = None):
	if cint(qty) < cint(sample_quantity):
		frappe.throw(_("Sample quantity {0} cannot be more than received quantity {1}").format(sample_quantity, qty))
	retention_warehouse = frappe.db.get_single_value('Stock Settings', 'sample_retention_warehouse')
	retainted_qty = 0
	if batch_no:
		retainted_qty = get_batch_qty(batch_no, retention_warehouse, item_code)
	max_retain_qty = frappe.get_value('Item', item_code, 'sample_quantity')
	if retainted_qty >= max_retain_qty:
		frappe.msgprint(_("Maximum Samples - {0} have already been retained for Batch {1} and Item {2} in Batch {3}.").
			format(retainted_qty, batch_no, item_code, batch_no), alert=True)
		sample_quantity = 0
	qty_diff = max_retain_qty-retainted_qty
	if cint(sample_quantity) > cint(qty_diff):
		frappe.msgprint(_("Maximum Samples - {0} can be retained for Batch {1} and Item {2}.").
			format(max_retain_qty, batch_no, item_code), alert=True)
		sample_quantity = qty_diff
	return sample_quantity
Exemplo n.º 34
0
def set_batch_nos_for_bundels(doc, warehouse_field, throw=False):
    """Automatically select `batch_no` for outgoing items in item table"""
    for d in doc.packed_items:
        qty = d.get("stock_qty") or d.get("transfer_qty") or d.get("qty") or 0
        has_batch_no = frappe.db.get_value("Item", d.item_code, "has_batch_no")
        warehouse = d.get(warehouse_field, None)
        if has_batch_no and warehouse and qty > 0:
            if not d.batch_no:
                d.batch_no = get_batch_no(d.item_code, warehouse, qty, throw,
                                          d.serial_no)
            else:
                batch_qty = get_batch_qty(batch_no=d.batch_no,
                                          warehouse=warehouse)
                if flt(batch_qty, d.precision("qty")) < flt(
                        qty, d.precision("qty")):
                    frappe.throw(
                        _("Row #{0}: The batch {1} has only {2} qty. Please select another batch which has {3} qty available or split the row into multiple rows, to deliver/issue from multiple batches"
                          ).format(d.idx, d.batch_no, batch_qty, qty))
Exemplo n.º 35
0
    def test_purchase_receipt(self, batch_qty=100):
        '''Test automated batch creation from Purchase Receipt'''
        self.make_batch_item('ITEM-BATCH-1')

        receipt = frappe.get_doc(
            dict(
                doctype='Purchase Receipt',
                supplier='_Test Supplier',
                items=[dict(item_code='ITEM-BATCH-1', qty=batch_qty,
                            rate=10)])).insert()
        receipt.submit()

        self.assertTrue(receipt.items[0].batch_no)
        self.assertEqual(
            get_batch_qty(receipt.items[0].batch_no,
                          receipt.items[0].warehouse), batch_qty)

        return receipt
Exemplo n.º 36
0
def get_stock_balance_for(item_code,
                          warehouse,
                          posting_date,
                          posting_time,
                          batch_no=None,
                          with_valuation_rate=True):
    frappe.has_permission("Stock Reconciliation", "write", throw=True)

    item_dict = frappe.db.get_value("Item",
                                    item_code,
                                    ["has_serial_no", "has_batch_no"],
                                    as_dict=1)

    if not item_dict:
        # In cases of data upload to Items table
        msg = _("Item {} does not exist.").format(item_code)
        frappe.throw(msg, title=_("Missing"))

    serial_nos = ""
    with_serial_no = True if item_dict.get("has_serial_no") else False
    data = get_stock_balance(
        item_code,
        warehouse,
        posting_date,
        posting_time,
        with_valuation_rate=with_valuation_rate,
        with_serial_no=with_serial_no,
    )

    if with_serial_no:
        qty, rate, serial_nos = data
    else:
        qty, rate = data

    if item_dict.get("has_batch_no"):
        qty = (get_batch_qty(batch_no,
                             warehouse,
                             posting_date=posting_date,
                             posting_time=posting_time) or 0)

    return {"qty": qty, "rate": rate, "serial_nos": serial_nos}
Exemplo n.º 37
0
	def test_purchase_receipt(self, batch_qty = 100):
		'''Test automated batch creation from Purchase Receipt'''
		self.make_batch_item('ITEM-BATCH-1')

		receipt = frappe.get_doc(dict(
			doctype='Purchase Receipt',
			supplier='_Test Supplier',
			items=[
				dict(
					item_code='ITEM-BATCH-1',
					qty=batch_qty,
					rate=10
				)
			]
		)).insert()
		receipt.submit()

		self.assertTrue(receipt.items[0].batch_no)
		self.assertEquals(get_batch_qty(receipt.items[0].batch_no,
			receipt.items[0].warehouse), batch_qty)

		return receipt
Exemplo n.º 38
0
	def test_stock_entry_incoming(self):
		'''Test batch creation via Stock Entry (Production Order)'''

		self.make_batch_item('ITEM-BATCH-1')

		stock_entry = frappe.get_doc(dict(
			doctype = 'Stock Entry',
			purpose = 'Material Receipt',
			company = '_Test Company',
			items = [
				dict(
					item_code = 'ITEM-BATCH-1',
					qty = 90,
					t_warehouse = '_Test Warehouse - _TC',
					cost_center = 'Main - _TC',
					rate = 10
				)
			]
		)).insert()
		stock_entry.submit()

		self.assertTrue(stock_entry.items[0].batch_no)
		self.assertEquals(get_batch_qty(stock_entry.items[0].batch_no, stock_entry.items[0].t_warehouse), 90)
Exemplo n.º 39
0
def get_batch_qty(batch_no, warehouse, item_code):
	from erpnext.stock.doctype.batch import batch
	if batch_no:
		return {'actual_batch_qty': batch.get_batch_qty(batch_no, warehouse)}