def write_docfile(data): import os for indx, file_path in enumerate(data.get('files')): base_dir_path = os.path.join(os.getcwd(), get_site_path().replace('.',"").replace('/', ""), 'public', 'files') folder_lst = file_path.split('/') file_path = '/'.join(folder_lst[:-1]) doc_name = folder_lst[-1:][0] doc_base_path = os.path.join(base_dir_path, file_path) if '-watermark' not in doc_name: dname = doc_name.split('.') doc_name = dname[0]+'-watermark.'+dname[1] data.get('files')[indx] = "{doc_base_path}/{doc_name}".format(doc_base_path=doc_base_path, doc_name=doc_name) if not os.path.exists(doc_base_path + '/' +doc_name): frappe.create_folder(doc_base_path) data = { "entityid": folder_lst[4], "profile_id": folder_lst[0], "event_id": folder_lst[1], "tag_id": folder_lst[4] + '-' + cstr(folder_lst[2].split('-')[1]) + cstr(folder_lst[3].split('_')[1]), "file_id": [ doc_name.replace('-watermark','') ], "file_location": [ doc_base_path + '/' + doc_name ] } from templates.pages.event import write_file res = write_file(data)
def get_data(filters): conditions, filters = get_conditions(filters) data = frappe.db.sql(""" select t1.employee, t3.employee_name, t1.designation, t3.passport_number, sum(case when t2.salary_component = 'Basic Pay' then ifnull(t2.amount,0) else 0 end) as basicpay, t3.nppf_number, sum(case when t2.salary_component = 'PF' then ifnull(t2.amount,0) else 0 end) as employeepf, sum(case when t2.salary_component = 'PF' then ifnull(t2.amount,0) else 0 end) as employerpf, sum(case when t2.salary_component = 'PF' then ifnull(t2.amount,0)*2 else 0 end) as total, t1.company, t1.branch, t1.department, t1.division, t1.section, t1.fiscal_year, t1.month from `tabSalary Slip` t1, `tabSalary Detail` t2, `tabEmployee` t3 where t1.docstatus = 1 %s and t3.employee = t1.employee and t2.parent = t1.name and t2.salary_component in ('Basic Pay','PF') group by t1.employee, t3.employee_name, t1.designation, t3.passport_number, t3.nppf_number, t1.company, t1.branch, t1.department, t1.division, t1.section, t1.fiscal_year, t1.month """ % conditions, filters) if not data: msgprint(_("No Data Found for month: ") + cstr(filters.get("month")) + _(" and year: ") + cstr(filters.get("fiscal_year")), raise_exception=1) return data
def get_outstanding_invoices(self): self.set('accounts', []) total = 0 for d in self.get_values(): total += flt(d.outstanding_amount, self.precision("credit", "accounts")) jd1 = self.append('accounts', {}) jd1.account = d.account jd1.party = d.party if self.write_off_based_on == 'Accounts Receivable': jd1.party_type = "Customer" jd1.credit = flt(d.outstanding_amount, self.precision("credit", "accounts")) jd1.reference_type = "Sales Invoice" jd1.reference_name = cstr(d.name) elif self.write_off_based_on == 'Accounts Payable': jd1.party_type = "Supplier" jd1.debit = flt(d.outstanding_amount, self.precision("debit", "accounts")) jd1.reference_type = "Purchase Invoice" jd1.reference_name = cstr(d.name) jd2 = self.append('accounts', {}) if self.write_off_based_on == 'Accounts Receivable': jd2.debit = total elif self.write_off_based_on == 'Accounts Payable': jd2.credit = total self.validate_total_debit_and_credit()
def merge_merchandise_items(doc): amount = 0.0 for d in doc.get('merchandise_item'): e = doc.append('entries', {}) e.barcode=d.merchandise_barcode e.item_code=d.merchandise_item_code e.item_name=d.merchandise_item_name e.item_group = frappe.db.get_value('Item', d.merchandise_item_code, 'item_group') e.work_order=d.merchandise_work_order e.description=d.merchandise_description e.sales_invoice_branch = d.merchandise_branch e.warehouse = frappe.db.get_value('Branch', d.merchandise_branch, 'warehouse') e.income_account=d.merchandise_income_account e.cost_center=d.merchandise_cost_center e.batch_no=d.merchandise_batch_no e.item_tax_rate=d.merchandise_item_tax_rate e.stock_uom=d.merchandise_stock_uom e.price_list_rate=d.merchandise_price_list_rate e.discount_percentage=d.merchandise_discount_percentage e.amount= d.merchandise_amount e.base_amount=cstr(flt(d.merchandise_amount)*flt(doc.conversion_rate)) e.base_rate=cstr(flt(d.merchandise_rate)*flt(doc.conversion_rate)) e.rate=d.merchandise_rate e.base_price_list_rate=d.merchandise_base_price_list_rate e.qty=d.merchandise_qty e.base_price_list_rate=d.merchandise_base_price_list_rate e.delivery_date = d.merchandise_delivery_date amount += flt(e.amount) return amount
def find_variant(template, args, variant_item_code=None): conditions = ["""(iv_attribute.attribute="{0}" and iv_attribute.attribute_value="{1}")"""\ .format(frappe.db.escape(key), frappe.db.escape(cstr(value))) for key, value in args.items()] conditions = " or ".join(conditions) # use approximate match and shortlist possible variant matches # it is approximate because we are matching using OR condition # and it need not be exact match at this stage # this uses a simpler query instead of using multiple exists conditions possible_variants = frappe.db.sql_list("""select name from `tabItem` item where variant_of=%s and exists ( select name from `tabItem Variant Attribute` iv_attribute where iv_attribute.parent=item.name and ({conditions}) and parent != %s )""".format(conditions=conditions), (template, cstr(variant_item_code))) for variant in possible_variants: variant = frappe.get_doc("Item", variant) if len(args.keys()) == len(variant.get("attributes")): # has the same number of attributes and values # assuming no duplication as per the validation in Item match_count = 0 for attribute, value in args.items(): for row in variant.attributes: if row.attribute==attribute and row.attribute_value== cstr(value): # this row matches match_count += 1 break if match_count == len(args.keys()): return variant.name
def get_item_list(self): il = [] for d in self.get("items"): if d.qty is None: frappe.throw(_("Row {0}: Qty is mandatory").format(d.idx)) if self.has_product_bundle(d.item_code): for p in self.get("packed_items"): if p.parent_detail_docname == d.name and p.parent_item == d.item_code: # the packing details table's qty is already multiplied with parent's qty il.append(frappe._dict({ 'warehouse': p.warehouse or d.warehouse, 'item_code': p.item_code, 'qty': flt(p.qty), 'uom': p.uom, 'batch_no': cstr(p.batch_no).strip(), 'serial_no': cstr(p.serial_no).strip(), 'name': d.name, 'target_warehouse': p.target_warehouse })) else: il.append(frappe._dict({ 'warehouse': d.warehouse, 'item_code': d.item_code, 'qty': d.stock_qty, 'uom': d.uom, 'stock_uom': d.stock_uom, 'conversion_factor': d.conversion_factor, 'batch_no': cstr(d.get("batch_no")).strip(), 'serial_no': cstr(d.get("serial_no")).strip(), 'name': d.name, 'target_warehouse': d.target_warehouse })) return il
def get_fy_details(fy_start_date, fy_end_date): start_year = getdate(fy_start_date).year if start_year == getdate(fy_end_date).year: fy = cstr(start_year) else: fy = cstr(start_year) + '-' + cstr(start_year + 1) return fy
def get_last_purchase_details(item_code, doc_name=None, conversion_rate=1.0): """returns last purchase details in stock uom""" # get last purchase order item details last_purchase_order = frappe.db.sql("""\ select po.name, po.transaction_date, po.conversion_rate, po_item.conversion_factor, po_item.base_price_list_rate, po_item.discount_percentage, po_item.base_rate from `tabPurchase Order` po, `tabPurchase Order Item` po_item where po.docstatus = 1 and po_item.item_code = %s and po.name != %s and po.name = po_item.parent order by po.transaction_date desc, po.name desc limit 1""", (item_code, cstr(doc_name)), as_dict=1) # get last purchase receipt item details last_purchase_receipt = frappe.db.sql("""\ select pr.name, pr.posting_date, pr.posting_time, pr.conversion_rate, pr_item.conversion_factor, pr_item.base_price_list_rate, pr_item.discount_percentage, pr_item.base_rate from `tabPurchase Receipt` pr, `tabPurchase Receipt Item` pr_item where pr.docstatus = 1 and pr_item.item_code = %s and pr.name != %s and pr.name = pr_item.parent order by pr.posting_date desc, pr.posting_time desc, pr.name desc limit 1""", (item_code, cstr(doc_name)), as_dict=1) purchase_order_date = getdate(last_purchase_order and last_purchase_order[0].transaction_date \ or "1900-01-01") purchase_receipt_date = getdate(last_purchase_receipt and \ last_purchase_receipt[0].posting_date or "1900-01-01") if (purchase_order_date > purchase_receipt_date) or \ (last_purchase_order and not last_purchase_receipt): # use purchase order last_purchase = last_purchase_order[0] purchase_date = purchase_order_date elif (purchase_receipt_date > purchase_order_date) or \ (last_purchase_receipt and not last_purchase_order): # use purchase receipt last_purchase = last_purchase_receipt[0] purchase_date = purchase_receipt_date else: return frappe._dict() conversion_factor = flt(last_purchase.conversion_factor) out = frappe._dict({ "base_price_list_rate": flt(last_purchase.base_price_list_rate) / conversion_factor, "base_rate": flt(last_purchase.base_rate) / conversion_factor, "discount_percentage": flt(last_purchase.discount_percentage), "purchase_date": purchase_date }) conversion_rate = flt(conversion_rate) or 1.0 out.update({ "price_list_rate": out.base_price_list_rate / conversion_rate, "rate": out.base_rate / conversion_rate, "base_rate": out.base_rate }) return out
def update_stock_ledger(self): sl_entries = [] stock_items = self.get_stock_items() for d in self.get('purchase_receipt_details'): if d.item_code in stock_items and d.warehouse: pr_qty = flt(d.qty) * flt(d.conversion_factor) if pr_qty: sl_entries.append(self.get_sl_entries(d, { "actual_qty": flt(pr_qty), "serial_no": cstr(d.serial_no).strip(), "incoming_rate": d.valuation_rate })) if flt(d.rejected_qty) > 0: sl_entries.append(self.get_sl_entries(d, { "warehouse": d.rejected_warehouse, "actual_qty": flt(d.rejected_qty) * flt(d.conversion_factor), "serial_no": cstr(d.rejected_serial_no).strip(), "incoming_rate": 0.0 })) self.bk_flush_supp_wh(sl_entries) self.make_sl_entries(sl_entries)
def update_details(prop_list=None,followup_type=None,followup_date=None): properties = json.loads(prop_list) for i in properties: lead_property = frappe.get_doc("Lead Property Details", i.get('name')) if followup_type=='Follow-Up For Share': lead_property.share_followup_status = i.get('status') lead_property.share_followup_date = datetime.datetime.strptime(cstr(followup_date),'%d-%m-%Y') if i.get("status") in ["Intrested"]: lead_property.schedule_se = 1 elif followup_type=='Follow-Up For SE': lead_property.se_follow_up_status = i.get('status') lead_property.se_followup_date = datetime.datetime.strptime(cstr(followup_date),'%d-%m-%Y') if i.get("status") in ["Reschedule"]: lead_property.schedule_se = 1 if i.get("status") in ["Intrested"]: lead_property.schedule_acm = 1 else: lead_property.acm_followup_status = i.get('status') lead_property.acm_followup_date = datetime.datetime.strptime(cstr(followup_date),'%d-%m-%Y') if i.get("status") in ["Reschedule"]: lead_property.schedule_acm = 1 lead_property.save(ignore_permissions=True) return True
def update_stock_ledger(self): sl_entries = [] # make sl entries for source warehouse first, then do for target warehouse for d in self.get('items'): if cstr(d.s_warehouse): sl_entries.append(self.get_sl_entries(d, { "warehouse": cstr(d.s_warehouse), "actual_qty": -flt(d.transfer_qty), "incoming_rate": 0 })) for d in self.get('items'): if cstr(d.t_warehouse): sl_entries.append(self.get_sl_entries(d, { "warehouse": cstr(d.t_warehouse), "actual_qty": flt(d.transfer_qty), "incoming_rate": flt(d.valuation_rate) })) # On cancellation, make stock ledger entry for # target warehouse first, to update serial no values properly # if cstr(d.s_warehouse) and self.docstatus == 2: # sl_entries.append(self.get_sl_entries(d, { # "warehouse": cstr(d.s_warehouse), # "actual_qty": -flt(d.transfer_qty), # "incoming_rate": 0 # })) if self.docstatus == 2: sl_entries.reverse() self.make_sl_entries(sl_entries, self.amended_from and 'Yes' or 'No')
def validate_bom(self): if cstr(self.current_bom) == cstr(self.new_bom): frappe.throw(_("Current BOM and New BOM can not be same")) if frappe.db.get_value("BOM", self.current_bom, "item") \ != frappe.db.get_value("BOM", self.new_bom, "item"): frappe.throw(_("The selected BOMs are not for the same item"))
def db_insert(self): """INSERT the document (with valid columns) in the database.""" if not self.name: # name will be set by document class in most cases set_new_name(self) d = self.get_valid_dict() columns = d.keys() try: frappe.db.sql("""insert into `tab{doctype}` ({columns}) values ({values})""".format( doctype = self.doctype, columns = ", ".join(["`"+c+"`" for c in columns]), values = ", ".join(["%s"] * len(columns)) ), d.values()) except Exception, e: if e.args[0]==1062: if "PRIMARY" in cstr(e.args[1]): if self.meta.autoname=="hash": # hash collision? try again self.name = None self.db_insert() return type, value, traceback = sys.exc_info() frappe.msgprint(_("Duplicate name {0} {1}").format(self.doctype, self.name)) raise frappe.DuplicateEntryError, (self.doctype, self.name, e), traceback elif "Duplicate" in cstr(e.args[1]): # unique constraint self.show_unique_validation_message(e) else: raise else: raise
def get_disease_fields(name,profile_id=None): if name: dm = frappe.get_doc("Disease Monitoring", frappe.db.get_value("Disease Monitoring",{"disease_name":name},"name")) if dm: fields, rows, dm_cols, field_mapper = [], [], [{"title":"", "width":"10px !important;"}], ["sr"] row_count = 0 sec_label=name+' Readings' fields.append({"fieldname":"","fieldtype":"section_break","label":sec_label,"options":"<i class='fa fa-pencil-square-o'></i>"}) for d in dm.get('parameters'): row_count += 1 f_dic = {"fieldname":d.fieldname,"fieldtype":d.fieldtype,"label":d.label,"placeholder":"", "required": d.required or 0,} fields.append(f_dic) dm_cols.append({"title":d.label,"width":cstr(d.width) and cstr(d.width) + 'px !important;' or "10px;"}) field_mapper.append(d.fieldname) if row_count==4: row_count=0 fields.append({"fieldname":"","fieldtype":"column_break","label":""}) #s_break = {"fieldname":"","fieldtype":"section_break","label":""} #fields.append(s_break) rows.append(dm_cols) sec_label=name+' Monitoring Logs' fields.append({"fieldname":"","fieldtype":"section_break","label":sec_label,"options":"<i class='fa fa-list-alt'></i>"}) #raw_fields.append(s_break) row_dic={"fieldname":"tab","fieldtype": "table","label": "Disease Monitoring","rows":rows} fields.append(row_dic) values=get_values(profile_id, fields, dm.event_master_id, field_mapper) return { "fields":fields, "event_master_id":dm.event_master_id, "values":values, "field_mapper":field_mapper }
def check_stock_uom_with_bin(item, stock_uom): if stock_uom == frappe.db.get_value("Item", item, "stock_uom"): return matched=True ref_uom = frappe.db.get_value("Stock Ledger Entry", {"item_code": item}, "stock_uom") if ref_uom: if cstr(ref_uom) != cstr(stock_uom): matched = False else: bin_list = frappe.db.sql("select * from tabBin where item_code=%s", item, as_dict=1) for bin in bin_list: if (bin.reserved_qty > 0 or bin.ordered_qty > 0 or bin.indented_qty > 0 \ or bin.planned_qty > 0) and cstr(bin.stock_uom) != cstr(stock_uom): matched = False break if matched and bin_list: frappe.db.sql("""update tabBin set stock_uom=%s where item_code=%s""", (stock_uom, item)) if not matched: frappe.throw(_("Default Unit of Measure for Item {0} cannot be changed directly because \ you have already made some transaction(s) with another UOM. To change default UOM, \ use 'UOM Replace Utility' tool under Stock module.").format(item))
def store_request_in_elastic_search(property_data, search_query, request_type, adv_search_query=None): request_id = "REQ-" + cstr(int(time.time())) + '-' + cstr(random.randint(100000,999999)) request_dict = { "user_id":property_data.get("user_id"), "request_id":request_id, "operation":property_data.get("operation"), "property_type":property_data.get("property_type"), "property_subtype":property_data.get("property_subtype"), "project_type":property_data.get("project_type"), "project_subtype":property_data.get("project_subtype"), "location":property_data.get("location"), "property_subtype_option":property_data.get("property_subtype_option"), "min_area":property_data.get("min_area"), "max_area":property_data.get("max_area"), "min_budget":property_data.get("min_budget"), "max_budget":property_data.get("max_budget"), "city":property_data.get("city"), "unit_of_area":property_data.get("unit_of_area"), "search_query":cstr(search_query), "adv_search_query":cstr(adv_search_query), "request_type":request_type } meta_dict = add_meta_fields_before_posting(property_data) request_dict.update(meta_dict) es = ElasticSearchController() es_result = es.index_document("request",request_dict, request_id) return request_id
def process_property_data_before_posting(property_data, request_data, email): """ Add necessary fields to property dictionary before indexing property data, store image in full size & thumbnails format. """ custom_id = "PROP-" + cstr(int(time.time())) + '-' + cstr(random.randint(10000,99999)) property_data["property_id"] = custom_id meta_dict = add_meta_fields_before_posting(request_data) property_data.update(meta_dict) property_photo_url_dict = store_property_photos_in_propshikari(request_data.get("property_photos"),custom_id) property_data["full_size_images"] = property_photo_url_dict.get("full_size",[]) property_data["thumbnails"] = property_photo_url_dict.get("thumbnails",[]) property_data["property_photo"] = property_photo_url_dict.get("thumbnails")[0] if len(property_photo_url_dict.get("thumbnails")) else "" property_data["posted_by"] = request_data.get("user_id") property_data["user_email"] = email property_data["posting_date"] = property_data.get("posting_date") if property_data.get("posting_date") else property_data.get("creation_date") property_data["amenities"] = putil.prepare_amenities_data(property_data.get("amenities",""), property_data.get("property_type")) property_data["flat_facilities"] = putil.prepare_flat_facilities_data(property_data.get("flat_facilities",""), property_data.get("property_type")) property_data["possession_status"] = "Immediate" if property_data.get("possession") else property_data.get("possession_date") property_data["discounted_price"] = putil.get_discounted_price(property_data) if property_data.get("discount_percentage") else 0.0 mandatory_list = property_mandatory_fields.get(property_data.get("property_type")) property_data["percent_completion"] = putil.calculate_percent_completion(property_data, mandatory_list) if not property_data.get("possession_date"): property_data.pop("possession_date", None) return custom_id
def make_new_document(ref_wrapper, date_field, posting_date): from erpnext.accounts.utils import get_fiscal_year new_document = frappe.copy_doc(ref_wrapper) mcount = month_map[ref_wrapper.recurring_type] from_date = get_next_date(ref_wrapper.from_date, mcount) # get last day of the month to maintain period if the from date is first day of its own month # and to date is the last day of its own month if (cstr(get_first_day(ref_wrapper.from_date)) == cstr(ref_wrapper.from_date)) and \ (cstr(get_last_day(ref_wrapper.to_date)) == cstr(ref_wrapper.to_date)): to_date = get_last_day(get_next_date(ref_wrapper.to_date, mcount)) else: to_date = get_next_date(ref_wrapper.to_date, mcount) new_document.update({ date_field: posting_date, "from_date": from_date, "to_date": to_date, "fiscal_year": get_fiscal_year(posting_date)[0], "owner": ref_wrapper.owner, }) if ref_wrapper.doctype == "Sales Order": new_document.update({ "delivery_date": get_next_date(ref_wrapper.delivery_date, mcount, cint(ref_wrapper.repeat_on_day_of_month)) }) new_document.submit() return new_document
def update_stock_ledger(self, allow_negative_stock=False, via_landed_cost_voucher=False): sl_entries = [] stock_items = self.get_stock_items() for d in self.get('items'): if d.item_code in stock_items and d.warehouse: pr_qty = flt(d.qty) * flt(d.conversion_factor) if pr_qty: val_rate_db_precision = 6 if cint(self.precision("valuation_rate", d)) <= 6 else 9 sl_entries.append(self.get_sl_entries(d, { "actual_qty": flt(pr_qty), "serial_no": cstr(d.serial_no).strip(), "incoming_rate": flt(d.valuation_rate, val_rate_db_precision) })) if flt(d.rejected_qty) > 0: sl_entries.append(self.get_sl_entries(d, { "warehouse": d.rejected_warehouse, "actual_qty": flt(d.rejected_qty) * flt(d.conversion_factor), "serial_no": cstr(d.rejected_serial_no).strip(), "incoming_rate": 0.0 })) self.bk_flush_supp_wh(sl_entries) self.make_sl_entries(sl_entries, allow_negative_stock=allow_negative_stock, via_landed_cost_voucher=via_landed_cost_voucher)
def create_item(item, warehouse, has_variant=0, attributes=None,variant_of=None): item_dict = { "doctype": "Item", "shopify_id": item.get("id"), "shopify_variant_id": item.get("variant_id"), "variant_of": variant_of, "sync_with_shopify": 1, "item_code": cstr(item.get("item_code")) or cstr(item.get("id")), "item_name": item.get("title"), "description": item.get("body_html") or item.get("title"), "item_group": get_item_group(item.get("product_type")), "has_variants": has_variant, "attributes":attributes or [], "stock_uom": item.get("uom") or _("Nos"), "stock_keeping_unit": item.get("sku") or get_sku(item), "default_warehouse": warehouse, "image": get_item_image(item) } name, item_details = get_item_details(item) if not name: new_item = frappe.get_doc(item_dict) new_item.insert() name = new_item.name else: update_item(item_details, item_dict) if not has_variant: add_to_price_list(item, name)
def allocate_leave(self): self.validate_values() leave_allocated_for = [] employees = self.get_employees() if not employees: frappe.throw(_("No employee found")) for d in self.get_employees(): try: la = frappe.new_doc('Leave Allocation') la.set("__islocal", 1) la.employee = cstr(d[0]) la.employee_name = frappe.db.get_value('Employee',cstr(d[0]),'employee_name') la.leave_type = self.leave_type la.from_date = self.from_date la.to_date = self.to_date la.carry_forward = cint(self.carry_forward) la.new_leaves_allocated = flt(self.no_of_days) la.docstatus = 1 la.save() leave_allocated_for.append(d[0]) except: pass if leave_allocated_for: msgprint(_("Leaves Allocated Successfully for {0}").format(comma_and(leave_allocated_for)))
def message_braudcast_send(data): """ this will return recipents details """ dts=json.loads(data) from frappe.model.db_query import DatabaseQuery qry="select user from __Auth where user='******'username'])+"' and password=password('"+cstr(dts['userpass'])+"') " valid=frappe.db.sql(qry) msg='' if not valid: return { "status":"401", "message":"User name or Password is incorrect" } if dts['sms']: from erpnext.setup.doctype.sms_settings.sms_settings import send_sms rc_list=frappe.db.sql("select phone_1 from tabMember where phone_1 is not null and email_id in ('%s') limit 3" %(dts['recipents'].replace(",","','")),as_list=1) if rc_list: send_sms([ x[0] for x in rc_list ], cstr(dts['message'])) msg+= "SMS " rc_list=dts['recipents'].split(',') if dts['push']: data={} data['Message']=dts['message'] gcm = GCM('AIzaSyBIc4LYCnUU9wFV_pBoFHHzLoGm_xHl-5k') res=frappe.db.sql("select device_id from tabUser where name in ('%s')" % "','".join(map(str,rc_list)),as_list=1) if res: res = gcm.json_request(registration_ids=res, data=data,collapse_key='uptoyou', delay_while_idle=True, time_to_live=3600) msg+= "Push notification" if dts['email']: frappe.sendmail(recipients=dts['recipents'], sender='*****@*****.**', content=dts['message'], subject='Broadcast Message') msg+=" Email" return msg +" sent Successfully"
def test_make_vehicle_log(self): license_plate=random_string(10).upper() employee_id=frappe.db.sql("""select name from `tabEmployee` order by modified desc limit 1""")[0][0] vehicle = frappe.get_doc({ "doctype": "Vehicle", "license_plate": cstr(license_plate), "make": "Maruti", "model": "PCM", "last_odometer":5000, "acquisition_date":frappe.utils.nowdate(), "location": "Mumbai", "chassis_no": "1234ABCD", "vehicle_value":frappe.utils.flt(500000) }) try: vehicle.insert() except frappe.DuplicateEntryError: pass vehicle_log = frappe.get_doc({ "doctype": "Vehicle Log", "license_plate": cstr(license_plate), "employee":employee_id, "date":frappe.utils.nowdate(), "odometer":5010, "fuel_qty":frappe.utils.flt(50), "price": frappe.utils.flt(500) }) vehicle_log.insert() vehicle_log.submit()
def check_if_latest(self): conflict = False self._action = "save" if not self.get('__islocal'): if self.meta.issingle: modified = frappe.db.get_value(self.doctype, self.name, "modified") if cstr(modified) and cstr(modified) != cstr(self._original_modified): conflict = True else: tmp = frappe.db.get_value(self.doctype, self.name, ["modified", "docstatus"], as_dict=True) if not tmp: frappe.throw(_("Record does not exist")) modified = cstr(tmp.modified) if modified and modified != cstr(self._original_modified): conflict = True self.check_docstatus_transition(tmp.docstatus) if conflict: frappe.msgprint(_("Error: Document has been modified after you have opened it") \ + (" (%s, %s). " % (modified, self.modified)) \ + _("Please refresh to get the latest document."), raise_exception=frappe.TimestampMismatchError) else: self.check_docstatus_transition(0)
def update_outstanding_amt(account, party_type, party, against_voucher_type, against_voucher, on_cancel=False): # get final outstanding amt bal = flt(frappe.db.sql("""select sum(ifnull(debit, 0)) - sum(ifnull(credit, 0)) from `tabGL Entry` where against_voucher_type=%s and against_voucher=%s and account = %s and ifnull(party_type, '')=%s and ifnull(party, '')=%s""", (against_voucher_type, against_voucher, account, party_type, party))[0][0] or 0.0) if against_voucher_type == 'Purchase Invoice': bal = -bal elif against_voucher_type == "Journal Entry": against_voucher_amount = flt(frappe.db.sql(""" select sum(ifnull(debit, 0)) - sum(ifnull(credit, 0)) from `tabGL Entry` where voucher_type = 'Journal Entry' and voucher_no = %s and account = %s and ifnull(party_type, '')=%s and ifnull(party, '')=%s and ifnull(against_voucher, '') = ''""", (against_voucher, account, cstr(party_type), cstr(party)))[0][0]) if not against_voucher_amount: frappe.throw(_("Against Journal Entry {0} is already adjusted against some other voucher") .format(against_voucher)) bal = against_voucher_amount + bal if against_voucher_amount < 0: bal = -bal # Validation : Outstanding can not be negative if bal < 0 and not on_cancel: frappe.throw(_("Outstanding for {0} cannot be less than zero ({1})").format(against_voucher, fmt_money(bal))) # Update outstanding amt on against voucher if against_voucher_type in ["Sales Invoice", "Purchase Invoice"]: frappe.db.sql("update `tab%s` set outstanding_amount=%s where name=%s" % (against_voucher_type, '%s', '%s'), (bal, against_voucher))
def validate_for_items(self): chk_dupl_itm = [] for d in self.get('quotation_details'): if [cstr(d.item_code),cstr(d.description)] in chk_dupl_itm: frappe.throw(_("Item {0} with same description entered twice").format(d.item_code)) else: chk_dupl_itm.append([cstr(d.item_code),cstr(d.description)])
def update_against_doc(d, jv_obj): """ Updates against document, if partial amount splits into rows """ jv_detail = jv_obj.get("entries", {"name": d["voucher_detail_no"]})[0] jv_detail.set(d["dr_or_cr"], d["allocated_amt"]) jv_detail.set(d["against_fld"], d["against_voucher"]) if d["allocated_amt"] < d["unadjusted_amt"]: jvd = frappe.db.sql( """select cost_center, balance, against_account, is_advance from `tabJournal Voucher Detail` where name = %s""", d["voucher_detail_no"], ) # new entry with balance amount ch = jv_obj.append("entries") ch.account = d["account"] ch.cost_center = cstr(jvd[0][0]) ch.balance = cstr(jvd[0][1]) ch.set(d["dr_or_cr"], flt(d["unadjusted_amt"]) - flt(d["allocated_amt"])) ch.set(d["dr_or_cr"] == "debit" and "credit" or "debit", 0) ch.against_account = cstr(jvd[0][2]) ch.is_advance = cstr(jvd[0][3]) ch.docstatus = 1 # will work as update after submit jv_obj.ignore_validate_update_after_submit = True jv_obj.save()
def update_against_doc(d, jv_obj): """ Updates against document, if partial amount splits into rows """ jv_detail = jv_obj.get("accounts", {"name": d["voucher_detail_no"]})[0] jv_detail.set(d["dr_or_cr"], d["allocated_amt"]) jv_detail.set(d["against_fld"], d["against_voucher"]) if d['allocated_amt'] < d['unadjusted_amt']: jvd = frappe.db.sql("""select cost_center, balance, against_account, is_advance from `tabJournal Entry Account` where name = %s""", d['voucher_detail_no']) # new entry with balance amount ch = jv_obj.append("accounts") ch.account = d['account'] ch.party_type = d["party_type"] ch.party = d["party"] ch.cost_center = cstr(jvd[0][0]) ch.balance = flt(jvd[0][1]) ch.set(d['dr_or_cr'], flt(d['unadjusted_amt']) - flt(d['allocated_amt'])) ch.set(d['dr_or_cr']== 'debit' and 'credit' or 'debit', 0) ch.against_account = cstr(jvd[0][2]) ch.is_advance = cstr(jvd[0][3]) ch.docstatus = 1 # will work as update after submit jv_obj.flags.ignore_validate_update_after_submit = True jv_obj.save()
def get_outstanding_invoices(self): self.set("accounts", []) total = 0 for d in self.get_values(): total += flt(d.outstanding_amount, self.precision("credit", "accounts")) jd1 = self.append("accounts", {}) jd1.account = d.account jd1.party = d.party if self.write_off_based_on == "Accounts Receivable": jd1.party_type = "Customer" jd1.credit = flt(d.outstanding_amount, self.precision("credit", "accounts")) jd1.against_invoice = cstr(d.name) elif self.write_off_based_on == "Accounts Payable": jd1.party_type = "Supplier" jd1.debit = flt(d.outstanding_amount, self.precision("debit", "accounts")) jd1.against_voucher = cstr(d.name) jd2 = self.append("accounts", {}) if self.write_off_based_on == "Accounts Receivable": jd2.debit = total elif self.write_off_based_on == "Accounts Payable": jd2.credit = total self.validate_debit_and_credit()
def get_data(filters): conditions, filters = get_conditions(filters) data = frappe.db.sql(""" select t1.employee, t3.employee_name, t1.designation, t3.passport_number, t3.date_of_birth, t1.employee_subgroup, t3.gis_number, t2.amount, t1.company, t1.branch, t1.department, t1.division, t1.section, t1.fiscal_year, t1.month from `tabSalary Slip` t1, `tabSalary Detail` t2, `tabEmployee` t3 where t1.docstatus = 1 %s and t3.employee = t1.employee and t2.parent = t1.name and t2.parentfield = 'deductions' and exists(select 1 from `tabSalary Component` sc where sc.name = t2.salary_component and sc.gl_head = 'Group Insurance Scheme - SMCL') order by t1.employee """ % conditions, filters) if not data: msgprint(_("No Data Found for month: ") + cstr(filters.get("month")) + _(" and year: ") + cstr(filters.get("fiscal_year")), raise_exception=1) return data
def get_invoice_summary(items, taxes): summary_data = frappe._dict() for tax in taxes: #Include only VAT charges. if tax.charge_type == "Actual": continue #Charges to appear as items in the e-invoice. if tax.charge_type in [ "On Previous Row Total", "On Previous Row Amount" ]: reference_row = next( (row for row in taxes if row.idx == int(tax.row_id or 0)), None) if reference_row: items.append( frappe._dict( idx=len(items) + 1, item_code=reference_row.description, item_name=reference_row.description, description=reference_row.description, rate=reference_row.tax_amount, qty=1.0, amount=reference_row.tax_amount, stock_uom=frappe.db.get_single_value( "Stock Settings", "stock_uom") or _("Nos"), tax_rate=tax.rate, tax_amount=(reference_row.tax_amount * tax.rate) / 100, net_amount=reference_row.tax_amount, taxable_amount=reference_row.tax_amount, item_tax_rate={tax.account_head: tax.rate}, charges=True)) #Check item tax rates if tax rate is zero. if tax.rate == 0: for item in items: item_tax_rate = item.item_tax_rate if isinstance(item.item_tax_rate, string_types): item_tax_rate = json.loads(item.item_tax_rate) if item_tax_rate and tax.account_head in item_tax_rate: key = cstr(item_tax_rate[tax.account_head]) if key not in summary_data: summary_data.setdefault( key, { "tax_amount": 0.0, "taxable_amount": 0.0, "tax_exemption_reason": "", "tax_exemption_law": "" }) summary_data[key]["tax_amount"] += item.tax_amount summary_data[key]["taxable_amount"] += item.net_amount if key == "0.0": summary_data[key][ "tax_exemption_reason"] = tax.tax_exemption_reason summary_data[key][ "tax_exemption_law"] = tax.tax_exemption_law if summary_data == {}: #Implies that Zero VAT has not been set on any item. summary_data.setdefault( "0.0", { "tax_amount": 0.0, "taxable_amount": tax.total, "tax_exemption_reason": tax.tax_exemption_reason, "tax_exemption_law": tax.tax_exemption_law }) else: item_wise_tax_detail = json.loads(tax.item_wise_tax_detail) for rate_item in [ tax_item for tax_item in item_wise_tax_detail.items() if tax_item[1][0] == tax.rate ]: key = cstr(tax.rate) if not summary_data.get(key): summary_data.setdefault(key, { "tax_amount": 0.0, "taxable_amount": 0.0 }) summary_data[key]["tax_amount"] += rate_item[1][1] summary_data[key]["taxable_amount"] += sum([ item.net_amount for item in items if item.item_code == rate_item[0] ]) for item in items: key = cstr(tax.rate) if item.get("charges"): if not summary_data.get(key): summary_data.setdefault(key, {"taxable_amount": 0.0}) summary_data[key]["taxable_amount"] += item.taxable_amount return summary_data
def validate_warehouse(self): """perform various (sometimes conditional) validations on warehouse""" source_mandatory = [ "Material Issue", "Material Transfer", "Subcontract", "Material Transfer for Manufacture" ] target_mandatory = [ "Material Receipt", "Material Transfer", "Subcontract", "Material Transfer for Manufacture" ] validate_for_manufacture_repack = any( [d.bom_no for d in self.get("items")]) if self.purpose in source_mandatory and self.purpose not in target_mandatory: self.to_warehouse = None for d in self.get('items'): d.t_warehouse = None elif self.purpose in target_mandatory and self.purpose not in source_mandatory: self.from_warehouse = None for d in self.get('items'): d.s_warehouse = None for d in self.get('items'): if not d.s_warehouse and not d.t_warehouse: d.s_warehouse = self.from_warehouse d.t_warehouse = self.to_warehouse if not (d.s_warehouse or d.t_warehouse): frappe.throw(_("Atleast one warehouse is mandatory")) if self.purpose in source_mandatory and not d.s_warehouse: if self.from_warehouse: d.s_warehouse = self.from_warehouse else: frappe.throw( _("Source warehouse is mandatory for row {0}").format( d.idx)) if self.purpose in target_mandatory and not d.t_warehouse: if self.to_warehouse: d.t_warehouse = self.to_warehouse else: frappe.throw( _("Target warehouse is mandatory for row {0}").format( d.idx)) if self.purpose in ["Manufacture", "Repack"]: if validate_for_manufacture_repack: if d.bom_no: d.s_warehouse = None if not d.t_warehouse: frappe.throw( _("Target warehouse is mandatory for row {0}"). format(d.idx)) elif self.pro_doc and cstr( d.t_warehouse) != self.pro_doc.fg_warehouse: frappe.throw( _("Target warehouse in row {0} must be same as Production Order" ).format(d.idx)) else: d.t_warehouse = None if not d.s_warehouse: frappe.throw( _("Source warehouse is mandatory for row {0}"). format(d.idx)) if cstr(d.s_warehouse) == cstr(d.t_warehouse): frappe.throw( _("Source and target warehouse cannot be same for row {0}" ).format(d.idx))
def get_item_list(self): il = [] for d in self.get("items"): if d.qty is None: frappe.throw(_("Row {0}: Qty is mandatory").format(d.idx)) if self.has_product_bundle(d.item_code): for p in self.get("packed_items"): if p.parent_detail_docname == d.name and p.parent_item == d.item_code: # the packing details table's qty is already multiplied with parent's qty il.append( frappe._dict({ 'warehouse': p.warehouse or d.warehouse, 'item_code': p.item_code, 'qty': flt(p.qty), 'uom': p.uom, 'batch_no': cstr(p.batch_no).strip(), 'serial_no': cstr(p.serial_no).strip(), 'name': d.name, 'target_warehouse': p.target_warehouse, 'company': self.company, 'voucher_type': self.doctype, 'allow_zero_valuation': d.allow_zero_valuation_rate })) else: il.append( frappe._dict({ 'warehouse': d.warehouse, 'item_code': d.item_code, 'qty': d.stock_qty, 'uom': d.uom, 'stock_uom': d.stock_uom, 'conversion_factor': d.conversion_factor, 'batch_no': cstr(d.get("batch_no")).strip(), 'serial_no': cstr(d.get("serial_no")).strip(), 'name': d.name, 'target_warehouse': d.target_warehouse, 'company': self.company, 'voucher_type': self.doctype, 'allow_zero_valuation': d.allow_zero_valuation_rate })) return il
def validate_bom(self): if cstr(self.current_bom) == cstr(self.new_bom): frappe.throw(_("Current BOM and New BOM can not be same"))
def execute_job(site, method, event, job_name, kwargs, user=None, is_async=True, retry=0): """Executes job in a worker, performs commit/rollback and logs if there is any error""" if is_async: frappe.connect(site) if os.environ.get("CI"): frappe.flags.in_test = True if user: frappe.set_user(user) if isinstance(method, string_types): method_name = method method = frappe.get_attr(method) else: method_name = cstr(method.__name__) frappe.monitor.start("job", method_name, kwargs) try: method(**kwargs) except (frappe.db.InternalError, frappe.RetryBackgroundJobError) as e: frappe.db.rollback() if retry < 5 and (isinstance(e, frappe.RetryBackgroundJobError) or (frappe.db.is_deadlocked(e) or frappe.db.is_timedout(e))): # retry the job if # 1213 = deadlock # 1205 = lock wait timeout # or RetryBackgroundJobError is explicitly raised frappe.destroy() time.sleep(retry + 1) return execute_job(site, method, event, job_name, kwargs, is_async=is_async, retry=retry + 1) else: frappe.log_error(title=method_name) raise except: frappe.db.rollback() frappe.log_error(title=method_name) frappe.db.commit() print(frappe.get_traceback()) raise else: frappe.db.commit() finally: frappe.monitor.stop() if is_async: frappe.destroy()
def create_pos_profile(doc): item_groups = [] for service_type in doc.services_type: if service_type.is_provided == 'Yes': item_groups.append({ "doctype": "POS Item Group", "item_group": cstr(service_type.services) }) pos_profile = frappe.get_doc({ "doctype": "POS Profile", "pos_profile_name": str(doc.employee_name) + " - " + str(doc.name), "naming_series": "SINV-", "update_stock": 1, "ignore_pricing_rule": 1, "allow_delete": 1, "allow_user_to_edit_rate": 1, "allow_user_to_edit_discount": 1, "allow_print_before_pay": 1, "warehouse": frappe.db.get_value("Branch", doc.branch, "warehouse"), "select_print_heading": str(doc.branch), "selling_price_list": str(doc.employee_price_list), "write_off_account": "Write Off - " + str(frappe.db.get_value("Company", doc.company, "abbr")), "write_off_cost_center": str(doc.branch) + " - " + str(frappe.db.get_value("Company", doc.company, "abbr")), "income_account": str(doc.branch) + " - " + str(frappe.db.get_value("Company", doc.company, "abbr")), "cost_center": str(doc.branch) + " - " + str(frappe.db.get_value("Company", doc.company, "abbr")), "employee": str(doc.name), "item_groups": item_groups, "letter_head": frappe.db.get_value("Company", doc.company, "default_letter_head"), "tc_name": frappe.db.get_value("Company", doc.company, "default_terms"), "taxes_and_charges": frappe.db.get_value("Sales Taxes and Charges Template", {"is_default": 1}, "name"), "territory": frappe.db.get_value("Branch", doc.branch, "territory") }) pos_profile.flags.ignore_permissions = True pos_profile.insert()
def on_update(doc, method): # --------------------------------------------------------------------------- # update pos profile # --------------------------------------------------------------------------- */ if frappe.db.get_value("POS Profile", {"employee": cstr(doc.employee)}, "name"): # --------------------------------------------------------------------------- # update pos profile warehouse # --------------------------------------------------------------------------- */ pos_profile_warehouse = frappe.db.get_value( "POS Profile", {"employee": cstr(doc.name)}, "warehouse") branch_warehouse = frappe.db.get_value("Branch", doc.branch, "warehouse") if branch_warehouse != pos_profile_warehouse: frappe.db.set_value("POS Profile", {"employee": cstr(doc.name)}, "warehouse", cstr(branch_warehouse)) frappe.db.commit() # --------------------------------------------------------------------------- # update pos profile item groups # --------------------------------------------------------------------------- */ for service_type in doc.services_type: pos_profile = frappe.get_doc( "POS Profile", cstr(doc.employee_name) + " - " + cstr(doc.name)) if service_type.is_provided == "Yes": if len(pos_profile.item_groups) > 0: pos_lambda = lambda i: "Yes" if cstr( pos_profile.item_groups[i].item_group).strip() == cstr( service_type.services).strip() else "No" is_exist = "No" for j in range(len(pos_profile.item_groups)): is_exist = pos_lambda(j) if is_exist == "Yes": break if is_exist == "No": # Add service in pos profile pos_profile.append( "item_groups", { "item_group": cstr(service_type.services), "doctype": "POS Item Group" }) pos_profile.flags.ignore_permissions = True pos_profile.save() if len(pos_profile.item_groups) == 0: # Add service in pos profile pos_profile.append( "item_groups", { "item_group": cstr(service_type.services), "doctype": "POS Item Group" }) pos_profile.flags.ignore_permissions = True pos_profile.save() elif service_type.is_provided == "No": # Delete service in pos pos profile service_name = frappe.db.get_value( "POS Item Group", { "parent": cstr(pos_profile.name), "item_group": cstr(service_type.services) }, "name") delete = frappe.delete_doc("POS Item Group", cstr(service_name)) frappe.db.commit() # --------------------------------------------------------------------------- # update pos profile price list # --------------------------------------------------------------------------- */ pos_profile_price_list = frappe.db.get_value( "POS Profile", {"employee": cstr(doc.name)}, "selling_price_list") if cstr(pos_profile_price_list) != cstr(doc.employee_price_list): frappe.db.set_value("POS Profile", {"employee": cstr(doc.name)}, "selling_price_list", cstr(doc.employee_price_list)) frappe.db.commit() # --------------------------------------------------------------------------- # update pos print heading # --------------------------------------------------------------------------- */ pos_profile_print_heading = frappe.db.get_value( "POS Profile", {"employee": cstr(doc.name)}, "select_print_heading") print_heading = frappe.db.get_value("Print Heading", cstr(doc.branch), "name") if cstr(pos_profile_print_heading) != cstr(print_heading): frappe.db.set_value("POS Profile", {"employee": cstr(doc.name)}, "select_print_heading", cstr(print_heading)) frappe.db.commit() # --------------------------------------------------------------------------- # update pos write off cost center # --------------------------------------------------------------------------- */ pos_profile_write_off_cost_center = frappe.db.get_value( "POS Profile", {"employee": cstr(doc.name)}, "write_off_cost_center") branch_cost_center = frappe.db.get_value("Branch", cstr(doc.branch), "cost_center") if cstr(pos_profile_write_off_cost_center) != cstr(branch_cost_center): frappe.db.set_value("POS Profile", {"employee": cstr(doc.name)}, "write_off_cost_center", cstr(branch_cost_center)) frappe.db.commit() # --------------------------------------------------------------------------- # update pos cost center # --------------------------------------------------------------------------- */ pos_profile_cost_center = frappe.db.get_value( "POS Profile", {"employee": cstr(doc.name)}, "cost_center") branch_cost_center = frappe.db.get_value("Branch", cstr(doc.branch), "cost_center") if cstr(pos_profile_cost_center) != cstr(branch_cost_center): frappe.db.set_value("POS Profile", {"employee": cstr(doc.name)}, "cost_center", cstr(branch_cost_center)) frappe.db.commit() # --------------------------------------------------------------------------- # update income account # --------------------------------------------------------------------------- */ pos_profile_income_account = frappe.db.get_value( "POS Profile", {"employee": cstr(doc.name)}, "income_account") income_account = cstr(doc.branch) + " - " + frappe.db.get_value( "Company", doc.company, "abbr") if cstr(pos_profile_income_account) != cstr(income_account): frappe.db.set_value("POS Profile", {"employee": cstr(doc.name)}, "income_account", cstr(income_account)) frappe.db.commit() # --------------------------------------------------------------------------- # update pos territory # --------------------------------------------------------------------------- */ pos_profile_territory = frappe.db.get_value( "POS Profile", {"employee": cstr(doc.name)}, "territory") territory = frappe.db.get_value("Branch", cstr(doc.branch), "territory") if cstr(pos_profile_territory) != cstr(print_heading): frappe.db.set_value("POS Profile", {"employee": cstr(doc.name)}, "territory", cstr(territory)) frappe.db.commit()
def link_exists(self, value, df): key = df.options + "::" + cstr(value) if Row.link_values_exist_map.get(key) is None: Row.link_values_exist_map[key] = frappe.db.exists(df.options, value) return Row.link_values_exist_map.get(key)
def update_status(self, status): self.check_modified_date() frappe.db.set(self, 'status', cstr(status)) self.update_requested_qty() frappe.msgprint(_("Status updated to {0}").format(_(status)))
def block_invoice(self, hold_comment=None): self.db_set('on_hold', 1) self.db_set('hold_comment', cstr(hold_comment))
def get_balance_on(account=None, date=None, party_type=None, party=None, in_account_currency=True): if not account and frappe.form_dict.get("account"): account = frappe.form_dict.get("account") if not date and frappe.form_dict.get("date"): date = frappe.form_dict.get("date") if not party_type and frappe.form_dict.get("party_type"): party_type = frappe.form_dict.get("party_type") if not party and frappe.form_dict.get("party"): party = frappe.form_dict.get("party") cond = [] if date: cond.append("posting_date <= '%s'" % frappe.db.escape(cstr(date))) else: # get balance of all entries that exist date = nowdate() try: year_start_date = get_fiscal_year(date, verbose=0)[1] except FiscalYearError: if getdate(date) > getdate(nowdate()): # if fiscal year not found and the date is greater than today # get fiscal year for today's date and its corresponding year start date year_start_date = get_fiscal_year(nowdate(), verbose=1)[1] else: # this indicates that it is a date older than any existing fiscal year. # hence, assuming balance as 0.0 return 0.0 if account: acc = frappe.get_doc("Account", account) if not frappe.flags.ignore_account_permission: acc.check_permission("read") # for pl accounts, get balance within a fiscal year if acc.report_type == 'Profit and Loss': cond.append("posting_date >= '%s' and voucher_type != 'Period Closing Voucher'" \ % year_start_date) # different filter for group and ledger - improved performance if acc.is_group: cond.append("""exists ( select name from `tabAccount` ac where ac.name = gle.account and ac.lft >= %s and ac.rgt <= %s )""" % (acc.lft, acc.rgt)) # If group and currency same as company, # always return balance based on debit and credit in company currency if acc.account_currency == frappe.db.get_value( "Company", acc.company, "default_currency"): in_account_currency = False else: cond.append("""gle.account = "%s" """ % (frappe.db.escape(account, percent=False), )) if party_type and party: cond.append("""gle.party_type = "%s" and gle.party = "%s" """ % (frappe.db.escape(party_type), frappe.db.escape(party, percent=False))) if account or (party_type and party): if in_account_currency: select_field = "sum(debit_in_account_currency) - sum(credit_in_account_currency)" else: select_field = "sum(debit) - sum(credit)" bal = frappe.db.sql(""" SELECT {0} FROM `tabGL Entry` gle WHERE {1}""".format(select_field, " and ".join(cond)))[0][0] # if bal is None, return 0 return flt(bal)
def get_basic_details(args, item, overwrite_warehouse=True): """ :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() item_defaults = get_item_defaults(item.name, args.company) item_group_defaults = get_item_group_defaults(item.name, args.company) brand_defaults = get_brand_defaults(item.name, args.company) if overwrite_warehouse or not args.warehouse: warehouse = (args.get("set_warehouse") or item_defaults.get("default_warehouse") or item_group_defaults.get("default_warehouse") or brand_defaults.get("default_warehouse") or args.warehouse) if not warehouse: defaults = frappe.defaults.get_defaults() or {} warehouse_exists = frappe.db.exists("Warehouse", { 'name': defaults.default_warehouse, 'company': args.company }) if defaults.get("default_warehouse") and warehouse_exists: warehouse = defaults.default_warehouse else: warehouse = args.warehouse if args.get('doctype') == "Material Request" and not args.get( 'material_request_type'): args['material_request_type'] = frappe.db.get_value( 'Material Request', args.get('name'), 'material_request_type', cache=True) expense_account = None if args.get('doctype') == 'Purchase Invoice' and item.is_fixed_asset: from erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account expense_account = get_asset_category_account( fieldname="fixed_asset_account", item=args.item_code, company=args.company) #Set the UOM to the Default Sales UOM or Default Purchase UOM if configured in the Item Master if not args.uom: if args.get('doctype') in sales_doctypes: args.uom = item.sales_uom if item.sales_uom else item.stock_uom elif (args.get('doctype') in ['Purchase Order', 'Purchase Receipt', 'Purchase Invoice']) or \ (args.get('doctype') == 'Material Request' and args.get('material_request_type') == 'Purchase'): args.uom = item.purchase_uom if item.purchase_uom else item.stock_uom else: args.uom = 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, brand_defaults), "expense_account": expense_account or get_default_expense_account( args, item_defaults, item_group_defaults, brand_defaults), "cost_center": get_default_cost_center(args, item_defaults, item_group_defaults, brand_defaults), 'has_serial_no': item.has_serial_no, 'has_batch_no': item.has_batch_no, "batch_no": None, "uom": args.uom, "min_order_qty": flt(item.min_order_qty) if args.doctype == "Material Request" else "", "qty": flt(args.qty) or 1.0, "stock_qty": flt(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, brand_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 if args.get('doctype') in purchase_doctypes: 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) if args.get("manufacturer"): part_no = get_item_manufacturer_part_no(args.get("item_code"), args.get("manufacturer")) if part_no: out["manufacturer_part_no"] = part_no else: out["manufacturer_part_no"] = None out["manufacturer"] = None child_doctype = args.doctype + ' Item' meta = frappe.get_meta(child_doctype) if meta.get_field("barcode"): update_barcode_value(out) return out
def has_login_limit_exceeded(self, e): return "-ERR Exceeded the login limit" in strip(cstr(e.message))
def autoname(self): self.name = " ".join(filter(None, [cstr(self.get(f)).strip() for f in ["number", "period"]]))
def encrypt(pwd): cipher_suite = Fernet(encode(get_encryption_key())) cipher_text = cstr(cipher_suite.encrypt(encode(pwd))) return cipher_text
def update_reference_in_journal_entry(d, jv_obj): """ Updates against document, if partial amount splits into rows """ jv_detail = jv_obj.get("accounts", {"name": d["voucher_detail_no"]})[0] jv_detail.set(d["dr_or_cr"], d["allocated_amount"]) jv_detail.set( 'debit' if d['dr_or_cr'] == 'debit_in_account_currency' else 'credit', d["allocated_amount"] * flt(jv_detail.exchange_rate)) original_reference_type = jv_detail.reference_type original_reference_name = jv_detail.reference_name jv_detail.set("reference_type", d["against_voucher_type"]) jv_detail.set("reference_name", d["against_voucher"]) if d['allocated_amount'] < d['unadjusted_amount']: jvd = frappe.db.sql(""" select cost_center, balance, against_account, is_advance, account_type, exchange_rate, account_currency from `tabJournal Entry Account` where name = %s """, d['voucher_detail_no'], as_dict=True) amount_in_account_currency = flt(d['unadjusted_amount']) - flt( d['allocated_amount']) amount_in_company_currency = amount_in_account_currency * flt( jvd[0]['exchange_rate']) # new entry with balance amount ch = jv_obj.append("accounts") ch.account = d['account'] ch.account_type = jvd[0]['account_type'] ch.account_currency = jvd[0]['account_currency'] ch.exchange_rate = jvd[0]['exchange_rate'] ch.party_type = d["party_type"] ch.party = d["party"] ch.cost_center = cstr(jvd[0]["cost_center"]) ch.balance = flt(jvd[0]["balance"]) ch.set(d['dr_or_cr'], amount_in_account_currency) ch.set( 'debit' if d['dr_or_cr'] == 'debit_in_account_currency' else 'credit', amount_in_company_currency) ch.set( 'credit_in_account_currency' if d['dr_or_cr'] == 'debit_in_account_currency' else 'debit_in_account_currency', 0) ch.set( 'credit' if d['dr_or_cr'] == 'debit_in_account_currency' else 'debit', 0) ch.against_account = cstr(jvd[0]["against_account"]) ch.reference_type = original_reference_type ch.reference_name = original_reference_name ch.is_advance = cstr(jvd[0]["is_advance"]) ch.docstatus = 1 # will work as update after submit jv_obj.flags.ignore_validate_update_after_submit = True jv_obj.save(ignore_permissions=True)
def set_stock_balance_as_per_serial_no(item_code=None, posting_date=None, posting_time=None, fiscal_year=None): if not posting_date: posting_date = nowdate() if not posting_time: posting_time = nowtime() condition = " and item.name='%s'" % item_code.replace( "'", "\'") if item_code else "" bin = frappe.db.sql( """select bin.item_code, bin.warehouse, bin.actual_qty, item.stock_uom from `tabBin` bin, tabItem item where bin.item_code = item.name and item.has_serial_no = 1 %s""" % condition) for d in bin: serial_nos = frappe.db.sql( """select count(name) from `tabSerial No` where item_code=%s and warehouse=%s and docstatus < 2""", (d[0], d[1])) if serial_nos and flt(serial_nos[0][0]) != flt(d[2]): print(d[0], d[1], d[2], serial_nos[0][0]) sle = frappe.db.sql( """select valuation_rate, company from `tabStock Ledger Entry` where item_code = %s and warehouse = %s and is_cancelled = 0 order by posting_date desc limit 1""", (d[0], d[1])) sle_dict = { 'doctype': 'Stock Ledger Entry', 'item_code': d[0], 'warehouse': d[1], 'transaction_date': nowdate(), 'posting_date': posting_date, 'posting_time': posting_time, 'voucher_type': 'Stock Reconciliation (Manual)', 'voucher_no': '', 'voucher_detail_no': '', 'actual_qty': flt(serial_nos[0][0]) - flt(d[2]), 'stock_uom': d[3], 'incoming_rate': sle and flt(serial_nos[0][0]) > flt(d[2]) and flt(sle[0][0]) or 0, 'company': sle and cstr(sle[0][1]) or 0, 'batch_no': '', 'serial_no': '' } sle_doc = frappe.get_doc(sle_dict) sle_doc.flags.ignore_validate = True sle_doc.flags.ignore_links = True sle_doc.insert() args = sle_dict.copy() args.update({"sle_id": sle_doc.name}) update_bin(args) create_repost_item_valuation_entry({ "item_code": d[0], "warehouse": d[1], "posting_date": posting_date, "posting_time": posting_time })
def make_route(self): if not self.route: return cstr(frappe.db.get_value('Item Group', self.item_group, 'route')) + '/' + self.scrub(self.item_name + '-' + random_string(5))
def send_form_sms(self, arg): "called from client side" args = json.loads(arg) self.send_sms([cstr(args['number'])], cstr(args['message']))
def get_serial_nos(serial_no): return [s.strip() for s in cstr(serial_no).strip().upper().replace(',', '\n').split('\n') if s.strip()]
def generate_report_result(report, filters=None, user=None): status = None if not user: user = frappe.session.user if not filters: filters = [] if filters and isinstance(filters, string_types): filters = json.loads(filters) columns, result, message, chart, data_to_be_printed = [], [], None, None, None if report.report_type == "Query Report": if not report.query: status = "error" frappe.msgprint(_("Must specify a Query to run"), raise_exception=True) if not report.query.lower().startswith("select"): status = "error" frappe.msgprint(_("Query must be a SELECT"), raise_exception=True) result = [list(t) for t in frappe.db.sql(report.query, filters)] columns = [cstr(c[0]) for c in frappe.db.get_description()] else: module = report.module or frappe.db.get_value( "DocType", report.ref_doctype, "module") if report.is_standard == "Yes": method_name = get_report_module_dotted_path( module, report.name) + ".execute" threshold = 30 res = [] start_time = datetime.datetime.now() # The JOB res = frappe.get_attr(method_name)(frappe._dict(filters)) end_time = datetime.datetime.now() execution_time = (end_time - start_time).seconds if execution_time > threshold and not report.prepared_report: report.db_set('prepared_report', 1) frappe.cache().hset('report_execution_time', report.name, execution_time) columns, result = res[0], res[1] if len(res) > 2: message = res[2] if len(res) > 3: chart = res[3] if len(res) > 4: data_to_be_printed = res[4] if report.custom_columns: columns = json.loads(report.custom_columns) result = add_data_to_custom_columns(columns, result) if result: result = get_filtered_data(report.ref_doctype, columns, result, user) if cint(report.add_total_row) and result: result = add_total_row(result, columns) return { "result": result, "columns": columns, "message": message, "chart": chart, "data_to_be_printed": data_to_be_printed, "status": status, "execution_time": frappe.cache().hget('report_execution_time', report.name) or 0 }
def _get_children(bom_no): return [ cstr(d[0]) for d in frappe.db.sql( """select bom_no from `tabBOM Item` where parent = %s and ifnull(bom_no, '') != ''""", bom_no) ]
def get_items(self): self.set('mtn_details', []) self.validate_production_order() pro_obj = None if self.production_order: # common validations pro_obj = frappe.get_doc('Production Order', self.production_order) if pro_obj: self.bom_no = pro_obj.bom_no else: # invalid production order self.production_order = None if self.bom_no: if self.purpose in ["Material Issue", "Material Transfer", "Manufacture", "Repack", "Subcontract"]: if self.production_order and self.purpose == "Material Transfer": item_dict = self.get_pending_raw_materials(pro_obj) if self.to_warehouse and pro_obj: for item in item_dict.values(): item["to_warehouse"] = pro_obj.wip_warehouse else: if not self.fg_completed_qty: frappe.throw(_("Manufacturing Quantity is mandatory")) item_dict = self.get_bom_raw_materials(self.fg_completed_qty) for item in item_dict.values(): if pro_obj: item["from_warehouse"] = pro_obj.wip_warehouse item["to_warehouse"] = self.to_warehouse if self.purpose=="Subcontract" else "" # add raw materials to Stock Entry Detail table self.add_to_stock_entry_detail(item_dict) # add finished good item to Stock Entry Detail table -- along with bom_no if self.production_order and self.purpose == "Manufacture": item = frappe.db.get_value("Item", pro_obj.production_item, ["item_name", "description", "stock_uom", "expense_account", "buying_cost_center"], as_dict=1) self.add_to_stock_entry_detail({ cstr(pro_obj.production_item): { "to_warehouse": pro_obj.fg_warehouse, "from_warehouse": "", "qty": self.fg_completed_qty, "item_name": item.item_name, "description": item.description, "stock_uom": item.stock_uom, "expense_account": item.expense_account, "cost_center": item.buying_cost_center, } }, bom_no=pro_obj.bom_no) elif self.purpose in ["Material Receipt", "Repack"]: if self.purpose=="Material Receipt": self.from_warehouse = "" item = frappe.db.sql("""select name, item_name, description, stock_uom, expense_account, buying_cost_center from `tabItem` where name=(select item from tabBOM where name=%s)""", self.bom_no, as_dict=1) self.add_to_stock_entry_detail({ item[0]["name"] : { "qty": self.fg_completed_qty, "item_name": item[0].item_name, "description": item[0]["description"], "stock_uom": item[0]["stock_uom"], "from_warehouse": "", "expense_account": item[0].expense_account, "cost_center": item[0].buying_cost_center, } }, bom_no=self.bom_no) self.get_stock_and_rate()
def make_boilerplate(dest, app_name): if not os.path.exists(dest): print("Destination directory does not exist") return # app_name should be in snake_case app_name = frappe.scrub(app_name) hooks = frappe._dict() hooks.app_name = app_name app_title = hooks.app_name.replace("_", " ").title() for key in ("App Title (default: {0})".format(app_title), "App Description", "App Publisher", "App Email", "App Icon (default 'octicon octicon-file-directory')", "App Color (default 'grey')", "App License (default 'MIT')"): hook_key = key.split(" (")[0].lower().replace(" ", "_") hook_val = None while not hook_val: hook_val = cstr(input(key + ": ")) if not hook_val: defaults = { "app_title": app_title, "app_icon": "octicon octicon-file-directory", "app_color": "grey", "app_license": "MIT" } if hook_key in defaults: hook_val = defaults[hook_key] if hook_key=="app_name" and hook_val.lower().replace(" ", "_") != hook_val: print("App Name must be all lowercase and without spaces") hook_val = "" elif hook_key=="app_title" and not re.match("^(?![\W])[^\d_\s][\w -]+$", hook_val, re.UNICODE): print("App Title should start with a letter and it can only consist of letters, numbers, spaces and underscores") hook_val = "" hooks[hook_key] = hook_val frappe.create_folder(os.path.join(dest, hooks.app_name, hooks.app_name, frappe.scrub(hooks.app_title)), with_init=True) frappe.create_folder(os.path.join(dest, hooks.app_name, hooks.app_name, "templates"), with_init=True) frappe.create_folder(os.path.join(dest, hooks.app_name, hooks.app_name, "www")) frappe.create_folder(os.path.join(dest, hooks.app_name, hooks.app_name, "templates", "pages"), with_init=True) frappe.create_folder(os.path.join(dest, hooks.app_name, hooks.app_name, "templates", "includes")) frappe.create_folder(os.path.join(dest, hooks.app_name, hooks.app_name, "config"), with_init=True) frappe.create_folder(os.path.join(dest, hooks.app_name, hooks.app_name, "public", "css")) frappe.create_folder(os.path.join(dest, hooks.app_name, hooks.app_name, "public", "js")) with open(os.path.join(dest, hooks.app_name, hooks.app_name, "__init__.py"), "w") as f: f.write(encode(init_template)) with open(os.path.join(dest, hooks.app_name, "MANIFEST.in"), "w") as f: f.write(encode(manifest_template.format(**hooks))) with open(os.path.join(dest, hooks.app_name, ".gitignore"), "w") as f: f.write(encode(gitignore_template.format(app_name = hooks.app_name))) with open(os.path.join(dest, hooks.app_name, "setup.py"), "w") as f: f.write(encode(setup_template.format(**hooks))) with open(os.path.join(dest, hooks.app_name, "requirements.txt"), "w") as f: f.write("frappe") with open(os.path.join(dest, hooks.app_name, "README.md"), "w") as f: f.write(encode("## {0}\n\n{1}\n\n#### License\n\n{2}".format(hooks.app_title, hooks.app_description, hooks.app_license))) with open(os.path.join(dest, hooks.app_name, "license.txt"), "w") as f: f.write(encode("License: " + hooks.app_license)) with open(os.path.join(dest, hooks.app_name, hooks.app_name, "modules.txt"), "w") as f: f.write(encode(hooks.app_title)) with open(os.path.join(dest, hooks.app_name, hooks.app_name, "hooks.py"), "w") as f: f.write(encode(hooks_template.format(**hooks))) touch_file(os.path.join(dest, hooks.app_name, hooks.app_name, "patches.txt")) with open(os.path.join(dest, hooks.app_name, hooks.app_name, "config", "desktop.py"), "w") as f: f.write(encode(desktop_template.format(**hooks))) with open(os.path.join(dest, hooks.app_name, hooks.app_name, "config", "docs.py"), "w") as f: f.write(encode(docs_template.format(**hooks))) print("'{app}' created at {path}".format(app=app_name, path=os.path.join(dest, app_name)))
def get_email_queue(recipients, sender, subject, **kwargs): '''Make Email Queue object''' e = frappe.new_doc('Email Queue') e.priority = kwargs.get('send_priority') attachments = kwargs.get('attachments') if attachments: # store attachments with fid or print format details, to be attached on-demand later _attachments = [] for att in attachments: if att.get('fid'): _attachments.append(att) elif att.get("print_format_attachment") == 1: if not att.get('lang', None): att['lang'] = frappe.local.lang att['print_letterhead'] = kwargs.get('print_letterhead') _attachments.append(att) e.attachments = json.dumps(_attachments) try: mail = get_email(recipients, sender=sender, subject=subject, formatted=kwargs.get('formatted'), text_content=kwargs.get('text_content'), attachments=kwargs.get('attachments'), reply_to=kwargs.get('reply_to'), cc=kwargs.get('cc'), bcc=kwargs.get('bcc'), email_account=kwargs.get('email_account'), expose_recipients=kwargs.get('expose_recipients'), inline_images=kwargs.get('inline_images'), header=kwargs.get('header')) mail.set_message_id(kwargs.get('message_id'), kwargs.get('is_notification')) if kwargs.get('read_receipt'): mail.msg_root["Disposition-Notification-To"] = sender if kwargs.get('in_reply_to'): mail.set_in_reply_to(kwargs.get('in_reply_to')) e.message_id = mail.msg_root["Message-Id"].strip(" <>") e.message = cstr(mail.as_string()) e.sender = mail.sender except frappe.InvalidEmailAddressError: # bad Email Address - don't add to queue import traceback frappe.log_error( 'Invalid Email ID Sender: {0}, Recipients: {1}, \nTraceback: {2} '. format(mail.sender, ', '.join(mail.recipients), traceback.format_exc()), 'Email Not Sent') recipients = list( set(recipients + kwargs.get('cc', []) + kwargs.get('bcc', []))) e.set_recipients(recipients) e.reference_doctype = kwargs.get('reference_doctype') e.reference_name = kwargs.get('reference_name') e.add_unsubscribe_link = kwargs.get("add_unsubscribe_link") e.unsubscribe_method = kwargs.get('unsubscribe_method') e.unsubscribe_params = kwargs.get('unsubscribe_params') e.expose_recipients = kwargs.get('expose_recipients') e.communication = kwargs.get('communication') e.send_after = kwargs.get('send_after') e.show_as_cc = ",".join(kwargs.get('cc', [])) e.show_as_bcc = ",".join(kwargs.get('bcc', [])) e.insert(ignore_permissions=True) return e
def set_fieldname_and_label(self): if not self.label: self.label = cstr(self.document_type) if not self.fieldname: self.fieldname = scrub(self.label)
def _check_serial_no_values(serial_no, field_values): serial_no = frappe.get_doc("Serial No", serial_no) for field, value in iteritems(field_values): self.assertEqual(cstr(serial_no.get(field)), value)
def get_count_on(account, fieldname, date): cond = [] if date: cond.append("posting_date <= '%s'" % frappe.db.escape(cstr(date))) else: # get balance of all entries that exist date = nowdate() try: year_start_date = get_fiscal_year(date, verbose=0)[1] except FiscalYearError: if getdate(date) > getdate(nowdate()): # if fiscal year not found and the date is greater than today # get fiscal year for today's date and its corresponding year start date year_start_date = get_fiscal_year(nowdate(), verbose=1)[1] else: # this indicates that it is a date older than any existing fiscal year. # hence, assuming balance as 0.0 return 0.0 if account: acc = frappe.get_doc("Account", account) if not frappe.flags.ignore_account_permission: acc.check_permission("read") # for pl accounts, get balance within a fiscal year if acc.report_type == 'Profit and Loss': cond.append("posting_date >= '%s' and voucher_type != 'Period Closing Voucher'" \ % year_start_date) # different filter for group and ledger - improved performance if acc.is_group: cond.append("""exists ( select name from `tabAccount` ac where ac.name = gle.account and ac.lft >= %s and ac.rgt <= %s )""" % (acc.lft, acc.rgt)) else: cond.append("""gle.account = "%s" """ % (frappe.db.escape(account, percent=False), )) entries = frappe.db.sql(""" SELECT name, posting_date, account, party_type, party,debit,credit, voucher_type, voucher_no, against_voucher_type, against_voucher FROM `tabGL Entry` gle WHERE {0}""".format(" and ".join(cond)), as_dict=True) count = 0 for gle in entries: if fieldname not in ('invoiced_amount', 'payables'): count += 1 else: dr_or_cr = "debit" if fieldname == "invoiced_amount" else "credit" cr_or_dr = "credit" if fieldname == "invoiced_amount" else "debit" select_fields = "ifnull(sum(credit-debit),0)" \ if fieldname == "invoiced_amount" else "ifnull(sum(debit-credit),0)" if ((not gle.against_voucher) or (gle.against_voucher_type in ["Sales Order", "Purchase Order"]) or (gle.against_voucher == gle.voucher_no and gle.get(dr_or_cr) > 0)): payment_amount = frappe.db.sql( """ SELECT {0} FROM `tabGL Entry` gle WHERE docstatus < 2 and posting_date <= %(date)s and against_voucher = %(voucher_no)s and party = %(party)s and name != %(name)s""".format(select_fields), { "date": date, "voucher_no": gle.voucher_no, "party": gle.party, "name": gle.name })[0][0] outstanding_amount = flt(gle.get(dr_or_cr)) - flt( gle.get(cr_or_dr)) - payment_amount currency_precision = get_currency_precision() or 2 if abs(flt(outstanding_amount) ) > 0.1 / 10**currency_precision: count += 1 return count
def check_modified_date(self): mod_db = frappe.db.get_value("Sales Order", self.name, "modified") date_diff = frappe.db.sql("select TIMEDIFF('%s', '%s')" % ( mod_db, cstr(self.modified))) if date_diff and date_diff[0][0]: frappe.throw(_("{0} {1} has been modified. Please refresh.").format(self.doctype, self.name))