def check_prev_docstatus(self): for d in getlist(self.doclist, 'entries'): if d.purchase_order: submitted = frappe.db.sql( "select name from `tabPurchase Order` where docstatus = 1 and name = '%s'" % d.purchase_order) if not submitted: frappe.throw("Purchase Order : " + cstr(d.purchase_order) + " is not submitted") if d.purchase_receipt: submitted = frappe.db.sql( "select name from `tabPurchase Receipt` where docstatus = 1 and name = '%s'" % d.purchase_receipt) if not submitted: frappe.throw("Purchase Receipt : " + cstr(d.purchase_receipt) + " is not submitted")
def update_bin(self, is_submit, is_stopped=0): from erpnext.stock.utils import update_bin pc_obj = get_obj('Purchase Common') for d in getlist(self.doclist, 'po_details'): #1. Check if is_stock_item == 'Yes' if frappe.db.get_value("Item", d.item_code, "is_stock_item") == "Yes": # this happens when item is changed from non-stock to stock item if not d.warehouse: continue ind_qty, po_qty = 0, flt(d.qty) * flt(d.conversion_factor) if is_stopped: po_qty = flt(d.qty) > flt(d.received_qty) and \ flt( flt(flt(d.qty) - flt(d.received_qty))*flt(d.conversion_factor)) or 0 # No updates in Material Request on Stop / Unstop if cstr(d.prevdoc_doctype ) == 'Material Request' and not is_stopped: # get qty and pending_qty of prevdoc curr_ref_qty = pc_obj.get_qty( d.doctype, 'prevdoc_detail_docname', d.prevdoc_detail_docname, 'Material Request Item', 'Material Request - Purchase Order', self.doc.name) max_qty, qty, curr_qty = flt(curr_ref_qty.split('~~~')[1]), \ flt(curr_ref_qty.split('~~~')[0]), 0 if flt(qty) + flt(po_qty) > flt(max_qty): curr_qty = flt(max_qty) - flt(qty) # special case as there is no restriction # for Material Request - Purchase Order curr_qty = curr_qty > 0 and curr_qty or 0 else: curr_qty = flt(po_qty) ind_qty = -flt(curr_qty) # Update ordered_qty and indented_qty in bin args = { "item_code": d.item_code, "warehouse": d.warehouse, "ordered_qty": (is_submit and 1 or -1) * flt(po_qty), "indented_qty": (is_submit and 1 or -1) * flt(ind_qty), "posting_date": self.doc.transaction_date } update_bin(args)
def validate_accepted_rejected_qty(self): for d in getlist(self.doclist, "purchase_receipt_details"): if not flt(d.received_qty) and flt(d.qty): d.received_qty = flt(d.qty) - flt(d.rejected_qty) elif not flt(d.qty) and flt(d.rejected_qty): d.qty = flt(d.received_qty) - flt(d.rejected_qty) elif not flt(d.rejected_qty): d.rejected_qty = flt(d.received_qty) - flt(d.qty) # Check Received Qty = Accepted Qty + Rejected Qty if ((flt(d.qty) + flt(d.rejected_qty)) != flt(d.received_qty)): msgprint( "Sum of Accepted Qty and Rejected Qty must be equal to Received quantity. Error for Item: " + cstr(d.item_code)) raise Exception
def get_stock_and_rate(self): """get stock and incoming rate on posting date""" for d in getlist(self.doclist, 'mtn_details'): args = frappe._dict({ "item_code": d.item_code, "warehouse": d.s_warehouse or d.t_warehouse, "posting_date": self.doc.posting_date, "posting_time": self.doc.posting_time, "qty": d.s_warehouse and -1*d.transfer_qty or d.transfer_qty, "serial_no": d.serial_no, "bom_no": d.bom_no, }) # get actual stock at source warehouse d.actual_qty = get_previous_sle(args).get("qty_after_transaction") or 0 # get incoming rate if not flt(d.incoming_rate): d.incoming_rate = self.get_incoming_rate(args) d.amount = flt(d.transfer_qty) * flt(d.incoming_rate)
def check_item_tax(self): """Check whether Tax Rate is not entered twice for same Tax Type""" check_list = [] for d in getlist(self.doclist, 'item_tax'): if d.tax_type: account_type = frappe.db.get_value("Account", d.tax_type, "account_type") if account_type not in [ 'Tax', 'Chargeable', 'Income Account', 'Expense Account' ]: msgprint( "'%s' is not Tax / Chargeable / Income / Expense Account" % d.tax_type, raise_exception=1) else: if d.tax_type in check_list: msgprint("Rate is entered twice for: '%s'" % d.tax_type, raise_exception=1) else: check_list.append(d.tax_type)
def validate_conversion_factor(self): check_list = [] for d in getlist(self.doclist, 'uom_conversion_details'): if cstr(d.uom) in check_list: msgprint(_( "UOM %s has been entered more than once in Conversion Factor Table." % cstr(d.uom)), raise_exception=1) else: check_list.append(cstr(d.uom)) if d.uom and cstr(d.uom) == cstr( self.doc.stock_uom) and flt(d.conversion_factor) != 1: msgprint(_( """Conversion Factor of UOM: %s should be equal to 1. As UOM: %s is Stock UOM of Item: %s.""" % (d.uom, d.uom, self.doc.name)), raise_exception=1) elif d.uom and cstr(d.uom) != self.doc.stock_uom and flt( d.conversion_factor) == 1: msgprint(_( """Conversion Factor of UOM: %s should not be equal to 1. As UOM: %s is not Stock UOM of Item: %s""" % (d.uom, d.uom, self.doc.name)), raise_exception=1)
def reconcile(self): """ Links booking and payment voucher 1. cancel payment voucher 2. split into multiple rows if partially adjusted, assign against voucher 3. submit payment voucher """ if not self.doc.voucher_no or not frappe.db.sql( """select name from `tab%s` where name = %s""" % (self.doc.voucher_type, '%s'), self.doc.voucher_no): msgprint("Please select valid Voucher No to proceed", raise_exception=1) lst = [] for d in getlist(self.doclist, 'ir_payment_details'): if flt(d.amt_to_be_reconciled) > 0: args = { 'voucher_no': d.voucher_no, 'voucher_detail_no': d.voucher_detail_no, 'against_voucher_type': self.doc.voucher_type, 'against_voucher': self.doc.voucher_no, 'account': self.doc.account, 'is_advance': 'No', 'dr_or_cr': self.doc.account_type == 'debit' and 'credit' or 'debit', 'unadjusted_amt': flt(d.amt_due), 'allocated_amt': flt(d.amt_to_be_reconciled) } lst.append(args) if lst: from erpnext.accounts.utils import reconcile_against_document reconcile_against_document(lst) msgprint("Successfully allocated.") else: msgprint("No amount allocated.", raise_exception=1)
def validate_materials(self): """ Validate raw material entries """ check_list = [] for m in getlist(self.doclist, 'bom_materials'): # check if operation no not in op table if self.doc.with_operations and cstr( m.operation_no) not in self.op: msgprint("""Operation no: %s against item: %s at row no: %s \ is not present at Operations table""" % (m.operation_no, m.item_code, m.idx), raise_exception=1) item = self.get_item_det(m.item_code) if item[0]['is_manufactured_item'] == 'Yes': if not m.bom_no: msgprint( "Please enter BOM No aginst item: %s at row no: %s" % (m.item_code, m.idx), raise_exception=1) else: self.validate_bom_no(m.item_code, m.bom_no, m.idx) elif m.bom_no: msgprint( """As Item %s is not a manufactured / sub-contracted item, \ you can not enter BOM against it (Row No: %s).""" % (m.item_code, m.idx), raise_exception=1) if flt(m.qty) <= 0: msgprint( "Please enter qty against raw material: %s at row no: %s" % (m.item_code, m.idx), raise_exception=1) self.check_if_item_repeated(m.item_code, m.operation_no, check_list)
def validate_approving_authority(self, doctype_name, company, total, doc_obj=''): av_dis = 0 if doc_obj: price_list_rate, base_rate = 0, 0 for d in getlist(doc_obj.doclist, doc_obj.fname): if d.base_price_list_rate and d.base_rate: price_list_rate += flt(d.base_price_list_rate) base_rate += flt(d.base_rate) if price_list_rate: av_dis = 100 - flt(base_rate * 100 / price_list_rate) final_based_on = [ 'Grand Total', 'Average Discount', 'Customerwise Discount', 'Itemwise Discount' ] # Individual User # ================ # Check for authorization set for individual user based_on = [ x[0] for x in frappe.db.sql( "select distinct based_on from `tabAuthorization Rule` where transaction = %s and system_user = %s and (company = %s or ifnull(company,'')='') and docstatus != 2", (doctype_name, session['user'], company)) ] for d in based_on: self.bifurcate_based_on_type(doctype_name, total, av_dis, d, doc_obj, 1, company) # Remove user specific rules from global authorization rules for r in based_on: if r in final_based_on and r != 'Itemwise Discount': final_based_on.remove(r) # Specific Role # =============== # Check for authorization set on particular roles based_on = [ x[0] for x in frappe.db.sql( """select based_on from `tabAuthorization Rule` where transaction = %s and system_role IN (%s) and based_on IN (%s) and (company = %s or ifnull(company,'')='') and docstatus != 2 """ % ('%s', "'" + "','".join(frappe.user.get_roles()) + "'", "'" + "','".join(final_based_on) + "'", '%s'), (doctype_name, company)) ] for d in based_on: self.bifurcate_based_on_type(doctype_name, total, av_dis, d, doc_obj, 2, company) # Remove role specific rules from global authorization rules for r in based_on: if r in final_based_on and r != 'Itemwise Discount': final_based_on.remove(r) # Global Rule # ============= # Check for global authorization for g in final_based_on: self.bifurcate_based_on_type(doctype_name, total, av_dis, g, doc_obj, 0, company)
def get_schedule_dates(self): for d in getlist(self.doclist, 'po_details'): if d.prevdoc_detail_docname and not d.schedule_date: d.schedule_date = frappe.db.get_value("Material Request Item", d.prevdoc_detail_docname, "schedule_date")
def check_item_table(self): if not (getlist(self.doclist, 'installed_item_details')): msgprint("Please fetch items from Delivery Note selected", raise_exception=1)
def fill_customer_code(self): """ Append all the customer codes and insert into "customer_code" field of item table """ cust_code = [] for d in getlist(self.doclist, 'item_customer_details'): cust_code.append(d.ref_code) self.doc.customer_code = ','.join(cust_code)
def validate_finished_goods(self): """validation: finished good quantity should be same as manufacturing quantity""" for d in getlist(self.doclist, 'mtn_details'): if d.bom_no and flt(d.transfer_qty) != flt(self.doc.fg_completed_qty): msgprint(_("Row #") + " %s: " % d.idx + _("Quantity should be equal to Manufacturing Quantity. To fetch items again, click on 'Get Items' button or update the Quantity manually."), raise_exception=1)