Exemplo n.º 1
0
	def test_stock_reco_for_serialized_item(self):
		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)

		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_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()
Exemplo n.º 2
0
def get_batches(item_code, warehouse, qty=1, throw=False, serial_no=None):
    from erpbee.stock.doctype.serial_no.serial_no import get_serial_nos
    cond = ''
    if serial_no and frappe.get_cached_value('Item', item_code,
                                             'has_batch_no'):
        serial_nos = get_serial_nos(serial_no)
        batch = frappe.get_all("Serial No",
                               fields=["distinct batch_no"],
                               filters={
                                   "item_code": item_code,
                                   "warehouse": warehouse,
                                   "name": ("in", serial_nos)
                               })

        if not batch:
            validate_serial_no_with_batch(serial_nos, item_code)

        if batch and len(batch) > 1:
            return []

        cond = " and `tabBatch`.name = %s" % (frappe.db.escape(
            batch[0].batch_no))

    return frappe.db.sql("""
		select batch_id, sum(`tabStock Ledger Entry`.actual_qty) as qty
		from `tabBatch`
			join `tabStock Ledger Entry` ignore index (item_code, warehouse)
				on (`tabBatch`.batch_id = `tabStock Ledger Entry`.batch_no )
		where `tabStock Ledger Entry`.item_code = %s and `tabStock Ledger Entry`.warehouse = %s
			and (`tabBatch`.expiry_date >= CURDATE() or `tabBatch`.expiry_date IS NULL) {0}
		group by batch_id
		order by `tabBatch`.expiry_date ASC, `tabBatch`.creation ASC
	""".format(cond), (item_code, warehouse),
                         as_dict=True)
Exemplo n.º 3
0
    def validate_serialised_or_batched_item(self):
        error_msg = []
        for d in self.get("items"):
            serialized = d.get("has_serial_no")
            batched = d.get("has_batch_no")
            no_serial_selected = not d.get("serial_no")
            no_batch_selected = not d.get("batch_no")

            msg = ""
            item_code = frappe.bold(d.item_code)
            serial_nos = get_serial_nos(d.serial_no)
            if serialized and batched and (no_batch_selected
                                           or no_serial_selected):
                msg = (_(
                    'Row #{}: Please select a serial no and batch against item: {} or remove it to complete transaction.'
                ).format(d.idx, item_code))
            elif serialized and no_serial_selected:
                msg = (_(
                    'Row #{}: No serial number selected against item: {}. Please select one or remove it to complete transaction.'
                ).format(d.idx, item_code))
            elif batched and no_batch_selected:
                msg = (_(
                    'Row #{}: No batch selected against item: {}. Please select a batch or remove it to complete transaction.'
                ).format(d.idx, item_code))
            elif serialized and not no_serial_selected and len(
                    serial_nos) != d.qty:
                msg = (
                    _("Row #{}: You must select {} serial numbers for item {}."
                      ).format(d.idx, frappe.bold(cint(d.qty)), item_code))

            if msg:
                error_msg.append(msg)

        if error_msg:
            frappe.throw(error_msg, title=_("Invalid Item"), as_list=True)
Exemplo n.º 4
0
 def update_rate_in_serial_no_for_non_asset_items(self, receipt_document):
     for item in receipt_document.get("items"):
         if not item.is_fixed_asset and 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))
Exemplo n.º 5
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)))
Exemplo n.º 6
0
    def test_serialized_item_transaction(self):
        from erpbee.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
        from erpbee.stock.doctype.serial_no.serial_no import get_serial_nos

        se = make_serialized_item(company='_Test Company',
                                  target_warehouse="Stores - _TC",
                                  cost_center='Main - _TC',
                                  expense_account='Cost of Goods Sold - _TC')

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

        pos = create_pos_invoice(company='_Test Company',
                                 debit_to='Debtors - _TC',
                                 account_for_change_amount='Cash - _TC',
                                 warehouse='Stores - _TC',
                                 income_account='Sales - _TC',
                                 expense_account='Cost of Goods Sold - _TC',
                                 cost_center='Main - _TC',
                                 item=se.get("items")[0].item_code,
                                 rate=1000,
                                 do_not_save=1)

        pos.get("items")[0].serial_no = serial_nos[0]
        pos.append(
            "payments", {
                'mode_of_payment': 'Bank Draft',
                'account': '_Test Bank - _TC',
                'amount': 1000
            })

        pos.insert()
        pos.submit()

        pos2 = create_pos_invoice(company='_Test Company',
                                  debit_to='Debtors - _TC',
                                  account_for_change_amount='Cash - _TC',
                                  warehouse='Stores - _TC',
                                  income_account='Sales - _TC',
                                  expense_account='Cost of Goods Sold - _TC',
                                  cost_center='Main - _TC',
                                  item=se.get("items")[0].item_code,
                                  rate=1000,
                                  do_not_save=1)

        pos2.get("items")[0].serial_no = serial_nos[0]
        pos2.append(
            "payments", {
                'mode_of_payment': 'Bank Draft',
                'account': '_Test Bank - _TC',
                'amount': 1000
            })

        self.assertRaises(frappe.ValidationError, pos2.insert)
Exemplo n.º 7
0
    def validate_stock_availablility(self):
        if self.is_return:
            return

        allow_negative_stock = frappe.db.get_value('Stock Settings', None,
                                                   'allow_negative_stock')
        error_msg = []
        for d in self.get('items'):
            msg = ""
            if d.serial_no:
                filters = {"item_code": d.item_code, "warehouse": d.warehouse}
                if d.batch_no:
                    filters["batch_no"] = d.batch_no

                reserved_serial_nos = get_pos_reserved_serial_nos(filters)
                serial_nos = get_serial_nos(d.serial_no)
                invalid_serial_nos = [
                    s for s in serial_nos if s in reserved_serial_nos
                ]

                bold_invalid_serial_nos = frappe.bold(
                    ', '.join(invalid_serial_nos))
                if len(invalid_serial_nos) == 1:
                    msg = (_(
                        "Row #{}: Serial No. {} has already been transacted into another POS Invoice. Please select valid serial no."
                    ).format(d.idx, bold_invalid_serial_nos))
                elif invalid_serial_nos:
                    msg = (_(
                        "Row #{}: Serial Nos. {} has already been transacted into another POS Invoice. Please select valid serial no."
                    ).format(d.idx, bold_invalid_serial_nos))

            else:
                if allow_negative_stock:
                    return

                available_stock = get_stock_availability(
                    d.item_code, d.warehouse)
                item_code, warehouse, qty = frappe.bold(
                    d.item_code), frappe.bold(d.warehouse), frappe.bold(d.qty)
                if flt(available_stock) <= 0:
                    msg = (_(
                        'Row #{}: Item Code: {} is not available under warehouse {}.'
                    ).format(d.idx, item_code, warehouse))
                elif flt(available_stock) < flt(d.qty):
                    msg = (_(
                        'Row #{}: Stock quantity not enough for Item Code: {} under warehouse {}. Available quantity {}.'
                    ).format(d.idx, item_code, warehouse, qty))
            if msg:
                error_msg.append(msg)

        if error_msg:
            frappe.throw(error_msg, title=_("Item Unavailable"), as_list=True)
Exemplo n.º 8
0
	def validate_serialized_batch(self):
		from erpbee.stock.doctype.serial_no.serial_no import get_serial_nos
		for d in self.get("items"):
			if hasattr(d, 'serial_no') and hasattr(d, 'batch_no') and d.serial_no and d.batch_no:
				serial_nos = get_serial_nos(d.serial_no)
				for serial_no_data in frappe.get_all("Serial No",
					filters={"name": ("in", serial_nos)}, fields=["batch_no", "name"]):
					if serial_no_data.batch_no != d.batch_no:
						frappe.throw(_("Row #{0}: Serial No {1} does not belong to Batch {2}")
							.format(d.idx, serial_no_data.name, d.batch_no))

			if flt(d.qty) > 0.0 and d.get("batch_no") and self.get("posting_date") and self.docstatus < 2:
				expiry_date = frappe.get_cached_value("Batch", d.get("batch_no"), "expiry_date")

				if expiry_date and getdate(expiry_date) < getdate(self.posting_date):
					frappe.throw(_("Row #{0}: The batch {1} has already expired.")
						.format(d.idx, get_link_to_form("Batch", d.get("batch_no"))))
Exemplo n.º 9
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
            })
Exemplo n.º 10
0
def update_available_serial_nos(available_serial_nos, sle):
	serial_nos = get_serial_nos(sle.serial_no)
	key = (sle.item_code, sle.warehouse)
	if key not in available_serial_nos:
		available_serial_nos.setdefault(key, [])

	existing_serial_no = available_serial_nos[key]
	for sn in serial_nos:
		if sle.actual_qty > 0:
			if sn in existing_serial_no:
				existing_serial_no.remove(sn)
			else:
				existing_serial_no.append(sn)
		else:
			if sn in existing_serial_no:
				existing_serial_no.remove(sn)
			else:
				existing_serial_no.append(sn)

	sle.balance_serial_no = '\n'.join(existing_serial_no)
Exemplo n.º 11
0
def get_returned_serial_nos(child_doc, parent_doc):
    from erpbee.stock.doctype.serial_no.serial_no import get_serial_nos
    return_ref_field = frappe.scrub(child_doc.doctype)
    if child_doc.doctype == "Delivery Note Item":
        return_ref_field = "dn_detail"

    serial_nos = []

    fields = ["`{0}`.`serial_no`".format("tab" + child_doc.doctype)]

    filters = [[parent_doc.doctype, "return_against", "=", parent_doc.name],
               [parent_doc.doctype, "is_return", "=", 1],
               [child_doc.doctype, return_ref_field, "=", child_doc.name],
               [parent_doc.doctype, "docstatus", "=", 1]]

    for row in frappe.get_all(parent_doc.doctype,
                              fields=fields,
                              filters=filters):
        serial_nos.extend(get_serial_nos(row.serial_no))

    return serial_nos
Exemplo n.º 12
0
	def get_sle_for_items(self, row, serial_nos=None):
		"""Insert Stock Ledger Entries"""

		if not serial_nos and row.serial_no:
			serial_nos = get_serial_nos(row.serial_no)

		data = frappe._dict({
			"doctype": "Stock Ledger Entry",
			"item_code": row.item_code,
			"warehouse": row.warehouse,
			"posting_date": self.posting_date,
			"posting_time": self.posting_time,
			"voucher_type": self.doctype,
			"voucher_no": self.name,
			"voucher_detail_no": row.name,
			"company": self.company,
			"stock_uom": frappe.db.get_value("Item", row.item_code, "stock_uom"),
			"is_cancelled": 1 if self.docstatus == 2 else 0,
			"serial_no": '\n'.join(serial_nos) if serial_nos else '',
			"batch_no": row.batch_no,
			"valuation_rate": flt(row.valuation_rate, row.precision("valuation_rate"))
		})

		if not row.batch_no:
			data.qty_after_transaction = flt(row.qty, row.precision("qty"))

		if self.docstatus == 2 and not row.batch_no:
			if row.current_qty:
				data.actual_qty = -1 * row.current_qty
				data.qty_after_transaction = flt(row.current_qty)
				data.valuation_rate = flt(row.current_valuation_rate)
				data.stock_value = data.qty_after_transaction * data.valuation_rate
				data.stock_value_difference = -1 * flt(row.amount_difference)
			else:
				data.actual_qty = row.qty
				data.qty_after_transaction = 0.0
				data.valuation_rate = flt(row.valuation_rate)
				data.stock_value_difference = -1 * flt(row.amount_difference)

		return data
Exemplo n.º 13
0
	def make_sle_on_cancel(self):
		sl_entries = []

		has_serial_no = False
		for row in self.items:
			if row.serial_no or row.batch_no or row.current_serial_no:
				has_serial_no = True
				serial_nos = ''
				if row.current_serial_no:
					serial_nos = get_serial_nos(row.current_serial_no)

				sl_entries.append(self.get_sle_for_items(row, serial_nos))
			else:
				sl_entries.append(self.get_sle_for_items(row))

		if sl_entries:
			if has_serial_no:
				sl_entries = self.merge_similar_item_serial_nos(sl_entries)

			sl_entries.reverse()
			allow_negative_stock = frappe.db.get_value("Stock Settings", None, "allow_negative_stock")
			self.make_sl_entries(sl_entries, allow_negative_stock=allow_negative_stock)
Exemplo n.º 14
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")
Exemplo 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
        })

        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": ""
        })
Exemplo n.º 16
0
def get_ref_item_dict(valid_items, ref_item_row):
    from erpbee.stock.doctype.serial_no.serial_no import get_serial_nos

    valid_items.setdefault(
        ref_item_row.item_code,
        frappe._dict({
            "qty":
            0,
            "rate":
            0,
            "stock_qty":
            0,
            "rejected_qty":
            0,
            "received_qty":
            0,
            "serial_no": [],
            "conversion_factor":
            ref_item_row.get("conversion_factor", 1),
            "batch_no": []
        }))
    item_dict = valid_items[ref_item_row.item_code]
    item_dict["qty"] += ref_item_row.qty
    item_dict["stock_qty"] += ref_item_row.get('stock_qty', 0)
    if ref_item_row.get("rate", 0) > item_dict["rate"]:
        item_dict["rate"] = ref_item_row.get("rate", 0)

    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
Exemplo n.º 17
0
    def validate_return_items_qty(self):
        if not self.get("is_return"): return

        for d in self.get("items"):
            if d.get("qty") > 0:
                frappe.throw(_(
                    "Row #{}: You cannot add postive quantities in a return invoice. Please remove item {} to complete the return."
                ).format(d.idx, frappe.bold(d.item_code)),
                             title=_("Invalid Item"))
            if d.get("serial_no"):
                serial_nos = get_serial_nos(d.serial_no)
                for sr in serial_nos:
                    serial_no_exists = frappe.db.exists(
                        "POS Invoice Item", {
                            "parent": self.return_against,
                            "serial_no": ["like", d.get("serial_no")]
                        })
                    if not serial_no_exists:
                        bold_return_against = frappe.bold(self.return_against)
                        bold_serial_no = frappe.bold(sr)
                        frappe.throw(
                            _("Row #{}: Serial No {} cannot be returned since it was not transacted in original invoice {}"
                              ).format(d.idx, bold_serial_no,
                                       bold_return_against))
Exemplo n.º 18
0
def validate_returned_items(doc):
    from erpbee.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 exist 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"
              ))
Exemplo n.º 19
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
            target_doc.sales_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
Exemplo n.º 20
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)
Exemplo n.º 21
0
def get_serial_nos_data(serial_nos):
    from erpbee.stock.doctype.serial_no.serial_no import get_serial_nos
    return get_serial_nos(serial_nos)
Exemplo n.º 22
0
	def get_sle_for_serialized_items(self, row, sl_entries):
		from erpbee.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)
Exemplo n.º 23
0
def get_fifo_queue(filters, sle=None):
	item_details = {}
	transferred_item_details = {}
	serial_no_batch_purchase_details = {}

	if sle == None:
		sle = get_stock_ledger_entries(filters)

	for d in sle:
		key = (d.name, d.warehouse) if filters.get('show_warehouse_wise_stock') else d.name
		item_details.setdefault(key, {"details": d, "fifo_queue": []})
		fifo_queue = item_details[key]["fifo_queue"]

		transferred_item_key = (d.voucher_no, d.name, d.warehouse)
		transferred_item_details.setdefault(transferred_item_key, [])

		if d.voucher_type == "Stock Reconciliation":
			d.actual_qty = flt(d.qty_after_transaction) - flt(item_details[key].get("qty_after_transaction", 0))

		serial_no_list = get_serial_nos(d.serial_no) if d.serial_no else []

		if d.actual_qty > 0:
			if transferred_item_details.get(transferred_item_key):
				batch = transferred_item_details[transferred_item_key][0]
				fifo_queue.append(batch)
				transferred_item_details[transferred_item_key].pop(0)
			else:
				if serial_no_list:
					for serial_no in serial_no_list:
						if serial_no_batch_purchase_details.get(serial_no):
							fifo_queue.append([serial_no, serial_no_batch_purchase_details.get(serial_no)])
						else:
							serial_no_batch_purchase_details.setdefault(serial_no, d.posting_date)
							fifo_queue.append([serial_no, d.posting_date])
				else:
					fifo_queue.append([d.actual_qty, d.posting_date])
		else:
			if serial_no_list:
				for serial_no in fifo_queue:
					if serial_no[0] in serial_no_list:
						fifo_queue.remove(serial_no)
			else:
				qty_to_pop = abs(d.actual_qty)
				while qty_to_pop:
					batch = fifo_queue[0] if fifo_queue else [0, None]
					if 0 < flt(batch[0]) <= qty_to_pop:
						# if batch qty > 0
						# not enough or exactly same qty in current batch, clear batch
						qty_to_pop -= flt(batch[0])
						transferred_item_details[transferred_item_key].append(fifo_queue.pop(0))
					else:
						# all from current batch
						batch[0] = flt(batch[0]) - qty_to_pop
						transferred_item_details[transferred_item_key].append([qty_to_pop, batch[1]])
						qty_to_pop = 0

		item_details[key]["qty_after_transaction"] = d.qty_after_transaction

		if "total_qty" not in item_details[key]:
			item_details[key]["total_qty"] = d.actual_qty
		else:
			item_details[key]["total_qty"] += d.actual_qty

	return item_details