def update_stock_ledger(self): self.update_reserved_qty() sl_entries = [] # Loop over items and packed items table for d in self.get_item_list(): if frappe.get_cached_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 # On cancellation or 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_sle_for_source_warehouse(d)) if d.target_warehouse: sl_entries.append(self.get_sle_for_target_warehouse(d)) 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_sle_for_source_warehouse(d)) self.make_sl_entries(sl_entries)
def get_price_list_rate_general(args, item_code, variant_template_code=None): item_price_args = { "item_code": item_code, "price_list": args.get('price_list'), "uom": "", "transaction_date": args.get('transaction_date'), } if variant_template_code: # frappe.errprint("variant set"+ str(variant_template_code)) general_price_list_rate = get_item_price(item_price_args, item_code) if not general_price_list_rate: general_price_list_rate = get_item_price(item_price_args, variant_template_code) else: general_price_list_rate = get_item_price(item_price_args, item_code) # frappe.errprint("best matches"+str(general_price_list_rate)) item_price_data = None conversion_factor = None for price_entry in general_price_list_rate: conversion_rate = get_conversion_factor( item_code, price_entry[2])["conversion_factor"] # frappe.errprint("********"+str(conversion_rate)+" " + str(price_entry)) if conversion_rate: conversion_factor = conversion_rate item_price_data = [price_entry] break # frappe.errprint("4" + item_code + " " + str(general_price_list_rate) + str(conversion_factor)) if item_price_data: # found uom -> stock uom -> uom return flt(item_price_data[0][1] * (1 / flt(conversion_factor)) * flt(args.get("conversion_factor", 1)))
def filter_pricing_rules_for_qty_amount(qty, rate, pricing_rules, args=None): rules = [] for rule in pricing_rules: status = False conversion_factor = 1 if rule.get("uom"): conversion_factor = get_conversion_factor( rule.item_code, rule.uom).get("conversion_factor", 1) if (flt(qty) >= (flt(rule.min_qty) * conversion_factor) and (flt(qty) <= (rule.max_qty * conversion_factor) if rule.max_qty else True)): status = True # if user has created item price against the transaction UOM if args and rule.get("uom") == args.get("uom"): conversion_factor = 1.0 if status and ( flt(rate) >= (flt(rule.min_amt) * conversion_factor) and (flt(rate) <= (rule.max_amt * conversion_factor) if rule.max_amt else True)): status = True else: status = False if status: rules.append(rule) return rules
def get_item_details(item_code, uom=None): details = frappe.db.get_value('Item', item_code, ['stock_uom', 'name'], as_dict=1) details.uom = uom or details.stock_uom if uom: details.update(get_conversion_factor(item_code, uom)) return details
def get_product_discount_rule(pricing_rule, item_details, args=None, doc=None): free_item = pricing_rule.free_item if pricing_rule.same_item and pricing_rule.get("apply_on") != 'Transaction': free_item = item_details.item_code or args.item_code if not free_item: frappe.throw(_("Free item not set in the pricing rule {0}") .format(get_link_to_form("Pricing Rule", pricing_rule.name))) item_details.free_item_data = { 'item_code': free_item, 'qty': pricing_rule.free_qty or 1, 'rate': pricing_rule.free_item_rate or 0, 'price_list_rate': pricing_rule.free_item_rate or 0, 'is_free_item': 1 } item_data = frappe.get_cached_value('Item', free_item, ['item_name', 'description', 'stock_uom'], as_dict=1) item_details.free_item_data.update(item_data) item_details.free_item_data['uom'] = pricing_rule.free_item_uom or item_data.stock_uom item_details.free_item_data['conversion_factor'] = get_conversion_factor(free_item, item_details.free_item_data['uom']).get("conversion_factor", 1) if item_details.get("parenttype") == 'Purchase Order': item_details.free_item_data['schedule_date'] = doc.schedule_date if doc else today() if item_details.get("parenttype") == 'Sales Order': item_details.free_item_data['delivery_date'] = doc.delivery_date if doc else today()
def update_valuation_rate(self, reset_outgoing_rate=True): """ item_tax_amount is the total tax amount applied on that item stored for valuation TODO: rename item_tax_amount to valuation_tax_amount """ stock_and_asset_items = [] stock_and_asset_items = self.get_stock_items() + self.get_asset_items() stock_and_asset_items_qty, stock_and_asset_items_amount = 0, 0 last_item_idx = 1 for d in self.get("items"): if d.item_code and d.item_code in stock_and_asset_items: stock_and_asset_items_qty += flt(d.qty) stock_and_asset_items_amount += flt(d.base_net_amount) last_item_idx = d.idx total_valuation_amount = sum( flt(d.base_tax_amount_after_discount_amount) for d in self.get("taxes") if d.category in ["Valuation", "Valuation and Total"] ) valuation_amount_adjustment = total_valuation_amount for i, item in enumerate(self.get("items")): if item.item_code and item.qty and item.item_code in stock_and_asset_items: item_proportion = ( flt(item.base_net_amount) / stock_and_asset_items_amount if stock_and_asset_items_amount else flt(item.qty) / stock_and_asset_items_qty ) if i == (last_item_idx - 1): item.item_tax_amount = flt( valuation_amount_adjustment, self.precision("item_tax_amount", item) ) else: item.item_tax_amount = flt( item_proportion * total_valuation_amount, self.precision("item_tax_amount", item) ) valuation_amount_adjustment -= item.item_tax_amount self.round_floats_in(item) if flt(item.conversion_factor) == 0.0: item.conversion_factor = ( get_conversion_factor(item.item_code, item.uom).get("conversion_factor") or 1.0 ) qty_in_stock_uom = flt(item.qty * item.conversion_factor) item.rm_supp_cost = self.get_supplied_items_cost(item.name, reset_outgoing_rate) item.valuation_rate = ( item.base_net_amount + item.item_tax_amount + item.rm_supp_cost + flt(item.landed_cost_voucher_amount) ) / qty_in_stock_uom else: item.valuation_rate = 0.0
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 update_valuation_rate(self, parentfield): """ item_tax_amount is the total tax amount applied on that item stored for valuation TODO: rename item_tax_amount to valuation_tax_amount """ stock_items = self.get_stock_items() stock_items_qty, stock_items_amount = 0, 0 last_stock_item_idx = 1 for d in self.get(parentfield): if d.item_code and d.item_code in stock_items: stock_items_qty += flt(d.qty) stock_items_amount += flt(d.base_net_amount) last_stock_item_idx = d.idx total_valuation_amount = sum([ flt(d.base_tax_amount_after_discount_amount) for d in self.get("taxes") if d.category in ["Valuation", "Valuation and Total"] ]) valuation_amount_adjustment = total_valuation_amount for i, item in enumerate(self.get(parentfield)): if item.item_code and item.qty and item.item_code in stock_items: item_proportion = flt(item.base_net_amount) / stock_items_amount if stock_items_amount \ else flt(item.qty) / stock_items_qty if i == (last_stock_item_idx - 1): item.item_tax_amount = flt( valuation_amount_adjustment, self.precision("item_tax_amount", item)) else: item.item_tax_amount = flt( item_proportion * total_valuation_amount, self.precision("item_tax_amount", item)) valuation_amount_adjustment -= item.item_tax_amount self.round_floats_in(item) if flt(item.conversion_factor) == 0: item.conversion_factor = get_conversion_factor( item.item_code, item.uom).get("conversion_factor") or 1.0 qty_in_stock_uom = flt(item.qty * item.conversion_factor) rm_supp_cost = flt( item.rm_supp_cost ) if self.doctype == "Purchase Receipt" else 0.0 landed_cost_voucher_amount = flt(item.landed_cost_voucher_amount) \ if self.doctype == "Purchase Receipt" else 0.0 item.valuation_rate = ( (item.base_net_amount + item.item_tax_amount + rm_supp_cost + landed_cost_voucher_amount) / qty_in_stock_uom) else: item.valuation_rate = 0.0
def update_stock_qty(self): for m in self.get('items'): if not m.conversion_factor: m.conversion_factor = flt(get_conversion_factor(m.item_code, m.uom)['conversion_factor']) if m.uom and m.qty: m.stock_qty = flt(m.conversion_factor)*flt(m.qty) if not m.uom and m.stock_uom: m.uom = m.stock_uom m.qty = m.stock_qty
def get_uom_details(self, args): conversion_factor = get_conversion_factor(args.get("item_code"), args.get("uom")).get("conversion_factor") if not conversion_factor: frappe.msgprint(_("UOM coversion factor required for UOM: {0} in Item: {1}") .format(args.get("uom"), args.get("item_code"))) ret = {'uom' : ''} else: ret = { 'conversion_factor' : flt(conversion_factor), 'transfer_qty' : flt(args.get("qty")) * flt(conversion_factor) } return ret
def set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, item_code): """ Returns a Sales Order Item child item containing the default values """ p_doctype = frappe.get_doc(parent_doctype, parent_doctype_name) child_item = frappe.new_doc('Sales Order Item', p_doctype, child_docname) item = frappe.get_doc("Item", item_code) child_item.item_code = item.item_code child_item.item_name = item.item_name child_item.description = item.description child_item.reqd_by_date = p_doctype.delivery_date child_item.uom = item.stock_uom child_item.conversion_factor = get_conversion_factor(item_code, item.stock_uom).get("conversion_factor") or 1.0 return child_item
def set_collect_production_item_defaults(parent_doctype, parent_doctype_name, child_docname, item_code): """ Returns a Collect Production Item Materials containing the default values """ p_doctype = frappe.get_doc(parent_doctype, parent_doctype_name) child_item = frappe.new_doc('Collect Production Item Materials', p_doctype, child_docname) item = frappe.get_doc("Item", item_code) child_item.item_code = item.item_code child_item.item_name = item.item_name child_item.description = item.description child_item.uom = item.stock_uom child_item.stock_uom = item.stock_uom child_item.rate = 0 child_item.conversion_factor = get_conversion_factor(item_code, item.stock_uom).get("conversion_factor") or 1.0 return child_item
def get_uom_details(item_code, uom): """Returns dict `{"conversion_factor": [value], "transfer_qty": qty * [value]}` :param args: dict with `item_code`, `uom` and `qty`""" conversion_factor = get_conversion_factor(item_code, uom).get("conversion_factor") if not conversion_factor: frappe.msgprint( _("UOM coversion factor required for UOM: {0} in Item: {1}"). format(uom, item_code)) ret = {'transfer_uom': ''} else: ret = {'conversion_factor': flt(conversion_factor)} return ret
def update_valuation_rate(self, parentfield): """ item_tax_amount is the total tax amount applied on that item stored for valuation TODO: rename item_tax_amount to valuation_tax_amount """ stock_items = self.get_stock_items() stock_items_qty, stock_items_amount = 0, 0 last_stock_item_idx = 1 for d in self.get(parentfield): if d.item_code and d.item_code in stock_items: stock_items_qty += flt(d.qty) stock_items_amount += flt(d.base_net_amount) last_stock_item_idx = d.idx total_valuation_amount = sum([flt(d.base_tax_amount_after_discount_amount) for d in self.get("taxes") if d.category in ["Valuation", "Valuation and Total"]]) valuation_amount_adjustment = total_valuation_amount for i, item in enumerate(self.get(parentfield)): if item.item_code and item.qty and item.item_code in stock_items: item_proportion = flt(item.base_net_amount) / stock_items_amount if stock_items_amount \ else flt(item.qty) / stock_items_qty if i == (last_stock_item_idx - 1): item.item_tax_amount = flt(valuation_amount_adjustment, self.precision("item_tax_amount", item)) else: item.item_tax_amount = flt(item_proportion * total_valuation_amount, self.precision("item_tax_amount", item)) valuation_amount_adjustment -= item.item_tax_amount self.round_floats_in(item) item.conversion_factor = get_conversion_factor(item.item_code, item.uom).get("conversion_factor") or 1.0 qty_in_stock_uom = flt(item.qty * item.conversion_factor) rm_supp_cost = flt(item.rm_supp_cost) if self.doctype=="Purchase Receipt" else 0.0 landed_cost_voucher_amount = flt(item.landed_cost_voucher_amount) \ if self.doctype == "Purchase Receipt" else 0.0 item.valuation_rate = ((item.base_net_amount + item.item_tax_amount + rm_supp_cost + landed_cost_voucher_amount) / qty_in_stock_uom) else: item.valuation_rate = 0.0
def calculate_total_qty(self): from erpnext.stock.get_item_details import get_conversion_factor total_qty = 0 qty_txt = "" item_dict = {} import copy if self.get("items"): new_list = copy.deepcopy(self.get("items")) for item in new_list: uom = item.uom qty = item.qty if self.stock_uom == uom: if item_dict.has_key(uom): item_dict[uom].qty += qty else: item_dict[uom] = item else: conversion_factor = get_conversion_factor(self.item_code, uom).get("conversion_factor") or 1 if conversion_factor == 1: if item_dict.has_key(uom): item_dict[uom].qty += qty else: item_dict[uom] = item else: conversion_factor = flt(1/conversion_factor) new_qty = flt(qty) / flt(conversion_factor) if item_dict.has_key(self.stock_uom): item_dict[self.stock_uom].qty += new_qty else: item_dict[self.stock_uom] = item item_dict[self.stock_uom].qty = new_qty for key in item_dict: d = item_dict[key] display_uom = str(key) display_qty = str(d.qty) qty_txt += str(display_qty) + ' ' + str(display_uom) + '<br>' total_qty = total_qty + flt(d.qty) else: qty_txt = "0" self.total_qty = qty_txt
def get_uom_details(item_code, uom, qty): """Returns dict `{"conversion_factor": [value], "transfer_qty": qty * [value]}` :param args: dict with `item_code`, `uom` and `qty`""" conversion_factor = get_conversion_factor(item_code, uom).get("conversion_factor") if not conversion_factor: frappe.msgprint(_("UOM coversion factor required for UOM: {0} in Item: {1}") .format(uom, item_code)) ret = {'uom' : ''} else: ret = { 'conversion_factor' : flt(conversion_factor), 'transfer_qty' : flt(qty) * flt(conversion_factor) } return ret
def set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docname, item_code): """ Returns a Purchase Order Item child item containing the default values """ p_doctype = frappe.get_doc(parent_doctype, parent_doctype_name) child_item = frappe.new_doc('Purchase Order Item', p_doctype, child_docname) item = frappe.get_doc("Item", item_code) child_item.item_code = item.item_code child_item.item_name = item.item_name child_item.description = item.description child_item.schedule_date = p_doctype.schedule_date child_item.uom = item.stock_uom child_item.conversion_factor = get_conversion_factor(item_code, item.stock_uom).get("conversion_factor") or 1.0 child_item.base_rate = 1 # Initiallize value will update in parent validation child_item.base_amount = 1 # Initiallize value will update in parent validation return child_item
def change_item_code(sales_order, so_detail, new_item_code, old_item_code): from erpnext.api2 import customerPriceList from erpnext.stock.get_item_details import get_conversion_factor doc = frappe.get_doc("Sales Order", sales_order) row = filter(lambda d: d.name == so_detail, doc.items) if not row: frappe.throw( _("Could not find Item {0} in {1}").format(old_item_code, sales_order)) else: row = row[0] rate = flt( customerPriceList(row.idx, row.idx, doc.customer, new_item_code, doc.selling_price_list)[0].get('price')) item_details = frappe.db.get_value( "Item", new_item_code, [ "item_name", "description", "sale_pallets", "gross_weight", "sales_uom", "stock_uom", "hst", "weight_per_unit", "weight_uom", "cost_center" ], as_dict=1) row.update(item_details) row.item_code = new_item_code row.rate = rate row.uom = item_details.sales_uom or item_details.stock_uom row.conversion_factor = get_conversion_factor(new_item_code, row.uom)['conversion_factor'] row.weight_lbs = item_details.weight_per_unit row.gross_weight_lbs = item_details.gross_weight row.weight_kgs = flt(item_details.weight_per_unit * 0.45359237, 2) row.hst = 1 if item_details.hst == "Yes" else 0 doc.total_boxes = 0 doc.total_pallets = 0 doc.total_gross_weight_lbs = 0 for d in doc.items: doc.total_boxes += flt(d.qty) doc.total_gross_weight_lbs += flt(d.qty) * flt(d.gross_weight_lbs) if flt(d.sale_pallets) > 0: doc.total_pallets += flt(d.qty) / flt(d.sale_pallets) doc.total_weight_kg = flt(doc.total_gross_weight_lbs * 0.45359237, 2) doc.save()
def get_product_discount_rule(pricing_rule, item_details, args=None, doc=None): free_item = pricing_rule.free_item if pricing_rule.same_item and pricing_rule.get( "apply_on") != "Transaction": free_item = item_details.item_code or args.item_code if not free_item: frappe.throw( _("Free item not set in the pricing rule {0}").format( get_link_to_form("Pricing Rule", pricing_rule.name))) qty = pricing_rule.free_qty or 1 if pricing_rule.is_recursive: transaction_qty = args.get("qty") if args else doc.total_qty if transaction_qty: qty = flt(transaction_qty) * qty free_item_data_args = { "item_code": free_item, "qty": qty, "pricing_rules": pricing_rule.name, "rate": pricing_rule.free_item_rate or 0, "price_list_rate": pricing_rule.free_item_rate or 0, "is_free_item": 1, } item_data = frappe.get_cached_value( "Item", free_item, ["item_name", "description", "stock_uom"], as_dict=1) free_item_data_args.update(item_data) free_item_data_args[ "uom"] = pricing_rule.free_item_uom or item_data.stock_uom free_item_data_args["conversion_factor"] = get_conversion_factor( free_item, free_item_data_args["uom"]).get("conversion_factor", 1) if item_details.get("parenttype") == "Purchase Order": free_item_data_args[ "schedule_date"] = doc.schedule_date if doc else today() if item_details.get("parenttype") == "Sales Order": free_item_data_args[ "delivery_date"] = doc.delivery_date if doc else today() item_details.free_item_data.append(free_item_data_args)
def set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, trans_item): """ Returns a Sales Order Item child item containing the default values """ p_doc = frappe.get_doc(parent_doctype, parent_doctype_name) child_item = frappe.new_doc('Sales Order Item', p_doc, child_docname) item = frappe.get_doc("Item", trans_item.get('item_code')) child_item.item_code = item.item_code child_item.item_name = item.item_name child_item.description = item.description child_item.delivery_date = trans_item.get('delivery_date') or p_doc.delivery_date child_item.uom = item.stock_uom child_item.conversion_factor = get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0 child_item.warehouse = get_item_warehouse(item, p_doc, overwrite_warehouse=True) if not child_item.warehouse: frappe.throw(_("Cannot find {} for item {}. Please set the same in Item Master or Stock Settings.") .format(frappe.bold("default warehouse"), frappe.bold(item.item_code))) return child_item
def create_putaway_rule(**args): args = frappe._dict(args) putaway = frappe.new_doc("Putaway Rule") putaway.disable = args.disable or 0 putaway.company = args.company or "_Test Company" putaway.item_code = args.item or args.item_code or "_Test Item" putaway.warehouse = args.warehouse putaway.priority = args.priority or 1 putaway.capacity = args.capacity or 1 putaway.stock_uom = frappe.db.get_value("Item", putaway.item_code, "stock_uom") putaway.uom = args.uom or putaway.stock_uom putaway.conversion_factor = get_conversion_factor(putaway.item_code, putaway.uom)['conversion_factor'] if not args.do_not_save: putaway.save() return putaway
def get_uom_details(self, args): """Returns dict `{"conversion_factor": [value], "transfer_qty": qty * [value]}` :param args: dict with `item_code`, `uom` and `qty`""" conversion_factor = get_conversion_factor(args.get("item_code"), args.get("uom")).get("conversion_factor") if not conversion_factor: frappe.msgprint( _("UOM coversion factor required for UOM: {0} in Item: {1}").format( args.get("uom"), args.get("item_code") ) ) ret = {"uom": ""} else: ret = { "conversion_factor": flt(conversion_factor), "transfer_qty": flt(args.get("qty")) * flt(conversion_factor), } return ret
def update_item(obj, target, source_parent): item = frappe.get_doc("Item", obj.item_code) uom = item.purchase_uom if item.purchase_uom else item.stock_uom if uom != obj.uom: from erpnext.stock.get_item_details import get_conversion_factor conversion_factor = flt( get_conversion_factor(obj.item_code, uom)['conversion_factor']) if conversion_factor == 0: frappe.throw( """Debe completar el apartado 'Unidades de medida' del producto <a href='#Form/Item/{0}' target='_blank'>{1}</a>, ya que está tratando de convertir de {2} a {3} sin especificar el factor de conversión. <br><br>-> <a class='btn btn-warning' href='#Form/Item/{0}' target='_blank'>Abrir {1}</a> <a class='btn btn-default' href='https://docs.google.com/document/d/1pHBYzlVx7vL9XSLt3o0cAyqsoycF3ZE5Zr6vUTyR4Dc/edit?usp=sharing' target='_blank'>Manual de ayuda</a>""" .format(item.item_code, item.item_code, uom, item.stock_uom)) target.uom = uom target.qty = (flt(obj.qty) / conversion_factor) - ( flt(obj.ordered_qty) / conversion_factor) target.stock_qty = target.qty * conversion_factor target.conversion_factor = conversion_factor else: target.conversion_factor = 1 target.qty = flt(obj.qty) - flt(obj.ordered_qty) target.stock_qty = target.qty
def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, child_docname="items"): def check_doc_permissions(doc, perm_type="create"): try: doc.check_permission(perm_type) except frappe.PermissionError: actions = {"create": "add", "write": "update"} frappe.throw( _("You do not have permissions to {} items in a {}.").format( actions[perm_type], parent_doctype), title=_("Insufficient Permissions"), ) def validate_workflow_conditions(doc): workflow = get_workflow_name(doc.doctype) if not workflow: return workflow_doc = frappe.get_doc("Workflow", workflow) current_state = doc.get(workflow_doc.workflow_state_field) roles = frappe.get_roles() transitions = [] for transition in workflow_doc.transitions: if transition.next_state == current_state and transition.allowed in roles: if not is_transition_condition_satisfied(transition, doc): continue transitions.append(transition.as_dict()) if not transitions: frappe.throw( _("You are not allowed to update as per the conditions set in {} Workflow." ).format(get_link_to_form("Workflow", workflow)), title=_("Insufficient Permissions"), ) def get_new_child_item(item_row): child_doctype = "Sales Order Item" if parent_doctype == "Sales Order" else "Purchase Order Item" return set_order_defaults(parent_doctype, parent_doctype_name, child_doctype, child_docname, item_row) def validate_quantity(child_item, d): if parent_doctype == "Sales Order" and flt(d.get("qty")) < flt( child_item.delivered_qty): frappe.throw(_("Cannot set quantity less than delivered quantity")) if parent_doctype == "Purchase Order" and flt(d.get("qty")) < flt( child_item.received_qty): frappe.throw(_("Cannot set quantity less than received quantity")) data = json.loads(trans_items) sales_doctypes = [ "Sales Order", "Sales Invoice", "Delivery Note", "Quotation" ] parent = frappe.get_doc(parent_doctype, parent_doctype_name) check_doc_permissions(parent, "write") validate_and_delete_children(parent, data) for d in data: new_child_flag = False if not d.get("item_code"): # ignore empty rows continue if not d.get("docname"): new_child_flag = True check_doc_permissions(parent, "create") child_item = get_new_child_item(d) else: check_doc_permissions(parent, "write") child_item = frappe.get_doc(parent_doctype + " Item", d.get("docname")) prev_rate, new_rate = flt(child_item.get("rate")), flt( d.get("rate")) prev_qty, new_qty = flt(child_item.get("qty")), flt(d.get("qty")) prev_con_fac, new_con_fac = flt( child_item.get("conversion_factor")), flt( d.get("conversion_factor")) prev_uom, new_uom = child_item.get("uom"), d.get("uom") if parent_doctype == "Sales Order": prev_date, new_date = child_item.get("delivery_date"), d.get( "delivery_date") elif parent_doctype == "Purchase Order": prev_date, new_date = child_item.get("schedule_date"), d.get( "schedule_date") rate_unchanged = prev_rate == new_rate qty_unchanged = prev_qty == new_qty uom_unchanged = prev_uom == new_uom conversion_factor_unchanged = prev_con_fac == new_con_fac date_unchanged = (prev_date == getdate(new_date) if prev_date and new_date else False ) # in case of delivery note etc if (rate_unchanged and qty_unchanged and conversion_factor_unchanged and uom_unchanged and date_unchanged): continue validate_quantity(child_item, d) child_item.qty = flt(d.get("qty")) rate_precision = child_item.precision("rate") or 2 conv_fac_precision = child_item.precision("conversion_factor") or 2 qty_precision = child_item.precision("qty") or 2 if flt(child_item.billed_amt, rate_precision) > flt( flt(d.get("rate"), rate_precision) * flt(d.get("qty"), qty_precision), rate_precision): frappe.throw( _("Row #{0}: Cannot set Rate if amount is greater than billed amount for Item {1}." ).format(child_item.idx, child_item.item_code)) else: child_item.rate = flt(d.get("rate"), rate_precision) if d.get("conversion_factor"): if child_item.stock_uom == child_item.uom: child_item.conversion_factor = 1 else: child_item.conversion_factor = flt(d.get("conversion_factor"), conv_fac_precision) if d.get("uom"): child_item.uom = d.get("uom") conversion_factor = flt( get_conversion_factor(child_item.item_code, child_item.uom).get("conversion_factor")) child_item.conversion_factor = (flt(d.get("conversion_factor"), conv_fac_precision) or conversion_factor) if d.get("delivery_date") and parent_doctype == "Sales Order": child_item.delivery_date = d.get("delivery_date") if d.get("schedule_date") and parent_doctype == "Purchase Order": child_item.schedule_date = d.get("schedule_date") if flt(child_item.price_list_rate): if flt(child_item.rate) > flt(child_item.price_list_rate): # if rate is greater than price_list_rate, set margin # or set discount child_item.discount_percentage = 0 if parent_doctype in sales_doctypes: child_item.margin_type = "Amount" child_item.margin_rate_or_amount = flt( child_item.rate - child_item.price_list_rate, child_item.precision("margin_rate_or_amount")) child_item.rate_with_margin = child_item.rate else: child_item.discount_percentage = flt( (1 - flt(child_item.rate) / flt(child_item.price_list_rate)) * 100.0, child_item.precision("discount_percentage"), ) child_item.discount_amount = flt( child_item.price_list_rate) - flt(child_item.rate) if parent_doctype in sales_doctypes: child_item.margin_type = "" child_item.margin_rate_or_amount = 0 child_item.rate_with_margin = 0 child_item.flags.ignore_validate_update_after_submit = True if new_child_flag: parent.load_from_db() child_item.idx = len(parent.items) + 1 child_item.insert() else: child_item.save() parent.reload() parent.flags.ignore_validate_update_after_submit = True parent.set_qty_as_per_stock_uom() parent.calculate_taxes_and_totals() parent.set_total_in_words() if parent_doctype == "Sales Order": make_packing_list(parent) parent.set_gross_profit() frappe.get_doc("Authorization Control").validate_approving_authority( parent.doctype, parent.company, parent.base_grand_total) parent.set_payment_schedule() if parent_doctype == "Purchase Order": parent.validate_minimum_order_qty() parent.validate_budget() if parent.is_against_so(): parent.update_status_updater() else: parent.check_credit_limit() # reset index of child table for idx, row in enumerate(parent.get(child_docname), start=1): row.idx = idx parent.save() if parent_doctype == "Purchase Order": update_last_purchase_rate(parent, is_submit=1) parent.update_prevdoc_status() parent.update_requested_qty() parent.update_ordered_qty() parent.update_ordered_and_reserved_qty() parent.update_receiving_percentage() if parent.is_subcontracted == "Yes": parent.update_reserved_qty_for_subcontract() parent.create_raw_materials_supplied("supplied_items") parent.save() else: parent.update_reserved_qty() parent.update_project() parent.update_prevdoc_status("submit") parent.update_delivery_status() parent.reload() validate_workflow_conditions(parent) parent.update_blanket_order() parent.update_billing_percentage() parent.set_status()
def convert_to_liters(item, quantity): uom = get_conversion_factor(item.item_code, "Litre") return quantity * uom['conversion_factor']
def make_stock_entries(self): from erpnext.stock.stock_ledger import NegativeStockError from erpnext.stock.doctype.stock_entry.stock_entry import IncorrectValuationRateError, \ DuplicateEntryForProductionOrderError, OperationsNotCompleteError, get_best_warehouse stock_entry_list = [] items = self.get("items") if not items: return False for fg_item in items: prev_stock_entry = frappe.db.sql("""select name,docstatus from `tabStock Entry` where custom_production_order=%s and manufactured_item=%s and docstatus < 2""", (self.name,fg_item.item_code), as_dict = 1) if prev_stock_entry: break if not fg_item.depth or not fg_item.width or not fg_item.height: frappe.throw(_("Item {0} needs all dimensions").format(fg_item.item_code)) if not fg_item.bom: frappe.throw(_("Item {0} has missing bom").format(fg_item.item_code)) elif fg_item.bom: bom = frappe.get_doc("BOM", fg_item.bom) from erpnext.stock.utils import get_default_warehouse default_warehouses = get_default_warehouse(company = self.company) try: stock_entry = frappe.new_doc("Stock Entry") stock_entry.purpose = "Manufacture" # stock_entry.sales_order = sales_order_no if self.reference_doctype == "Delivery Note": stock_entry.delivery_note_no = self.reference_name stock_entry.project = self.project stock_entry.company = self.company stock_entry.custom_production_order = self.name stock_entry.from_bom = 0 stock_entry.use_multi_level_bom = 0 stock_entry.manufactured_item = fg_item.item_code stock_entry.remarks = self.remarks stock_entry.posting_date = nowdate() stock_entry.posting_time = nowtime() conversion_factor = get_conversion_factor(fg_item.item_code,fg_item.uom).get("conversion_factor") if not conversion_factor: conversion_factor = 1 stock_entry.fg_completed_qty = flt(fg_item.qty) * flt(conversion_factor) stock_entry.from_warehouse = default_warehouses.get("source_warehouse") stock_entry.to_warehouse = default_warehouses.get("fg_warehouse") stock_entry.title = 'Manufacture {0}'.format(fg_item.item_code) if fg_item.uom == bom.uom: qty = fg_item.qty else: conversion_factor = get_conversion_factor_between_two_units(fg_item.item_code,fg_item.uom, bom.uom).get("conversion_factor") if not conversion_factor: conversion_factor = 1 frappe.msgprint(_("Item {0} has no conversion factor for {1}").format(fg_item.item_code, bom.uom)) qty = flt(fg_item.qty) * flt(conversion_factor) depthOriginal = convert_units(fg_item.depthunit,fg_item.depth) widthOriginal = convert_units(fg_item.widthunit,fg_item.width) heightOriginal = convert_units(fg_item.heightunit,fg_item.height) if bom.get("bomitems"): updated_builder_items = calculate_builder_items_dimensions(bom.get("bomitems"),fg_item.depth,fg_item.depthunit,fg_item.width,fg_item.widthunit,fg_item.height,fg_item.heightunit) merged,summary,unmerged = build_bom_ext(updated_builder_items,qty,depthOriginal,widthOriginal,heightOriginal) elif bom.get("items"): qtyOriginal = bom.quantity merged,summary,unmerged = get_material_list(bom.get("items"),qty,qtyOriginal) else: raise Exception('Represents a hidden bug, do not catch this') exploded_items,raw_material_cost = self.update_bom_builder(merged) for item in exploded_items: # item = frappe._dict(item) best_warehouse,enough_stock = get_best_warehouse(item.item_code,item.stock_qty,stock_entry.from_warehouse,company = stock_entry.company) stock_entry.add_to_stock_entry_detail({ item.item_code: { "to_warehouse": "", "from_warehouse": best_warehouse, "qty": item.stock_qty, "item_name": item.item_name, "description": item.description, "stock_uom": item.stock_uom, "expense_account": None, "cost_center": None } }) item_dict = get_item_det(fg_item.item_code) stock_entry.add_to_stock_entry_detail({ fg_item.item_code: { "to_warehouse": stock_entry.to_warehouse, "from_warehouse": "", "qty": qty, "item_name": item_dict['item_name'], "description": item_dict['description'], "stock_uom": item_dict['stock_uom'], "expense_account": None, "cost_center": None, "basic_rate" : raw_material_cost } }) # additional_costs = [] # if purpose=="Manufacture" and add_operating_costs: # additional_costs.append({ # "description": "Operating Cost as per Production Order / BOM", # "amount": self.operating_cost * flt(qty) # }) # stock_entry.set("additional_costs", additional_costs) stock_entry.get_stock_and_rate() # stock_entry.get_items() stock_entry.insert() frappe.db.commit() # if submit: # stock_entry.submit() link = ['<a href="#Form/Stock Entry/{0}">{0}</a>'.format(stock_entry.name)] stock_entry_list.append(link) except (NegativeStockError, IncorrectValuationRateError, DuplicateEntryForProductionOrderError, OperationsNotCompleteError,OperationsNotCompleteError): frappe.db.rollback() except Exception as error: frappe.db.rollback() if len(stock_entry_list) > 0: return True else: return False
def _get_basic_details(args, item): """ :param args: { "item_code": "", "warehouse": None, "customer": "", "conversion_rate": 1.0, "selling_price_list": None, "price_list_currency": None, "price_list_uom_dependant": 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": "", barcode: "", serial_no: "", currency: "", update_stock: "", price_list: "", company: "", order_type: "", is_pos: "", project: "", qty: "", stock_qty: "", conversion_factor: "" } :param item: `item_code` of Item object :return: frappe._dict """ if not item: item = frappe.get_doc("Item", args.get("item_code")) if item.variant_of: item.update_template_tables() from frappe.defaults import get_user_default_as_list user_default_warehouse_list = get_user_default_as_list("Warehouse") user_default_warehouse = (user_default_warehouse_list[0] if len(user_default_warehouse_list) == 1 else "") item_defaults = get_item_defaults(item.name, args.company) item_group_defaults = get_item_group_defaults(item.name, args.company) warehouse = (args.get("set_warehouse") or user_default_warehouse or item_defaults.get("default_warehouse") or item_group_defaults.get("default_warehouse") or args.warehouse) if not args.get("material_request_type"): args["material_request_type"] = frappe.db.get_value( "Material Request", args.get("name"), "material_request_type", cache=True) # Set the UOM to the Default Sales UOM or Default Purchase UOM if configured in the Item Master if not args.uom: args.uom = item.purchase_uom if item.purchase_uom else item.stock_uom out = frappe._dict({ "item_code": item.name, "item_name": item.item_name, "description": cstr(item.description).strip(), "image": cstr(item.image).strip(), "warehouse": warehouse, "income_account": get_default_income_account(args, item_defaults, item_group_defaults), "expense_account": get_default_expense_account(args, item_defaults, item_group_defaults), "cost_center": get_default_cost_center(args, item_defaults, item_group_defaults), "has_serial_no": item.has_serial_no, "has_batch_no": item.has_batch_no, "batch_no": None, "item_tax_rate": json.dumps(dict( ([d.tax_type, d.tax_rate] for d in item.get("taxes")))), "uom": args.uom, "min_order_qty": flt(item.min_order_qty), "qty": args.qty or 1.0, "stock_qty": args.qty or 1.0, "price_list_rate": 0.0, "base_price_list_rate": 0.0, "rate": 0.0, "base_rate": 0.0, "amount": 0.0, "base_amount": 0.0, "net_rate": 0.0, "net_amount": 0.0, "discount_percentage": 0.0, "supplier": get_default_supplier(args, item_defaults, item_group_defaults), "update_stock": args.get("update_stock") if args.get("doctype") in ["Sales Invoice", "Purchase Invoice"] else 0, "delivered_by_supplier": item.delivered_by_supplier if args.get("doctype") in ["Sales Order", "Sales Invoice"] else 0, "is_fixed_asset": item.is_fixed_asset, "weight_per_unit": item.weight_per_unit, "weight_uom": item.weight_uom, "last_purchase_rate": item.last_purchase_rate if args.get("doctype") in ["Purchase Order"] else 0, "transaction_date": args.get("transaction_date"), }) if item.get("enable_deferred_revenue") or item.get( "enable_deferred_expense"): out.update(calculate_service_end_date(args, item)) # calculate conversion factor if item.stock_uom == args.uom: out.conversion_factor = 1.0 else: out.conversion_factor = args.conversion_factor or get_conversion_factor( item.name, args.uom).get("conversion_factor") args.conversion_factor = out.conversion_factor out.stock_qty = out.qty * out.conversion_factor # calculate last purchase rate from erpnext.buying.doctype.purchase_order.purchase_order import ( item_last_purchase_rate, ) out.last_purchase_rate = item_last_purchase_rate(args.name, args.conversion_rate, item.name, out.conversion_factor) # if default specified in item is for another company, fetch from company for d in [ ["Account", "income_account", "default_income_account"], ["Account", "expense_account", "default_expense_account"], ["Cost Center", "cost_center", "cost_center"], ["Warehouse", "warehouse", ""], ]: if not out[d[1]]: out[d[1]] = (frappe.get_cached_value("Company", args.company, d[2]) if d[2] else None) for fieldname in ("item_name", "item_group", "barcodes", "brand", "stock_uom"): out[fieldname] = item.get(fieldname) meta = frappe.get_meta(args.child_doctype) if meta.get_field("barcode"): update_barcode_value(out) return out