more message fixes
This commit is contained in:
parent
a6e2ca9d3b
commit
ff93802d58
@ -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 <b>%s</b> can not be \
|
||||
greater than credit limit (%s). To change your credit limit settings, \
|
||||
please update in the <b>%s</b> 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)
|
||||
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)
|
||||
|
@ -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]))
|
||||
|
||||
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]))
|
||||
|
@ -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
|
||||
return invoice_posting_date_map
|
||||
|
@ -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':
|
||||
|
@ -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({
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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"""
|
||||
|
@ -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 '<th style="min-width: 100px;">' + (head || "") + "</th>" }).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 '<p><a href="#" onclick="$(\'.tax-break-up\').toggleClass(\'hide\'); return false;">Show / Hide tax break-up</a><br><br></p>\
|
||||
<div class="tax-break-up hide" style="overflow-x: auto;"><table class="table table-bordered table-hover">\
|
||||
@ -486,16 +466,16 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({
|
||||
<tbody>' + rows + '</tbody> \
|
||||
</table></div>';
|
||||
},
|
||||
|
||||
|
||||
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());
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
||||
|
@ -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`
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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<br><b>Preferred Industry: Chemicals etc</b>",
|
||||
"fieldname": "fs_item_batch_nos",
|
||||
"fieldtype": "Check",
|
||||
"label": "Item Batch Nos",
|
||||
"description": "To track items in sales and purchase documents with batch nos<br><b>Preferred Industry: Chemicals etc</b>",
|
||||
"fieldname": "fs_item_batch_nos",
|
||||
"fieldtype": "Check",
|
||||
"label": "Item Batch Nos",
|
||||
"permlevel": 0
|
||||
},
|
||||
},
|
||||
{
|
||||
"description": "To track brand name in the following documents<br>\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 <br>\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 <br>\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 <b>Point of Sale</b> features",
|
||||
"fieldname": "fs_pos",
|
||||
"fieldtype": "Check",
|
||||
"label": "Point of Sale",
|
||||
"description": "To enable <b>Point of Sale</b> features",
|
||||
"fieldname": "fs_pos",
|
||||
"fieldtype": "Check",
|
||||
"label": "Point of Sale",
|
||||
"permlevel": 0
|
||||
},
|
||||
},
|
||||
{
|
||||
"description": "To enable <b>Point of Sale</b> view",
|
||||
"fieldname": "fs_pos_view",
|
||||
"fieldtype": "Check",
|
||||
"label": "POS View",
|
||||
"description": "To enable <b>Point of Sale</b> 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<br>\nEnables item <b>Is Manufactured</b>",
|
||||
"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<br>\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
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -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]
|
||||
return naming_series[0] or naming_series[1]
|
||||
|
@ -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
|
||||
return frappe.db.get_value("User", user, "email") or user
|
||||
|
@ -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
|
||||
|
||||
|
@ -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 """
|
||||
|
@ -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"])
|
||||
|
@ -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'):
|
||||
|
@ -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:
|
||||
|
@ -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()
|
||||
return read_csv_content_from_uploaded_file()
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
return doclist
|
||||
|
Loading…
x
Reference in New Issue
Block a user