def test_stock_reco_for_serialized_item(self): set_perpetual_inventory() to_delete_records = [] to_delete_serial_nos = [] # Add new serial nos serial_item_code = "Stock-Reco-Serial-Item-1" serial_warehouse = "_Test Warehouse for Stock Reco1 - _TC" sr = create_stock_reconciliation(item_code=serial_item_code, warehouse = serial_warehouse, qty=5, rate=200) # print(sr.name) serial_nos = get_serial_nos(sr.items[0].serial_no) self.assertEqual(len(serial_nos), 5) args = { "item_code": serial_item_code, "warehouse": serial_warehouse, "posting_date": nowdate(), "posting_time": nowtime(), "serial_no": sr.items[0].serial_no } valuation_rate = get_incoming_rate(args) self.assertEqual(valuation_rate, 200) to_delete_records.append(sr.name) sr = create_stock_reconciliation(item_code=serial_item_code, warehouse = serial_warehouse, qty=5, rate=300, serial_no = '\n'.join(serial_nos)) # print(sr.name) serial_nos1 = get_serial_nos(sr.items[0].serial_no) self.assertEqual(len(serial_nos1), 5) args = { "item_code": serial_item_code, "warehouse": serial_warehouse, "posting_date": nowdate(), "posting_time": nowtime(), "serial_no": sr.items[0].serial_no } valuation_rate = get_incoming_rate(args) self.assertEqual(valuation_rate, 300) to_delete_records.append(sr.name) to_delete_records.reverse() for d in to_delete_records: stock_doc = frappe.get_doc("Stock Reconciliation", d) stock_doc.cancel() for d in serial_nos + serial_nos1: if frappe.db.exists("Serial No", d): frappe.delete_doc("Serial No", d)
def set_basic_rate(self, force=False, update_finished_item_rate=True): """get stock and incoming rate on posting date""" raw_material_cost = 0.0 scrap_material_cost = 0.0 fg_basic_rate = 0.0 for d in self.get('items'): if d.t_warehouse: fg_basic_rate = flt(d.basic_rate) args = self.get_args_for_incoming_rate(d) # get basic rate if not d.bom_no: if (not flt(d.basic_rate) and not d.allow_zero_valuation_rate ) or d.s_warehouse or force: basic_rate = flt(get_incoming_rate(args), self.precision("basic_rate", d)) if basic_rate > 0: d.basic_rate = basic_rate d.basic_amount = flt( flt(d.transfer_qty) * flt(d.basic_rate), d.precision("basic_amount")) if not d.t_warehouse: raw_material_cost += flt(d.basic_amount) # get scrap items basic rate if d.bom_no: if not flt(d.basic_rate) and not d.allow_zero_valuation_rate and \ getattr(self, "pro_doc", frappe._dict()).scrap_warehouse == d.t_warehouse: basic_rate = flt(get_incoming_rate(args), self.precision("basic_rate", d)) if basic_rate > 0: d.basic_rate = basic_rate d.basic_amount = flt( flt(d.transfer_qty) * flt(d.basic_rate), d.precision("basic_amount")) if getattr(self, "pro_doc", frappe._dict()).scrap_warehouse == d.t_warehouse: scrap_material_cost += flt(d.basic_amount) number_of_fg_items = len( [t.t_warehouse for t in self.get("items") if t.t_warehouse]) if (fg_basic_rate == 0.0 and number_of_fg_items == 1) or update_finished_item_rate: self.set_basic_rate_for_finished_goods(raw_material_cost, scrap_material_cost)
def set_basic_rate(self, force=False): """get stock and incoming rate on posting date""" raw_material_cost = 0.0 for d in self.get("items"): args = frappe._dict( { "item_code": d.item_code, "warehouse": d.s_warehouse or d.t_warehouse, "posting_date": self.posting_date, "posting_time": self.posting_time, "qty": d.s_warehouse and -1 * flt(d.transfer_qty) or flt(d.transfer_qty), "serial_no": d.serial_no, } ) # get basic rate if not d.bom_no: if not flt(d.basic_rate) or d.s_warehouse or force: basic_rate = flt(get_incoming_rate(args), self.precision("basic_rate", d)) if basic_rate > 0: d.basic_rate = basic_rate d.basic_amount = flt(flt(d.transfer_qty) * flt(d.basic_rate), d.precision("basic_amount")) if not d.t_warehouse: raw_material_cost += flt(d.basic_amount) self.set_basic_rate_for_finished_goods(raw_material_cost)
def update_raw_materials_supplied(self, item, raw_material_table, rm_supplied_idx): bom_items = self.get_items_from_default_bom(item.item_code) raw_materials_cost = 0 for bom_item in bom_items: # check if exists exists = 0 for d in self.get(raw_material_table): if d.main_item_code == item.item_code and d.rm_item_code == bom_item.item_code \ and d.reference_name == item.name: rm, exists = d, 1 break if not exists: rm = self.append(raw_material_table, {}) required_qty = flt(bom_item.qty_consumed_per_unit) * flt( item.qty) * flt(item.conversion_factor) rm.reference_name = item.name rm.bom_detail_no = bom_item.name rm.main_item_code = item.item_code rm.rm_item_code = bom_item.item_code rm.stock_uom = bom_item.stock_uom rm.required_qty = required_qty rm.conversion_factor = item.conversion_factor rm.idx = rm_supplied_idx if self.doctype == "Purchase Receipt": rm.consumed_qty = required_qty rm.description = bom_item.description if item.batch_no and not rm.batch_no: rm.batch_no = item.batch_no rm_supplied_idx += 1 # get raw materials rate if self.doctype == "Purchase Receipt": from erpnext.stock.utils import get_incoming_rate rm.rate = get_incoming_rate({ "item_code": bom_item.item_code, "warehouse": self.supplier_warehouse, "posting_date": self.posting_date, "posting_time": self.posting_time, "qty": -1 * required_qty, "serial_no": rm.serial_no }) if not rm.rate: from erpnext.stock.stock_ledger import get_valuation_rate rm.rate = get_valuation_rate(bom_item.item_code, self.supplier_warehouse) else: rm.rate = bom_item.rate rm.amount = required_qty * flt(rm.rate) raw_materials_cost += flt(rm.amount) if self.doctype == "Purchase Receipt": item.rm_supp_cost = raw_materials_cost
def get_average_buying_rate(self, row, item_code): if not item_code in self.average_buying_rate: if item_code in self.non_stock_items: self.average_buying_rate[item_code] = flt( frappe.db.sql( """ select sum(base_net_amount) / sum(qty * conversion_factor) from `tabPurchase Invoice Item` where item_code = %s and docstatus=1""", item_code)[0][0]) else: row.voucher_type = row.parenttype row.voucher_no = row.parent average_buying_rate = get_incoming_rate(row) if not average_buying_rate: average_buying_rate = get_valuation_rate( item_code, row.warehouse, row.parenttype, row.parent, allow_zero_rate=row.allow_zero_valuation, currency=self.filters.currency, company=self.filters.company) self.average_buying_rate[item_code] = flt(average_buying_rate) return self.average_buying_rate[item_code]
def set_incoming_rate(self): if self.doctype not in ("Delivery Note", "Sales Invoice"): return items = self.get("items") + (self.get("packed_items") or []) for d in items: if not cint(self.get("is_return")): # Get incoming rate based on original item cost based on valuation method d.incoming_rate = get_incoming_rate( { "item_code": d.item_code, "warehouse": d.warehouse, "posting_date": self.posting_date, "posting_time": self.posting_time, "qty": -1 * flt(d.qty), "serial_no": d.serial_no, "company": self.company, "voucher_type": self.doctype, "voucher_no": self.name, "allow_zero_valuation": d.get("allow_zero_valuation") }, raise_error_if_no_rate=False) elif self.get("return_against"): # Get incoming rate of return entry from reference document # based on original item cost as per valuation method d.incoming_rate = get_rate_for_return(self.doctype, self.name, d.item_code, self.return_against, item_row=d)
def get_valuation_rate(self, args): """ Get average valuation rate of relevant warehouses as per valuation method (MAR/FIFO) as on costing date """ from erpnext.stock.utils import get_incoming_rate dt = self.doc.costing_date or nowdate() time = self.doc.costing_date == nowdate() and now().split( )[1] or '23:59' warehouse = frappe.db.sql( "select warehouse from `tabBin` where item_code = %s", args['item_code']) rate = [] for wh in warehouse: r = get_incoming_rate({ "item_code": args.get("item_code"), "warehouse": wh[0], "posting_date": dt, "posting_time": time, "qty": args.get("qty") or 0 }) if r: rate.append(r) return rate and flt(sum(rate)) / len(rate) or 0
def update_basic_rates(self): total_rm_rate = 0 for rm in self.items: rm.basic_rate = flt( get_incoming_rate({ "item_code": rm.item_code, "warehouse": rm.warehouse, "posting_date": self.posting_date, "posting_time": self.posting_time, "qty": -1 * flt(rm.stock_qty), "serial_no": rm.serial_no, "voucher_type": self.doctype, "voucher_no": rm.name, "company": self.company, "allow_zero_valuation": rm.allow_zero_valuation_rate }), # raise_error_if_no_rate self.precision("basic_rate", rm)) rm.basic_amount = flt(rm.basic_rate * rm.stock_qty, self.precision("basic_amount", rm)) total_rm_rate += rm.basic_amount # rate = raw_material_csot + (supplier cost if subcontracting) self.rate = flt((total_rm_rate + (self.base_total or 0)) / self.qty, self.precision("rate"))
def set_basic_rate(self, force=False, update_finished_item_rate=True): """get stock and incoming rate on posting date""" for d in self.get('items'): args = frappe._dict({ "item_code": d.item_code, "warehouse": d.s_warehouse or d.t_warehouse, "posting_date": self.posting_date, "posting_time": self.posting_time, "qty": d.s_warehouse and -1 * flt(d.transfer_qty) or flt(d.transfer_qty), "serial_no": d.serial_no, }) if not flt(d.basic_rate) or d.s_warehouse or force: # if not flt(d.basic_rate): basic_rate = flt(get_incoming_rate(args), self.precision("basic_rate", d)) if basic_rate > 0: d.basic_rate = basic_rate d.amount = flt(flt(d.transfer_qty) * flt(d.basic_rate))
def _get_qty_rate_for_serial_nos(item_code, warehouse, posting_date, posting_time, item_dict): args = { "item_code": item_code, "warehouse": warehouse, "posting_date": posting_date, "posting_time": posting_time, } serial_nos_list = [ serial_no.get("name") for serial_no in frappe.db.sql( """ SELECT name FFROM `tabSerial No` WHERE item_code = %(item_code)s AND warehouse = %(warehouse)s AND timestamp(purchase_date, purchase_time) <= timestamp(%(posting_date)s, %(posting_time)s) """, args, as_dict=1, ) ] qty = len(serial_nos_list) serial_nos = "\n".join(serial_nos_list) args.update({"qty": qty, "serial_nos": serial_nos}) rate = get_incoming_rate(args, raise_error_if_no_rate=False) or 0 return qty, rate, serial_nos
def get_stock_and_rate(self, force=False): """get stock and incoming rate on posting date""" raw_material_cost = 0.0 if not self.posting_date or not self.posting_time: frappe.throw(_("Posting date and posting time is mandatory")) allow_negative_stock = cint( frappe.db.get_value("Stock Settings", None, "allow_negative_stock")) for d in self.get('items'): d.transfer_qty = flt(d.transfer_qty) args = frappe._dict({ "item_code": d.item_code, "warehouse": d.s_warehouse or d.t_warehouse, "posting_date": self.posting_date, "posting_time": self.posting_time, "qty": d.s_warehouse and -1 * d.transfer_qty or d.transfer_qty, "serial_no": d.serial_no, }) # get actual stock at source warehouse d.actual_qty = get_previous_sle(args).get( "qty_after_transaction") or 0 # validate qty during submit if d.docstatus == 1 and d.s_warehouse and not allow_negative_stock and d.actual_qty < d.transfer_qty: frappe.throw( _("""Row {0}: Qty not avalable in warehouse {1} on {2} {3}. Available Qty: {4}, Transfer Qty: {5}""").format(d.idx, d.s_warehouse, self.posting_date, self.posting_time, d.actual_qty, d.transfer_qty), NegativeStockError) # get incoming rate if not d.bom_no: if not flt(d.incoming_rate) or d.s_warehouse or force: incoming_rate = flt(get_incoming_rate(args), self.precision("incoming_rate", d)) if incoming_rate > 0: d.incoming_rate = incoming_rate d.amount = flt( flt(d.transfer_qty) * flt(d.incoming_rate), d.precision("amount")) if not d.t_warehouse: raw_material_cost += flt(d.amount) self.add_operation_cost(raw_material_cost, force)
def set_basic_rate(self, force=False): """get stock and incoming rate on posting date""" raw_material_cost = 0.0 for d in self.get('items'): args = frappe._dict({ "item_code": d.item_code, "warehouse": d.s_warehouse or d.t_warehouse, "posting_date": self.posting_date, "posting_time": self.posting_time, "qty": d.s_warehouse and -1*flt(d.transfer_qty) or flt(d.transfer_qty), "serial_no": d.serial_no, }) # get basic rate if not d.bom_no: if not flt(d.basic_rate) or d.s_warehouse or force: basic_rate = flt(get_incoming_rate(args), self.precision("basic_rate", d)) if basic_rate > 0: d.basic_rate = basic_rate d.basic_amount = flt(flt(d.transfer_qty) * flt(d.basic_rate), d.precision("basic_amount")) if not d.t_warehouse: raw_material_cost += flt(d.basic_amount) self.set_basic_rate_for_finished_goods(raw_material_cost)
def update_raw_materials_supplied_based_on_stock_entries(self, raw_material_table): self.set(raw_material_table, []) purchase_orders = [d.purchase_order for d in self.items] if purchase_orders: items = get_subcontracted_raw_materials_from_se(purchase_orders) backflushed_raw_materials = get_backflushed_subcontracted_raw_materials_from_se(purchase_orders, self.name) for d in items: qty = d.qty - backflushed_raw_materials.get(d.item_code, 0) rm = self.append(raw_material_table, {}) rm.rm_item_code = d.item_code rm.item_name = d.item_name rm.main_item_code = d.main_item_code rm.description = d.description rm.stock_uom = d.stock_uom rm.required_qty = qty rm.consumed_qty = qty rm.serial_no = d.serial_no rm.batch_no = d.batch_no # get raw materials rate from erpnext.stock.utils import get_incoming_rate rm.rate = get_incoming_rate({ "item_code": d.item_code, "warehouse": self.supplier_warehouse, "posting_date": self.posting_date, "posting_time": self.posting_time, "qty": -1 * qty, "serial_no": rm.serial_no }) if not rm.rate: rm.rate = get_valuation_rate(d.item_code, self.supplier_warehouse, self.doctype, self.name, currency=self.company_currency, company = self.company) rm.amount = qty * flt(rm.rate)
def get_incoming_rate(self, args): incoming_rate = 0 if self.purpose == "Sales Return" and \ (self.delivery_note_no or self.sales_invoice_no): sle = frappe.db.sql("""select name, posting_date, posting_time, actual_qty, stock_value, warehouse from `tabStock Ledger Entry` where voucher_type = %s and voucher_no = %s and item_code = %s limit 1""", ((self.delivery_note_no and "Delivery Note" or "Sales Invoice"), self.delivery_note_no or self.sales_invoice_no, args.item_code), as_dict=1) if sle: args.update({ "posting_date": sle[0].posting_date, "posting_time": sle[0].posting_time, "sle": sle[0].name, "warehouse": sle[0].warehouse, }) previous_sle = get_previous_sle(args) incoming_rate = (flt(sle[0].stock_value) - flt(previous_sle.get("stock_value"))) / \ flt(sle[0].actual_qty) else: incoming_rate = get_incoming_rate(args) return incoming_rate
def update_basic_rates(self): total_rm_rate = 0 for rm in self.items: rm.basic_rate = flt( get_incoming_rate({ "item_code": rm.item_code, "warehouse": rm.warehouse, "posting_date": self.posting_date, "posting_time": self.posting_time, "qty": -1*flt(rm.stock_qty), "serial_no": rm.serial_no, "voucher_type": self.doctype, "voucher_no": rm.name, "company": self.company, "allow_zero_valuation": rm.allow_zero_valuation_rate }), # raise_error_if_no_rate self.precision("basic_rate", rm)) rm.basic_amount = flt(rm.basic_rate * rm.stock_qty, self.precision("basic_amount", rm)) total_rm_rate += rm.basic_amount # get additional charges (included for valuation) charges_in_valuation = 0 for tax in self.taxes: if tax.category in ['Valuation', 'Valuation and Total']: charges_in_valuation += tax.base_tax_amount # rate = raw_material_csot + (supplier cost if subcontracting) self.rate = flt((total_rm_rate + (self.base_total or 0) + charges_in_valuation) / self.qty, self.precision("rate"))
def append_raw_material_to_be_backflushed(self, fg_item_doc, raw_material_data, qty): rm = self.append('supplied_items', {}) rm.update(raw_material_data) if not rm.main_item_code: rm.main_item_code = fg_item_doc.item_code rm.reference_name = fg_item_doc.name rm.required_qty = qty rm.consumed_qty = qty if not raw_material_data.get('non_stock_item'): from erpnext.stock.utils import get_incoming_rate rm.rate = get_incoming_rate({ "item_code": raw_material_data.rm_item_code, "warehouse": self.supplier_warehouse, "posting_date": self.posting_date, "posting_time": self.posting_time, "qty": -1 * qty, "serial_no": rm.serial_no }) if not rm.rate: rm.rate = get_valuation_rate(raw_material_data.rm_item_code, self.supplier_warehouse, self.doctype, self.name, currency=self.company_currency, company=self.company) rm.amount = qty * flt(rm.rate) fg_item_doc.rm_supp_cost += rm.amount
def set_incoming_rate(self): if self.doctype not in ("Purchase Receipt", "Purchase Invoice", "Purchase Order"): return ref_doctype_map = { "Purchase Order": "Sales Order Item", "Purchase Receipt": "Delivery Note Item", "Purchase Invoice": "Sales Invoice Item", } ref_doctype = ref_doctype_map.get(self.doctype) items = self.get("items") for d in items: if not cint(self.get("is_return")): # Get outgoing rate based on original item cost based on valuation method if not d.get(frappe.scrub(ref_doctype)): outgoing_rate = get_incoming_rate( { "item_code": d.item_code, "warehouse": d.get('from_warehouse'), "posting_date": self.get('posting_date') or self.get('transation_date'), "posting_time": self.get('posting_time'), "qty": -1 * flt(d.get('stock_qty')), "serial_no": d.get('serial_no'), "company": self.company, "voucher_type": self.doctype, "voucher_no": self.name, "allow_zero_valuation": d.get("allow_zero_valuation") }, raise_error_if_no_rate=False) rate = flt(outgoing_rate * d.conversion_factor, d.precision('rate')) else: rate = frappe.db.get_value( ref_doctype, d.get(frappe.scrub(ref_doctype)), 'rate') if self.is_internal_transfer(): if rate != d.rate: d.rate = rate d.discount_percentage = 0 d.discount_amount = 0 frappe.msgprint(_( "Row {0}: Item rate has been updated as per valuation rate since its an internal stock transfer" ).format(d.idx), alert=1)
def update_stock_ledger(self): self.update_reserved_qty() sl_entries = [] for d in self.get_item_list(): if frappe.db.get_value("Item", d.item_code, "is_stock_item") == 1 and flt(d.qty): if flt(d.conversion_factor)==0.0: d.conversion_factor = get_conversion_factor(d.item_code, d.uom).get("conversion_factor") or 1.0 return_rate = 0 if cint(self.is_return) and self.return_against and self.docstatus==1: return_rate = self.get_incoming_rate_for_sales_return(d.item_code, self.return_against) # On cancellation or if return entry submission, make stock ledger entry for # target warehouse first, to update serial no values properly if d.warehouse and ((not cint(self.is_return) and self.docstatus==1) or (cint(self.is_return) and self.docstatus==2)): sl_entries.append(self.get_sl_entries(d, { "actual_qty": -1*flt(d.qty), "incoming_rate": return_rate })) if d.target_warehouse: target_warehouse_sle = self.get_sl_entries(d, { "actual_qty": flt(d.qty), "warehouse": d.target_warehouse }) if self.docstatus == 1: if not cint(self.is_return): args = frappe._dict({ "item_code": d.item_code, "warehouse": d.warehouse, "posting_date": self.posting_date, "posting_time": self.posting_time, "qty": -1*flt(d.qty), "serial_no": d.serial_no, "company": d.company, "voucher_type": d.voucher_type, "voucher_no": d.name, "allow_zero_valuation": d.allow_zero_valuation }) target_warehouse_sle.update({ "incoming_rate": get_incoming_rate(args) }) else: target_warehouse_sle.update({ "outgoing_rate": return_rate }) sl_entries.append(target_warehouse_sle) if d.warehouse and ((not cint(self.is_return) and self.docstatus==2) or (cint(self.is_return) and self.docstatus==1)): sl_entries.append(self.get_sl_entries(d, { "actual_qty": -1*flt(d.qty), "incoming_rate": return_rate })) self.make_sl_entries(sl_entries)
def get_incoming_rate(self, args): incoming_rate = 0 if self.purpose == "Sales Return": incoming_rate = self.get_incoming_rate_for_sales_return(args) else: incoming_rate = get_incoming_rate(args) return incoming_rate
def update_raw_materials_supplied(self, item, raw_material_table, rm_supplied_idx): bom_items = self.get_items_from_default_bom(item.item_code) raw_materials_cost = 0 for bom_item in bom_items: # check if exists exists = 0 for d in self.get(raw_material_table): if d.main_item_code == item.item_code and d.rm_item_code == bom_item.item_code \ and d.reference_name == item.name: rm, exists = d, 1 break if not exists: rm = self.append(raw_material_table, {}) required_qty = flt(bom_item.qty_consumed_per_unit) * flt(item.qty) * flt(item.conversion_factor) rm.reference_name = item.name rm.bom_detail_no = bom_item.name rm.main_item_code = item.item_code rm.rm_item_code = bom_item.item_code rm.stock_uom = bom_item.stock_uom rm.required_qty = required_qty rm.conversion_factor = item.conversion_factor rm.idx = rm_supplied_idx if self.doctype == "Purchase Receipt": rm.consumed_qty = required_qty rm.description = bom_item.description if item.batch_no and not rm.batch_no: rm.batch_no = item.batch_no rm_supplied_idx += 1 # get raw materials rate if self.doctype == "Purchase Receipt": from erpnext.stock.utils import get_incoming_rate item_rate = get_incoming_rate({ "item_code": bom_item.item_code, "warehouse": self.supplier_warehouse, "posting_date": self.posting_date, "posting_time": self.posting_time, "qty": -1 * required_qty, "serial_no": rm.serial_no }) if not item_rate: from erpnext.controllers.stock_controller import get_valuation_rate item_rate = get_valuation_rate(bom_item.item_code, self.supplier_warehouse) rm.rate = item_rate or bom_item.rate else: rm.rate = bom_item.rate rm.amount = required_qty * flt(rm.rate) raw_materials_cost += flt(rm.amount) if self.doctype == "Purchase Receipt": item.rm_supp_cost = raw_materials_cost
def set_basic_rate(self, force=False, update_finished_item_rate=True): if print_debug: frappe.logger().debug("---radplusplus.manufacturing_controllers.set_basic_rate---") """get stock and incoming rate on posting date""" raw_material_cost = 0.0 scrap_material_cost = 0.0 fg_basic_rate = 0.0 for d in self.get('items'): if d.t_warehouse: fg_basic_rate = flt(d.basic_rate) args = frappe._dict({ "item_code": d.item_code, "warehouse": d.s_warehouse or d.t_warehouse, "posting_date": self.posting_date, "posting_time": self.posting_time, "qty": d.s_warehouse and -1*flt(d.transfer_qty) or flt(d.transfer_qty), "serial_no": d.serial_no, }) # get basic rate if not d.bom_no: if not flt(d.basic_rate) or d.s_warehouse or force: basic_rate = flt(get_incoming_rate(args), self.precision("basic_rate", d)) if basic_rate > 0: d.basic_rate = basic_rate d.basic_amount = flt(flt(d.transfer_qty) * flt(d.basic_rate), d.precision("basic_amount")) if not d.t_warehouse: raw_material_cost += flt(d.basic_amount) # get scrap items basic rate if d.bom_no: if not flt(d.basic_rate) and getattr(self, "pro_doc", frappe._dict()).scrap_warehouse == d.t_warehouse: basic_rate = flt(get_incoming_rate(args), self.precision("basic_rate", d)) if basic_rate > 0: d.basic_rate = basic_rate d.basic_amount = flt(flt(d.transfer_qty) * flt(d.basic_rate), d.precision("basic_amount")) if getattr(self, "pro_doc", frappe._dict()).scrap_warehouse == d.t_warehouse: scrap_material_cost += flt(d.basic_amount) number_of_fg_items = len([t.t_warehouse for t in self.get("items") if t.t_warehouse]) if (fg_basic_rate == 0.0 and number_of_fg_items == 1) or update_finished_item_rate: set_basic_rate_for_finished_goods(self, raw_material_cost, scrap_material_cost)
def set_incoming_rate(self): if self.doctype not in ("Delivery Note", "Sales Invoice", "Sales Order"): return items = self.get("items") + (self.get("packed_items") or []) for d in items: if not cint(self.get("is_return")): # Get incoming rate based on original item cost based on valuation method d.incoming_rate = get_incoming_rate( { "item_code": d.item_code, "warehouse": d.warehouse, "posting_date": self.get('posting_date') or self.get('transaction_date'), "posting_time": self.get('posting_time') or nowtime(), "qty": -1 * flt(d.get('stock_qty') or d.get('actual_qty')), "serial_no": d.get('serial_no'), "company": self.company, "voucher_type": self.doctype, "voucher_no": self.name, "allow_zero_valuation": d.get("allow_zero_valuation") }, raise_error_if_no_rate=False) # For internal transfers use incoming rate as the valuation rate if self.is_internal_transfer(): rate = flt(d.incoming_rate * d.conversion_factor, d.precision('rate')) if d.rate != rate: d.rate = rate d.discount_percentage = 0 d.discount_amount = 0 frappe.msgprint(_( "Row {0}: Item rate has been updated as per valuation rate since its an internal stock transfer" ).format(d.idx), alert=1) elif self.get("return_against"): # Get incoming rate of return entry from reference document # based on original item cost as per valuation method d.incoming_rate = get_rate_for_return(self.doctype, self.name, d.item_code, self.return_against, item_row=d)
def test_stock_reco_for_batch_item(self): set_perpetual_inventory() to_delete_records = [] to_delete_serial_nos = [] # Add new serial nos item_code = "Stock-Reco-batch-Item-1" warehouse = "_Test Warehouse for Stock Reco2 - _TC" sr = create_stock_reconciliation(item_code=item_code, warehouse=warehouse, qty=5, rate=200, do_not_submit=1) sr.save(ignore_permissions=True) sr.submit() self.assertTrue(sr.items[0].batch_no) to_delete_records.append(sr.name) sr1 = create_stock_reconciliation(item_code=item_code, warehouse=warehouse, qty=6, rate=300, batch_no=sr.items[0].batch_no) args = { "item_code": item_code, "warehouse": warehouse, "posting_date": nowdate(), "posting_time": nowtime(), } valuation_rate = get_incoming_rate(args) self.assertEqual(valuation_rate, 300) to_delete_records.append(sr1.name) sr2 = create_stock_reconciliation(item_code=item_code, warehouse=warehouse, qty=0, rate=0, batch_no=sr.items[0].batch_no) stock_value = get_stock_value_on(warehouse, nowdate(), item_code) self.assertEqual(stock_value, 0) to_delete_records.append(sr2.name) to_delete_records.reverse() for d in to_delete_records: stock_doc = frappe.get_doc("Stock Reconciliation", d) stock_doc.cancel() frappe.delete_doc("Batch", sr.items[0].batch_no) for d in to_delete_records: frappe.delete_doc("Stock Reconciliation", d)
def set_basic_rate(self, force=False, update_finished_item_rate=True): """get stock and incoming rate on posting date""" raw_material_cost = 0.0 scrap_material_cost = 0.0 fg_basic_rate = 0.0 for d in self.get('items'): if d.t_warehouse: fg_basic_rate = flt(d.basic_rate) args = frappe._dict({ "item_code": d.item_code, "warehouse": d.s_warehouse or d.t_warehouse, "posting_date": self.posting_date, "posting_time": self.posting_time, "qty": d.s_warehouse and -1*flt(d.transfer_qty) or flt(d.transfer_qty), "serial_no": d.serial_no, }) # get basic rate if not d.bom_no: if not flt(d.basic_rate) or d.s_warehouse or force: basic_rate = flt(get_incoming_rate(args), self.precision("basic_rate", d)) if basic_rate > 0: d.basic_rate = basic_rate d.basic_amount = flt(flt(d.transfer_qty) * flt(d.basic_rate), d.precision("basic_amount")) if not d.t_warehouse: raw_material_cost += flt(d.basic_amount) # get scrap items basic rate if d.bom_no: if not flt(d.basic_rate) and getattr(self, "pro_doc", frappe._dict()).scrap_warehouse == d.t_warehouse: basic_rate = flt(get_incoming_rate(args), self.precision("basic_rate", d)) if basic_rate > 0: d.basic_rate = basic_rate d.basic_amount = flt(flt(d.transfer_qty) * flt(d.basic_rate), d.precision("basic_amount")) if getattr(self, "pro_doc", frappe._dict()).scrap_warehouse == d.t_warehouse: scrap_material_cost += flt(d.basic_amount) number_of_fg_items = len([t.t_warehouse for t in self.get("items") if t.t_warehouse]) if (fg_basic_rate == 0.0 and number_of_fg_items == 1) or update_finished_item_rate: self.set_basic_rate_for_finished_goods(raw_material_cost, scrap_material_cost)
def get_rate_for_return( voucher_type, voucher_no, item_code, return_against=None, item_row=None, voucher_detail_no=None, sle=None, ): if not return_against: return_against = frappe.get_cached_value(voucher_type, voucher_no, "return_against") return_against_item_field = get_return_against_item_fields(voucher_type) filters = get_filters( voucher_type, voucher_no, voucher_detail_no, return_against, item_code, return_against_item_field, item_row, ) if voucher_type in ("Purchase Receipt", "Purchase Invoice"): select_field = "incoming_rate" else: select_field = "abs(stock_value_difference / actual_qty)" rate = flt(frappe.db.get_value("Stock Ledger Entry", filters, select_field)) if not (rate and return_against) and voucher_type in [ "Sales Invoice", "Delivery Note" ]: rate = frappe.db.get_value(f"{voucher_type} Item", voucher_detail_no, "incoming_rate") if not rate and sle: rate = get_incoming_rate( { "item_code": sle.item_code, "warehouse": sle.warehouse, "posting_date": sle.get("posting_date"), "posting_time": sle.get("posting_time"), "qty": sle.actual_qty, "serial_no": sle.get("serial_no"), "company": sle.company, "voucher_type": sle.voucher_type, "voucher_no": sle.voucher_no, }, raise_error_if_no_rate=False, ) return rate
def get_warehouse_details(args): if isinstance(args, basestring): args = json.loads(args) args = frappe._dict(args) ret = {} if args.warehouse and args.item_code: args.update({ "posting_date": args.posting_date, "posting_time": args.posting_time, }) ret = { "actual_qty": get_previous_sle(args).get("qty_after_transaction") or 0, "basic_rate": get_incoming_rate(args), "valuation_rate": get_incoming_rate(args) } return ret
def set_incoming_rate(self): for d in self.items: if d.s_warehouse: args = self.get_args_for_incoming_rate(d) d.basic_rate = get_incoming_rate(args) elif d.allow_zero_valuation_rate and not d.s_warehouse: d.basic_rate = 0.0 elif d.t_warehouse and not d.basic_rate: d.basic_rate = get_valuation_rate(d.item_code, d.t_warehouse, self.doctype, d.name, d.allow_zero_valuation_rate, currency=erpnext.get_company_currency(self.company))
def get_warehouse_details(self, args): ret = {} if args.get("warehouse") and args.get("item_code"): args.update({"posting_date": self.posting_date, "posting_time": self.posting_time}) args = frappe._dict(args) ret = { "actual_qty": get_previous_sle(args).get("qty_after_transaction") or 0, "basic_rate": get_incoming_rate(args), } return ret
def get_average_buying_rate(self, row, item_code): if not item_code in self.average_buying_rate: if item_code in self.non_stock_items: self.average_buying_rate[item_code] = flt( frappe.db.sql( """select sum(base_net_amount) / sum(qty * conversion_factor) from `tabPurchase Invoice Item` where item_code = %s and docstatus=1""", item_code)[0][0]) else: self.average_buying_rate[item_code] = get_incoming_rate(row) return self.average_buying_rate[item_code]
def get_average_buying_rate(self, row, item_code): if not item_code in self.average_buying_rate: if item_code in self.non_stock_items: self.average_buying_rate[item_code] = flt(frappe.db.sql("""select sum(base_net_amount) / sum(qty * conversion_factor) from `tabPurchase Invoice Item` where item_code = %s and docstatus=1""", item_code)[0][0]) else: average_buying_rate = get_incoming_rate(row) if not average_buying_rate: average_buying_rate = get_valuation_rate(item_code, row.warehouse, allow_zero_rate=True) self.average_buying_rate[item_code] = average_buying_rate return self.average_buying_rate[item_code]
def get_average_buying_rate(self, row, item_code): args = row if not item_code in self.average_buying_rate: args.update({ 'voucher_type': row.parenttype, 'voucher_no': row.parent, 'allow_zero_valuation': True, 'company': self.filters.company }) average_buying_rate = get_incoming_rate(args) self.average_buying_rate[item_code] = flt(average_buying_rate) return self.average_buying_rate[item_code]
def get_warehouse_details(self, args): ret = {} if args.get('warehouse') and args.get('item_code'): args.update({ "posting_date": self.posting_date, "posting_time": self.posting_time, }) args = frappe._dict(args) ret = { "actual_qty" : get_previous_sle(args).get("qty_after_transaction") or 0, "basic_rate" : get_incoming_rate(args) } return ret
def get_average_buying_rate(self, row, item_code): args = row if not item_code in self.average_buying_rate: args.update({ "voucher_type": row.parenttype, "voucher_no": row.parent, "allow_zero_valuation": True, "company": self.filters.company, }) average_buying_rate = get_incoming_rate(args) self.average_buying_rate[item_code] = flt(average_buying_rate) return self.average_buying_rate[item_code]
def set_incoming_rate(self): precision = cint(frappe.db.get_default("float_precision")) for d in self.items: if d.source_warehouse: args = self.get_args_for_incoming_rate(d) d.basic_rate = flt(get_incoming_rate(args), precision) elif not d.source_warehouse: d.basic_rate = 0.0 elif self.warehouse and not d.basic_rate: d.basic_rate = flt(get_valuation_rate(d.item_code, self.warehouse, self.doctype, d.name, 1, currency=erpnext.get_company_currency(self.company)), precision) d.basic_amount = d.basic_rate * d.qty
def get_warehouse_details(args): if isinstance(args, basestring): args = json.loads(args) args = frappe._dict(args) ret = {} if args.warehouse and args.item_code: args.update({"posting_date": args.posting_date, "posting_time": args.posting_time}) ret = { "actual_qty": get_previous_sle(args).get("qty_after_transaction") or 0, "basic_rate": get_incoming_rate(args), } return ret
def set_incoming_rate(self): for d in self.items: if d.source_warehouse: args = self.get_args_for_incoming_rate(d) d.basic_rate = get_incoming_rate(args) elif not d.source_warehouse: d.basic_rate = 0.0 elif self.warehouse and not d.basic_rate: d.basic_rate = get_valuation_rate( d.item_code, self.warehouse, self.doctype, d.name, 1, currency=erpnext.get_company_currency(self.company)) d.basic_amount = d.basic_rate * d.quantity
def get_stock_and_rate(self, force=False): """get stock and incoming rate on posting date""" raw_material_cost = 0.0 if not self.posting_date or not self.posting_time: frappe.throw(_("Posting date and posting time is mandatory")) allow_negative_stock = cint(frappe.db.get_value("Stock Settings", None, "allow_negative_stock")) for d in self.get('items'): d.transfer_qty = flt(d.transfer_qty) args = frappe._dict({ "item_code": d.item_code, "warehouse": d.s_warehouse or d.t_warehouse, "posting_date": self.posting_date, "posting_time": self.posting_time, "qty": d.s_warehouse and -1*d.transfer_qty or d.transfer_qty, "serial_no": d.serial_no, }) # get actual stock at source warehouse d.actual_qty = get_previous_sle(args).get("qty_after_transaction") or 0 # validate qty during submit if d.docstatus==1 and d.s_warehouse and not allow_negative_stock and d.actual_qty < d.transfer_qty: frappe.throw(_("""Row {0}: Qty not avalable in warehouse {1} on {2} {3}. Available Qty: {4}, Transfer Qty: {5}""").format(d.idx, d.s_warehouse, self.posting_date, self.posting_time, d.actual_qty, d.transfer_qty), NegativeStockError) # get incoming rate if not d.bom_no: if not flt(d.incoming_rate) or d.s_warehouse or force: incoming_rate = flt(get_incoming_rate(args), self.precision("incoming_rate", d)) if incoming_rate > 0: d.incoming_rate = incoming_rate d.amount = flt(flt(d.transfer_qty) * flt(d.incoming_rate), d.precision("amount")) if not d.t_warehouse: raw_material_cost += flt(d.amount) self.add_operation_cost(raw_material_cost, force)
def get_average_buying_rate(self, row, item_code): args = row if not item_code in self.average_buying_rate: if item_code in self.non_stock_items: self.average_buying_rate[item_code] = flt(frappe.db.sql(""" select sum(base_net_amount) / sum(qty * conversion_factor) from `tabPurchase Invoice Item` where item_code = %s and docstatus=1""", item_code)[0][0]) else: args.update({ 'voucher_type': row.parenttype, 'voucher_no': row.parent, 'allow_zero_valuation': True, 'company': self.filters.company }) average_buying_rate = get_incoming_rate(args) self.average_buying_rate[item_code] = flt(average_buying_rate) return self.average_buying_rate[item_code]
def get_valuation_rate(self, args): """ Get average valuation rate of relevant warehouses as per valuation method (MAR/FIFO) as on costing date """ from erpnext.stock.utils import get_incoming_rate posting_date, posting_time = nowdate(), now().split()[1] warehouse = frappe.db.sql("select warehouse from `tabBin` where item_code = %s", args['item_code']) rate = [] for wh in warehouse: r = get_incoming_rate({ "item_code": args.get("item_code"), "warehouse": wh[0], "posting_date": posting_date, "posting_time": posting_time, "qty": args.get("qty") or 0 }) if r: rate.append(r) return rate and flt(sum(rate))/len(rate) or 0
def update_raw_materials_supplied_based_on_bom(self, item, raw_material_table): exploded_item = 1 if hasattr(item, 'include_exploded_items'): exploded_item = item.get('include_exploded_items') bom_items = get_items_from_bom(item.item_code, item.bom, exploded_item) used_alternative_items = [] if self.doctype == 'Purchase Receipt' and item.purchase_order: used_alternative_items = get_used_alternative_items(purchase_order = item.purchase_order) raw_materials_cost = 0 items = list(set([d.item_code for d in bom_items])) item_wh = frappe._dict(frappe.db.sql("""select i.item_code, id.default_warehouse from `tabItem` i, `tabItem Default` id where id.parent=i.name and id.company=%s and i.name in ({0})""" .format(", ".join(["%s"] * len(items))), [self.company] + items)) for bom_item in bom_items: if self.doctype == "Purchase Order": reserve_warehouse = bom_item.source_warehouse or item_wh.get(bom_item.item_code) if frappe.db.get_value("Warehouse", reserve_warehouse, "company") != self.company: reserve_warehouse = None conversion_factor = item.conversion_factor if (self.doctype == 'Purchase Receipt' and item.purchase_order and bom_item.item_code in used_alternative_items): alternative_item_data = used_alternative_items.get(bom_item.item_code) bom_item.item_code = alternative_item_data.item_code bom_item.item_name = alternative_item_data.item_name bom_item.stock_uom = alternative_item_data.stock_uom conversion_factor = alternative_item_data.conversion_factor bom_item.description = alternative_item_data.description # check if exists exists = 0 for d in self.get(raw_material_table): if d.main_item_code == item.item_code and d.rm_item_code == bom_item.item_code \ and d.reference_name == item.name: rm, exists = d, 1 break if not exists: rm = self.append(raw_material_table, {}) required_qty = flt(flt(bom_item.qty_consumed_per_unit) * (flt(item.qty) + getattr(item, 'rejected_qty', 0)) * flt(conversion_factor), rm.precision("required_qty")) rm.reference_name = item.name rm.bom_detail_no = bom_item.name rm.main_item_code = item.item_code rm.rm_item_code = bom_item.item_code rm.stock_uom = bom_item.stock_uom rm.required_qty = required_qty if self.doctype == "Purchase Order" and not rm.reserve_warehouse: rm.reserve_warehouse = reserve_warehouse rm.conversion_factor = conversion_factor if self.doctype in ["Purchase Receipt", "Purchase Invoice"]: rm.consumed_qty = required_qty rm.description = bom_item.description if item.batch_no and not rm.batch_no: rm.batch_no = item.batch_no # get raw materials rate if self.doctype == "Purchase Receipt": from erpnext.stock.utils import get_incoming_rate rm.rate = get_incoming_rate({ "item_code": bom_item.item_code, "warehouse": self.supplier_warehouse, "posting_date": self.posting_date, "posting_time": self.posting_time, "qty": -1 * required_qty, "serial_no": rm.serial_no }) if not rm.rate: rm.rate = get_valuation_rate(bom_item.item_code, self.supplier_warehouse, self.doctype, self.name, currency=self.company_currency, company = self.company) else: rm.rate = bom_item.rate rm.amount = required_qty * flt(rm.rate) raw_materials_cost += flt(rm.amount) if self.doctype in ("Purchase Receipt", "Purchase Invoice"): item.rm_supp_cost = raw_materials_cost
def update_raw_materials_supplied(self, item, raw_material_table): bom_items = self.get_items_from_bom(item.item_code, item.bom) raw_materials_cost = 0 items = list(set([d.item_code for d in bom_items])) item_wh = frappe._dict(frappe.db.sql("""select item_code, default_warehouse from `tabItem` where name in ({0})""".format(", ".join(["%s"] * len(items))), items)) for bom_item in bom_items: if self.doctype == "Purchase Order": reserve_warehouse = bom_item.source_warehouse or item_wh.get(bom_item.item_code) if frappe.db.get_value("Warehouse", reserve_warehouse, "company") != self.company: reserve_warehouse = None # check if exists exists = 0 for d in self.get(raw_material_table): if d.main_item_code == item.item_code and d.rm_item_code == bom_item.item_code \ and d.reference_name == item.name: rm, exists = d, 1 break if not exists: rm = self.append(raw_material_table, {}) required_qty = flt(flt(bom_item.qty_consumed_per_unit) * flt(item.qty) * flt(item.conversion_factor), rm.precision("required_qty")) rm.reference_name = item.name rm.bom_detail_no = bom_item.name rm.main_item_code = item.item_code rm.rm_item_code = bom_item.item_code rm.stock_uom = bom_item.stock_uom rm.required_qty = required_qty if self.doctype == "Purchase Order" and not rm.reserve_warehouse: rm.reserve_warehouse = reserve_warehouse rm.conversion_factor = item.conversion_factor if self.doctype in ["Purchase Receipt", "Purchase Invoice"]: rm.consumed_qty = required_qty rm.description = bom_item.description if item.batch_no and not rm.batch_no: rm.batch_no = item.batch_no # get raw materials rate if self.doctype == "Purchase Receipt": from erpnext.stock.utils import get_incoming_rate rm.rate = get_incoming_rate({ "item_code": bom_item.item_code, "warehouse": self.supplier_warehouse, "posting_date": self.posting_date, "posting_time": self.posting_time, "qty": -1 * required_qty, "serial_no": rm.serial_no }) if not rm.rate: rm.rate = get_valuation_rate(bom_item.item_code, self.supplier_warehouse, self.doctype, self.name, currency=self.company_currency, company = self.company) else: rm.rate = bom_item.rate rm.amount = required_qty * flt(rm.rate) raw_materials_cost += flt(rm.amount) if self.doctype in ("Purchase Receipt", "Purchase Invoice"): item.rm_supp_cost = raw_materials_cost