diff --git a/erpnext/accounts/doctype/account/account.py b/erpnext/accounts/doctype/account/account.py index 59637a59793..502a484236c 100644 --- a/erpnext/accounts/doctype/account/account.py +++ b/erpnext/accounts/doctype/account/account.py @@ -3,7 +3,7 @@ from __future__ import unicode_literals import frappe -from frappe.utils import flt, fmt_money, cstr, cint +from frappe.utils import flt, cstr, cint from frappe import msgprint, throw, _ from frappe.model.document import Document @@ -16,8 +16,8 @@ class Account(Document): def get_address(self): return {'address': frappe.db.get_value(self.master_type, self.master_name, "address")} - - def validate(self): + + def validate(self): self.validate_master_name() self.validate_parent() self.validate_duplicate_account() @@ -25,18 +25,18 @@ class Account(Document): self.validate_mandatory() self.validate_warehouse_account() self.validate_frozen_accounts_modifier() - + def validate_master_name(self): if self.master_type in ('Customer', 'Supplier') or self.account_type == "Warehouse": if not self.master_name: msgprint(_("Please enter Master Name once the account is created.")) elif not frappe.db.exists(self.master_type or self.account_type, self.master_name): throw(_("Invalid Master Name")) - + def validate_parent(self): """Fetch Parent Details and validation for account not to be created under ledger""" if self.parent_account: - par = frappe.db.get_value("Account", self.parent_account, + par = frappe.db.get_value("Account", self.parent_account, ["name", "group_or_ledger", "report_type"], as_dict=1) if not par: throw(_("Parent account does not exists")) @@ -44,46 +44,36 @@ class Account(Document): throw(_("You can not assign itself as parent account")) elif par["group_or_ledger"] != 'Group': throw(_("Parent account can not be a ledger")) - + if par["report_type"]: self.report_type = par["report_type"] - + def validate_duplicate_account(self): if self.get('__islocal') or not self.name: company_abbr = frappe.db.get_value("Company", self.company, "abbr") if frappe.db.exists("Account", (self.account_name + " - " + company_abbr)): - throw("{name}: {acc_name} {exist}, {rename}".format(**{ - "name": _("Account Name"), - "acc_name": self.account_name, - "exist": _("already exists"), - "rename": _("please rename") - })) - + throw(_("Account {0} already exists").format(self.account_name)) + def validate_root_details(self): #does not exists parent if frappe.db.exists("Account", self.name): if not frappe.db.get_value("Account", self.name, "parent_account"): throw(_("Root cannot be edited.")) - + def validate_frozen_accounts_modifier(self): old_value = frappe.db.get_value("Account", self.name, "freeze_account") if old_value and old_value != self.freeze_account: - frozen_accounts_modifier = frappe.db.get_value( 'Accounts Settings', None, + frozen_accounts_modifier = frappe.db.get_value( 'Accounts Settings', None, 'frozen_accounts_modifier') if not frozen_accounts_modifier or \ frozen_accounts_modifier not in frappe.user.get_roles(): throw(_("You are not authorized to set Frozen value")) - + def convert_group_to_ledger(self): if self.check_if_child_exists(): - throw("{acc}: {account_name} {child}. {msg}".format(**{ - "acc": _("Account"), - "account_name": self.name, - "child": _("has existing child"), - "msg": _("You can not convert this account to ledger") - })) + throw(_("Account with child nodes cannot be converted to ledger")) elif self.check_gle_exists(): - throw(_("Account with existing transaction can not be converted to ledger.")) + throw(_("Account with existing transaction cannot be converted to ledger")) else: self.group_or_ledger = 'Ledger' self.save() @@ -104,17 +94,17 @@ class Account(Document): return frappe.db.get_value("GL Entry", {"account": self.name}) def check_if_child_exists(self): - return frappe.db.sql("""select name from `tabAccount` where parent_account = %s + return frappe.db.sql("""select name from `tabAccount` where parent_account = %s and docstatus != 2""", self.name) - + def validate_mandatory(self): if not self.report_type: throw(_("Report Type is mandatory")) - + def validate_warehouse_account(self): if not cint(frappe.defaults.get_global_default("auto_accounting_for_stock")): return - + if self.account_type == "Warehouse": old_warehouse = cstr(frappe.db.get_value("Account", self.name, "master_name")) if old_warehouse != cstr(self.master_name): @@ -124,10 +114,10 @@ class Account(Document): self.validate_warehouse(self.master_name) else: throw(_("Master Name is mandatory if account type is Warehouse")) - + def validate_warehouse(self, warehouse): if frappe.db.get_value("Stock Ledger Entry", {"warehouse": warehouse}): - throw(_("Stock transactions exist against warehouse ") + warehouse + + throw(_("Stock transactions exist against warehouse ") + warehouse + _(" .You can not assign / modify / remove Master Name")) def update_nsm_model(self): @@ -135,73 +125,67 @@ class Account(Document): import frappe import frappe.utils.nestedset frappe.utils.nestedset.update_nsm(self) - + def on_update(self): - self.update_nsm_model() + self.update_nsm_model() def get_authorized_user(self): # Check logged-in user is authorized if frappe.db.get_value('Accounts Settings', None, 'credit_controller') \ in frappe.user.get_roles(): return 1 - + def check_credit_limit(self, total_outstanding): # Get credit limit credit_limit_from = 'Customer' - cr_limit = frappe.db.sql("""select t1.credit_limit from tabCustomer t1, `tabAccount` t2 + cr_limit = frappe.db.sql("""select t1.credit_limit from tabCustomer t1, `tabAccount` t2 where t2.name=%s and t1.name = t2.master_name""", self.name) credit_limit = cr_limit and flt(cr_limit[0][0]) or 0 if not credit_limit: credit_limit = frappe.db.get_value('Company', self.company, 'credit_limit') credit_limit_from = 'Company' - + # If outstanding greater than credit limit and not authorized person raise exception if credit_limit > 0 and flt(total_outstanding) > credit_limit \ and not self.get_authorized_user(): - throw("""Total Outstanding amount (%s) for %s can not be \ - greater than credit limit (%s). To change your credit limit settings, \ - please update in the %s master""" % (fmt_money(total_outstanding), - self.name, fmt_money(credit_limit), credit_limit_from)) - + throw(_("{0} Credit limit {0} crossed").format(_(credit_limit_from), credit_limit)) + def validate_trash(self): """checks gl entries and if child exists""" if not self.parent_account: throw(_("Root account can not be deleted")) - + if self.check_gle_exists(): - throw("""Account with existing transaction (Sales Invoice / Purchase Invoice / \ - Journal Voucher) can not be deleted""") + throw(_("Account with existing transaction can not be deleted")) if self.check_if_child_exists(): throw(_("Child account exists for this account. You can not delete this account.")) - def on_trash(self): + def on_trash(self): self.validate_trash() self.update_nsm_model() - + def before_rename(self, old, new, merge=False): # Add company abbr if not provided from erpnext.setup.doctype.company.company import get_name_with_abbr new_account = get_name_with_abbr(new, self.company) - + # Validate properties before merging if merge: if not frappe.db.exists("Account", new): throw(_("Account ") + new +_(" does not exists")) - - val = list(frappe.db.get_value("Account", new_account, + + val = list(frappe.db.get_value("Account", new_account, ["group_or_ledger", "report_type", "company"])) - + if val != [self.group_or_ledger, self.report_type, self.company]: - throw(_("""Merging is only possible if following \ - properties are same in both records. - Group or Ledger, Report Type, Company""")) - + throw(_("""Merging is only possible if following properties are same in both records. Group or Ledger, Report Type, Company""")) + return new_account def after_rename(self, old, new, merge=False): if not merge: - frappe.db.set_value("Account", new, "account_name", + frappe.db.set_value("Account", new, "account_name", " - ".join(new.split(" - ")[:-1])) else: from frappe.utils.nestedset import rebuild_tree @@ -209,15 +193,15 @@ class Account(Document): def get_master_name(doctype, txt, searchfield, start, page_len, filters): conditions = (" and company='%s'"% filters["company"].replace("'", "\'")) if doctype == "Warehouse" else "" - + return frappe.db.sql("""select name from `tab%s` where %s like %s %s order by name limit %s, %s""" % - (filters["master_type"], searchfield, "%s", conditions, "%s", "%s"), + (filters["master_type"], searchfield, "%s", conditions, "%s", "%s"), ("%%%s%%" % txt, start, page_len), as_list=1) - + def get_parent_account(doctype, txt, searchfield, start, page_len, filters): - return frappe.db.sql("""select name from tabAccount + return frappe.db.sql("""select name from tabAccount where group_or_ledger = 'Group' and docstatus != 2 and company = %s - and %s like %s order by name limit %s, %s""" % - ("%s", searchfield, "%s", "%s", "%s"), - (filters["company"], "%%%s%%" % txt, start, page_len), as_list=1) \ No newline at end of file + and %s like %s order by name limit %s, %s""" % + ("%s", searchfield, "%s", "%s", "%s"), + (filters["company"], "%%%s%%" % txt, start, page_len), as_list=1) diff --git a/erpnext/accounts/doctype/c_form/c_form.py b/erpnext/accounts/doctype/c_form/c_form.py index 95e2e4060f0..11d05470aa5 100644 --- a/erpnext/accounts/doctype/c_form/c_form.py +++ b/erpnext/accounts/doctype/c_form/c_form.py @@ -9,46 +9,42 @@ from frappe.model.document import Document class CForm(Document): def validate(self): - """Validate invoice that c-form is applicable + """Validate invoice that c-form is applicable and no other c-form is received for that""" for d in self.get('invoice_details'): if d.invoice_no: inv = frappe.db.sql("""select c_form_applicable, c_form_no from `tabSales Invoice` where name = %s and docstatus = 1""", d.invoice_no) - - if not inv: - frappe.throw("""Invoice: %s is not exists in the system or - is not submitted, please check.""" % d.invoice_no) - - elif inv[0][0] != 'Yes': + + if inv[0][0] != 'Yes': frappe.throw("C-form is not applicable for Invoice: %s" % d.invoice_no) - + elif inv[0][1] and inv[0][1] != self.name: frappe.throw("""Invoice %s is tagged in another C-form: %s. If you want to change C-form no for this invoice, - please remove invoice no from the previous c-form and then try again""" % + please remove invoice no from the previous c-form and then try again""" % (d.invoice_no, inv[0][1])) def on_update(self): """ Update C-Form No on invoices""" self.set_total_invoiced_amount() - + def on_submit(self): self.set_cform_in_sales_invoices() - + def before_cancel(self): # remove cform reference frappe.db.sql("""update `tabSales Invoice` set c_form_no=null where c_form_no=%s""", self.name) - + def set_cform_in_sales_invoices(self): inv = [d.invoice_no for d in self.get('invoice_details')] if inv: - frappe.db.sql("""update `tabSales Invoice` set c_form_no=%s, modified=%s where name in (%s)""" % + frappe.db.sql("""update `tabSales Invoice` set c_form_no=%s, modified=%s where name in (%s)""" % ('%s', '%s', ', '.join(['%s'] * len(inv))), tuple([self.name, self.modified] + inv)) - - frappe.db.sql("""update `tabSales Invoice` set c_form_no = null, modified = %s - where name not in (%s) and ifnull(c_form_no, '') = %s""" % + + frappe.db.sql("""update `tabSales Invoice` set c_form_no = null, modified = %s + where name not in (%s) and ifnull(c_form_no, '') = %s""" % ('%s', ', '.join(['%s']*len(inv)), '%s'), tuple([self.modified] + inv + [self.name])) else: frappe.throw(_("Please enter atleast 1 invoice in the table")) @@ -60,7 +56,7 @@ class CForm(Document): def get_invoice_details(self, invoice_no): """ Pull details from invoices for referrence """ - inv = frappe.db.get_value("Sales Invoice", invoice_no, + inv = frappe.db.get_value("Sales Invoice", invoice_no, ["posting_date", "territory", "net_total", "grand_total"], as_dict=True) return { 'invoice_date' : inv.posting_date, @@ -72,9 +68,9 @@ class CForm(Document): def get_invoice_nos(doctype, txt, searchfield, start, page_len, filters): from erpnext.utilities import build_filter_conditions conditions, filter_values = build_filter_conditions(filters) - - return frappe.db.sql("""select name from `tabSales Invoice` where docstatus = 1 - and c_form_applicable = 'Yes' and ifnull(c_form_no, '') = '' %s - and %s like %s order by name limit %s, %s""" % - (conditions, searchfield, "%s", "%s", "%s"), - tuple(filter_values + ["%%%s%%" % txt, start, page_len])) \ No newline at end of file + + return frappe.db.sql("""select name from `tabSales Invoice` where docstatus = 1 + and c_form_applicable = 'Yes' and ifnull(c_form_no, '') = '' %s + and %s like %s order by name limit %s, %s""" % + (conditions, searchfield, "%s", "%s", "%s"), + tuple(filter_values + ["%%%s%%" % txt, start, page_len])) diff --git a/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py b/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py index 33e0495293b..b70c1dda8ee 100644 --- a/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py +++ b/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py @@ -8,13 +8,13 @@ from erpnext.accounts.report.accounts_receivable.accounts_receivable import get_ def execute(filters=None): if not filters: filters = {} - + columns = get_columns() entries = get_entries(filters) invoice_posting_date_map = get_invoice_posting_date_map(filters) against_date = "" outstanding_amount = 0.0 - + data = [] for d in entries: if d.against_voucher: @@ -23,67 +23,66 @@ def execute(filters=None): else: against_date = d.against_invoice and invoice_posting_date_map[d.against_invoice] or "" outstanding_amount = d.credit or -1*d.debit - - row = [d.name, d.account, d.posting_date, d.against_voucher or d.against_invoice, + + row = [d.name, d.account, d.posting_date, d.against_voucher or d.against_invoice, against_date, d.debit, d.credit, d.cheque_no, d.cheque_date, d.remark] - + if d.against_voucher or d.against_invoice: row += get_ageing_data(d.posting_date, against_date, outstanding_amount) else: row += ["", "", "", "", ""] - + data.append(row) - + return columns, data - + def get_columns(): - return ["Journal Voucher:Link/Journal Voucher:140", "Account:Link/Account:140", - "Posting Date:Date:100", "Against Invoice:Link/Purchase Invoice:130", - "Against Invoice Posting Date:Date:130", "Debit:Currency:120", "Credit:Currency:120", - "Reference No::100", "Reference Date:Date:100", "Remarks::150", "Age:Int:40", + return ["Journal Voucher:Link/Journal Voucher:140", "Account:Link/Account:140", + "Posting Date:Date:100", "Against Invoice:Link/Purchase Invoice:130", + "Against Invoice Posting Date:Date:130", "Debit:Currency:120", "Credit:Currency:120", + "Reference No::100", "Reference Date:Date:100", "Remarks::150", "Age:Int:40", "0-30:Currency:100", "30-60:Currency:100", "60-90:Currency:100", "90-Above:Currency:100" ] def get_conditions(filters): conditions = "" party_accounts = [] - + if filters.get("account"): party_accounts = [filters["account"]] else: - cond = filters.get("company") and (" and company = '%s'" % + cond = filters.get("company") and (" and company = '%s'" % filters["company"].replace("'", "\'")) or "" - + if filters.get("payment_type") == "Incoming": cond += " and master_type = 'Customer'" else: cond += " and master_type = 'Supplier'" - party_accounts = frappe.db.sql_list("""select name from `tabAccount` + party_accounts = frappe.db.sql_list("""select name from `tabAccount` where ifnull(master_name, '')!='' and docstatus < 2 %s""" % cond) - + if party_accounts: conditions += " and jvd.account in (%s)" % (", ".join(['%s']*len(party_accounts))) else: - msgprint(_("No Customer or Supplier Accounts found. Accounts are identified based on \ - 'Master Type' value in account record."), raise_exception=1) - + msgprint(_("No Customer or Supplier Accounts found"), raise_exception=1) + if filters.get("from_date"): conditions += " and jv.posting_date >= '%s'" % filters["from_date"] if filters.get("to_date"): conditions += " and jv.posting_date <= '%s'" % filters["to_date"] return conditions, party_accounts - + def get_entries(filters): conditions, party_accounts = get_conditions(filters) - entries = frappe.db.sql("""select jv.name, jvd.account, jv.posting_date, - jvd.against_voucher, jvd.against_invoice, jvd.debit, jvd.credit, - jv.cheque_no, jv.cheque_date, jv.remark - from `tabJournal Voucher Detail` jvd, `tabJournal Voucher` jv - where jvd.parent = jv.name and jv.docstatus=1 %s order by jv.name DESC""" % + entries = frappe.db.sql("""select jv.name, jvd.account, jv.posting_date, + jvd.against_voucher, jvd.against_invoice, jvd.debit, jvd.credit, + jv.cheque_no, jv.cheque_date, jv.remark + from `tabJournal Voucher Detail` jvd, `tabJournal Voucher` jv + where jvd.parent = jv.name and jv.docstatus=1 %s order by jv.name DESC""" % (conditions), tuple(party_accounts), as_dict=1) - + return entries - + def get_invoice_posting_date_map(filters): invoice_posting_date_map = {} if filters.get("payment_type") == "Incoming": @@ -93,4 +92,4 @@ def get_invoice_posting_date_map(filters): for t in frappe.db.sql("""select name, posting_date from `tabPurchase Invoice`"""): invoice_posting_date_map[t[0]] = t[1] - return invoice_posting_date_map \ No newline at end of file + return invoice_posting_date_map diff --git a/erpnext/buying/doctype/purchase_common/purchase_common.py b/erpnext/buying/doctype/purchase_common/purchase_common.py index 1addb425599..94e8ea367c6 100644 --- a/erpnext/buying/doctype/purchase_common/purchase_common.py +++ b/erpnext/buying/doctype/purchase_common/purchase_common.py @@ -147,7 +147,7 @@ class PurchaseCommon(BuyingController): stopped = frappe.db.sql("""select name from `tab%s` where name = %s and status = 'Stopped'""" % (doctype, '%s'), docname) if stopped: - frappe.throw("{0} {1} status is 'Stopped'".format(doctype, docname), frappe.InvalidStatusError) + frappe.throw(_("{0} {1} status is 'Stopped'").format(doctype, docname), frappe.InvalidStatusError) def check_docstatus(self, check, doctype, docname, detail_doctype = ''): if check == 'Next': diff --git a/erpnext/hr/doctype/employee/employee.js b/erpnext/hr/doctype/employee/employee.js index e91435e31a8..49914d21bc4 100644 --- a/erpnext/hr/doctype/employee/employee.js +++ b/erpnext/hr/doctype/employee/employee.js @@ -9,23 +9,23 @@ erpnext.hr.EmployeeController = frappe.ui.form.Controller.extend({ this.frm.fields_dict.reports_to.get_query = function(doc, cdt, cdn) { return { query: "erpnext.controllers.queries.employee_query"} } }, - + onload: function() { this.setup_leave_approver_select(); this.frm.toggle_display(["esic_card_no", "gratuity_lic_id", "pan_number", "pf_number"], frappe.boot.sysdefaults.country==="India"); if(this.frm.doc.__islocal) this.frm.set_value("employee_name", ""); }, - + refresh: function() { var me = this; erpnext.hide_naming_series(); - if(!this.frm.doc.__islocal) { + if(!this.frm.doc.__islocal) { cur_frm.add_custom_button(__('Make Salary Structure'), function() { me.make_salary_structure(this); }); } }, - + setup_leave_approver_select: function() { var me = this; return this.frm.call({ @@ -33,21 +33,21 @@ erpnext.hr.EmployeeController = frappe.ui.form.Controller.extend({ callback: function(r) { var df = frappe.meta.get_docfield("Employee Leave Approver", "leave_approver", me.frm.doc.name); - df.options = $.map(r.message, function(user) { - return {value: user, label: frappe.user_info(user).fullname}; + df.options = $.map(r.message, function(user) { + return {value: user, label: frappe.user_info(user).fullname}; }); me.frm.fields_dict.employee_leave_approvers.refresh(); } }); }, - + date_of_birth: function() { return cur_frm.call({ method: "get_retirement_date", args: {date_of_birth: this.frm.doc.date_of_birth} }); }, - + salutation: function() { if(this.frm.doc.salutation) { this.frm.set_value("gender", { @@ -56,16 +56,12 @@ erpnext.hr.EmployeeController = frappe.ui.form.Controller.extend({ }[this.frm.doc.salutation]); } }, - + make_salary_structure: function(btn) { var me = this; this.validate_salary_structure(btn, function(r) { if(r.message) { - msgprint(__("Employee {0}:\ - An active Salary Structure already exists. \ - If you want to create new one, please ensure that no active \ - Salary Structure exists for this Employee. \ - Go to the active Salary Structure and set \"Is Active\" = \"No\"", [me.frm.doc.name])); + msgprint(__("Active Salary Sructure already exists for Employee {0}", [me.frm.doc.name])); } else if(!r.exc) { frappe.model.map({ source: me.frm.doc, @@ -74,7 +70,7 @@ erpnext.hr.EmployeeController = frappe.ui.form.Controller.extend({ } }); }, - + validate_salary_structure: function(btn, callback) { var me = this; return this.frm.call({ diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.py b/erpnext/hr/doctype/leave_allocation/leave_allocation.py index 912f8f043f9..32cfd868e90 100755 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation.py +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.py @@ -63,7 +63,7 @@ class LeaveAllocation(Document): cf = cf and cint(cf[0][0]) or 0 if not cf: frappe.db.set(self,'carry_forward',0) - frappe.throw("Cannot carry forward {0}".format(self.leave_type)) + frappe.throw(_("Cannot carry forward {0}").format(self.leave_type)) def get_carry_forwarded_leaves(self): if self.carry_forward: diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index 126291191f9..f7084eff60d 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -144,8 +144,7 @@ class LeaveApplication(DocListController): def validate_max_days(self): max_days = frappe.db.get_value("Leave Type", self.leave_type, "max_days_allowed") if max_days and self.total_leave_days > max_days: - frappe.throw("Sorry ! You cannot apply for %s for more than %s days" % - (self.leave_type, max_days)) + frappe.throw(_("Leave of type {0} cannot be longer than {1}").format(self.leave_type, max_days)) def validate_leave_approver(self): employee = frappe.get_doc("Employee", self.employee) diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.py b/erpnext/hr/doctype/salary_slip/salary_slip.py index 12733e3c06e..99122529970 100644 --- a/erpnext/hr/doctype/salary_slip/salary_slip.py +++ b/erpnext/hr/doctype/salary_slip/salary_slip.py @@ -130,7 +130,7 @@ class SalarySlip(TransactionBase): (self.month, self.fiscal_year, self.employee, self.name)) if ret_exist: self.employee = '' - frappe.throw("Salary Slip of employee {0} already created for this month".format(self.employee)) + frappe.throw(_("Salary Slip of employee {0} already created for this month").format(self.employee)) def validate(self): from frappe.utils import money_in_words diff --git a/erpnext/manufacturing/doctype/production_order/production_order.py b/erpnext/manufacturing/doctype/production_order/production_order.py index 78270e53ca0..edcb25d37aa 100644 --- a/erpnext/manufacturing/doctype/production_order/production_order.py +++ b/erpnext/manufacturing/doctype/production_order/production_order.py @@ -5,7 +5,7 @@ from __future__ import unicode_literals import frappe from frappe.utils import cstr, flt, nowdate -from frappe import msgprint, _ +from frappe import _ class OverProductionError(frappe.ValidationError): pass @@ -34,9 +34,7 @@ class ProductionOrder(Document): and is_active=1 and item=%s""" , (self.bom_no, self.production_item), as_dict =1) if not bom: - frappe.throw("""Incorrect BOM: %s entered. - May be BOM not exists or inactive or not submitted - or for some other item.""" % cstr(self.bom_no)) + frappe.throw(_("BOM {0} is not active or not submitted").format(self.bom_no)) def validate_sales_order(self): if self.sales_order: @@ -44,7 +42,7 @@ class ProductionOrder(Document): where name=%s and docstatus = 1""", self.sales_order, as_dict=1)[0] if not so.name: - frappe.throw("Sales Order: %s is not valid" % self.sales_order) + frappe.throw(_("Sales Order {0} is not valid") % self.sales_order) if not self.expected_delivery_date: self.expected_delivery_date = so.delivery_date @@ -115,8 +113,7 @@ class ProductionOrder(Document): stock_entry = frappe.db.sql("""select name from `tabStock Entry` where production_order = %s and docstatus = 1""", self.name) if stock_entry: - frappe.throw("""Submitted Stock Entry %s exists against this production order. - Hence can not be cancelled.""" % stock_entry[0][0]) + frappe.throw(_("Cannot cancel because submitted Stock Entry {0} exists").format(stock_entry[0][0])) frappe.db.set(self,'status', 'Cancelled') self.update_planned_qty(-self.qty) diff --git a/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.py b/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.py index 5556fbe113e..1e753196ebb 100644 --- a/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.py +++ b/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.py @@ -152,21 +152,17 @@ class ProductionPlanningTool(Document): for d in self.get('pp_details'): self.validate_bom_no(d) if not flt(d.planned_qty): - frappe.throw("Please Enter Planned Qty for item: %s at row no: %s" % - (d.item_code, d.idx)) + frappe.throw(_("Please enter Planned Qty for Item {0} at row {1}").format(d.item_code, d.idx)) def validate_bom_no(self, d): if not d.bom_no: - frappe.throw("Please enter bom no for item: %s at row no: %s" % - (d.item_code, d.idx)) + frappe.throw(_("Please enter BOM for Item {0} at row {1}").format(d.item_code, d.idx)) else: bom = frappe.db.sql("""select name from `tabBOM` where name = %s and item = %s and docstatus = 1 and is_active = 1""", (d.bom_no, d.item_code), as_dict = 1) if not bom: - frappe.throw("""Incorrect BOM No: %s entered for item: %s at row no: %s - May be BOM is inactive or for other item or does not exists in the system""" % - (d.bom_no, d.item_doce, d.idx)) + frappe.throw(_("Incorrect or Inactive BOM {0} for Item {1} at row {2}").format(d.bom_no, d.item_code, d.idx)) def raise_production_order(self): """It will raise production order (Draft) for all distinct FG items""" diff --git a/erpnext/public/js/transaction.js b/erpnext/public/js/transaction.js index f76bbe0c24b..5d43ad415ea 100644 --- a/erpnext/public/js/transaction.js +++ b/erpnext/public/js/transaction.js @@ -10,7 +10,7 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({ if(this.frm.doc.__islocal) { var today = get_today(), currency = frappe.defaults.get_user_default("currency"); - + $.each({ posting_date: today, due_date: today, @@ -24,18 +24,18 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({ }, function(fieldname, value) { if(me.frm.fields_dict[fieldname] && !me.frm.doc[fieldname]) me.frm.set_value(fieldname, value); - }); + }); } if(this.other_fname) { this[this.other_fname + "_remove"] = this.calculate_taxes_and_totals; } - + if(this.fname) { this[this.fname + "_remove"] = this.calculate_taxes_and_totals; } }, - + onload_post_render: function() { var me = this; if(this.frm.doc.__islocal && this.frm.doc.company && !this.frm.doc.is_pos) { @@ -55,7 +55,7 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({ } } }, - + refresh: function() { this.frm.clear_custom_buttons(); erpnext.hide_naming_series(); @@ -77,7 +77,7 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({ icon = "icon-file-text"; } var me = this; - + this.$pos_btn = this.frm.appframe.add_button(btn_label, function() { me.toggle_pos(); }, icon); @@ -87,7 +87,7 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({ // Check whether it is Selling or Buying cycle var price_list = frappe.meta.has_field(cur_frm.doc.doctype, "selling_price_list") ? this.frm.doc.selling_price_list : this.frm.doc.buying_price_list; - + if (!price_list) msgprint(__("Please select Price List")) else { @@ -109,8 +109,8 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({ this.frm.refresh(); } }, - - + + item_code: function(doc, cdt, cdn) { var me = this; var item = frappe.get_doc(cdt, cdn); @@ -152,7 +152,7 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({ }); } } - }, + }, serial_no: function(doc, cdt, cdn) { var me = this; @@ -169,7 +169,7 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({ var serial_nos = item.serial_no.trim().replace(/,/g, '\n'); serial_nos = serial_nos.trim().split('\n'); - + // Trim each string and push unique string to new list for (var x=0; x<=serial_nos.length - 1; x++) { if (serial_nos[x].trim() != "" && sr_no.indexOf(serial_nos[x].trim()) == -1) { @@ -187,18 +187,18 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({ } } }, - + validate: function() { this.calculate_taxes_and_totals(); }, - + company: function() { if(this.frm.doc.company && this.frm.fields_dict.currency) { var company_currency = this.get_company_currency(); if (!this.frm.doc.currency) { this.frm.set_value("currency", company_currency); } - + if (this.frm.doc.currency == company_currency) { this.frm.set_value("conversion_rate", 1.0); } @@ -209,27 +209,27 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({ this.frm.script_manager.trigger("currency"); } }, - + get_company_currency: function() { return erpnext.get_currency(this.frm.doc.company); }, - + currency: function() { var me = this; this.set_dynamic_labels(); var company_currency = this.get_company_currency(); if(this.frm.doc.currency !== company_currency) { - this.get_exchange_rate(this.frm.doc.currency, company_currency, + this.get_exchange_rate(this.frm.doc.currency, company_currency, function(exchange_rate) { me.frm.set_value("conversion_rate", exchange_rate); me.conversion_rate(); }); } else { - this.conversion_rate(); + this.conversion_rate(); } }, - + conversion_rate: function() { if(this.frm.doc.currency === this.get_company_currency()) { this.frm.set_value("conversion_rate", 1.0); @@ -240,14 +240,14 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({ } if(flt(this.frm.doc.conversion_rate)>0.0) this.calculate_taxes_and_totals(); }, - + get_price_list_currency: function(buying_or_selling) { var me = this; var fieldname = buying_or_selling.toLowerCase() + "_price_list"; if(this.frm.doc[fieldname]) { return this.frm.call({ method: "erpnext.setup.utils.get_price_list_currency", - args: { + args: { price_list: this.frm.doc[fieldname], }, callback: function(r) { @@ -258,7 +258,7 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({ }); } }, - + get_exchange_rate: function(from_currency, to_currency, callback) { var exchange_name = from_currency + "-" + to_currency; frappe.model.with_doc("Currency Exchange", exchange_name, function(name) { @@ -266,14 +266,14 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({ callback(exchange_doc ? flt(exchange_doc.exchange_rate) : 0); }); }, - + price_list_currency: function() { var me=this; this.set_dynamic_labels(); - + var company_currency = this.get_company_currency(); if(this.frm.doc.price_list_currency !== company_currency) { - this.get_exchange_rate(this.frm.doc.price_list_currency, company_currency, + this.get_exchange_rate(this.frm.doc.price_list_currency, company_currency, function(exchange_rate) { if(exchange_rate) { me.frm.set_value("plc_conversion_rate", exchange_rate); @@ -284,7 +284,7 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({ this.plc_conversion_rate(); } }, - + plc_conversion_rate: function() { if(this.frm.doc.price_list_currency === this.get_company_currency()) { this.frm.set_value("plc_conversion_rate", 1.0); @@ -294,11 +294,11 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({ this.calculate_taxes_and_totals(); } }, - + qty: function(doc, cdt, cdn) { this.calculate_taxes_and_totals(); }, - + tax_rate: function(doc, cdt, cdn) { this.calculate_taxes_and_totals(); }, @@ -314,30 +314,30 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({ throw e; } }, - + set_dynamic_labels: function() { // What TODO? should we make price list system non-mandatory? this.frm.toggle_reqd("plc_conversion_rate", !!(this.frm.doc.price_list_name && this.frm.doc.price_list_currency)); - + var company_currency = this.get_company_currency(); this.change_form_labels(company_currency); this.change_grid_labels(company_currency); this.frm.refresh_fields(); }, - + recalculate: function() { this.calculate_taxes_and_totals(); }, - + recalculate_values: function() { this.calculate_taxes_and_totals(); }, - + calculate_charges: function() { this.calculate_taxes_and_totals(); }, - + included_in_print_rate: function(doc, cdt, cdn) { var tax = frappe.get_doc(cdt, cdn); try { @@ -350,52 +350,32 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({ throw e; } }, - + validate_on_previous_row: function(tax) { // validate if a valid row id is mentioned in case of // On Previous Row Amount and On Previous Row Total - if(([__("On Previous Row Amount"), __("On Previous Row Total")].indexOf(tax.charge_type) != -1) && + if((["On Previous Row Amount", "On Previous Row Total"].indexOf(tax.charge_type) != -1) && (!tax.row_id || cint(tax.row_id) >= tax.idx)) { - var msg = repl(__("Row") + " # %(idx)s [%(doctype)s]: " + - __("Please specify a valid") + " %(row_id_label)s", { - idx: tax.idx, - doctype: tax.doctype, - row_id_label: frappe.meta.get_label(tax.doctype, "row_id", tax.name) - }); + var msg = __("Please specify a valid Row ID for row {0} in table {1}", [tax.idx, __(tax.doctype)]) frappe.throw(msg); } }, - + validate_inclusive_tax: function(tax) { if(!this.frm.tax_doclist) this.frm.tax_doclist = this.get_tax_doclist(); - + var actual_type_error = function() { - var msg = repl(__("For row") + " # %(idx)s [%(doctype)s]: " + - "%(charge_type_label)s = \"%(charge_type)s\" " + - __("cannot be included in Item's rate"), { - idx: tax.idx, - doctype: tax.doctype, - charge_type_label: frappe.meta.get_label(tax.doctype, "charge_type", tax.name), - charge_type: tax.charge_type - }); + var msg = __("Actual type tax cannot be included in Item rate in row {0}", [tax.idx]) frappe.throw(msg); }; - + var on_previous_row_error = function(row_range) { - var msg = repl(__("For row") + " # %(idx)s [%(doctype)s]: " + - __("to be included in Item's rate, it is required that: ") + - " [" + __("Row") + " # %(row_range)s] " + __("also be included in Item's rate"), { - idx: tax.idx, - doctype: tax.doctype, - charge_type_label: frappe.meta.get_label(tax.doctype, "charge_type", tax.name), - charge_type: tax.charge_type, - inclusive_label: frappe.meta.get_label(tax.doctype, "included_in_print_rate", tax.name), - row_range: row_range, - }); - + var msg = __("For row {0} in {1}. To include {2} in Item rate, rows {3} must also be included", + [tax.idx, __(tax.doctype), tax.charge_type, row_range]) + frappe.throw(msg); }; - + if(cint(tax.included_in_print_rate)) { if(tax.charge_type == "Actual") { // inclusive tax cannot be of type Actual @@ -405,7 +385,7 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({ // referred row should also be an inclusive tax on_previous_row_error(tax.row_id); } else if(tax.charge_type == "On Previous Row Total") { - var taxes_not_included = $.map(this.frm.tax_doclist.slice(0, tax.row_id), + var taxes_not_included = $.map(this.frm.tax_doclist.slice(0, tax.row_id), function(t) { return cint(t.included_in_print_rate) ? null : t; }); if(taxes_not_included.length > 0) { // all rows above this tax should be inclusive @@ -414,26 +394,26 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({ } } }, - + _load_item_tax_rate: function(item_tax_rate) { return item_tax_rate ? JSON.parse(item_tax_rate) : {}; }, - + _get_tax_rate: function(tax, item_tax_map) { return (keys(item_tax_map).indexOf(tax.account_head) != -1) ? flt(item_tax_map[tax.account_head], precision("rate", tax)) : tax.rate; }, - + get_item_wise_taxes_html: function() { var item_tax = {}; var tax_accounts = []; var company_currency = this.get_company_currency(); - + $.each(this.get_tax_doclist(), function(i, tax) { var tax_amount_precision = precision("tax_amount", tax); var tax_rate_precision = precision("rate", tax); - $.each(JSON.parse(tax.item_wise_tax_detail || '{}'), + $.each(JSON.parse(tax.item_wise_tax_detail || '{}'), function(item_code, tax_data) { if(!item_tax[item_code]) item_tax[item_code] = {}; if($.isArray(tax_data)) { @@ -445,7 +425,7 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({ } var tax_amount = format_currency(flt(tax_data[1], tax_amount_precision), company_currency, tax_amount_precision); - + item_tax[item_code][tax.name] = [tax_rate, tax_amount]; } else { item_tax[item_code][tax.name] = [flt(tax_data, tax_rate_precision) + "%", ""]; @@ -453,10 +433,10 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({ }); tax_accounts.push([tax.name, tax.account_head]); }); - - var headings = $.map([__("Item Name")].concat($.map(tax_accounts, function(head) { return head[1]; })), + + var headings = $.map([__("Item Name")].concat($.map(tax_accounts, function(head) { return head[1]; })), function(head) { return '' + (head || "") + "" }).join("\n"); - + var distinct_item_names = []; var distinct_items = []; $.each(this.get_item_doclist(), function(i, item) { @@ -465,7 +445,7 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({ distinct_items.push(item); } }); - + var rows = $.map(distinct_items, function(item) { var item_tax_record = item_tax[item.item_code || item.item_name]; if(!item_tax_record) { return null; } @@ -478,7 +458,7 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({ }).join("\n") }); }).join("\n"); - + if(!rows) return ""; return '

Show / Hide tax break-up

\
\ @@ -486,16 +466,16 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({ ' + rows + ' \
'; }, - + validate_company_and_party: function() { var me = this; var valid = true; - + $.each(["company", "customer"], function(i, fieldname) { if(frappe.meta.has_field(me.frm.doc.doctype, fieldname)) { if (!me.frm.doc[fieldname]) { - msgprint(__("Please specify") + ": " + - frappe.meta.get_label(me.frm.doc.doctype, fieldname, me.frm.doc.name) + + msgprint(__("Please specify") + ": " + + frappe.meta.get_label(me.frm.doc.doctype, fieldname, me.frm.doc.name) + ". " + __("It is needed to fetch Item Details.")); valid = false; } @@ -503,25 +483,25 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({ }); return valid; }, - + get_item_doclist: function() { return this.frm.doc[this.fname] || []; }, - + get_tax_doclist: function() { return this.frm.doc[this.other_fname] || []; }, - + validate_conversion_rate: function() { this.frm.doc.conversion_rate = flt(this.frm.doc.conversion_rate, precision("conversion_rate")); - var conversion_rate_label = frappe.meta.get_label(this.frm.doc.doctype, "conversion_rate", + var conversion_rate_label = frappe.meta.get_label(this.frm.doc.doctype, "conversion_rate", this.frm.doc.name); var company_currency = this.get_company_currency(); - + if(!this.frm.doc.conversion_rate) { - frappe.throw(repl('%(conversion_rate_label)s' + - __(' is mandatory. Maybe Currency Exchange record is not created for ') + - '%(from_currency)s' + __(" to ") + '%(to_currency)s', + frappe.throw(repl('%(conversion_rate_label)s' + + __(' is mandatory. Maybe Currency Exchange record is not created for ') + + '%(from_currency)s' + __(" to ") + '%(to_currency)s', { "conversion_rate_label": conversion_rate_label, "from_currency": this.frm.doc.currency, @@ -529,7 +509,7 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({ })); } }, - + calculate_taxes_and_totals: function() { this.discount_amount_applied = false; this._calculate_taxes_and_totals(); @@ -552,13 +532,13 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({ this.show_item_wise_taxes(); }, - + initialize_taxes: function() { var me = this; $.each(this.frm.tax_doclist, function(i, tax) { tax.item_wise_tax_detail = {}; - tax_fields = ["total", "tax_amount_after_discount_amount", + tax_fields = ["total", "tax_amount_after_discount_amount", "tax_amount_for_current_item", "grand_total_for_current_item", "tax_fraction_for_current_item", "grand_total_fraction_for_current_item"] @@ -566,17 +546,17 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({ tax_fields.push("tax_amount"); $.each(tax_fields, function(i, fieldname) { tax[fieldname] = 0.0 }); - + me.validate_on_previous_row(tax); me.validate_inclusive_tax(tax); frappe.model.round_floats_in(tax); }); }, - + calculate_taxes: function() { var me = this; var actual_tax_dict = {}; - + // maintain actual tax rate based on idx $.each(this.frm.tax_doclist, function(i, tax) { if (tax.charge_type == "Actual") { @@ -602,34 +582,34 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({ // store tax_amount for current item as it will be used for // charge type = 'On Previous Row Amount' tax.tax_amount_for_current_item = current_tax_amount; - + // accumulate tax amount into tax.tax_amount if (!me.discount_amount_applied) tax.tax_amount += current_tax_amount; tax.tax_amount_after_discount_amount += current_tax_amount; - + // for buying if(tax.category) { // if just for valuation, do not add the tax amount in total // hence, setting it as 0 for further steps current_tax_amount = (tax.category == "Valuation") ? 0.0 : current_tax_amount; - + current_tax_amount *= (tax.add_deduct_tax == "Deduct") ? -1.0 : 1.0; } - + // Calculate tax.total viz. grand total till that step - // note: grand_total_for_current_item contains the contribution of + // note: grand_total_for_current_item contains the contribution of // item's amount, previously applied tax and the current tax on that item if(i==0) { tax.grand_total_for_current_item = flt(item.base_amount + current_tax_amount, precision("total", tax)); } else { - tax.grand_total_for_current_item = + tax.grand_total_for_current_item = flt(me.frm.tax_doclist[i-1].grand_total_for_current_item + current_tax_amount, precision("total", tax)); } - + // in tax.total, accumulate grand total for each item tax.total += tax.grand_total_for_current_item; @@ -648,54 +628,54 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({ round_off_totals: function(tax) { tax.total = flt(tax.total, precision("total", tax)); tax.tax_amount = flt(tax.tax_amount, precision("tax_amount", tax)); - tax.tax_amount_after_discount_amount = flt(tax.tax_amount_after_discount_amount, + tax.tax_amount_after_discount_amount = flt(tax.tax_amount_after_discount_amount, precision("tax_amount", tax)); }, adjust_discount_amount_loss: function(tax) { var discount_amount_loss = this.frm.doc.grand_total - flt(this.frm.doc.discount_amount) - tax.total; - tax.tax_amount_after_discount_amount = flt(tax.tax_amount_after_discount_amount + + tax.tax_amount_after_discount_amount = flt(tax.tax_amount_after_discount_amount + discount_amount_loss, precision("tax_amount", tax)); tax.total = flt(tax.total + discount_amount_loss, precision("total", tax)); }, - + get_current_tax_amount: function(item, tax, item_tax_map) { var tax_rate = this._get_tax_rate(tax, item_tax_map); var current_tax_amount = 0.0; - + if(tax.charge_type == "Actual") { // distribute the tax amount proportionally to each item row var actual = flt(tax.rate, precision("tax_amount", tax)); current_tax_amount = this.frm.doc.net_total ? ((item.base_amount / this.frm.doc.net_total) * actual) : 0.0; - + } else if(tax.charge_type == "On Net Total") { current_tax_amount = (tax_rate / 100.0) * item.base_amount; - + } else if(tax.charge_type == "On Previous Row Amount") { current_tax_amount = (tax_rate / 100.0) * this.frm.tax_doclist[cint(tax.row_id) - 1].tax_amount_for_current_item; - + } else if(tax.charge_type == "On Previous Row Total") { current_tax_amount = (tax_rate / 100.0) * this.frm.tax_doclist[cint(tax.row_id) - 1].grand_total_for_current_item; } current_tax_amount = flt(current_tax_amount, precision("tax_amount", tax)); - + // store tax breakup for each item tax.item_wise_tax_detail[item.item_code || item.item_name] = [tax_rate, current_tax_amount]; - + return current_tax_amount; }, - + _cleanup: function() { $.each(this.frm.tax_doclist, function(i, tax) { $.each(["tax_amount_for_current_item", "grand_total_for_current_item", - "tax_fraction_for_current_item", "grand_total_fraction_for_current_item"], + "tax_fraction_for_current_item", "grand_total_fraction_for_current_item"], function(i, fieldname) { delete tax[fieldname]; }); - + tax.item_wise_tax_detail = JSON.stringify(tax.item_wise_tax_detail); }); }, @@ -706,17 +686,17 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({ this.frm.doc.total_advance = flt(frappe.utils.sum( $.map(advance_doclist, function(adv) { return adv.allocated_amount }) ), precision("total_advance")); - + this.calculate_outstanding_amount(); } }, - + _set_in_company_currency: function(item, print_field, base_field) { // set values in base currency item[base_field] = flt(item[print_field] * this.frm.doc.conversion_rate, precision(base_field, item)); }, - + get_terms: function() { var me = this; if(this.frm.doc.tc_name) { @@ -752,4 +732,4 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({ .appendTo($(this.frm.fields_dict.other_charges_calculation.wrapper).empty()); } }, -}); \ No newline at end of file +}); diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index b0cb5404855..9af6b8694d6 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -27,7 +27,7 @@ class Customer(TransactionBase): def validate_values(self): if frappe.defaults.get_global_default('cust_master_name') == 'Naming Series' and not self.naming_series: - frappe.throw("Series is Mandatory.", frappe.MandatoryError) + frappe.throw(_("Series is mandatory"), frappe.MandatoryError) def validate(self): self.validate_values() @@ -66,7 +66,7 @@ class Customer(TransactionBase): c.is_primary_contact = 1 try: c.save() - except NameError, e: + except NameError: pass def on_update(self): @@ -86,7 +86,7 @@ class Customer(TransactionBase): def validate_name_with_customer_group(self): if frappe.db.exists("Customer Group", self.name): - frappe.throw("A Customer Group exists with same name please change the Customer name or rename the Customer Group") + frappe.throw(_("A Customer Group exists with same name please change the Customer name or rename the Customer Group")) def delete_customer_address(self): addresses = frappe.db.sql("""select name, lead from `tabAddress` diff --git a/erpnext/selling/doctype/lead/lead.py b/erpnext/selling/doctype/lead/lead.py index c400e555fe6..5689d6d3578 100644 --- a/erpnext/selling/doctype/lead/lead.py +++ b/erpnext/selling/doctype/lead/lead.py @@ -27,11 +27,11 @@ class Lead(SellingController): self.set_status() if self.source == 'Campaign' and not self.campaign_name and session['user'] != 'Guest': - frappe.throw("Please specify campaign name") + frappe.throw(_("Campaign Name is required")) if self.email_id: if not validate_email_add(self.email_id): - frappe.throw('Please enter valid email id.') + frappe.throw(_('{0} is not a valid email id').format(self.email_id)) def on_update(self): self.check_email_id_is_unique() diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index d4054dd7e84..0551aaaf0cd 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -145,7 +145,7 @@ class SalesOrder(SellingController): if quotation: doc = frappe.get_doc("Quotation", quotation) if doc.docstatus==2: - frappe.throw(quotation + ": " + frappe._("Quotation is cancelled.")) + frappe.throw(_("Quotation {0} is cancelled").format(quotation)) doc.set_status(update=True) diff --git a/erpnext/setup/doctype/customer_group/customer_group.py b/erpnext/setup/doctype/customer_group/customer_group.py index 0eefebe989a..57f919771d7 100644 --- a/erpnext/setup/doctype/customer_group/customer_group.py +++ b/erpnext/setup/doctype/customer_group/customer_group.py @@ -17,6 +17,4 @@ class CustomerGroup(NestedSet): def validate_name_with_customer(self): if frappe.db.exists("Customer", self.name): - frappe.msgprint(_("An Customer exists with same name (%s), \ - please change the Customer Group name or rename the Customer") % - self.name, raise_exception=1) + frappe.msgprint(_("An Customer exists with same name"), raise_exception=1) diff --git a/erpnext/setup/doctype/features_setup/features_setup.json b/erpnext/setup/doctype/features_setup/features_setup.json index 8122da0c1e6..fee7c178263 100644 --- a/erpnext/setup/doctype/features_setup/features_setup.json +++ b/erpnext/setup/doctype/features_setup/features_setup.json @@ -1,241 +1,241 @@ { - "creation": "2012-12-20 12:50:49.000000", - "docstatus": 0, - "doctype": "DocType", + "creation": "2012-12-20 12:50:49.000000", + "docstatus": 0, + "doctype": "DocType", "fields": [ { - "fieldname": "materials", - "fieldtype": "Section Break", - "label": "Materials", + "fieldname": "materials", + "fieldtype": "Section Break", + "label": "Materials", "permlevel": 0 - }, + }, { - "description": "To track item in sales and purchase documents based on their serial nos. This is can also used to track warranty details of the product.", - "fieldname": "fs_item_serial_nos", - "fieldtype": "Check", - "label": "Item Serial Nos", + "description": "To track item in sales and purchase documents based on their serial nos. This is can also used to track warranty details of the product.", + "fieldname": "fs_item_serial_nos", + "fieldtype": "Check", + "label": "Item Serial Nos", "permlevel": 0 - }, + }, { - "description": "To track items in sales and purchase documents with batch nos
Preferred Industry: Chemicals etc", - "fieldname": "fs_item_batch_nos", - "fieldtype": "Check", - "label": "Item Batch Nos", + "description": "To track items in sales and purchase documents with batch nos
Preferred Industry: Chemicals etc", + "fieldname": "fs_item_batch_nos", + "fieldtype": "Check", + "label": "Item Batch Nos", "permlevel": 0 - }, + }, { - "description": "To track brand name in the following documents
\nDelivery Note, Enuiry, Material Request, Item, Purchase Order, Purchase Voucher, Purchaser Receipt, Quotation, Sales Invoice, Sales BOM, Sales Order, Serial No", - "fieldname": "fs_brands", - "fieldtype": "Check", - "label": "Brands", + "description": "To track brand name in the following documents Delivery Note, Opportunity, Material Request, Item, Purchase Order, Purchase Voucher, Purchaser Receipt, Quotation, Sales Invoice, Sales BOM, Sales Order, Serial No", + "fieldname": "fs_brands", + "fieldtype": "Check", + "label": "Brands", "permlevel": 0 - }, + }, { - "description": "To track items using barcode. You will be able to enter items in Delivery Note and Sales Invoice by scanning barcode of item.", - "fieldname": "fs_item_barcode", - "fieldtype": "Check", - "label": "Item Barcode", + "description": "To track items using barcode. You will be able to enter items in Delivery Note and Sales Invoice by scanning barcode of item.", + "fieldname": "fs_item_barcode", + "fieldtype": "Check", + "label": "Item Barcode", "permlevel": 0 - }, + }, { - "fieldname": "column_break0", - "fieldtype": "Column Break", + "fieldname": "column_break0", + "fieldtype": "Column Break", "permlevel": 0 - }, + }, { - "description": "1. To maintain the customer wise item code and to make them searchable based on their code use this option", - "fieldname": "fs_item_advanced", - "fieldtype": "Check", - "label": "Item Advanced", + "description": "1. To maintain the customer wise item code and to make them searchable based on their code use this option", + "fieldname": "fs_item_advanced", + "fieldtype": "Check", + "label": "Item Advanced", "permlevel": 0 - }, + }, { - "description": "If Sale BOM is defined, the actual BOM of the Pack is displayed as table.\nAvailable in Delivery Note and Sales Order", - "fieldname": "fs_packing_details", - "fieldtype": "Check", - "label": "Packing Details", + "description": "If Sale BOM is defined, the actual BOM of the Pack is displayed as table. Available in Delivery Note and Sales Order", + "fieldname": "fs_packing_details", + "fieldtype": "Check", + "label": "Packing Details", "permlevel": 0 - }, + }, { - "description": "To get Item Group in details table", - "fieldname": "fs_item_group_in_details", - "fieldtype": "Check", - "label": "Item Groups in Details", + "description": "To get Item Group in details table", + "fieldname": "fs_item_group_in_details", + "fieldtype": "Check", + "label": "Item Groups in Details", "permlevel": 0 - }, + }, { - "fieldname": "sales_and_purchase", - "fieldtype": "Section Break", - "label": "Sales and Purchase", + "fieldname": "sales_and_purchase", + "fieldtype": "Section Break", + "label": "Sales and Purchase", "permlevel": 0 - }, + }, { - "description": "All export related fields like currency, conversion rate, export total, export grand total etc are available in
\nDelivery Note, POS, Quotation, Sales Invoice, Sales Order etc.", - "fieldname": "fs_exports", - "fieldtype": "Check", - "label": "Exports", + "description": "All export related fields like currency, conversion rate, export total, export grand total etc are available in Delivery Note, POS, Quotation, Sales Invoice, Sales Order etc.", + "fieldname": "fs_exports", + "fieldtype": "Check", + "label": "Exports", "permlevel": 0 - }, + }, { - "description": "All import related fields like currency, conversion rate, import total, import grand total etc are available in
\nPurchase Receipt, Supplier Quotation, Purchase Invoice, Purchase Order etc.", - "fieldname": "fs_imports", - "fieldtype": "Check", - "label": "Imports", + "description": "All import related fields like currency, conversion rate, import total, import grand total etc are available in Purchase Receipt, Supplier Quotation, Purchase Invoice, Purchase Order etc.", + "fieldname": "fs_imports", + "fieldtype": "Check", + "label": "Imports", "permlevel": 0 - }, + }, { - "fieldname": "column_break1", - "fieldtype": "Column Break", + "fieldname": "column_break1", + "fieldtype": "Column Break", "permlevel": 0 - }, + }, { - "description": "Field available in Delivery Note, Quotation, Sales Invoice, Sales Order", - "fieldname": "fs_discounts", - "fieldtype": "Check", - "label": "Sales Discounts", + "description": "Field available in Delivery Note, Quotation, Sales Invoice, Sales Order", + "fieldname": "fs_discounts", + "fieldtype": "Check", + "label": "Sales Discounts", "permlevel": 0 - }, + }, { - "description": "Discount Fields will be available in Purchase Order, Purchase Receipt, Purchase Invoice", - "fieldname": "fs_purchase_discounts", - "fieldtype": "Check", - "label": "Purchase Discounts", + "description": "Discount Fields will be available in Purchase Order, Purchase Receipt, Purchase Invoice", + "fieldname": "fs_purchase_discounts", + "fieldtype": "Check", + "label": "Purchase Discounts", "permlevel": 0 - }, + }, { - "description": "To track any installation or commissioning related work after sales", - "fieldname": "fs_after_sales_installations", - "fieldtype": "Check", - "label": "After Sale Installations", + "description": "To track any installation or commissioning related work after sales", + "fieldname": "fs_after_sales_installations", + "fieldtype": "Check", + "label": "After Sale Installations", "permlevel": 0 - }, + }, { - "description": "Available in \nBOM, Delivery Note, Purchase Invoice, Production Order, Purchase Order, Purchase Receipt, Sales Invoice, Sales Order, Stock Entry, Timesheet", - "fieldname": "fs_projects", - "fieldtype": "Check", - "label": "Projects", + "description": "Available in BOM, Delivery Note, Purchase Invoice, Production Order, Purchase Order, Purchase Receipt, Sales Invoice, Sales Order, Stock Entry, Timesheet", + "fieldname": "fs_projects", + "fieldtype": "Check", + "label": "Projects", "permlevel": 0 - }, + }, { - "description": "If you have Sales Team and Sale Partners (Channel Partners) they can be tagged and maintain their contribution in the sales activity", - "fieldname": "fs_sales_extras", - "fieldtype": "Check", - "label": "Sales Extras", + "description": "If you have Sales Team and Sale Partners (Channel Partners) they can be tagged and maintain their contribution in the sales activity", + "fieldname": "fs_sales_extras", + "fieldtype": "Check", + "label": "Sales Extras", "permlevel": 0 - }, + }, { - "fieldname": "accounts", - "fieldtype": "Section Break", - "label": "Accounts", + "fieldname": "accounts", + "fieldtype": "Section Break", + "label": "Accounts", "permlevel": 0 - }, + }, { - "description": "Check if you need automatic recurring invoices. After submitting any sales invoice, Recurring section will be visible.", - "fieldname": "fs_recurring_invoice", - "fieldtype": "Check", - "label": "Recurring Invoice", + "description": "Check if you need automatic recurring invoices. After submitting any sales invoice, Recurring section will be visible.", + "fieldname": "fs_recurring_invoice", + "fieldtype": "Check", + "label": "Recurring Invoice", "permlevel": 0 - }, + }, { - "fieldname": "column_break2", - "fieldtype": "Column Break", + "fieldname": "column_break2", + "fieldtype": "Column Break", "permlevel": 0 - }, + }, { - "description": "To enable Point of Sale features", - "fieldname": "fs_pos", - "fieldtype": "Check", - "label": "Point of Sale", + "description": "To enable Point of Sale features", + "fieldname": "fs_pos", + "fieldtype": "Check", + "label": "Point of Sale", "permlevel": 0 - }, + }, { - "description": "To enable Point of Sale view", - "fieldname": "fs_pos_view", - "fieldtype": "Check", - "label": "POS View", + "description": "To enable Point of Sale view", + "fieldname": "fs_pos_view", + "fieldtype": "Check", + "label": "POS View", "permlevel": 0 - }, + }, { - "fieldname": "production", - "fieldtype": "Section Break", - "label": "Manufacturing", + "fieldname": "production", + "fieldtype": "Section Break", + "label": "Manufacturing", "permlevel": 0 - }, + }, { - "description": "If you involve in manufacturing activity
\nEnables item Is Manufactured", - "fieldname": "fs_manufacturing", - "fieldtype": "Check", - "label": "Manufacturing", + "description": "If you involve in manufacturing activity. Enables Item 'Is Manufactured'", + "fieldname": "fs_manufacturing", + "fieldtype": "Check", + "label": "Manufacturing", "permlevel": 0 - }, + }, { - "fieldname": "column_break3", - "fieldtype": "Column Break", + "fieldname": "column_break3", + "fieldtype": "Column Break", "permlevel": 0 - }, + }, { - "description": "If you follow Quality Inspection
\nEnables item QA Required and QA No in Purchase Receipt", - "fieldname": "fs_quality", - "fieldtype": "Check", - "label": "Quality", + "description": "If you follow Quality Inspection. Enables Item QA Required and QA No in Purchase Receipt", + "fieldname": "fs_quality", + "fieldtype": "Check", + "label": "Quality", "permlevel": 0 - }, + }, { - "fieldname": "miscelleneous", - "fieldtype": "Section Break", - "label": "Miscelleneous", + "fieldname": "miscelleneous", + "fieldtype": "Section Break", + "label": "Miscelleneous", "permlevel": 0 - }, + }, { - "description": "If you have long print formats, this feature can be used to split the page to be printed on multiple pages with all headers and footers on each page", - "fieldname": "fs_page_break", - "fieldtype": "Check", - "label": "Page Break", + "description": "If you have long print formats, this feature can be used to split the page to be printed on multiple pages with all headers and footers on each page", + "fieldname": "fs_page_break", + "fieldtype": "Check", + "label": "Page Break", "permlevel": 0 - }, + }, { - "fieldname": "column_break4", - "fieldtype": "Column Break", + "fieldname": "column_break4", + "fieldtype": "Column Break", "permlevel": 0 - }, + }, { - "fieldname": "fs_more_info", - "fieldtype": "Check", - "label": "More Info", + "fieldname": "fs_more_info", + "fieldtype": "Check", + "label": "More Info", "permlevel": 0 } - ], - "icon": "icon-glass", - "idx": 1, - "issingle": 1, - "modified": "2013-12-24 11:40:19.000000", - "modified_by": "Administrator", - "module": "Setup", - "name": "Features Setup", - "name_case": "Title Case", - "owner": "Administrator", + ], + "icon": "icon-glass", + "idx": 1, + "issingle": 1, + "modified": "2013-12-24 11:40:19.000000", + "modified_by": "Administrator", + "module": "Setup", + "name": "Features Setup", + "name_case": "Title Case", + "owner": "Administrator", "permissions": [ { - "create": 1, - "email": 1, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 0, - "role": "System Manager", - "submit": 0, + "create": 1, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 0, + "role": "System Manager", + "submit": 0, "write": 1 - }, + }, { - "create": 1, - "email": 1, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 0, - "role": "Administrator", - "submit": 0, + "create": 1, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 0, + "role": "Administrator", + "submit": 0, "write": 1 } ] -} \ No newline at end of file +} diff --git a/erpnext/setup/doctype/naming_series/naming_series.py b/erpnext/setup/doctype/naming_series/naming_series.py index e93af3c387e..d44cd4b9f46 100644 --- a/erpnext/setup/doctype/naming_series/naming_series.py +++ b/erpnext/setup/doctype/naming_series/naming_series.py @@ -15,11 +15,11 @@ class NamingSeries(Document): return { "transactions": "\n".join([''] + sorted(list(set( frappe.db.sql_list("""select parent - from `tabDocField` where fieldname='naming_series'""") - + frappe.db.sql_list("""select dt from `tabCustom Field` + from `tabDocField` where fieldname='naming_series'""") + + frappe.db.sql_list("""select dt from `tabCustom Field` where fieldname='naming_series'""") )))), - "prefixes": "\n".join([''] + [i[0] for i in + "prefixes": "\n".join([''] + [i[0] for i in frappe.db.sql("""select name from tabSeries order by name""")]) } @@ -86,16 +86,16 @@ class NamingSeries(Document): dt = DocType() parent = list(set( - frappe.db.sql_list("""select dt.name - from `tabDocField` df, `tabDocType` dt + frappe.db.sql_list("""select dt.name + from `tabDocField` df, `tabDocType` dt where dt.name = df.parent and df.fieldname='naming_series' and dt.name != %s""", self.select_doc_for_series) - + frappe.db.sql_list("""select dt.name - from `tabCustom Field` df, `tabDocType` dt + + frappe.db.sql_list("""select dt.name + from `tabCustom Field` df, `tabDocType` dt where dt.name = df.dt and df.fieldname='naming_series' and dt.name != %s""", self.select_doc_for_series) )) - sr = [[frappe.get_meta(p).get_field("naming_series").options, p] + sr = [[frappe.get_meta(p).get_field("naming_series").options, p] for p in parent] options = self.scrub_options_list(self.set_options.split("\n")) for series in options: @@ -104,19 +104,12 @@ class NamingSeries(Document): if i[0]: existing_series = [d.split('.')[0] for d in i[0].split("\n")] if series.split(".")[0] in existing_series: - throw("{oops}! {sr} {series} {msg} {existing_series}. {select}".format(**{ - "oops": _("Oops"), - "sr": _("Series Name"), - "series": series, - "msg": _("is already in use in"), - "existing_series": i[1], - "select": _("Please select a new one") - })) + frappe.throw(_("Series {0} already used in {1}").format(series,i[1])) def validate_series_name(self, n): import re if not re.match("^[a-zA-Z0-9-/.#]*$", n): - throw('Special Characters except "-" and "/" not allowed in naming series') + throw(_('Special Characters except "-" and "/" not allowed in naming series')) def get_options(self, arg=''): return frappe.get_meta(self.select_doc_for_series).get_field("naming_series").options @@ -124,7 +117,7 @@ class NamingSeries(Document): def get_current(self, arg=None): """get series current""" if self.prefix: - self.current_value = frappe.db.get_value("Series", + self.current_value = frappe.db.get_value("Series", self.prefix.split('.')[0], "current") def insert_series(self, series): @@ -136,7 +129,7 @@ class NamingSeries(Document): if self.prefix: prefix = self.prefix.split('.')[0] self.insert_series(prefix) - frappe.db.sql("update `tabSeries` set current = %s where name = %s", + frappe.db.sql("update `tabSeries` set current = %s where name = %s", (self.current_value, prefix)) msgprint(_("Series Updated Successfully")) else: @@ -149,8 +142,8 @@ def set_by_naming_series(doctype, fieldname, naming_series, hide_name_field=True make_property_setter(doctype, "naming_series", "reqd", 1, "Check") # set values for mandatory - frappe.db.sql("""update `tab{doctype}` set naming_series={s} where - ifnull(naming_series, '')=''""".format(doctype=doctype, s="%s"), + frappe.db.sql("""update `tab{doctype}` set naming_series={s} where + ifnull(naming_series, '')=''""".format(doctype=doctype, s="%s"), get_default_naming_series(doctype)) if hide_name_field: @@ -165,10 +158,10 @@ def set_by_naming_series(doctype, fieldname, naming_series, hide_name_field=True make_property_setter(doctype, fieldname, "reqd", 1, "Check") # set values for mandatory - frappe.db.sql("""update `tab{doctype}` set `{fieldname}`=`name` where + frappe.db.sql("""update `tab{doctype}` set `{fieldname}`=`name` where ifnull({fieldname}, '')=''""".format(doctype=doctype, fieldname=fieldname)) def get_default_naming_series(doctype): naming_series = frappe.get_meta(doctype).get_field("naming_series").options or "" naming_series = naming_series.split("\n") - return naming_series[0] or naming_series[1] \ No newline at end of file + return naming_series[0] or naming_series[1] diff --git a/erpnext/setup/doctype/sales_person/sales_person.py b/erpnext/setup/doctype/sales_person/sales_person.py index 63897223497..bbd5690e7b8 100644 --- a/erpnext/setup/doctype/sales_person/sales_person.py +++ b/erpnext/setup/doctype/sales_person/sales_person.py @@ -10,19 +10,19 @@ from frappe.utils.nestedset import NestedSet class SalesPerson(NestedSet): nsm_parent_field = 'parent_sales_person'; - def validate(self): + def validate(self): for d in self.get('target_details'): if not flt(d.target_qty) and not flt(d.target_amount): frappe.throw(_("Either target qty or target amount is mandatory.")) - + def on_update(self): super(SalesPerson, self).on_update() self.validate_one_root() - + def get_email_id(self): if self.employee: user = frappe.db.get_value("Employee", self.employee, "user_id") if not user: - frappe.throw("User ID not set for Employee %s" % self.employee) + frappe.throw(_("User ID not set for Employee {0}").format(self.employee)) else: - return frappe.db.get_value("User", user, "email") or user \ No newline at end of file + return frappe.db.get_value("User", user, "email") or user diff --git a/erpnext/setup/utils.py b/erpnext/setup/utils.py index 63d18d24f65..efbc12f6f8b 100644 --- a/erpnext/setup/utils.py +++ b/erpnext/setup/utils.py @@ -11,8 +11,7 @@ def get_company_currency(company): if not currency: currency = frappe.db.get_default("currency") if not currency: - throw(_('Please specify Default Currency in Company Master \ - and Global Defaults')) + throw(_('Please specify Default Currency in Company Master and Global Defaults')) return currency diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index 45c571c0d81..bdbc0dc8402 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -109,8 +109,7 @@ class Item(WebsiteGenerator): self.is_pro_applicable = "No" if self.is_pro_applicable == 'Yes' and self.is_stock_item == 'No': - frappe.throw(_("As Production Order can be made for this item, \ - it must be a stock item.")) + frappe.throw(_("As Production Order can be made for this item, it must be a stock item.")) if self.has_serial_no == 'Yes' and self.is_stock_item == 'No': msgprint(_("'Has Serial No' can not be 'Yes' for non-stock item"), raise_exception=1) @@ -123,15 +122,13 @@ class Item(WebsiteGenerator): and t2.docstatus = 1 and t1.docstatus =1 """, self.name) if bom_mat and bom_mat[0][0]: - frappe.throw(_("Item must be a purchase item, \ - as it is present in one or many Active BOMs")) + frappe.throw(_("Item must be a purchase item, as it is present in one or many Active BOMs")) if self.is_manufactured_item != "Yes": bom = frappe.db.sql("""select name from `tabBOM` where item = %s and is_active = 1""", (self.name,)) if bom and bom[0][0]: - frappe.throw(_("""Allow Bill of Materials should be 'Yes'. Because one or many \ - active BOMs present for this item""")) + frappe.throw(_("""Allow Bill of Materials should be 'Yes'. Because one or many active BOMs present for this item""")) def fill_customer_code(self): """ Append all the customer codes and insert into "customer_code" field of item table """ diff --git a/erpnext/stock/doctype/item_price/item_price.py b/erpnext/stock/doctype/item_price/item_price.py index 89166288557..56b3632124f 100644 --- a/erpnext/stock/doctype/item_price/item_price.py +++ b/erpnext/stock/doctype/item_price/item_price.py @@ -17,41 +17,27 @@ class ItemPrice(Document): self.check_duplicate_item() self.update_price_list_details() self.update_item_details() - + def validate_item(self): if not frappe.db.exists("Item", self.item_code): - throw("{doctype}: {item} {not_found}".format(**{ - "doctype": _("Item"), - "item": self.item_code, - "not_found": _(" not found") - })) + throw(_("Item {0} not found").format(self.item_code)) def validate_price_list(self): enabled = frappe.db.get_value("Price List", self.price_list, "enabled") if not enabled: - throw("{message}: {price_list} {disabled}".format(**{ - "message": _("Price List"), - "price_list": self.price_list, - "disabled": _("is disabled.") - })) + throw(_("Price List {0} is disabled").format(self.price_list)) def check_duplicate_item(self): - if frappe.db.sql("""select name from `tabItem Price` - where item_code=%s and price_list=%s and name!=%s""", + if frappe.db.sql("""select name from `tabItem Price` + where item_code=%s and price_list=%s and name!=%s""", (self.item_code, self.price_list, self.name)): - throw("{duplicate_item}: {item_code}, {already}: {price_list}".format(**{ - "duplicate_item": _("Duplicate Item"), - "item_code": self.item_code, - "already": _("already available in Price List"), - "price_list": self.price_list - }), ItemPriceDuplicateItem) + frappe.throw(_("Item {0} appears multiple times in Price List {1}").format(self.item_code, self.price_list)) def update_price_list_details(self): self.buying, self.selling, self.currency = \ - frappe.db.get_value("Price List", {"name": self.price_list, "enabled": 1}, + frappe.db.get_value("Price List", {"name": self.price_list, "enabled": 1}, ["buying", "selling", "currency"]) def update_item_details(self): - self.item_name, self.item_description = frappe.db.get_value("Item", + self.item_name, self.item_description = frappe.db.get_value("Item", self.item_code, ["item_name", "description"]) - \ No newline at end of file diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py index db976119189..88119887bfd 100644 --- a/erpnext/stock/doctype/material_request/material_request.py +++ b/erpnext/stock/doctype/material_request/material_request.py @@ -8,7 +8,7 @@ from __future__ import unicode_literals import frappe from frappe.utils import cstr, flt -from frappe import msgprint, _ +from frappe import _ from erpnext.controllers.buying_controller import BuyingController class MaterialRequest(BuyingController): @@ -43,9 +43,7 @@ class MaterialRequest(BuyingController): actual_so_qty = actual_so_qty and flt(actual_so_qty[0][0]) or 0 if actual_so_qty and (flt(so_items[so_no][item]) + already_indented > actual_so_qty): - frappe.throw("You can raise indent of maximum qty: %s for item: %s against sales order: %s\ - \n Anyway, you can add more qty in new row for the same item." - % (actual_so_qty - already_indented, item, so_no)) + frappe.throw(_("Material Request of maximum {0} can be made for Item {1} against Sales Order {2}").format(actual_so_qty - already_indented, item, so_no)) def validate_schedule_date(self): for d in self.get('indent_details'): diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py index 37838ec51a8..9533e57aafa 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.py +++ b/erpnext/stock/doctype/serial_no/serial_no.py @@ -27,8 +27,7 @@ class SerialNo(StockController): def validate(self): if self.get("__islocal") and self.warehouse: - frappe.throw(_("New Serial No cannot have Warehouse. Warehouse must be \ - set by Stock Entry or Purchase Receipt"), SerialNoCannotCreateDirectError) + frappe.throw(_("New Serial No cannot have Warehouse. Warehouse must be set by Stock Entry or Purchase Receipt"), SerialNoCannotCreateDirectError) self.set_maintenance_status() self.validate_warehouse() @@ -166,8 +165,7 @@ class SerialNo(StockController): if self.status == 'Delivered': frappe.throw(_("Delivered Serial No ") + self.name + _(" can not be deleted")) if self.warehouse: - frappe.throw(_("Cannot delete Serial No in warehouse. \ - First remove from warehouse, then delete.") + ": " + self.name) + frappe.throw(_("Cannot delete Serial No in warehouse. First remove from warehouse, then delete.") + ": " + self.name) def before_rename(self, old, new, merge=False): if merge: diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index 1cc9c178c8a..07778a489b6 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -14,45 +14,45 @@ class StockReconciliation(StockController): def setup(self): self.head_row = ["Item Code", "Warehouse", "Quantity", "Valuation Rate"] self.entries = [] - + def validate(self): self.validate_data() self.validate_expense_account() - + def on_submit(self): self.insert_stock_ledger_entries() self.make_gl_entries() - + def on_cancel(self): self.delete_and_repost_sle() self.make_cancel_gl_entries() - + def validate_data(self): if not self.reconciliation_json: return - + data = json.loads(self.reconciliation_json) - + # strip out extra columns (if any) data = [row[:4] for row in data] - + if self.head_row not in data: msgprint(_("""Wrong Template: Unable to find head row."""), raise_exception=1) - + # remove the help part and save the json head_row_no = 0 if data.index(self.head_row) != 0: head_row_no = data.index(self.head_row) data = data[head_row_no:] self.reconciliation_json = json.dumps(data) - + def _get_msg(row_num, msg): return _("Row # ") + ("%d: " % (row_num+head_row_no+2)) + _(msg) - + self.validation_messages = [] item_warehouse_combinations = [] - + # validate no of rows rows = data[1:] if len(rows) > 100: @@ -64,69 +64,66 @@ class StockReconciliation(StockController): self.validation_messages.append(_get_msg(row_num, "Duplicate entry")) else: item_warehouse_combinations.append([row[0], row[1]]) - + self.validate_item(row[0], row_num+head_row_no+2) # note: warehouse will be validated through link validation - + # if both not specified if row[2] == "" and row[3] == "": self.validation_messages.append(_get_msg(row_num, "Please specify either Quantity or Valuation Rate or both")) - + # do not allow negative quantity if flt(row[2]) < 0: - self.validation_messages.append(_get_msg(row_num, + self.validation_messages.append(_get_msg(row_num, "Negative Quantity is not allowed")) - + # do not allow negative valuation if flt(row[3]) < 0: - self.validation_messages.append(_get_msg(row_num, + self.validation_messages.append(_get_msg(row_num, "Negative Valuation Rate is not allowed")) - + # throw all validation messages if self.validation_messages: for msg in self.validation_messages: msgprint(msg) - + raise frappe.ValidationError - + def validate_item(self, item_code, row_num): from erpnext.stock.doctype.item.item import validate_end_of_life, \ validate_is_stock_item, validate_cancelled_item - + # using try except to catch all validation msgs and display together - + try: item = frappe.get_doc("Item", item_code) - + # end of life and stock item validate_end_of_life(item_code, item.end_of_life, verbose=0) validate_is_stock_item(item_code, item.is_stock_item, verbose=0) - + # item should not be serialized if item.has_serial_no == "Yes": - raise frappe.ValidationError, (_("Serialized Item: '") + item_code + - _("""' can not be managed using Stock Reconciliation.\ - You can add/delete Serial No directly, \ - to modify stock of this item.""")) - + raise frappe.ValidationError, _("Serialized Item {0} cannot be updated using Stock Reconciliation").format(item_code) + # docstatus should be < 2 validate_cancelled_item(item_code, item.docstatus, verbose=0) - + except Exception, e: self.validation_messages.append(_("Row # ") + ("%d: " % (row_num)) + cstr(e)) - + def insert_stock_ledger_entries(self): """ find difference between current and expected entries and create stock ledger entries based on the difference""" from erpnext.stock.utils import get_valuation_method from erpnext.stock.stock_ledger import get_previous_sle - + row_template = ["item_code", "warehouse", "qty", "valuation_rate"] - + if not self.reconciliation_json: msgprint(_("""Stock Reconciliation file not uploaded"""), raise_exception=1) - + data = json.loads(self.reconciliation_json) for row_num, row in enumerate(data[data.index(self.head_row)+1:]): row = frappe._dict(zip(row_template, row)) @@ -141,22 +138,22 @@ class StockReconciliation(StockController): # check valuation rate mandatory if row.qty != "" and not row.valuation_rate and \ flt(previous_sle.get("qty_after_transaction")) <= 0: - frappe.throw(_("As existing qty for item: ") + row.item_code + + frappe.throw(_("As existing qty for item: ") + row.item_code + _(" at warehouse: ") + row.warehouse + _(" is less than equals to zero in the system, valuation rate is mandatory for this item")) - + change_in_qty = row.qty != "" and \ (flt(row.qty) - flt(previous_sle.get("qty_after_transaction"))) - + change_in_rate = row.valuation_rate != "" and \ (flt(row.valuation_rate) - flt(previous_sle.get("valuation_rate"))) - + if get_valuation_method(row.item_code) == "Moving Average": self.sle_for_moving_avg(row, previous_sle, change_in_qty, change_in_rate) - + else: self.sle_for_fifo(row, previous_sle, change_in_qty, change_in_rate) - + def sle_for_moving_avg(self, row, previous_sle, change_in_qty, change_in_rate): """Insert Stock Ledger Entries for Moving Average valuation""" def _get_incoming_rate(qty, valuation_rate, previous_qty, previous_valuation_rate): @@ -167,73 +164,73 @@ class StockReconciliation(StockController): valuation_rate = previous_valuation_rate return (qty * valuation_rate - previous_qty * previous_valuation_rate) \ / flt(qty - previous_qty) - + if change_in_qty: # if change in qty, irrespective of change in rate incoming_rate = _get_incoming_rate(flt(row.qty), flt(row.valuation_rate), flt(previous_sle.get("qty_after_transaction")), flt(previous_sle.get("valuation_rate"))) - + row["voucher_detail_no"] = "Row: " + cstr(row.row_num) + "/Actual Entry" self.insert_entries({"actual_qty": change_in_qty, "incoming_rate": incoming_rate}, row) - + elif change_in_rate and flt(previous_sle.get("qty_after_transaction")) > 0: - # if no change in qty, but change in rate + # if no change in qty, but change in rate # and positive actual stock before this reconciliation incoming_rate = _get_incoming_rate( flt(previous_sle.get("qty_after_transaction"))+1, flt(row.valuation_rate), - flt(previous_sle.get("qty_after_transaction")), + flt(previous_sle.get("qty_after_transaction")), flt(previous_sle.get("valuation_rate"))) - + # +1 entry row["voucher_detail_no"] = "Row: " + cstr(row.row_num) + "/Valuation Adjustment +1" self.insert_entries({"actual_qty": 1, "incoming_rate": incoming_rate}, row) - + # -1 entry row["voucher_detail_no"] = "Row: " + cstr(row.row_num) + "/Valuation Adjustment -1" self.insert_entries({"actual_qty": -1}, row) - + def sle_for_fifo(self, row, previous_sle, change_in_qty, change_in_rate): """Insert Stock Ledger Entries for FIFO valuation""" previous_stock_queue = json.loads(previous_sle.get("stock_queue") or "[]") previous_stock_qty = sum((batch[0] for batch in previous_stock_queue)) previous_stock_value = sum((batch[0] * batch[1] for batch in \ previous_stock_queue)) - + def _insert_entries(): if previous_stock_queue != [[row.qty, row.valuation_rate]]: # make entry as per attachment if row.qty: row["voucher_detail_no"] = "Row: " + cstr(row.row_num) + "/Actual Entry" - self.insert_entries({"actual_qty": row.qty, + self.insert_entries({"actual_qty": row.qty, "incoming_rate": flt(row.valuation_rate)}, row) - + # Make reverse entry if previous_stock_qty: row["voucher_detail_no"] = "Row: " + cstr(row.row_num) + "/Reverse Entry" - self.insert_entries({"actual_qty": -1 * previous_stock_qty, - "incoming_rate": previous_stock_qty < 0 and + self.insert_entries({"actual_qty": -1 * previous_stock_qty, + "incoming_rate": previous_stock_qty < 0 and flt(row.valuation_rate) or 0}, row) - - + + if change_in_qty: if row.valuation_rate == "": # dont want change in valuation if previous_stock_qty > 0: # set valuation_rate as previous valuation_rate row.valuation_rate = previous_stock_value / flt(previous_stock_qty) - + _insert_entries() - + elif change_in_rate and previous_stock_qty > 0: - # if no change in qty, but change in rate + # if no change in qty, but change in rate # and positive actual stock before this reconciliation - + row.qty = previous_stock_qty _insert_entries() - + def insert_entries(self, opts, row): - """Insert Stock Ledger Entries""" + """Insert Stock Ledger Entries""" args = frappe._dict({ "doctype": "Stock Ledger Entry", "item_code": row.item_code, @@ -253,19 +250,19 @@ class StockReconciliation(StockController): # append to entries self.entries.append(args) - + def delete_and_repost_sle(self): """ Delete Stock Ledger Entries related to this voucher and repost future Stock Ledger Entries""" - - existing_entries = frappe.db.sql("""select distinct item_code, warehouse - from `tabStock Ledger Entry` where voucher_type=%s and voucher_no=%s""", + + existing_entries = frappe.db.sql("""select distinct item_code, warehouse + from `tabStock Ledger Entry` where voucher_type=%s and voucher_no=%s""", (self.doctype, self.name), as_dict=1) - + # delete entries - frappe.db.sql("""delete from `tabStock Ledger Entry` + frappe.db.sql("""delete from `tabStock Ledger Entry` where voucher_type=%s and voucher_no=%s""", (self.doctype, self.name)) - + # repost future entries for selected item_code, warehouse for entries in existing_entries: update_entries_after({ @@ -274,29 +271,26 @@ class StockReconciliation(StockController): "posting_date": self.posting_date, "posting_time": self.posting_time }) - + def get_gl_entries(self, warehouse_account=None): if not self.cost_center: msgprint(_("Please enter Cost Center"), raise_exception=1) - - return super(StockReconciliation, self).get_gl_entries(warehouse_account, + + return super(StockReconciliation, self).get_gl_entries(warehouse_account, self.expense_account, self.cost_center) - + def validate_expense_account(self): if not cint(frappe.defaults.get_global_default("auto_accounting_for_stock")): return - + if not self.expense_account: msgprint(_("Please enter Expense Account"), raise_exception=1) elif not frappe.db.sql("""select * from `tabStock Ledger Entry`"""): - if frappe.db.get_value("Account", self.expense_account, + if frappe.db.get_value("Account", self.expense_account, "report_type") == "Profit and Loss": - msgprint(_("""Expense Account can not be a PL Account, as this stock \ - reconciliation is an opening entry. \ - Please select 'Temporary Account (Liabilities)' or relevant account"""), - raise_exception=1) - + frappe.throw(_("'Profit and Loss' type Account {0} used be set for Opening Entry").format(self.expense_account)) + @frappe.whitelist() def upload(): from frappe.utils.datautils import read_csv_content_from_uploaded_file - return read_csv_content_from_uploaded_file() \ No newline at end of file + return read_csv_content_from_uploaded_file() diff --git a/erpnext/stock/doctype/warehouse/warehouse.py b/erpnext/stock/doctype/warehouse/warehouse.py index 9bb710958b7..dcb00f38e57 100644 --- a/erpnext/stock/doctype/warehouse/warehouse.py +++ b/erpnext/stock/doctype/warehouse/warehouse.py @@ -65,8 +65,7 @@ class Warehouse(Document): if parent_account: self.create_account_under = parent_account else: - frappe.throw(_("Please enter account group under which account \ - for warehouse ") + self.name +_(" will be created")) + frappe.throw(_("Please enter parent account group for warehouse account")) def on_trash(self): # delete bin @@ -75,8 +74,7 @@ class Warehouse(Document): for d in bins: if d['actual_qty'] or d['reserved_qty'] or d['ordered_qty'] or \ d['indented_qty'] or d['projected_qty'] or d['planned_qty']: - throw("""Warehouse: %s can not be deleted as qty exists for item: %s""" - % (self.name, d['item_code'])) + throw(_("Warehouse {0} can not be deleted as quantity exists for Item {1}").format(self.name, d['item_code'])) else: frappe.db.sql("delete from `tabBin` where name = %s", d['name']) @@ -87,8 +85,7 @@ class Warehouse(Document): if frappe.db.sql("""select name from `tabStock Ledger Entry` where warehouse = %s""", self.name): - throw(_("""Warehouse can not be deleted as stock ledger entry - exists for this warehouse.""")) + throw(_("Warehouse can not be deleted as stock ledger entry exists for this warehouse.")) def before_rename(self, olddn, newdn, merge=False): # Add company abbr if not provided diff --git a/erpnext/support/doctype/maintenance_schedule/maintenance_schedule.py b/erpnext/support/doctype/maintenance_schedule/maintenance_schedule.py index afc08e8f066..fb051e31db8 100644 --- a/erpnext/support/doctype/maintenance_schedule/maintenance_schedule.py +++ b/erpnext/support/doctype/maintenance_schedule/maintenance_schedule.py @@ -11,19 +11,19 @@ from erpnext.utilities.transaction_base import TransactionBase, delete_events from erpnext.stock.utils import get_valid_serial_nos class MaintenanceSchedule(TransactionBase): - + def get_item_details(self, item_code): - item = frappe.db.sql("""select item_name, description from `tabItem` + item = frappe.db.sql("""select item_name, description from `tabItem` where name=%s""", (item_code), as_dict=1) ret = { 'item_name': item and item[0]['item_name'] or '', 'description' : item and item[0]['description'] or '' } return ret - + def generate_schedule(self): self.set('maintenance_schedule_detail', []) - frappe.db.sql("""delete from `tabMaintenance Schedule Detail` + frappe.db.sql("""delete from `tabMaintenance Schedule Detail` where parent=%s""", (self.name)) count = 1 for d in self.get('item_maintenance_detail'): @@ -41,7 +41,7 @@ class MaintenanceSchedule(TransactionBase): count = count + 1 child.sales_person = d.sales_person child.save(1) - + self.on_update() def on_submit(self): @@ -61,8 +61,8 @@ class MaintenanceSchedule(TransactionBase): sp = frappe.get_doc("Sales Person", d.sales_person) email_map[d.sales_person] = sp.get_email_id() - scheduled_date = frappe.db.sql("""select scheduled_date from - `tabMaintenance Schedule Detail` where sales_person=%s and item_code=%s and + scheduled_date = frappe.db.sql("""select scheduled_date from + `tabMaintenance Schedule Detail` where sales_person=%s and item_code=%s and parent=%s""", (d.sales_person, d.item_code, self.name), as_dict=1) for key in scheduled_date: @@ -80,10 +80,10 @@ class MaintenanceSchedule(TransactionBase): "ref_name": self.name }).insert(ignore_permissions=1) - frappe.db.set(self, 'status', 'Submitted') + frappe.db.set(self, 'status', 'Submitted') def create_schedule_list(self, start_date, end_date, no_of_visit, sales_person): - schedule_list = [] + schedule_list = [] start_date_copy = start_date date_diff = (getdate(end_date) - getdate(start_date)).days add_by = date_diff / no_of_visit @@ -92,7 +92,7 @@ class MaintenanceSchedule(TransactionBase): if (getdate(start_date_copy) < getdate(end_date)): start_date_copy = add_days(start_date_copy, add_by) if len(schedule_list) < no_of_visit: - schedule_date = self.validate_schedule_date_for_holiday_list(getdate(start_date_copy), + schedule_date = self.validate_schedule_date_for_holiday_list(getdate(start_date_copy), sales_person) if schedule_date > getdate(end_date): schedule_date = getdate(end_date) @@ -112,17 +112,17 @@ class MaintenanceSchedule(TransactionBase): if fy_details and fy_details[0]: # check holiday list in employee master - holiday_list = frappe.db.sql_list("""select h.holiday_date from `tabEmployee` emp, - `tabSales Person` sp, `tabHoliday` h, `tabHoliday List` hl - where sp.name=%s and emp.name=sp.employee - and hl.name=emp.holiday_list and - h.parent=hl.name and + holiday_list = frappe.db.sql_list("""select h.holiday_date from `tabEmployee` emp, + `tabSales Person` sp, `tabHoliday` h, `tabHoliday List` hl + where sp.name=%s and emp.name=sp.employee + and hl.name=emp.holiday_list and + h.parent=hl.name and hl.fiscal_year=%s""", (sales_person, fy_details[0])) if not holiday_list: # check global holiday list - holiday_list = frappe.db.sql("""select h.holiday_date from - `tabHoliday` h, `tabHoliday List` hl - where h.parent=hl.name and ifnull(hl.is_default, 0) = 1 + holiday_list = frappe.db.sql("""select h.holiday_date from + `tabHoliday` h, `tabHoliday List` hl + where h.parent=hl.name and ifnull(hl.is_default, 0) = 1 and hl.fiscal_year=%s""", fy_details[0]) if not validated and holiday_list: @@ -140,14 +140,14 @@ class MaintenanceSchedule(TransactionBase): period = (getdate(args['end_date']) - getdate(args['start_date'])).days + 1 - if (args['periodicity'] == 'Yearly' or args['periodicity'] == 'Half Yearly' or + if (args['periodicity'] == 'Yearly' or args['periodicity'] == 'Half Yearly' or args['periodicity'] == 'Quarterly') and period < 365: throw(cstr(args['periodicity']) + " periodicity can be set for period of atleast 1 year or more only") elif args['periodicity'] == 'Monthly' and period < 30: throw("Monthly periodicity can be set for period of atleast 1 month or more") elif args['periodicity'] == 'Weekly' and period < 7: throw("Weekly periodicity can be set for period of atleast 1 week or more") - + def get_no_of_visits(self, arg): args = eval(arg) self.validate_period(arg) @@ -159,19 +159,19 @@ class MaintenanceSchedule(TransactionBase): elif args['periodicity'] == 'Monthly': count = period/30 elif args['periodicity'] == 'Quarterly': - count = period/91 + count = period/91 elif args['periodicity'] == 'Half Yearly': count = period/182 elif args['periodicity'] == 'Yearly': count = period/365 - + ret = {'no_of_visits' : count} return ret def validate_maintenance_detail(self): if not self.get('item_maintenance_detail'): throw(_("Please enter Maintaince Details first")) - + for d in self.get('item_maintenance_detail'): if not d.item_code: throw(_("Please select item code")) @@ -181,23 +181,23 @@ class MaintenanceSchedule(TransactionBase): throw(_("Please mention no of visits required")) elif not d.sales_person: throw(_("Please select Incharge Person's name")) - + if getdate(d.start_date) >= getdate(d.end_date): throw(_("Start date should be less than end date for item") + " " + d.item_code) - + def validate_sales_order(self): for d in self.get('item_maintenance_detail'): if d.prevdoc_docname: - chk = frappe.db.sql("""select ms.name from `tabMaintenance Schedule` ms, - `tabMaintenance Schedule Item` msi where msi.parent=ms.name and + chk = frappe.db.sql("""select ms.name from `tabMaintenance Schedule` ms, + `tabMaintenance Schedule Item` msi where msi.parent=ms.name and msi.prevdoc_docname=%s and ms.docstatus=1""", d.prevdoc_docname) if chk: throw("Maintenance Schedule against " + d.prevdoc_docname + " already exist") - + def validate(self): self.validate_maintenance_detail() self.validate_sales_order() - + def on_update(self): frappe.db.set(self, 'status', 'Draft') @@ -209,21 +209,20 @@ class MaintenanceSchedule(TransactionBase): def validate_serial_no(self, serial_nos, amc_start_date): for serial_no in serial_nos: - sr_details = frappe.db.get_value("Serial No", serial_no, + sr_details = frappe.db.get_value("Serial No", serial_no, ["warranty_expiry_date", "amc_expiry_date", "status", "delivery_date"], as_dict=1) - + if sr_details.warranty_expiry_date and sr_details.warranty_expiry_date>=amc_start_date: - throw("""Serial No: %s is already under warranty upto %s. + throw("""Serial No: %s is already under warranty upto %s. Please check AMC Start Date.""" % (serial_no, sr_details.warranty_expiry_date)) - + if sr_details.amc_expiry_date and sr_details.amc_expiry_date >= amc_start_date: throw("""Serial No: %s is already under AMC upto %s. Please check AMC Start Date.""" % (serial_no, sr_details.amc_expiry_date)) - + if sr_details.status=="Delivered" and sr_details.delivery_date and \ sr_details.delivery_date >= amc_start_date: - throw(_("Maintenance start date can not be before \ - delivery date for serial no: ") + serial_no) + throw(_("Maintenance start date can not be before delivery date for Serial No {0}").format(serial_no)) def validate_schedule(self): item_lst1 =[] @@ -231,26 +230,25 @@ class MaintenanceSchedule(TransactionBase): for d in self.get('item_maintenance_detail'): if d.item_code not in item_lst1: item_lst1.append(d.item_code) - + for m in self.get('maintenance_schedule_detail'): if m.item_code not in item_lst2: item_lst2.append(m.item_code) - + if len(item_lst1) != len(item_lst2): - throw(_("Maintenance Schedule is not generated for all the items. \ - Please click on 'Generate Schedule'")) + throw(_("Maintenance Schedule is not generated for all the items. Please click on 'Generate Schedule'")) else: for x in item_lst1: if x not in item_lst2: - throw(_("Maintenance Schedule is not generated for item ") + x + + throw(_("Maintenance Schedule is not generated for item ") + x + _(". Please click on 'Generate Schedule'")) - + def check_serial_no_added(self): serial_present =[] for d in self.get('item_maintenance_detail'): if d.serial_no: serial_present.append(d.item_code) - + for m in self.get('maintenance_schedule_detail'): if serial_present: if m.item_code in serial_present and not m.serial_no: @@ -270,13 +268,13 @@ class MaintenanceSchedule(TransactionBase): @frappe.whitelist() def make_maintenance_visit(source_name, target_doc=None): from frappe.model.mapper import get_mapped_doc - + def update_status(source, target, parent): target.maintenance_type = "Scheduled" - + doclist = get_mapped_doc("Maintenance Schedule", source_name, { "Maintenance Schedule": { - "doctype": "Maintenance Visit", + "doctype": "Maintenance Visit", "field_map": { "name": "maintenance_schedule" }, @@ -284,15 +282,15 @@ def make_maintenance_visit(source_name, target_doc=None): "docstatus": ["=", 1] }, "postprocess": update_status - }, + }, "Maintenance Schedule Item": { - "doctype": "Maintenance Visit Purpose", + "doctype": "Maintenance Visit Purpose", "field_map": { - "parent": "prevdoc_docname", + "parent": "prevdoc_docname", "parenttype": "prevdoc_doctype", "sales_person": "service_person" } } }, target_doc) - return doclist \ No newline at end of file + return doclist