Esempio n. 1
0
	def test_delivery_note(self):
		'''Test automatic batch selection for outgoing items'''
		batch_qty = 15
		receipt = self.test_purchase_receipt(batch_qty)
		item_code = 'ITEM-BATCH-1'

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

		# shipped from FEFO batch
		self.assertEquals(
			delivery_note.items[0].batch_no,
			get_batch_no(item_code, receipt.items[0].warehouse, batch_qty)
		)
Esempio n. 2
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)
		item_code = 'ITEM-BATCH-1'

		stock_entry = frappe.get_doc(dict(
			doctype='Stock Entry',
			purpose='Material Issue',
			company=receipt.company,
			items=[
				dict(
					item_code=item_code,
					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,
			get_batch_no(item_code, receipt.items[0].warehouse, batch_qty)
		)
Esempio n. 3
0
	def get_item_details(self, args=None, for_update=False):
		item = frappe.db.sql("""select stock_uom, description, image, item_name,
				expense_account, buying_cost_center, item_group, has_serial_no,
				has_batch_no, sample_quantity
			from `tabItem`
			where name = %s
				and disabled=0
				and (end_of_life is null or end_of_life='0000-00-00' or end_of_life > %s)""",
			(args.get('item_code'), nowdate()), as_dict = 1)
		if not item:
			frappe.throw(_("Item {0} is not active or end of life has been reached").format(args.get("item_code")))

		item = item[0]

		ret = frappe._dict({
			'uom'			      	: item.stock_uom,
			'stock_uom'			  	: item.stock_uom,
			'description'		  	: item.description,
			'image'					: item.image,
			'item_name' 		  	: item.item_name,
			'expense_account'		: args.get("expense_account"),
			'cost_center'			: get_default_cost_center(args, item),
			'qty'					: 0,
			'transfer_qty'			: 0,
			'conversion_factor'		: 1,
			'batch_no'				: '',
			'actual_qty'			: 0,
			'basic_rate'			: 0,
			'serial_no'				: '',
			'has_serial_no'			: item.has_serial_no,
			'has_batch_no'			: item.has_batch_no,
			'sample_quantity'		: item.sample_quantity
		})
		for d in [["Account", "expense_account", "default_expense_account"],
			["Cost Center", "cost_center", "cost_center"]]:
				company = frappe.db.get_value(d[0], ret.get(d[1]), "company")
				if not ret[d[1]] or (company and self.company != company):
					ret[d[1]] = frappe.db.get_value("Company", self.company, d[2]) if d[2] else None

		# update uom
		if args.get("uom") and for_update:
			ret.update(get_uom_details(args.get('item_code'), args.get('uom'), args.get('qty')))

		if not ret["expense_account"]:
			ret["expense_account"] = frappe.db.get_value("Company", self.company, "stock_adjustment_account")

		args['posting_date'] = self.posting_date
		args['posting_time'] = self.posting_time

		stock_and_rate = get_warehouse_details(args) if args.get('warehouse') else {}
		ret.update(stock_and_rate)

		# automatically select batch for outgoing item
		if (args.get('s_warehouse', None) and args.get('qty') and
			ret.get('has_batch_no') and not args.get('batch_no')):
			args.batch_no = get_batch_no(args['item_code'], args['s_warehouse'], args['qty'])

		return ret
Esempio n. 4
0
def update_stock(args, out):
	if (args.get("doctype") == "Delivery Note" or
		(args.get("doctype") == "Sales Invoice" and args.get('update_stock'))) \
		and out.warehouse and out.stock_qty > 0:

		if out.has_batch_no and not args.get("batch_no"):
			out.batch_no = get_batch_no(out.item_code, out.warehouse, out.qty)
			actual_batch_qty = get_batch_qty(out.batch_no, out.warehouse, out.item_code)
			if actual_batch_qty:
				out.update(actual_batch_qty)

		if out.has_serial_no and args.get('batch_no'):
			reserved_so = get_so_reservation_for_item(args)
			out.batch_no = args.get('batch_no')
			out.serial_no = get_serial_no(out, args.serial_no, sales_order=reserved_so)

		elif out.has_serial_no:
			reserved_so = get_so_reservation_for_item(args)
			out.serial_no = get_serial_no(out, args.serial_no, sales_order=reserved_so)
Esempio n. 5
0
def update_stock(args, out):
	if (args.get("doctype") == "Delivery Note" or
		(args.get("doctype") == "Sales Invoice" and args.get('update_stock'))) \
		and out.warehouse and out.stock_qty > 0:

		if out.has_batch_no and not args.get("batch_no"):
			out.batch_no = get_batch_no(out.item_code, out.warehouse, out.qty)
			actual_batch_qty = get_batch_qty(out.batch_no, out.warehouse, out.item_code)
			if actual_batch_qty:
				out.update(actual_batch_qty)

		if out.has_serial_no and args.get('batch_no'):
			reserved_so = get_so_reservation_for_item(args)
			out.batch_no = args.get('batch_no')
			out.serial_no = get_serial_no(out, args.serial_no, sales_order=reserved_so)

		elif out.has_serial_no:
			reserved_so = get_so_reservation_for_item(args)
			out.serial_no = get_serial_no(out, args.serial_no, sales_order=reserved_so)
Esempio n. 6
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)
                    )
Esempio n. 7
0
    def test_delivery_note(self):
        '''Test automatic batch selection for outgoing items'''
        batch_qty = 15
        receipt = self.test_purchase_receipt(batch_qty)
        item_code = 'ITEM-BATCH-1'

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

        # shipped from FEFO batch
        self.assertEqual(
            delivery_note.items[0].batch_no,
            get_batch_no(item_code, receipt.items[0].warehouse, batch_qty))
Esempio n. 8
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)
        item_code = 'ITEM-BATCH-1'

        stock_entry = frappe.get_doc(
            dict(doctype='Stock Entry',
                 purpose='Material Issue',
                 company=receipt.company,
                 items=[
                     dict(
                         item_code=item_code,
                         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,
            get_batch_no(item_code, receipt.items[0].warehouse, batch_qty))
Esempio n. 9
0
def get_item_details(args):
	"""
		args = {
			"item_code": "",
			"warehouse": None,
			"customer": "",
			"conversion_rate": 1.0,
			"selling_price_list": None,
			"price_list_currency": None,
			"plc_conversion_rate": 1.0,
			"doctype": "",
			"name": "",
			"supplier": None,
			"transaction_date": None,
			"conversion_rate": 1.0,
			"buying_price_list": None,
			"is_subcontracted": "Yes" / "No",
			"ignore_pricing_rule": 0/1
			"project": ""
		}
	"""
	args = process_args(args)
	item_doc = frappe.get_doc("Item", args.item_code)
	item = item_doc

	validate_item_details(args, item)

	out = get_basic_details(args, item)

	get_party_item_code(args, item_doc, out)

	if frappe.db.exists("Product Bundle", args.item_code):
		valuation_rate = 0.0
		bundled_items = frappe.get_doc("Product Bundle", args.item_code)

		for bundle_item in bundled_items.items:
			valuation_rate += \
				flt(get_valuation_rate(bundle_item.item_code, out.get("warehouse")).get("valuation_rate") \
					* bundle_item.qty)

		out.update({
			"valuation_rate": valuation_rate
		})

	else:
		out.update(get_valuation_rate(args.item_code, out.get("warehouse")))

	get_price_list_rate(args, item_doc, out)

	if args.customer and cint(args.is_pos):
		out.update(get_pos_profile_item_details(args.company, args))

	if out.get("warehouse"):
		out.update(get_bin_details(args.item_code, out.warehouse))

	# update args with out, if key or value not exists
	for key, value in out.iteritems():
		if args.get(key) is None:
			args[key] = value

	out.update(get_pricing_rule_for_item(args))

	if args.get("doctype") in ("Sales Invoice", "Delivery Note") and out.stock_qty > 0:
		if out.has_serial_no:
			out.serial_no = get_serial_no(out)

		if out.has_batch_no:
			out.batch_no = get_batch_no(out.item_code, out.warehouse, out.qty)


	if args.transaction_date and item.lead_time_days:
		out.schedule_date = out.lead_time_date = add_days(args.transaction_date,
			item.lead_time_days)

	if args.get("is_subcontracted") == "Yes":
		out.bom = get_default_bom(args.item_code)

	get_gross_profit(out)

	return out
Esempio n. 10
0
def get_item_details(args):
    """
		args = {
			"item_code": "",
			"warehouse": None,
			"customer": "",
			"conversion_rate": 1.0,
			"selling_price_list": None,
			"price_list_currency": None,
			"plc_conversion_rate": 1.0,
			"doctype": "",
			"name": "",
			"supplier": None,
			"transaction_date": None,
			"conversion_rate": 1.0,
			"buying_price_list": None,
			"is_subcontracted": "Yes" / "No",
			"ignore_pricing_rule": 0/1
			"project": ""
		}
	"""
    args = process_args(args)
    item_doc = frappe.get_doc("Item", args.item_code)
    item = item_doc

    validate_item_details(args, item)

    out = get_basic_details(args, item)

    get_party_item_code(args, item_doc, out)

    if frappe.db.exists("Product Bundle", args.item_code):
        valuation_rate = 0.0
        bundled_items = frappe.get_doc("Product Bundle", args.item_code)

        for bundle_item in bundled_items.items:
            valuation_rate += \
             flt(get_valuation_rate(bundle_item.item_code, out.get("warehouse")).get("valuation_rate") \
              * bundle_item.qty)

        out.update({"valuation_rate": valuation_rate})

    else:
        out.update(get_valuation_rate(args.item_code, out.get("warehouse")))

    get_price_list_rate(args, item_doc, out)

    if args.customer and cint(args.is_pos):
        out.update(get_pos_profile_item_details(args.company, args))

    if out.get("warehouse"):
        out.update(get_bin_details(args.item_code, out.warehouse))

    # update args with out, if key or value not exists
    for key, value in out.iteritems():
        if args.get(key) is None:
            args[key] = value

    out.update(get_pricing_rule_for_item(args))

    if (args.get("doctype") == "Delivery Note" or
     (args.get("doctype") == "Sales Invoice" and args.get('update_stock'))) \
     and out.warehouse and out.stock_qty > 0:

        if out.has_serial_no:
            out.serial_no = get_serial_no(out, args.serial_no)

        if out.has_batch_no and not args.get("batch_no"):
            out.batch_no = get_batch_no(out.item_code, out.warehouse, out.qty)
            actual_batch_qty = get_batch_qty(out.batch_no, out.warehouse,
                                             out.item_code)
            if actual_batch_qty:
                out.update(actual_batch_qty)

    if args.transaction_date and item.lead_time_days:
        out.schedule_date = out.lead_time_date = add_days(
            args.transaction_date, item.lead_time_days)

    if args.get("is_subcontracted") == "Yes":
        out.bom = args.get('bom') or get_default_bom(args.item_code)

    get_gross_profit(out)

    return out
    def get_item_details(self, args=None, for_update=False):
        item = frappe.db.sql(
            """select i.name, i.stock_uom, i.description, i.image, i.item_name, i.item_group,
				i.has_batch_no, i.sample_quantity, i.has_serial_no,
				id.expense_account, id.buying_cost_center
			from `tabItem` i LEFT JOIN `tabItem Default` id ON i.name=id.parent and id.company=%s
			where i.name=%s
				and i.disabled=0
				and (i.end_of_life is null or i.end_of_life='0000-00-00' or i.end_of_life > %s)""",
            (self.company, args.get('item_code'), nowdate()),
            as_dict=1)

        if not item:
            frappe.throw(
                _("Item {0} is not active or end of life has been reached").
                format(args.get("item_code")))

        item = item[0]
        item_group_defaults = get_item_group_defaults(item.name, self.company)

        ret = frappe._dict({
            'uom':
            item.stock_uom,
            'stock_uom':
            item.stock_uom,
            'description':
            item.description,
            'image':
            item.image,
            'item_name':
            item.item_name,
            'cost_center':
            get_default_cost_center(args, item, item_group_defaults,
                                    self.company),
            'qty':
            args.get("qty"),
            'transfer_qty':
            args.get('qty'),
            'conversion_factor':
            1,
            'batch_no':
            '',
            'actual_qty':
            0,
            'basic_rate':
            0,
            'serial_no':
            '',
            'has_serial_no':
            item.has_serial_no,
            'has_batch_no':
            item.has_batch_no,
            'sample_quantity':
            item.sample_quantity
        })

        # update uom
        if args.get("uom") and for_update:
            ret.update(
                get_uom_details(args.get('item_code'), args.get('uom'),
                                args.get('qty')))

        args['posting_date'] = self.posting_date
        args['posting_time'] = self.posting_time

        stock_and_rate = get_warehouse_details(args) if args.get(
            'warehouse') else {}
        ret.update(stock_and_rate)

        # automatically select batch for outgoing item
        if (args.get('s_warehouse', None) and args.get('qty')
                and ret.get('has_batch_no') and not args.get('batch_no')):
            args.batch_no = get_batch_no(args['item_code'],
                                         args['s_warehouse'], args['qty'])

        return ret
Esempio n. 12
0
    def get_item_details(self, args=None, for_update=False):
        item = frappe.db.sql(
            """select stock_uom, description, image, item_name,
				expense_account, buying_cost_center, item_group, has_serial_no,
				has_batch_no
			from `tabItem`
			where name = %s
				and disabled=0
				and (end_of_life is null or end_of_life='0000-00-00' or end_of_life > %s)""",
            (args.get('item_code'), nowdate()),
            as_dict=1)
        if not item:
            frappe.throw(
                _("Item {0} is not active or end of life has been reached").
                format(args.get("item_code")))

        item = item[0]

        ret = frappe._dict({
            'uom': item.stock_uom,
            'stock_uom': item.stock_uom,
            'description': item.description,
            'image': item.image,
            'item_name': item.item_name,
            'expense_account': args.get("expense_account"),
            'cost_center': get_default_cost_center(args, item),
            'qty': 0,
            'transfer_qty': 0,
            'conversion_factor': 1,
            'batch_no': '',
            'actual_qty': 0,
            'basic_rate': 0,
            'serial_no': '',
            'has_serial_no': item.has_serial_no,
            'has_batch_no': item.has_batch_no
        })
        for d in [["Account", "expense_account", "default_expense_account"],
                  ["Cost Center", "cost_center", "cost_center"]]:
            company = frappe.db.get_value(d[0], ret.get(d[1]), "company")
            if not ret[d[1]] or (company and self.company != company):
                ret[d[1]] = frappe.db.get_value("Company", self.company,
                                                d[2]) if d[2] else None

        # update uom
        if args.get("uom") and for_update:
            ret.update(
                get_uom_details(args.get('item_code'), args.get('uom'),
                                args.get('qty')))

        if not ret["expense_account"]:
            ret["expense_account"] = frappe.db.get_value(
                "Company", self.company, "stock_adjustment_account")

        args['posting_date'] = self.posting_date
        args['posting_time'] = self.posting_time

        stock_and_rate = args.get('warehouse') and get_warehouse_details(
            args) or {}
        ret.update(stock_and_rate)

        # automatically select batch for outgoing item
        if (args.get('s_warehouse', None) and args.get('qty')
                and ret.get('has_batch_no') and not args.get('batch_no')):
            args.batch_no = get_batch_no(args['item_code'],
                                         args['s_warehouse'], args['qty'])

        return ret