Example #1
0
    def set_actual_qty(self):
        allow_negative_stock = cint(frappe.db.get_value("Stock Settings", None, "allow_negative_stock"))

        for d in self.get("items"):
            previous_sle = get_previous_sle(
                {
                    "item_code": d.item_code,
                    "warehouse": d.s_warehouse or d.t_warehouse,
                    "posting_date": self.posting_date,
                    "posting_time": self.posting_time,
                }
            )

            # get actual stock at source warehouse
            d.actual_qty = previous_sle.get("qty_after_transaction") or 0

            # validate qty during submit
            if d.docstatus == 1 and d.s_warehouse and not allow_negative_stock and d.actual_qty < d.transfer_qty:
                frappe.throw(
                    _(
                        "Row {0}: Qty not available for {4} in warehouse {1} at posting time of the entry ({2} {3})".format(
                            d.idx,
                            frappe.bold(d.s_warehouse),
                            formatdate(self.posting_date),
                            format_time(self.posting_time),
                            frappe.bold(d.item_code),
                        )
                    )
                    + "<br><br>"
                    + _("Available qty is {0}, you need {1}").format(
                        frappe.bold(d.actual_qty), frappe.bold(d.transfer_qty)
                    ),
                    NegativeStockError,
                    title=_("Insufficient Stock"),
                )
Example #2
0
def return_success_page(doc):
	frappe.respond_as_web_page(_("Success"),
		_("{0}: {1} is set to state {2}".format(
			doc.get('doctype'),
			frappe.bold(doc.get('name')),
			frappe.bold(get_doc_workflow_state(doc))
		)), indicator_color='green')
Example #3
0
def return_link_expired_page(doc, doc_workflow_state):
	frappe.respond_as_web_page(_("Link Expired"),
		_("Document {0} has been set to state {1} by {2}"
			.format(
				frappe.bold(doc.get('name')),
				frappe.bold(doc_workflow_state),
				frappe.bold(frappe.get_value('User', doc.get("modified_by"), 'full_name'))
			)), indicator_color='blue')
Example #4
0
	def limits_crossed_error(self, args, item):
		'''Raise exception for limits crossed'''
		frappe.throw(_('This document is over limit by {0} {1} for item {4}. Are you making another {3} against the same {2}?')
			.format(
				frappe.bold(_(item["target_ref_field"].title())),
				frappe.bold(item["reduce_by"]),
				frappe.bold(_(args.get('target_dt'))),
				frappe.bold(_(self.doctype)),
				frappe.bold(item.get('item_code'))
			) + '<br><br>' +
				_('To allow over-billing or over-ordering, update "Allowance" in Stock Settings or the Item.'),
			title = _('Limit Crossed'))
	def make_invoices(self):
		names = []
		mandatory_error_msg = _("Row {0}: {1} is required to create the Opening {2} Invoices")
		if not self.company:
			frappe.throw(_("Please select the Company"))

		for row in self.invoices:
			if not row.qty:
				row.qty = 1.0

			# always mandatory fields for the invoices
			if not row.temporary_opening_account:
				row.temporary_opening_account = get_temporary_opening_account(self.company)
			row.party_type = "Customer" if self.invoice_type == "Sales" else "Supplier"

			# Allow to create invoice even if no party present in customer or supplier.
			if not frappe.db.exists(row.party_type, row.party):
				if self.create_missing_party:
					self.add_party(row.party_type, row.party)
				else:
					frappe.throw(_("{0} {1} does not exist.").format(frappe.bold(row.party_type), frappe.bold(row.party)))

			if not row.item_name:
				row.item_name = _("Opening Invoice Item")
			if not row.posting_date:
				row.posting_date = nowdate()
			if not row.due_date:
				row.due_date = nowdate()

			for d in ("Party", "Outstanding Amount", "Temporary Opening Account"):
				if not row.get(scrub(d)):
					frappe.throw(mandatory_error_msg.format(row.idx, _(d), self.invoice_type))

			args = self.get_invoice_dict(row=row)
			if not args:
				continue

			doc = frappe.get_doc(args).insert()
			doc.submit()
			names.append(doc.name)

			if len(self.invoices) > 5:
				frappe.publish_realtime(
					"progress", dict(
						progress=[row.idx, len(self.invoices)],
						title=_('Creating {0}').format(doc.doctype)
					),
					user=frappe.session.user
				)

		return names
Example #6
0
def compare_expense_with_budget(args, cost_center, budget_amount, action_for, action):
	actual_expense = get_actual_expense(args, cost_center)
	if actual_expense > budget_amount:
		diff = actual_expense - budget_amount
		currency = frappe.db.get_value('Company', frappe.db.get_value('Cost Center',
			cost_center, 'company'), 'default_currency')

		msg = _("{0} Budget for Account {1} against Cost Center {2} is {3}. It will exceed by {4}").format(_(action_for),
			frappe.bold(args.account), frappe.bold(cost_center),
			frappe.bold(fmt_money(budget_amount, currency=currency)), frappe.bold(fmt_money(diff, currency=currency)))

		if action=="Stop":
			frappe.throw(msg, BudgetError)
		else:
			frappe.msgprint(msg, indicator='orange')
Example #7
0
	def send(self):
		if self.filter_meta and not self.filters:
			frappe.throw(_("Please set filters value in Report Filter table."))
		
		data = self.get_report_content()
		if not data:
			return

		attachments = None
		message = '<p>{0}</p>'.format(_('{0} generated on {1}')\
				.format(frappe.bold(self.name),
					frappe.utils.format_datetime(frappe.utils.now_datetime())))

		if self.description:
			message += '<hr>' + self.description

		if self.format=='HTML':
			message += '<hr>' + data
		else:
			attachments = [{
				'fname': self.get_file_name(),
				'fcontent': data
			}]

		message += '<hr><p style="font-size: 10px;"> Edit Auto Email Report Settings: {0}</p>'.format(frappe.utils.get_link_to_form('Auto Email Report', self.name))

		frappe.sendmail(
			recipients = self.email_to.split(),
			subject = self.name,
			message = message,
			attachments = attachments
		)
Example #8
0
def update(doctype, field, value, condition='', limit=500):
	if not limit or cint(limit) > 500:
		limit = 500

	if condition:
		condition = ' where ' + condition

	if ';' in condition:
		frappe.throw(_('; not allowed in condition'))

	items = frappe.db.sql_list('''select name from `tab{0}`{1} limit 0, {2}'''.format(doctype,
		condition, limit), debug=1)
	n = len(items)

	for i, d in enumerate(items):
		doc = frappe.get_doc(doctype, d)
		doc.set(field, value)

		try:
			doc.save()
		except Exception as e:
			frappe.msgprint(_("Validation failed for {0}").format(frappe.bold(doc.name)))
			raise e

		frappe.publish_progress(float(i)*100/n,
			title = _('Updating Records'), doctype='Bulk Update', docname='Bulk Update')

	# clear messages
	frappe.local.message_log = []
	frappe.msgprint(_('{0} records updated').format(n), title=_('Success'), indicator='green')
Example #9
0
def validate_workflow(doc):
	'''Validate Workflow State and Transition for the current user.

	- Check if user is allowed to edit in current state
	- Check if user is allowed to transition to the next state (if changed)
	'''
	workflow = get_workflow(doc.doctype)

	current_state = None
	if getattr(doc, '_doc_before_save', None):
		current_state = doc._doc_before_save.get(workflow.workflow_state_field)
	next_state = doc.get(workflow.workflow_state_field)

	if not next_state:
		next_state = workflow.states[0].state
		doc.set(workflow.workflow_state_field, next_state)

	if not current_state:
		current_state = workflow.states[0].state

	state_row = [d for d in workflow.states if d.state == current_state]
	if not state_row:
		frappe.throw(_('{0} is not a valid Workflow State. Please update your Workflow and try again.'.format(frappe.bold(current_state))))
	state_row = state_row[0]

	# if transitioning, check if user is allowed to transition
	if current_state != next_state:
		transitions = get_transitions(doc._doc_before_save)
		transition = [d for d in transitions if d.next_state == next_state]
		if not transition:
			frappe.throw(_('Workflow State {0} is not allowed').format(frappe.bold(next_state)), WorkflowPermissionError)
Example #10
0
	def sum_components(self, component_type, total_field):
		joining_date, relieving_date = frappe.db.get_value("Employee", self.employee,
			["date_of_joining", "relieving_date"])

		if not relieving_date:
			relieving_date = getdate(self.end_date)

		if not joining_date:
			frappe.throw(_("Please set the Date Of Joining for employee {0}").format(frappe.bold(self.employee_name)))

		for d in self.get(component_type):
			if (self.salary_structure and
				cint(d.depends_on_lwp) and
				(not
				    self.salary_slip_based_on_timesheet or
					getdate(self.start_date) < joining_date or
					getdate(self.end_date) > relieving_date
				)):

				d.amount = rounded(
					(flt(d.default_amount) * flt(self.payment_days)
					/ cint(self.total_working_days)), self.precision("amount", component_type)
				)

			elif not self.payment_days and not self.salary_slip_based_on_timesheet and \
				cint(d.depends_on_lwp):
				d.amount = 0
			elif not d.amount:
				d.amount = d.default_amount
			if not d.do_not_include_in_total:
				self.set(total_field, self.get(total_field) + flt(d.amount))
Example #11
0
def remove_app(name):
	"""Remove installed app"""
	frappe.only_for("System Manager")

	if name in frappe.get_installed_apps():
		enqueue('frappe.desk.page.applications.applications.start_remove', name=name)

	frappe.msgprint(_('Queued for backup and removing {0}').format(frappe.bold(name)))
Example #12
0
	def validate_with_parent_plan(self, staffing_plan_detail):
		if not frappe.get_cached_value('Company',  self.company,  "parent_company"):
			return # No parent, nothing to validate

		# Get staffing plan applicable for the company (Parent Company)
		parent_plan_details = get_active_staffing_plan_details(self.company, staffing_plan_detail.designation, self.from_date, self.to_date)
		if not parent_plan_details:
			return #no staffing plan for any parent Company in hierarchy

		# Fetch parent company which owns the staffing plan. NOTE: Parent could be higher up in the hierarchy
		parent_company = frappe.db.get_value("Staffing Plan", parent_plan_details[0].name, "company")
		# Parent plan available, validate with parent, siblings as well as children of staffing plan Company
		if cint(staffing_plan_detail.vacancies) > cint(parent_plan_details[0].vacancies) or \
			flt(staffing_plan_detail.total_estimated_cost) > flt(parent_plan_details[0].total_estimated_cost):
			frappe.throw(_("You can only plan for upto {0} vacancies and budget {1} \
				for {2} as per staffing plan {3} for parent company {4}."
				.format(cint(parent_plan_details[0].vacancies),
					parent_plan_details[0].total_estimated_cost,
					frappe.bold(staffing_plan_detail.designation),
					parent_plan_details[0].name,
					parent_company)), ParentCompanyError)

		#Get vacanices already planned for all companies down the hierarchy of Parent Company
		lft, rgt = frappe.get_cached_value('Company',  parent_company,  ["lft", "rgt"])
		all_sibling_details = frappe.db.sql("""select sum(spd.vacancies) as vacancies,
			sum(spd.total_estimated_cost) as total_estimated_cost
			from `tabStaffing Plan Detail` spd join `tabStaffing Plan` sp on spd.parent=sp.name
			where spd.designation=%s and sp.docstatus=1
			and sp.to_date >= %s and sp.from_date <=%s
			and sp.company in (select name from tabCompany where lft > %s and rgt < %s)
		""", (staffing_plan_detail.designation, self.from_date, self.to_date, lft, rgt), as_dict = 1)[0]

		if (cint(parent_plan_details[0].vacancies) < \
			(cint(staffing_plan_detail.vacancies) + cint(all_sibling_details.vacancies))) or \
			(flt(parent_plan_details[0].total_estimated_cost) < \
			(flt(staffing_plan_detail.total_estimated_cost) + flt(all_sibling_details.total_estimated_cost))):
			frappe.throw(_("{0} vacancies and {1} budget for {2} already planned for subsidiary companies of {3}. \
				You can only plan for upto {4} vacancies and and budget {5} as per staffing plan {6} for parent company {3}."
				.format(cint(all_sibling_details.vacancies),
					all_sibling_details.total_estimated_cost,
					frappe.bold(staffing_plan_detail.designation),
					parent_company,
					cint(parent_plan_details[0].vacancies),
					parent_plan_details[0].total_estimated_cost,
					parent_plan_details[0].name)))
Example #13
0
	def validate_assessment_criteria(self):
		assessment_criteria_list = frappe.db.sql_list(''' select apc.assessment_criteria
			from `tabAssessment Plan` ap , `tabAssessment Plan Criteria` apc
			where ap.name = apc.parent and ap.course=%s and ap.student_group=%s and ap.assessment_group=%s
			and ap.name != %s and ap.docstatus=1''', (self.course, self.student_group, self.assessment_group, self.name))
		for d in self.assessment_criteria:
			if d.assessment_criteria in assessment_criteria_list:
				frappe.throw(_("You have already assessed for the assessment criteria {}.")
					.format(frappe.bold(d.assessment_criteria)))
Example #14
0
		def get_msg(df):
			if df.fieldtype == "Table":
				return "{}: {}: {}".format(_("Error"), _("Data missing in table"), _(df.label))

			elif self.parentfield:
				return "{}: {} {} #{}: {}: {}".format(_("Error"), frappe.bold(_(self.doctype)),
					_("Row"), self.idx, _("Value missing for"), _(df.label))

			else:
				return _("Error: Value missing for {0}: {1}").format(_(df.parent), _(df.label))
Example #15
0
def compare_expense_with_budget(args, budget_amount, action_for, action, budget_against, amount=0):
	actual_expense = amount or get_actual_expense(args)
	if actual_expense > budget_amount:
		diff = actual_expense - budget_amount
		currency = frappe.get_cached_value('Company',  args.company,  'default_currency')

		msg = _("{0} Budget for Account {1} against {2} {3} is {4}. It will exceed by {5}").format(
				_(action_for), frappe.bold(args.account), args.budget_against_field, 
				frappe.bold(budget_against),
				frappe.bold(fmt_money(budget_amount, currency=currency)), 
				frappe.bold(fmt_money(diff, currency=currency)))

		if (frappe.flags.exception_approver_role
			and frappe.flags.exception_approver_role in frappe.get_roles(frappe.session.user)):
			action = "Warn"

		if action=="Stop":
			frappe.throw(msg, BudgetError)
		else:
			frappe.msgprint(msg, indicator='orange')
Example #16
0
	def validate(self):
		"""Check if change in varchar length isn't truncating the columns"""
		if self.is_new():
			return

		self.get_columns_from_db()

		columns = [frappe._dict({"fieldname": f, "fieldtype": "Data"}) for f in standard_varchar_columns]
		columns += self.columns.values()

		for col in columns:
			if len(col.fieldname) >= 64:
				frappe.throw(_("Fieldname is limited to 64 characters ({0})")
					.format(frappe.bold(col.fieldname)))

			if col.fieldtype in type_map and type_map[col.fieldtype][0]=="varchar":

				# validate length range
				new_length = cint(col.length) or cint(varchar_len)
				if not (1 <= new_length <= 1000):
					frappe.throw(_("Length of {0} should be between 1 and 1000").format(col.fieldname))

				current_col = self.current_columns.get(col.fieldname, {})
				if not current_col:
					continue
				current_type = self.current_columns[col.fieldname]["type"]
				current_length = re.findall('varchar\(([\d]+)\)', current_type)
				if not current_length:
					# case when the field is no longer a varchar
					continue
				current_length = current_length[0]
				if cint(current_length) != cint(new_length):
					try:
						# check for truncation
						max_length = frappe.db.sql("""select max(char_length(`{fieldname}`)) from `tab{doctype}`"""\
							.format(fieldname=col.fieldname, doctype=self.doctype))

					except pymysql.InternalError as e:
						if e.args[0] == ER.BAD_FIELD_ERROR:
							# Unknown column 'column_name' in 'field list'
							continue

						else:
							raise

					if max_length and max_length[0][0] and max_length[0][0] > new_length:
						if col.fieldname in self.columns:
							self.columns[col.fieldname].length = current_length

						frappe.msgprint(_("Reverting length to {0} for '{1}' in '{2}'; Setting the length as {3} will cause truncation of data.")\
							.format(current_length, col.fieldname, self.doctype, new_length))
Example #17
0
	def validate(self):
		"""Validate Email Address and check POP3/IMAP and SMTP connections is enabled."""
		if self.email_id:
			validate_email_add(self.email_id, True)

		if self.login_id_is_different:
			if not self.login_id:
				frappe.throw(_("Login Id is required"))
		else:
			self.login_id = None

		duplicate_email_account = frappe.get_all("Email Account", filters={
			"email_id": self.email_id,
			"name": ("!=", self.name)
		})
		if duplicate_email_account:
			frappe.throw(_("Email id must be unique, Email Account is already exist \
				for {0}".format(frappe.bold(self.email_id))))

		if frappe.local.flags.in_patch or frappe.local.flags.in_test:
			return

		#if self.enable_incoming and not self.append_to:
		#	frappe.throw(_("Append To is mandatory for incoming mails"))

		if (not self.awaiting_password and not frappe.local.flags.in_install
			and not frappe.local.flags.in_patch):
			if self.password or self.smtp_server in ('127.0.0.1', 'localhost'):
				if self.enable_incoming:
					self.get_incoming_server()
					self.no_failed = 0


				if self.enable_outgoing:
					self.check_smtp()
			else:
				if self.enable_incoming or self.enable_outgoing:
					frappe.throw(_("Password is required or select Awaiting Password"))

		if self.notify_if_unreplied:
			if not self.send_notification_to:
				frappe.throw(_("{0} is mandatory").format(self.meta.get_label("send_notification_to")))
			for e in self.get_unreplied_notification_emails():
				validate_email_add(e, True)

		if self.enable_incoming and self.append_to:
			valid_doctypes = [d[0] for d in get_append_to()]
			if self.append_to not in valid_doctypes:
				frappe.throw(_("Append To can be one of {0}").format(comma_or(valid_doctypes)))
Example #18
0
def setup_user_email_inbox(email_account, awaiting_password, email_id, enable_outgoing):
	""" setup email inbox for user """
	def add_user_email(user):
		user = frappe.get_doc("User", user)
		row = user.append("user_emails", {})

		row.email_id = email_id
		row.email_account = email_account
		row.awaiting_password = awaiting_password or 0
		row.enable_outgoing = enable_outgoing or 0

		user.save(ignore_permissions=True)

	udpate_user_email_settings = False
	if not all([email_account, email_id]):
		return

	user_names = frappe.db.get_values("User", { "email": email_id }, as_dict=True)
	if not user_names:
		return

	for user in user_names:
		user_name = user.get("name")

		# check if inbox is alreay configured
		user_inbox = frappe.db.get_value("User Email", {
			"email_account": email_account,
			"parent": user_name
		}, ["name"]) or None

		if not user_inbox:
			add_user_email(user_name)
		else:
			# update awaiting password for email account
			udpate_user_email_settings = True

	if udpate_user_email_settings:
		frappe.db.sql("""UPDATE `tabUser Email` SET awaiting_password = %(awaiting_password)s,
			enable_outgoing = %(enable_outgoing)s WHERE email_account = %(email_account)s""", {
				"email_account": email_account,
				"enable_outgoing": enable_outgoing,
				"awaiting_password": awaiting_password or 0
			})
	else:
		frappe.msgprint(_("Enabled email inbox for user {users}".format(
			users=" and ".join([frappe.bold(user.get("name")) for user in user_names])
		)))

	ask_pass_update()
Example #19
0
def make_default(name):
	"""Set print format as default"""
	frappe.has_permission("Print Format", "write")

	print_format = frappe.get_doc("Print Format", name)

	if (frappe.conf.get('developer_mode') or 0) == 1:
		# developer mode, set it default in doctype
		doctype = frappe.get_doc("DocType", print_format.doc_type)
		doctype.default_print_format = name
		doctype.save()
	else:
		# customization
		frappe.make_property_setter({
			'doctype_or_field': "DocType",
			'doctype': print_format.doc_type,
			'property': "default_print_format",
			'value': name,
		})

	frappe.msgprint(frappe._("{0} is now default print format for {1} doctype").format(
		frappe.bold(name),
		frappe.bold(print_format.doc_type)
	))
Example #20
0
	def validate_with_subsidiary_plans(self, staffing_plan_detail):
		#Valdate this plan with all child company plan
		children_details = frappe.db.sql("""select sum(spd.vacancies) as vacancies,
			sum(spd.total_estimated_cost) as total_estimated_cost
			from `tabStaffing Plan Detail` spd join `tabStaffing Plan` sp on spd.parent=sp.name
			where spd.designation=%s and sp.docstatus=1
			and sp.to_date >= %s and sp.from_date <=%s
			and sp.company in (select name from tabCompany where parent_company = %s)
		""", (staffing_plan_detail.designation, self.from_date, self.to_date, self.company), as_dict = 1)[0]

		if children_details and \
			cint(staffing_plan_detail.vacancies) < cint(children_details.vacancies) or \
			flt(staffing_plan_detail.total_estimated_cost) < flt(children_details.total_estimated_cost):
			frappe.throw(_("Subsidiary companies have already planned for {1} vacancies at a budget of {2}. \
				Staffing Plan for {0} should allocate more vacancies and budget for {3} than planned for its subsidiary companies"
				.format(self.company,
					cint(children_details.vacancies),
					children_details.total_estimated_cost,
					frappe.bold(staffing_plan_detail.designation))), SubsidiaryCompanyError)
		def get_item_dict():
			default_uom = frappe.db.get_single_value("Stock Settings", "stock_uom") or _("Nos")
			cost_center = frappe.get_cached_value('Company',  self.company,  "cost_center")
			if not cost_center:
				frappe.throw(
					_("Please set the Default Cost Center in {0} company.").format(frappe.bold(self.company))
				)
			rate = flt(row.outstanding_amount) / flt(row.qty)

			return frappe._dict({
				"uom": default_uom,
				"rate": rate or 0.0,
				"qty": row.qty,
				"conversion_factor": 1.0,
				"item_name": row.item_name or "Opening Invoice Item",
				"description": row.item_name or "Opening Invoice Item",
				income_expense_account_field: row.temporary_opening_account,
				"cost_center": cost_center
			})
Example #22
0
def getdoc(doctype, name, user=None):
	"""
	Loads a doclist for a given document. This method is called directly from the client.
	Requries "doctype", "name" as form variables.
	Will also call the "onload" method on the document.
	"""

	if not (doctype and name):
		raise Exception('doctype and name required!')

	if not name:
		name = doctype

	if not frappe.db.exists(doctype, name):
		return []

	try:
		doc = frappe.get_doc(doctype, name)
		run_onload(doc)

		if not doc.has_permission("read"):
			frappe.flags.error_message = _('Insufficient Permission for {0}').format(frappe.bold(doctype + ' ' + name))
			raise frappe.PermissionError(("read", doctype, name))

		doc.apply_fieldlevel_read_permissions()

		# add file list
		get_docinfo(doc)

	except Exception:
		frappe.errprint(frappe.utils.get_traceback())
		frappe.msgprint(_('Did not load'))
		raise

	if doc and not name.startswith('_'):
		frappe.get_user().update_recent(doctype, name)

	doc.add_seen()

	frappe.response.docs.append(doc)
Example #23
0
def validate_column_name(n):
	special_characters = re.findall("[\W]", n, re.UNICODE)
	if special_characters:
		special_characters = ", ".join('"{0}"'.format(c) for c in special_characters)
		frappe.throw(_("Fieldname {0} cannot have special characters like {1}").format(frappe.bold(cstr(n)), special_characters), InvalidColumnName)
	return n
Example #24
0
def get_approvers(doctype, txt, searchfield, start, page_len, filters):

    if not filters.get("employee"):
        frappe.throw(_("Please select Employee first."))

    approvers = []
    department_details = {}
    department_list = []
    employee = frappe.get_value(
        "Employee",
        filters.get("employee"), [
            "employee_name", "department", "leave_approver",
            "expense_approver", "shift_request_approver"
        ],
        as_dict=True)

    employee_department = filters.get("department") or employee.department
    if employee_department:
        department_details = frappe.db.get_value("Department",
                                                 {"name": employee_department},
                                                 ["lft", "rgt"],
                                                 as_dict=True)
    if department_details:
        department_list = frappe.db.sql(
            """select name from `tabDepartment` where lft <= %s
			and rgt >= %s
			and disabled=0
			order by lft desc""", (department_details.lft, department_details.rgt),
            as_list=True)

    if filters.get(
            "doctype") == "Leave Application" and employee.leave_approver:
        approvers.append(
            frappe.db.get_value("User", employee.leave_approver,
                                ['name', 'first_name', 'last_name']))

    if filters.get("doctype") == "Expense Claim" and employee.expense_approver:
        approvers.append(
            frappe.db.get_value("User", employee.expense_approver,
                                ['name', 'first_name', 'last_name']))

    if filters.get(
            "doctype") == "Shift Request" and employee.shift_request_approver:
        approvers.append(
            frappe.db.get_value("User", employee.shift_request_approver,
                                ['name', 'first_name', 'last_name']))

    if filters.get("doctype") == "Leave Application":
        parentfield = "leave_approvers"
        field_name = "Leave Approver"
    elif filters.get("doctype") == "Expense Claim":
        parentfield = "expense_approvers"
        field_name = "Expense Approver"
    elif filters.get("doctype") == "Shift Request":
        parentfield = "shift_request_approver"
        field_name = "Shift Request Approver"
    if department_list:
        for d in department_list:
            approvers += frappe.db.sql(
                """select user.name, user.first_name, user.last_name from
				tabUser user, `tabDepartment Approver` approver where
				approver.parent = %s
				and user.name like %s
				and approver.parentfield = %s
				and approver.approver=user.name""", (d, "%" + txt + "%", parentfield),
                as_list=True)

    if len(approvers) == 0:
        error_msg = _("Please set {0} for the Employee: {1}").format(
            field_name, frappe.bold(employee.employee_name))
        if department_list:
            error_msg += _(" or for Department: {0}").format(
                frappe.bold(employee_department))
        frappe.throw(error_msg, title=_(field_name + " Missing"))

    return set(tuple(approver) for approver in approvers)
Example #25
0
def rename_doc(doctype,
               old,
               new,
               force=False,
               merge=False,
               ignore_permissions=False,
               ignore_if_exists=False):
    """
		Renames a doc(dt, old) to doc(dt, new) and
		updates all linked fields of type "Link"
	"""
    if not frappe.db.exists(doctype, old):
        return

    if ignore_if_exists and frappe.db.exists(doctype, new):
        return

    if old == new:
        frappe.msgprint(_('Please select a new name to rename'))
        return

    force = cint(force)
    merge = cint(merge)

    meta = frappe.get_meta(doctype)

    # call before_rename
    old_doc = frappe.get_doc(doctype, old)
    out = old_doc.run_method("before_rename", old, new, merge) or {}
    new = (out.get("new") or new) if isinstance(out, dict) else (out or new)

    if doctype != "DocType":
        new = validate_rename(doctype, new, meta, merge, force,
                              ignore_permissions)

    if not merge:
        rename_parent_and_child(doctype, old, new, meta)

    # update link fields' values
    link_fields = get_link_fields(doctype)
    update_link_field_values(link_fields, old, new, doctype)

    rename_dynamic_links(doctype, old, new)

    if doctype == 'DocType':
        rename_doctype(doctype, old, new, force)

    update_attachments(doctype, old, new)

    if merge:
        frappe.delete_doc(doctype, old)

    # call after_rename
    new_doc = frappe.get_doc(doctype, new)

    # copy any flags if required
    new_doc._local = getattr(old_doc, "_local", None)

    new_doc.run_method("after_rename", old, new, merge)

    rename_versions(doctype, old, new)

    if not merge:
        rename_password(doctype, old, new)

    # update user_permissions
    frappe.db.sql(
        """update tabDefaultValue set defvalue=%s where parenttype='User Permission'
		and defkey=%s and defvalue=%s""", (new, doctype, old))
    frappe.clear_cache()

    if merge:
        new_doc.add_comment(
            'Edit',
            _("merged {0} into {1}").format(frappe.bold(old),
                                            frappe.bold(new)))
    else:
        new_doc.add_comment(
            'Edit',
            _("renamed from {0} to {1}").format(frappe.bold(old),
                                                frappe.bold(new)))

    return new
Example #26
0
    def validate_stock_qty(self):
        """User should not allowed to create pick list if sales order item qty exceed."""
        for item in self.locations:
            if item.get("sales_order_item"):
                ordered_item_qty = frappe.db.get_value(
                    "Sales Order Item", item.get("sales_order_item"), "qty")
                prev_picked_qty = frappe.get_all(
                    "Pick List Item",
                    filters={
                        "sales_order_item": item.get("sales_order_item"),
                        "docstatus": 1
                    },
                    fields=['sum(qty) as prev_picked_qty'])

                if prev_picked_qty:
                    prev_picked_qty = prev_picked_qty[0].prev_picked_qty
                    pick_list_qty = flt(ordered_item_qty) - flt(
                        prev_picked_qty)
                    if pick_list_qty > 0 and item.qty > pick_list_qty:
                        frappe.throw(
                            _("Row #{0}: Picked quantity ({1}) for {2} cannot exceed unused ordered qty ({3})"
                              ).format(item.idx, frappe.bold(item.qty),
                                       frappe.bold(item.item_name),
                                       frappe.bold(pick_list_qty)))

                if item.qty > ordered_item_qty:
                    frappe.throw(
                        _("Row #{0}: {1}'s quantity ({2}) should be less than or equal to the ordered quantity ({3})"
                          ).format(item.idx, frappe.bold(item.item_name),
                                   frappe.bold(item.qty),
                                   frappe.bold(ordered_item_qty)))

                if item.source_package_tag:
                    package_tag_qty = get_package_tag_qty(
                        item.source_package_tag)
                    if not package_tag_qty:
                        frappe.throw(
                            _("Row #{0}: No record found for Package Tag {1} in Stock Ledger Entry "
                              ).format(item.idx,
                                       frappe.bold(item.source_package_tag)))
                    if item.qty > package_tag_qty[0].qty:
                        frappe.throw(
                            _("Row #{0}: {1}'s quantity ({2}) should not exceed Package Tag {3}'s quantity ({4})"
                              ).format(item.idx, frappe.bold(item.item_name),
                                       frappe.bold(item.qty),
                                       frappe.bold(item.source_package_tag),
                                       frappe.bold(package_tag_qty[0].qty)))
Example #27
0
	def cant_change(self):
		if not self.get("__islocal"):
			to_check = ("has_serial_no", "is_stock_item",
				"valuation_method", "has_batch_no", "is_fixed_asset")

			vals = frappe.db.get_value("Item", self.name, to_check, as_dict=True)

			if vals:
				for key in to_check:
					if self.get(key) != vals.get(key):
						if not self.check_if_linked_document_exists():
							break # no linked document, allowed
						else:
							frappe.throw(_("As there are existing transactions for this item, you can not change the value of {0}").format(frappe.bold(self.meta.get_label(key))))

			if vals and not self.is_fixed_asset and self.is_fixed_asset != vals.is_fixed_asset:
				asset = frappe.db.get_all("Asset", filters={"item_code": self.name, "docstatus": 1}, limit=1)
				if asset:
					frappe.throw(_('"Is Fixed Asset" cannot be unchecked, as Asset record exists against the item'))
Example #28
0
	def notify(self):
		if not frappe.db.get_single_value("Projects Settings", "send_notifications_for_project"):
			return

		notification_doc = {
			'type': 'Notify',
			'document_type': self.doctype,
			'subject': _("Project {0} has been updated.").format("<a href='{0}'>{1}</a>".format(self.get_url(), frappe.bold(self.name))),
			'document_name': self.name,
			'from_user': frappe.session.user
		}

		enqueue_create_notification(self.get_assigned_users(), notification_doc)

		for user in self.get_assigned_users():
			if user == frappe.session.user:
				continue

			frappe.publish_realtime('show_notification_alert', message=notification_doc.get("subject"), after_commit=True, user=user)
Example #29
0
	def validate_report_format(self):
		""" check if user has select correct report format """
		valid_report_formats = ["HTML", "XLSX", "CSV"]
		if self.format not in valid_report_formats:
			frappe.throw(_("%s is not a valid report format. Report format should \
				one of the following %s"%(frappe.bold(self.format), frappe.bold(", ".join(valid_report_formats)))))
Example #30
0
 def validate_child_items(self):
     for item in self.items:
         if frappe.db.exists("Product Bundle", item.item_code):
             frappe.throw(
                 _("Row #{0}: Child Item should not be a Product Bundle. Please remove Item {1} and Save"
                   ).format(item.idx, frappe.bold(item.item_code)))
Example #31
0
def validate_duration_format(duration):
	import re
	is_valid_duration = re.match("^(?:(\d+d)?((^|\s)\d+h)?((^|\s)\d+m)?((^|\s)\d+s)?)$", duration)
	if not is_valid_duration:
		frappe.throw(frappe._("Value {0} must be in the valid duration format: d h m s").format(frappe.bold(duration)))
Example #32
0
	def validate_for_subcontracting(self):
		if not self.is_subcontracted and self.sub_contracted_items:
			frappe.throw(_("Please enter 'Is Subcontracted' as Yes or No"))

		if self.is_subcontracted == "Yes":
			if self.doctype in ["Purchase Receipt", "Purchase Invoice"] and not self.supplier_warehouse:
				frappe.throw(_("Supplier Warehouse mandatory for sub-contracted Purchase Receipt"))

			for item in self.get("items"):
				if item in self.sub_contracted_items and not item.bom:
					frappe.throw(_("Please select BOM in BOM field for Item {0}").format(item.item_code))

			if self.doctype == "Purchase Order":
				for supplied_item in self.get("supplied_items"):
					if not supplied_item.reserve_warehouse:
						frappe.throw(_("Reserved Warehouse is mandatory for Item {0} in Raw Materials supplied").format(frappe.bold(supplied_item.rm_item_code)))

		else:
			for item in self.get("items"):
				if item.bom:
					item.bom = None
Example #33
0
	def validate_operation_id(self):
		if (self.get("operation_id") and self.get("operation_row_number") and self.operation and self.work_order and
			frappe.get_cached_value("Work Order Operation", self.operation_row_number, "name") != self.operation_id):
			work_order = frappe.bold(get_link_to_form("Work Order", self.work_order))
			frappe.throw(_("Operation {0} does not belong to the work order {1}")
				.format(frappe.bold(self.operation), work_order), OperationMismatchError)
Example #34
0
	def append_table(self, table_name):
		self.tables.append(table_name)
		doctype = table_name[4:-1]
		if (not self.flags.ignore_permissions) and (not frappe.has_permission(doctype)):
			frappe.flags.error_message = _('Insufficient Permission for {0}').format(frappe.bold(doctype))
			raise frappe.PermissionError(doctype)
Example #35
0
	def validate_report_format(self):
		""" check if user has select correct report format """
		valid_report_formats = ["HTML", "XLSX", "CSV"]
		if self.format not in valid_report_formats:
			frappe.throw(_("%s is not a valid report format. Report format should \
				one of the following %s"%(frappe.bold(self.format), frappe.bold(", ".join(valid_report_formats)))))
Example #36
0
	def validate_duplicate_items(self):
		item_list = []
		for item in self.items:
			if item.item_code in item_list:
				frappe.throw(_("Note: Item {0} added multiple times").format(frappe.bold(item.item_code)))
			item_list.append(item.item_code)
Example #37
0
def get_batch_no(item_code, warehouse, qty=1, throw=False):
	"""
	Get batch number using First Expiring First Out method.
	:param item_code: `item_code` of Item Document
	:param warehouse: name of Warehouse to check
	:param qty: quantity of Items
	:return: String represent batch number of batch with sufficient quantity else an empty String
	"""

	batch_no = None
	batches = get_batches(item_code, warehouse, qty, throw)

	for batch in batches:
		if cint(qty) <= cint(batch.qty):
			batch_no = batch.batch_id
			break

	if not batch_no:
		frappe.msgprint(_('Please select a Batch for Item {0}. Unable to find a single batch that fulfills this requirement').format(frappe.bold(item_code)))
		if throw:
			raise UnableToSelectBatchError

	return batch_no
Example #38
0
def add_comment(comment, comment_email, comment_by, reference_doctype,
                reference_name, route):
    doc = frappe.get_doc(reference_doctype, reference_name)

    if frappe.session.user == 'Guest' and doc.doctype not in [
            'Blog Post', 'Web Page'
    ]:
        return

    if not comment.strip():
        frappe.msgprint(_('The comment cannot be empty'))
        return False

    url_regex = re.compile(
        r"http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+",
        re.IGNORECASE)
    email_regex = re.compile(
        r"(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)", re.IGNORECASE)

    if url_regex.search(comment) or email_regex.search(comment):
        frappe.msgprint(_('Comments cannot have links or email addresses'))
        return False

    comments_count = frappe.db.count(
        "Comment", {
            "comment_type": "Comment",
            "comment_email": comment_email,
            "creation": (">", add_to_date(now(), hours=-1))
        })

    if comments_count > 20:
        frappe.msgprint(
            _('Hourly comment limit reached for: {0}').format(
                frappe.bold(comment_email)))
        return False

    comment = doc.add_comment(text=comment,
                              comment_email=comment_email,
                              comment_by=comment_by)

    comment.db_set('published', 1)

    # since comments are embedded in the page, clear the web cache
    if route:
        clear_cache(route)

    content = (
        comment.content +
        "<p><a href='{0}/app/Form/Comment/{1}' style='font-size: 80%'>{2}</a></p>"
        .format(frappe.utils.get_request_site_address(), comment.name,
                _("View Comment")))

    # notify creator
    frappe.sendmail(recipients=frappe.db.get_value('User', doc.owner, 'email')
                    or doc.owner,
                    subject=_('New Comment on {0}: {1}').format(
                        doc.doctype, doc.name),
                    message=content,
                    reference_doctype=doc.doctype,
                    reference_name=doc.name)

    # revert with template if all clear (no backlinks)
    template = frappe.get_template("templates/includes/comments/comment.html")
    return template.render({"comment": comment.as_dict()})
Example #39
0
def install_country_fixtures(company):
	company_doc = frappe.get_doc("Company", company)
	path = frappe.get_app_path('erpnext', 'regional', frappe.scrub(company_doc.country))
	if os.path.exists(path.encode("utf-8")):
		try:
			module_name = "erpnext.regional.{0}.setup.setup".format(frappe.scrub(company_doc.country))
			frappe.get_attr(module_name)(company_doc, False)
		except Exception as e:
			frappe.log_error()
			frappe.throw(_("Failed to setup defaults for country {0}. Please contact [email protected]").format(frappe.bold(company_doc.country)))
Example #40
0
    def validate(self):
        """Check if change in varchar length isn't truncating the columns"""
        if self.is_new():
            return

        self.setup_table_columns()

        columns = [
            frappe._dict({
                "fieldname": f,
                "fieldtype": "Data"
            }) for f in frappe.db.STANDARD_VARCHAR_COLUMNS
        ]
        columns += self.columns.values()

        for col in columns:
            if len(col.fieldname) >= 64:
                frappe.throw(
                    _("Fieldname is limited to 64 characters ({0})").format(
                        frappe.bold(col.fieldname)))

            if 'varchar' in frappe.db.type_map.get(col.fieldtype, ()):

                # validate length range
                new_length = cint(col.length) or cint(frappe.db.VARCHAR_LEN)
                if not (1 <= new_length <= 1000):
                    frappe.throw(
                        _("Length of {0} should be between 1 and 1000").format(
                            col.fieldname))

                current_col = self.current_columns.get(col.fieldname, {})
                if not current_col:
                    continue
                current_type = self.current_columns[col.fieldname]["type"]
                current_length = re.findall(r'varchar\(([\d]+)\)',
                                            current_type)
                if not current_length:
                    # case when the field is no longer a varchar
                    continue
                current_length = current_length[0]
                if cint(current_length) != cint(new_length):
                    try:
                        # check for truncation
                        max_length = frappe.db.sql(
                            """SELECT MAX(CHAR_LENGTH(`{fieldname}`)) FROM `tab{doctype}`"""
                            .format(fieldname=col.fieldname,
                                    doctype=self.doctype))

                    except frappe.db.InternalError as e:
                        if frappe.db.is_missing_column(e):
                            # Unknown column 'column_name' in 'field list'
                            continue
                        raise

                    if max_length and max_length[0][
                            0] and max_length[0][0] > new_length:
                        if col.fieldname in self.columns:
                            self.columns[col.fieldname].length = current_length
                        info_message = _("Reverting length to {0} for '{1}' in '{2}'. Setting the length as {3} will cause truncation of data.") \
                         .format(current_length, col.fieldname, self.doctype, new_length)
                        frappe.msgprint(info_message)
Example #41
0
    def update_stock_ledger(self):
        """find difference between current and expected entries
		and create stock ledger entries based on the difference"""
        from erpnext.stock.stock_ledger import get_previous_sle

        sl_entries = []
        has_serial_no = False
        has_batch_no = False
        for row in self.items:
            item = frappe.get_doc("Item", row.item_code)
            if item.has_batch_no:
                has_batch_no = True

            if item.has_serial_no or item.has_batch_no:
                has_serial_no = True
                self.get_sle_for_serialized_items(row, sl_entries)
            else:
                if row.serial_no or row.batch_no:
                    frappe.throw(
                        _("Row #{0}: Item {1} is not a Serialized/Batched Item. It cannot have a Serial No/Batch No against it."
                          ).format(row.idx, frappe.bold(row.item_code)))

                previous_sle = get_previous_sle({
                    "item_code":
                    row.item_code,
                    "warehouse":
                    row.warehouse,
                    "posting_date":
                    self.posting_date,
                    "posting_time":
                    self.posting_time,
                })

                if previous_sle:
                    if row.qty in ("", None):
                        row.qty = previous_sle.get("qty_after_transaction", 0)

                    if row.valuation_rate in ("", None):
                        row.valuation_rate = previous_sle.get(
                            "valuation_rate", 0)

                if row.qty and not row.valuation_rate and not row.allow_zero_valuation_rate:
                    frappe.throw(
                        _("Valuation Rate required for Item {0} at row {1}").
                        format(row.item_code, row.idx))

                if (previous_sle and row.qty
                        == previous_sle.get("qty_after_transaction") and
                    (row.valuation_rate == previous_sle.get("valuation_rate")
                     or row.qty == 0)) or (not previous_sle and not row.qty):
                    continue

                sl_entries.append(self.get_sle_for_items(row))

        if sl_entries:
            if has_serial_no:
                sl_entries = self.merge_similar_item_serial_nos(sl_entries)

            allow_negative_stock = False
            if has_batch_no:
                allow_negative_stock = True

            self.make_sl_entries(sl_entries,
                                 allow_negative_stock=allow_negative_stock)

        if has_serial_no and sl_entries:
            self.update_valuation_rate_for_serial_no()
Example #42
0
    def validate(self):
        """Check if change in varchar length isn't truncating the columns"""
        if self.is_new():
            return

        self.get_columns_from_db()

        columns = [
            frappe._dict({
                "fieldname": f,
                "fieldtype": "Data"
            }) for f in standard_varchar_columns
        ]
        columns += self.columns.values()

        for col in columns:
            if len(col.fieldname) >= 64:
                frappe.throw(
                    _("Fieldname is limited to 64 characters ({0})").format(
                        frappe.bold(col.fieldname)))

            if col.fieldtype in type_map and type_map[
                    col.fieldtype][0] == "varchar":

                # validate length range
                new_length = cint(col.length) or cint(varchar_len)
                if not (1 <= new_length <= 1000):
                    frappe.throw(
                        _("Length of {0} should be between 1 and 1000").format(
                            col.fieldname))

                current_col = self.current_columns.get(col.fieldname, {})
                if not current_col:
                    continue
                current_type = self.current_columns[col.fieldname]["type"]
                current_length = re.findall('varchar\(([\d]+)\)', current_type)
                if not current_length:
                    # case when the field is no longer a varchar
                    continue
                current_length = current_length[0]
                if cint(current_length) != cint(new_length):
                    try:
                        # check for truncation
                        max_length = frappe.db.sql("""select max(char_length(`{fieldname}`)) from `tab{doctype}`"""\
                         .format(fieldname=col.fieldname, doctype=self.doctype))

                    except pymysql.InternalError as e:
                        if e.args[0] == ER.BAD_FIELD_ERROR:
                            # Unknown column 'column_name' in 'field list'
                            continue

                        else:
                            raise

                    if max_length and max_length[0][
                            0] and max_length[0][0] > new_length:
                        if col.fieldname in self.columns:
                            self.columns[col.fieldname].length = current_length

                        frappe.msgprint(_("Reverting length to {0} for '{1}' in '{2}'; Setting the length as {3} will cause truncation of data.")\
                         .format(current_length, col.fieldname, self.doctype, new_length))
Example #43
0
	def check_sanctioned_amount_limit(self):
		total_loan_amount = get_total_loan_amount(self.applicant_type, self.applicant, self.company)
		sanctioned_amount_limit = get_sanctioned_amount_limit(self.applicant_type, self.applicant, self.company)

		if sanctioned_amount_limit and flt(self.loan_amount) + flt(total_loan_amount) > flt(sanctioned_amount_limit):
			frappe.throw(_("Sanctioned Amount limit crossed for {0} {1}").format(self.applicant_type, frappe.bold(self.applicant)))
Example #44
0
    def execute(self,
                query=None,
                fields=None,
                filters=None,
                or_filters=None,
                docstatus=None,
                group_by=None,
                order_by=None,
                limit_start=False,
                limit_page_length=None,
                as_list=False,
                with_childnames=False,
                debug=False,
                ignore_permissions=False,
                user=None,
                with_comment_count=False,
                join='left join',
                distinct=False,
                start=None,
                page_length=None,
                limit=None,
                ignore_ifnull=False,
                save_user_settings=False,
                save_user_settings_fields=False,
                update=None,
                add_total_row=None,
                user_settings=None):
        if not ignore_permissions and not frappe.has_permission(
                self.doctype, "read", user=user):
            frappe.flags.error_message = _(
                'Insufficient Permission for {0}').format(
                    frappe.bold(self.doctype))
            raise frappe.PermissionError(self.doctype)

        # fitlers and fields swappable
        # its hard to remember what comes first
        if (isinstance(fields, dict) or (isinstance(fields, list) and fields
                                         and isinstance(fields[0], list))):
            # if fields is given as dict/list of list, its probably filters
            filters, fields = fields, filters

        elif fields and isinstance(filters, list) \
         and len(filters) > 1 and isinstance(filters[0], string_types):
            # if `filters` is a list of strings, its probably fields
            filters, fields = fields, filters

        if fields:
            self.fields = fields
        else:
            self.fields = ["`tab{0}`.`name`".format(self.doctype)]

        if start: limit_start = start
        if page_length: limit_page_length = page_length
        if limit: limit_page_length = limit

        self.filters = filters or []
        self.or_filters = or_filters or []
        self.docstatus = docstatus or []
        self.group_by = group_by
        self.order_by = order_by
        self.limit_start = 0 if (limit_start is False) else cint(limit_start)
        self.limit_page_length = cint(
            limit_page_length) if limit_page_length else None
        self.with_childnames = with_childnames
        self.debug = debug
        self.join = join
        self.distinct = distinct
        self.as_list = as_list
        self.ignore_ifnull = ignore_ifnull
        self.flags.ignore_permissions = ignore_permissions
        self.user = user or frappe.session.user
        self.update = update
        self.user_settings_fields = copy.deepcopy(self.fields)
        #self.debug = True

        if user_settings:
            self.user_settings = json.loads(user_settings)

        if query:
            result = self.run_custom_query(query)
        else:
            result = self.build_and_run()

        if with_comment_count and not as_list and self.doctype:
            self.add_comment_count(result)

        if save_user_settings:
            self.save_user_settings_fields = save_user_settings_fields
            self.update_user_settings()

        return result
Example #45
0
def has_permission(doctype,
                   ptype="read",
                   doc=None,
                   verbose=False,
                   user=None,
                   raise_exception=True):
    """Returns True if user has permission `ptype` for given `doctype`.
	If `doc` is passed, it also checks user, share and owner permissions.

	Note: if Table DocType is passed, it always returns True.
	"""
    if not user: user = frappe.session.user

    if not doc and hasattr(doctype, 'doctype'):
        # first argument can be doc or doctype
        doc = doctype
        doctype = doc.doctype

    if frappe.is_table(doctype):
        return True

    if user == "Administrator":
        return True

    meta = frappe.get_meta(doctype)

    if doc:
        if isinstance(doc, string_types):
            doc = frappe.get_doc(meta.name, doc)
        perm = get_doc_permissions(doc, user=user, ptype=ptype).get(ptype)
        if not perm:
            push_perm_check_log(
                _('User {0} does not have access to this document').format(
                    frappe.bold(user)))
    else:
        if ptype == "submit" and not cint(meta.is_submittable):
            push_perm_check_log(_("Document Type is not submittable"))
            return False

        if ptype == "import" and not cint(meta.allow_import):
            push_perm_check_log(_("Document Type is not importable"))
            return False

        role_permissions = get_role_permissions(meta, user=user)
        perm = role_permissions.get(ptype)
        if not perm:
            push_perm_check_log(
                _('User {0} does not have doctype access via role permission for document {1}'
                  ).format(frappe.bold(user), frappe.bold(doctype)))

    def false_if_not_shared():
        if ptype in ("read", "write", "share", "email", "print"):
            shared = frappe.share.get_shared(
                doctype, user,
                ["read" if ptype in ("email", "print") else ptype])

            if doc:
                doc_name = get_doc_name(doc)
                if doc_name in shared:
                    if ptype in ("read", "write",
                                 "share") or meta.permissions[0].get(ptype):
                        return True

            elif shared:
                # if atleast one shared doc of that type, then return True
                # this is used in db_query to check if permission on DocType
                return True

        return False

    if not perm:
        perm = false_if_not_shared()

    return perm
	def validate_dependencies(self):
		for task in self.tasks:
			task_details = frappe.get_doc("Task", task.task)
			if task_details.depends_on:
				for dependency_task in task_details.depends_on:
					if not self.check_dependent_task_presence(dependency_task.task):
						task_details_format = get_link_to_form("Task",task_details.name)
						dependency_task_format = get_link_to_form("Task", dependency_task.task)
						frappe.throw(_("Task {0} depends on Task {1}. Please add Task {1} to the Tasks list.").format(frappe.bold(task_details_format), frappe.bold(dependency_task_format)))
Example #47
0
	def append_table(self, table_name):
		self.tables.append(table_name)
		doctype = table_name[4:-1]
		if (not self.flags.ignore_permissions) and (not frappe.has_permission(doctype)):
			frappe.flags.error_message = _('Insufficient Permission for {0}').format(frappe.bold(doctype))
			raise frappe.PermissionError(doctype)
def make_stock_requisition(args):
	global required_date_count
#	if getdate(required_date) == getdate(datetime.now().strftime('%Y-%m-%d')):
#		if required_date_count == False:
#			required_date_count = True
#			frappe.throw(_("Required Date is set to today's date, if you still want to proceed click on 'Stock Requisition' again"))

#	test_whole_number = check_for_whole_number(bom_for_validation)
#	if test_whole_number and float(qty_to_make) % 1 != 0:
#		frappe.throw("Quantity to Make should be whole number")

	innerJson_requisition = " "
	innerJson_transfer = " "
	ret = ""
	newJson_transfer = {
	"company": company,
	"doctype": "Stock Requisition",
	"title": "Material Transfer",
	"material_request_type": "Material Transfer",
	"quantity_to_make": 1,


	"items": [
]
}

	newJson_requisition = {
	"company": company,
	"doctype": "Stock Requisition",
	"title": "Purchase",
	"material_request_type": "Purchase",
	"quantity_to_make": 1,


	"items": [
]
}

	no_transfer = 0	
	required = ""
	whse_map = {}
	empty_desc = []
	empty_uom = []

	for rows in data:
		required = str(rows[14]).strip()

		if (required and float(required) != 0.0):
			
			if rows[4] == "":
				empty_desc.append(rows[0])
			if rows[6] == "":
				empty_uom.append(rows[0])
			no_transfer = no_transfer + 1
				
			innerJson_transfer =	{
			"doctype": "Stock Requisition Item",
			"item_code": rows[0],
			"qty": rows[14],
			"schedule_date": required_date,
			"warehouse": warehouse,
			"uom": rows[6],
			"description": rows[4]
			   }

			newJson_transfer["items"].append(innerJson_transfer)

	if float(no_transfer) == 0:
		frappe.msgprint("Planning Warehouse has all the item !! Stock transfer is not required")
	else:
		if empty_uom:
			frappe.throw(_("UOM for  {0} is empty,Please add UOM in Item Master Doctype.").format(frappe.bold(comma_and(empty_uom))))
		if empty_desc:
			frappe.throw(_("Description for  {0} is empty,Please add description in Item Master Doctype.").format(frappe.bold(comma_and(empty_desc))))
		doc = frappe.new_doc("Stock Requisition")
		doc.update(newJson_transfer)
		if args == "as a draft":
			doc.save()
		else:
			doc.submit()
		ret = doc.doctype

	
	del data[:]
	del required
	if ret:
		return ret
Example #49
0
def get_batch_no(item_code, warehouse, qty, throw=False):
	'''get the smallest batch with for the given item_code, warehouse and qty'''
	
	batch_no = None
	
	batches = get_batch_qty(item_code = item_code, warehouse = warehouse)
	if batches:
		batches = sorted(batches, lambda a, b: 1 if a.qty > b.qty else -1)

		for b in batches:
			if b.qty >= qty:
				batch_no = b.batch_no
				# found!
				break

	if not batch_no:
		frappe.msgprint(_('Please select a Batch for Item {0}. Unable to find a single batch that fulfills this requirement').format(frappe.bold(item_code)))
		if throw: raise UnableToSelectBatchError

	return batch_no
Example #50
0
def get_fiscal_years(transaction_date=None, fiscal_year=None, label="Date", verbose=1, company=None, as_dict=False):
	fiscal_years = frappe.cache().hget("fiscal_years", company) or []

	if not fiscal_years:
		# if year start date is 2012-04-01, year end date should be 2013-03-31 (hence subdate)
		cond = ""
		if fiscal_year:
			cond += " and fy.name = {0}".format(frappe.db.escape(fiscal_year))
		if company:
			cond += """
				and (not exists (select name
					from `tabFiscal Year Company` fyc
					where fyc.parent = fy.name)
				or exists(select company
					from `tabFiscal Year Company` fyc
					where fyc.parent = fy.name
					and fyc.company=%(company)s)
				)
			"""

		fiscal_years = frappe.db.sql("""
			select
				fy.name, fy.year_start_date, fy.year_end_date
			from
				`tabFiscal Year` fy
			where
				disabled = 0 {0}
			order by
				fy.year_start_date desc""".format(cond), {
				"company": company
			}, as_dict=True)

		frappe.cache().hset("fiscal_years", company, fiscal_years)

	if not transaction_date and not fiscal_year:
		return fiscal_years

	if transaction_date:
		transaction_date = getdate(transaction_date)

	for fy in fiscal_years:
		matched = False
		if fiscal_year and fy.name == fiscal_year:
			matched = True

		if (transaction_date and getdate(fy.year_start_date) <= transaction_date
			and getdate(fy.year_end_date) >= transaction_date):
			matched = True

		if matched:
			if as_dict:
				return (fy,)
			else:
				return ((fy.name, fy.year_start_date, fy.year_end_date),)

	error_msg = _("""{0} {1} is not in any active Fiscal Year""").format(label, formatdate(transaction_date))
	if company:
		error_msg = _("""{0} for {1}""").format(error_msg, frappe.bold(company))

	if verbose==1: frappe.msgprint(error_msg)
	raise FiscalYearError(error_msg)
Example #51
0
	def execute(self, query=None, fields=None, filters=None, or_filters=None,
		docstatus=None, group_by=None, order_by=None, limit_start=False,
		limit_page_length=None, as_list=False, with_childnames=False, debug=False,
		ignore_permissions=False, user=None, with_comment_count=False,
		join='left join', distinct=False, start=None, page_length=None, limit=None,
		ignore_ifnull=False, save_user_settings=False, save_user_settings_fields=False,
		update=None, add_total_row=None, user_settings=None, reference_doctype=None):
		if not ignore_permissions and not frappe.has_permission(self.doctype, "read", user=user):
			frappe.flags.error_message = _('Insufficient Permission for {0}').format(frappe.bold(self.doctype))
			raise frappe.PermissionError(self.doctype)

		# filters and fields swappable
		# its hard to remember what comes first
		if (isinstance(fields, dict)
			or (isinstance(fields, list) and fields and isinstance(fields[0], list))):
			# if fields is given as dict/list of list, its probably filters
			filters, fields = fields, filters

		elif fields and isinstance(filters, list) \
			and len(filters) > 1 and isinstance(filters[0], string_types):
			# if `filters` is a list of strings, its probably fields
			filters, fields = fields, filters

		if fields:
			self.fields = fields
		else:
			self.fields =  ["`tab{0}`.`name`".format(self.doctype)]

		if start: limit_start = start
		if page_length: limit_page_length = page_length
		if limit: limit_page_length = limit

		self.filters = filters or []
		self.or_filters = or_filters or []
		self.docstatus = docstatus or []
		self.group_by = group_by
		self.order_by = order_by
		self.limit_start = 0 if (limit_start is False) else cint(limit_start)
		self.limit_page_length = cint(limit_page_length) if limit_page_length else None
		self.with_childnames = with_childnames
		self.debug = debug
		self.join = join
		self.distinct = distinct
		self.as_list = as_list
		self.ignore_ifnull = ignore_ifnull
		self.flags.ignore_permissions = ignore_permissions
		self.user = user or frappe.session.user
		self.update = update
		self.user_settings_fields = copy.deepcopy(self.fields)

		# for contextual user permission check
		# to determine which user permission is applicable on link field of specific doctype
		self.reference_doctype = reference_doctype or self.doctype

		if user_settings:
			self.user_settings = json.loads(user_settings)

		if query:
			result = self.run_custom_query(query)
		else:
			result = self.build_and_run()

		if with_comment_count and not as_list and self.doctype:
			self.add_comment_count(result)

		if save_user_settings:
			self.save_user_settings_fields = save_user_settings_fields
			self.update_user_settings()

		return result
Example #52
0
def get_valuation_rate(item_code,
                       warehouse,
                       voucher_type,
                       voucher_no,
                       allow_zero_rate=False,
                       currency=None,
                       company=None,
                       raise_error_if_no_rate=True):
    # Get valuation rate from last sle for the same item and warehouse
    if not company:
        company = erpnext.get_default_company()

    last_valuation_rate = frappe.db.sql(
        """select valuation_rate
		from `tabStock Ledger Entry`
		where
			item_code = %s
			AND warehouse = %s
			AND valuation_rate >= 0
			AND NOT (voucher_no = %s AND voucher_type = %s)
		order by posting_date desc, posting_time desc, name desc limit 1""",
        (item_code, warehouse, voucher_no, voucher_type))

    if not last_valuation_rate:
        # Get valuation rate from last sle for the item against any warehouse
        last_valuation_rate = frappe.db.sql(
            """select valuation_rate
			from `tabStock Ledger Entry`
			where
				item_code = %s
				AND valuation_rate > 0
				AND NOT(voucher_no = %s AND voucher_type = %s)
			order by posting_date desc, posting_time desc, name desc limit 1""",
            (item_code, voucher_no, voucher_type))

    if last_valuation_rate:
        return flt(
            last_valuation_rate[0]
            [0])  # as there is previous records, it might come with zero rate

    # If negative stock allowed, and item delivered without any incoming entry,
    # system does not found any SLE, then take valuation rate from Item
    valuation_rate = frappe.db.get_value("Item", item_code, "valuation_rate")

    if not valuation_rate:
        # try Item Standard rate
        valuation_rate = frappe.db.get_value("Item", item_code,
                                             "standard_rate")

        if not valuation_rate:
            # try in price list
            valuation_rate = frappe.db.get_value(
                'Item Price',
                dict(item_code=item_code, buying=1, currency=currency),
                'price_list_rate')

    if not allow_zero_rate and not valuation_rate and raise_error_if_no_rate \
      and cint(erpnext.is_perpetual_inventory_enabled(company)):
        frappe.local.message_log = []
        form_link = frappe.utils.get_link_to_form("Item", item_code)

        message = _(
            "Valuation Rate for the Item {0}, is required to do accounting entries for {1} {2}."
        ).format(form_link, voucher_type, voucher_no)
        message += "<br><br>" + _(" Here are the options to proceed:")
        solutions = "<li>" + _(
            "If the item is transacting as a Zero Valuation Rate item in this entry, please enable 'Allow Zero Valuation Rate' in the {0} Item table."
        ).format(voucher_type) + "</li>"
        solutions += "<li>" + _("If not, you can Cancel / Submit this entry "
                                ) + _("{0}").format(frappe.bold("after")) + _(
                                    " performing either one below:") + "</li>"
        sub_solutions = "<ul><li>" + _(
            "Create an incoming stock transaction for the Item.") + "</li>"
        sub_solutions += "<li>" + _(
            "Mention Valuation Rate in the Item master.") + "</li></ul>"
        msg = message + solutions + sub_solutions + "</li>"

        frappe.throw(msg=msg, title=_("Valuation Rate Missing"))

    return valuation_rate
	def validate_for_subcontracting(self):
		if not self.is_subcontracted and self.sub_contracted_items:
			frappe.throw(_("Please enter 'Is Subcontracted' as Yes or No"))

		if self.is_subcontracted == "Yes":
			if self.doctype in ["Purchase Receipt", "Purchase Invoice"] and not self.supplier_warehouse:
				frappe.throw(_("Supplier Warehouse mandatory for sub-contracted Purchase Receipt"))

			for item in self.get("items"):
				if item in self.sub_contracted_items and not item.bom:
					frappe.throw(_("Please select BOM in BOM field for Item {0}").format(item.item_code))

			if self.doctype == "Purchase Order":
				for supplied_item in self.get("supplied_items"):
					if not supplied_item.reserve_warehouse:
						frappe.throw(_("Reserved Warehouse is mandatory for Item {0} in Raw Materials supplied").format(frappe.bold(supplied_item.rm_item_code)))

		else:
			for item in self.get("items"):
				if item.bom:
					item.bom = None
Example #54
0
	def update_raw_materials_supplied_based_on_stock_entries(self):
		self.set('supplied_items', [])

		purchase_orders = set([d.purchase_order for d in self.items])

		# qty of raw materials backflushed (for each item per purchase order)
		backflushed_raw_materials_map = get_backflushed_subcontracted_raw_materials(purchase_orders)

		# qty of "finished good" item yet to be received
		qty_to_be_received_map = get_qty_to_be_received(purchase_orders)

		for item in self.get('items'):
			if not item.purchase_order:
				continue

			# reset raw_material cost
			item.rm_supp_cost = 0

			# qty of raw materials transferred to the supplier
			transferred_raw_materials = get_subcontracted_raw_materials_from_se(item.purchase_order, item.item_code)

			non_stock_items = get_non_stock_items(item.purchase_order, item.item_code)

			item_key = '{}{}'.format(item.item_code, item.purchase_order)

			fg_yet_to_be_received = qty_to_be_received_map.get(item_key)

			if not fg_yet_to_be_received:
				frappe.throw(_("Row #{0}: Item {1} is already fully received in Purchase Order {2}")
					.format(item.idx, frappe.bold(item.item_code),
						frappe.utils.get_link_to_form("Purchase Order", item.purchase_order)),
					title=_("Limit Crossed"))

			transferred_batch_qty_map = get_transferred_batch_qty_map(item.purchase_order, item.item_code)
			# backflushed_batch_qty_map = get_backflushed_batch_qty_map(item.purchase_order, item.item_code)

			for raw_material in transferred_raw_materials + non_stock_items:
				rm_item_key = (raw_material.rm_item_code, item.item_code, item.purchase_order)
				raw_material_data = backflushed_raw_materials_map.get(rm_item_key, {})

				consumed_qty = raw_material_data.get('qty', 0)
				consumed_serial_nos = raw_material_data.get('serial_no', '')
				consumed_batch_nos = raw_material_data.get('batch_nos', '')

				transferred_qty = raw_material.qty

				rm_qty_to_be_consumed = transferred_qty - consumed_qty

				# backflush all remaining transferred qty in the last Purchase Receipt
				if fg_yet_to_be_received == item.qty:
					qty = rm_qty_to_be_consumed
				else:
					qty = (rm_qty_to_be_consumed / fg_yet_to_be_received) * item.qty

					if frappe.get_cached_value('UOM', raw_material.stock_uom, 'must_be_whole_number'):
						qty = frappe.utils.ceil(qty)

				if qty > rm_qty_to_be_consumed:
					qty = rm_qty_to_be_consumed

				if not qty: continue

				if raw_material.serial_nos:
					set_serial_nos(raw_material, consumed_serial_nos, qty)

				if raw_material.batch_nos:
					backflushed_batch_qty_map = raw_material_data.get('consumed_batch', {})

					batches_qty = get_batches_with_qty(raw_material.rm_item_code, raw_material.main_item_code,
						qty, transferred_batch_qty_map, backflushed_batch_qty_map, item.purchase_order)

					for batch_data in batches_qty:
						qty = batch_data['qty']
						raw_material.batch_no = batch_data['batch']
						if qty > 0:
							self.append_raw_material_to_be_backflushed(item, raw_material, qty)
				else:
					self.append_raw_material_to_be_backflushed(item, raw_material, qty)
def start_remove(name):
	frappe.installer.remove_app(app_name=name, yes=True)
	frappe.publish_realtime('msgprint', _('App {0} removed').format(frappe.bold(name)))
Example #56
0
def rename_doc(doctype,
               old,
               new,
               force=False,
               merge=False,
               ignore_permissions=False,
               ignore_if_exists=False,
               show_alert=True):
    """
		Renames a doc(dt, old) to doc(dt, new) and
		updates all linked fields of type "Link"
	"""
    if not frappe.db.exists(doctype, old):
        return

    if ignore_if_exists and frappe.db.exists(doctype, new):
        return

    if old == new:
        frappe.msgprint(_('Please select a new name to rename'))
        return

    force = cint(force)
    merge = cint(merge)
    meta = frappe.get_meta(doctype)

    # call before_rename
    old_doc = frappe.get_doc(doctype, old)
    out = old_doc.run_method("before_rename", old, new, merge) or {}
    new = (out.get("new") or new) if isinstance(out, dict) else (out or new)
    new = validate_rename(doctype, new, meta, merge, force, ignore_permissions)

    if not merge:
        rename_parent_and_child(doctype, old, new, meta)
    else:
        update_assignments(old, new, doctype)

    # update link fields' values
    link_fields = get_link_fields(doctype)
    update_link_field_values(link_fields, old, new, doctype)

    rename_dynamic_links(doctype, old, new)

    # save the user settings in the db
    update_user_settings(old, new, link_fields)

    if doctype == 'DocType':
        rename_doctype(doctype, old, new, force)

    update_attachments(doctype, old, new)

    rename_versions(doctype, old, new)

    # call after_rename
    new_doc = frappe.get_doc(doctype, new)

    # copy any flags if required
    new_doc._local = getattr(old_doc, "_local", None)

    new_doc.run_method("after_rename", old, new, merge)

    if not merge:
        rename_password(doctype, old, new)

    # update user_permissions
    frappe.db.sql(
        """UPDATE `tabDefaultValue` SET `defvalue`=%s WHERE `parenttype`='User Permission'
		AND `defkey`=%s AND `defvalue`=%s""", (new, doctype, old))

    if merge:
        new_doc.add_comment(
            'Edit',
            _("merged {0} into {1}").format(frappe.bold(old),
                                            frappe.bold(new)))
    else:
        new_doc.add_comment(
            'Edit',
            _("renamed from {0} to {1}").format(frappe.bold(old),
                                                frappe.bold(new)))

    if merge:
        frappe.delete_doc(doctype, old)

    frappe.clear_cache()
    frappe.enqueue('frappe.utils.global_search.rebuild_for_doctype',
                   doctype=doctype)

    if show_alert:
        frappe.msgprint(_('Document renamed from {0} to {1}').format(
            bold(old), bold(new)),
                        alert=True,
                        indicator='green')

    return new
Example #57
0
	def cant_change(self):
		if not self.get("__islocal"):
			fields = ("has_serial_no", "is_stock_item", "valuation_method", "has_batch_no")

			values = frappe.db.get_value("Item", self.name, fields, as_dict=True)
			if not values.get('valuation_method') and self.get('valuation_method'):
				values['valuation_method'] = frappe.db.get_single_value("Stock Settings", "valuation_method") or "FIFO"

			if values:
				for field in fields:
					if cstr(self.get(field)) != cstr(values.get(field)):
						if not self.check_if_linked_document_exists(field):
							break # no linked document, allowed
						else:
							frappe.throw(_("As there are existing transactions against item {0}, you can not change the value of {1}").format(self.name, frappe.bold(self.meta.get_label(field))))
Example #58
0
    def stock_entry_received(self):
        se = frappe.new_doc("Stock Entry")
        se.posting_date = self.received_date
        se.purpose = "Repack"
        se.naming_series = "STE-"
        se.company = self.company
        se.volume = self.volume
        se.volume_rate = self.volume_rate
        se.volume_cost = self.volume_cost

        abbr = frappe.db.get_value("Company", self.company, 'abbr')

        if self.get('bom_no'):
            se.from_bom = 1
            se.bom_no = self.bom_no
            se.fg_completed_qty = flt(self.finished_product_qty)
            se.based_on = self.based_on

        for row in self.items:
            se.append(
                "items", {
                    'item_code': row.item_code,
                    's_warehouse': 'Jobwork - ' + abbr,
                    'qty': row.received_qty,
                    'batch_no': row.batch_no,
                    'basic_rate': row.rate,
                    'lot_no': row.lot_no,
                    'packaging_material': row.packaging_material,
                    'packing_size': row.packing_size,
                    'batch_yield': row.batch_yield,
                    'concentration': row.concentration
                })

        se.append(
            "items", {
                'item_code': self.finished_product,
                't_warehouse': self.finished_product_warehouse,
                'qty': self.finished_product_qty,
                'packaging_material': self.packaging_material,
                'packing_size': self.packing_size,
                'lot_no': self.lot_no,
                'concentration': self.concentration,
                'batch_yield': self.batch_yield,
            })

        for row in self.additional_costs:
            se.append('additional_costs', {
                'description': row.description,
                'amount': row.amount,
            })

        try:
            se.save()
            se.submit()
            self.db_set('received_stock_entry', se.name)
            self.db_set('valuation_rate', se.items[-1].valuation_rate)
            url = get_url_to_form("Stock Entry", se.name)
            frappe.msgprint(
                "New Stock Entry - <a href='{url}'>{doc}</a> created of Repack for Finished Product"
                .format(url=url, doc=frappe.bold(se.name)))
        except:
            frappe.db.rollback()
            frappe.throw(_("Error creating Stock Entry"), title="Error")
Example #59
0
def add_call_summary(call_log, summary):
	doc = frappe.get_doc('Call Log', call_log)
	doc.add_comment('Comment', frappe.bold(_('Call Summary')) + '<br><br>' + summary)
Example #60
0
	def validate_perpetual_inventory_for_non_stock_items(self):
		if not self.get("__islocal"):
			if cint(self.enable_perpetual_inventory_for_non_stock_items) == 1 and not self.service_received_but_not_billed:
				frappe.throw(_("Set default {0} account for perpetual inventory for non stock items").format(
					frappe.bold('Service Received But Not Billed')))