more message fixes

This commit is contained in:
Rushabh Mehta 2014-04-15 18:40:00 +05:30
parent a6e2ca9d3b
commit ff93802d58
26 changed files with 551 additions and 646 deletions

View File

@ -3,7 +3,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe 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 import msgprint, throw, _
from frappe.model.document import Document from frappe.model.document import Document
@ -16,8 +16,8 @@ class Account(Document):
def get_address(self): def get_address(self):
return {'address': frappe.db.get_value(self.master_type, self.master_name, "address")} 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_master_name()
self.validate_parent() self.validate_parent()
self.validate_duplicate_account() self.validate_duplicate_account()
@ -25,18 +25,18 @@ class Account(Document):
self.validate_mandatory() self.validate_mandatory()
self.validate_warehouse_account() self.validate_warehouse_account()
self.validate_frozen_accounts_modifier() self.validate_frozen_accounts_modifier()
def validate_master_name(self): def validate_master_name(self):
if self.master_type in ('Customer', 'Supplier') or self.account_type == "Warehouse": if self.master_type in ('Customer', 'Supplier') or self.account_type == "Warehouse":
if not self.master_name: if not self.master_name:
msgprint(_("Please enter Master Name once the account is created.")) 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): elif not frappe.db.exists(self.master_type or self.account_type, self.master_name):
throw(_("Invalid Master Name")) throw(_("Invalid Master Name"))
def validate_parent(self): def validate_parent(self):
"""Fetch Parent Details and validation for account not to be created under ledger""" """Fetch Parent Details and validation for account not to be created under ledger"""
if self.parent_account: 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) ["name", "group_or_ledger", "report_type"], as_dict=1)
if not par: if not par:
throw(_("Parent account does not exists")) throw(_("Parent account does not exists"))
@ -44,46 +44,36 @@ class Account(Document):
throw(_("You can not assign itself as parent account")) throw(_("You can not assign itself as parent account"))
elif par["group_or_ledger"] != 'Group': elif par["group_or_ledger"] != 'Group':
throw(_("Parent account can not be a ledger")) throw(_("Parent account can not be a ledger"))
if par["report_type"]: if par["report_type"]:
self.report_type = par["report_type"] self.report_type = par["report_type"]
def validate_duplicate_account(self): def validate_duplicate_account(self):
if self.get('__islocal') or not self.name: if self.get('__islocal') or not self.name:
company_abbr = frappe.db.get_value("Company", self.company, "abbr") company_abbr = frappe.db.get_value("Company", self.company, "abbr")
if frappe.db.exists("Account", (self.account_name + " - " + company_abbr)): if frappe.db.exists("Account", (self.account_name + " - " + company_abbr)):
throw("{name}: {acc_name} {exist}, {rename}".format(**{ throw(_("Account {0} already exists").format(self.account_name))
"name": _("Account Name"),
"acc_name": self.account_name,
"exist": _("already exists"),
"rename": _("please rename")
}))
def validate_root_details(self): def validate_root_details(self):
#does not exists parent #does not exists parent
if frappe.db.exists("Account", self.name): if frappe.db.exists("Account", self.name):
if not frappe.db.get_value("Account", self.name, "parent_account"): if not frappe.db.get_value("Account", self.name, "parent_account"):
throw(_("Root cannot be edited.")) throw(_("Root cannot be edited."))
def validate_frozen_accounts_modifier(self): def validate_frozen_accounts_modifier(self):
old_value = frappe.db.get_value("Account", self.name, "freeze_account") old_value = frappe.db.get_value("Account", self.name, "freeze_account")
if old_value and old_value != self.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') 'frozen_accounts_modifier')
if not frozen_accounts_modifier or \ if not frozen_accounts_modifier or \
frozen_accounts_modifier not in frappe.user.get_roles(): frozen_accounts_modifier not in frappe.user.get_roles():
throw(_("You are not authorized to set Frozen value")) throw(_("You are not authorized to set Frozen value"))
def convert_group_to_ledger(self): def convert_group_to_ledger(self):
if self.check_if_child_exists(): if self.check_if_child_exists():
throw("{acc}: {account_name} {child}. {msg}".format(**{ throw(_("Account with child nodes cannot be converted to ledger"))
"acc": _("Account"),
"account_name": self.name,
"child": _("has existing child"),
"msg": _("You can not convert this account to ledger")
}))
elif self.check_gle_exists(): 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: else:
self.group_or_ledger = 'Ledger' self.group_or_ledger = 'Ledger'
self.save() self.save()
@ -104,17 +94,17 @@ class Account(Document):
return frappe.db.get_value("GL Entry", {"account": self.name}) return frappe.db.get_value("GL Entry", {"account": self.name})
def check_if_child_exists(self): 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) and docstatus != 2""", self.name)
def validate_mandatory(self): def validate_mandatory(self):
if not self.report_type: if not self.report_type:
throw(_("Report Type is mandatory")) throw(_("Report Type is mandatory"))
def validate_warehouse_account(self): def validate_warehouse_account(self):
if not cint(frappe.defaults.get_global_default("auto_accounting_for_stock")): if not cint(frappe.defaults.get_global_default("auto_accounting_for_stock")):
return return
if self.account_type == "Warehouse": if self.account_type == "Warehouse":
old_warehouse = cstr(frappe.db.get_value("Account", self.name, "master_name")) old_warehouse = cstr(frappe.db.get_value("Account", self.name, "master_name"))
if old_warehouse != cstr(self.master_name): if old_warehouse != cstr(self.master_name):
@ -124,10 +114,10 @@ class Account(Document):
self.validate_warehouse(self.master_name) self.validate_warehouse(self.master_name)
else: else:
throw(_("Master Name is mandatory if account type is Warehouse")) throw(_("Master Name is mandatory if account type is Warehouse"))
def validate_warehouse(self, warehouse): def validate_warehouse(self, warehouse):
if frappe.db.get_value("Stock Ledger Entry", {"warehouse": 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")) _(" .You can not assign / modify / remove Master Name"))
def update_nsm_model(self): def update_nsm_model(self):
@ -135,73 +125,67 @@ class Account(Document):
import frappe import frappe
import frappe.utils.nestedset import frappe.utils.nestedset
frappe.utils.nestedset.update_nsm(self) frappe.utils.nestedset.update_nsm(self)
def on_update(self): def on_update(self):
self.update_nsm_model() self.update_nsm_model()
def get_authorized_user(self): def get_authorized_user(self):
# Check logged-in user is authorized # Check logged-in user is authorized
if frappe.db.get_value('Accounts Settings', None, 'credit_controller') \ if frappe.db.get_value('Accounts Settings', None, 'credit_controller') \
in frappe.user.get_roles(): in frappe.user.get_roles():
return 1 return 1
def check_credit_limit(self, total_outstanding): def check_credit_limit(self, total_outstanding):
# Get credit limit # Get credit limit
credit_limit_from = 'Customer' 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) where t2.name=%s and t1.name = t2.master_name""", self.name)
credit_limit = cr_limit and flt(cr_limit[0][0]) or 0 credit_limit = cr_limit and flt(cr_limit[0][0]) or 0
if not credit_limit: if not credit_limit:
credit_limit = frappe.db.get_value('Company', self.company, 'credit_limit') credit_limit = frappe.db.get_value('Company', self.company, 'credit_limit')
credit_limit_from = 'Company' credit_limit_from = 'Company'
# If outstanding greater than credit limit and not authorized person raise exception # If outstanding greater than credit limit and not authorized person raise exception
if credit_limit > 0 and flt(total_outstanding) > credit_limit \ if credit_limit > 0 and flt(total_outstanding) > credit_limit \
and not self.get_authorized_user(): and not self.get_authorized_user():
throw("""Total Outstanding amount (%s) for <b>%s</b> can not be \ throw(_("{0} Credit limit {0} crossed").format(_(credit_limit_from), credit_limit))
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))
def validate_trash(self): def validate_trash(self):
"""checks gl entries and if child exists""" """checks gl entries and if child exists"""
if not self.parent_account: if not self.parent_account:
throw(_("Root account can not be deleted")) throw(_("Root account can not be deleted"))
if self.check_gle_exists(): if self.check_gle_exists():
throw("""Account with existing transaction (Sales Invoice / Purchase Invoice / \ throw(_("Account with existing transaction can not be deleted"))
Journal Voucher) can not be deleted""")
if self.check_if_child_exists(): if self.check_if_child_exists():
throw(_("Child account exists for this account. You can not delete this account.")) 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.validate_trash()
self.update_nsm_model() self.update_nsm_model()
def before_rename(self, old, new, merge=False): def before_rename(self, old, new, merge=False):
# Add company abbr if not provided # Add company abbr if not provided
from erpnext.setup.doctype.company.company import get_name_with_abbr from erpnext.setup.doctype.company.company import get_name_with_abbr
new_account = get_name_with_abbr(new, self.company) new_account = get_name_with_abbr(new, self.company)
# Validate properties before merging # Validate properties before merging
if merge: if merge:
if not frappe.db.exists("Account", new): if not frappe.db.exists("Account", new):
throw(_("Account ") + new +_(" does not exists")) 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"])) ["group_or_ledger", "report_type", "company"]))
if val != [self.group_or_ledger, self.report_type, self.company]: if val != [self.group_or_ledger, self.report_type, self.company]:
throw(_("""Merging is only possible if following \ throw(_("""Merging is only possible if following properties are same in both records. Group or Ledger, Report Type, Company"""))
properties are same in both records.
Group or Ledger, Report Type, Company"""))
return new_account return new_account
def after_rename(self, old, new, merge=False): def after_rename(self, old, new, merge=False):
if not merge: if not merge:
frappe.db.set_value("Account", new, "account_name", frappe.db.set_value("Account", new, "account_name",
" - ".join(new.split(" - ")[:-1])) " - ".join(new.split(" - ")[:-1]))
else: else:
from frappe.utils.nestedset import rebuild_tree 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): def get_master_name(doctype, txt, searchfield, start, page_len, filters):
conditions = (" and company='%s'"% filters["company"].replace("'", "\'")) if doctype == "Warehouse" else "" 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 return frappe.db.sql("""select name from `tab%s` where %s like %s %s
order by name limit %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) ("%%%s%%" % txt, start, page_len), as_list=1)
def get_parent_account(doctype, txt, searchfield, start, page_len, filters): 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 where group_or_ledger = 'Group' and docstatus != 2 and company = %s
and %s like %s order by name limit %s, %s""" % and %s like %s order by name limit %s, %s""" %
("%s", searchfield, "%s", "%s", "%s"), ("%s", searchfield, "%s", "%s", "%s"),
(filters["company"], "%%%s%%" % txt, start, page_len), as_list=1) (filters["company"], "%%%s%%" % txt, start, page_len), as_list=1)

View File

@ -9,46 +9,42 @@ from frappe.model.document import Document
class CForm(Document): class CForm(Document):
def validate(self): 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""" and no other c-form is received for that"""
for d in self.get('invoice_details'): for d in self.get('invoice_details'):
if d.invoice_no: if d.invoice_no:
inv = frappe.db.sql("""select c_form_applicable, c_form_no from inv = frappe.db.sql("""select c_form_applicable, c_form_no from
`tabSales Invoice` where name = %s and docstatus = 1""", d.invoice_no) `tabSales Invoice` where name = %s and docstatus = 1""", d.invoice_no)
if not inv: if inv[0][0] != 'Yes':
frappe.throw("""Invoice: %s is not exists in the system or
is not submitted, please check.""" % d.invoice_no)
elif inv[0][0] != 'Yes':
frappe.throw("C-form is not applicable for Invoice: %s" % d.invoice_no) frappe.throw("C-form is not applicable for Invoice: %s" % d.invoice_no)
elif inv[0][1] and inv[0][1] != self.name: elif inv[0][1] and inv[0][1] != self.name:
frappe.throw("""Invoice %s is tagged in another C-form: %s. frappe.throw("""Invoice %s is tagged in another C-form: %s.
If you want to change C-form no for this invoice, 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])) (d.invoice_no, inv[0][1]))
def on_update(self): def on_update(self):
""" Update C-Form No on invoices""" """ Update C-Form No on invoices"""
self.set_total_invoiced_amount() self.set_total_invoiced_amount()
def on_submit(self): def on_submit(self):
self.set_cform_in_sales_invoices() self.set_cform_in_sales_invoices()
def before_cancel(self): def before_cancel(self):
# remove cform reference # remove cform reference
frappe.db.sql("""update `tabSales Invoice` set c_form_no=null where c_form_no=%s""", self.name) 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): def set_cform_in_sales_invoices(self):
inv = [d.invoice_no for d in self.get('invoice_details')] inv = [d.invoice_no for d in self.get('invoice_details')]
if inv: 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)) ('%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 frappe.db.sql("""update `tabSales Invoice` set c_form_no = null, modified = %s
where name not in (%s) and ifnull(c_form_no, '') = %s""" % where name not in (%s) and ifnull(c_form_no, '') = %s""" %
('%s', ', '.join(['%s']*len(inv)), '%s'), tuple([self.modified] + inv + [self.name])) ('%s', ', '.join(['%s']*len(inv)), '%s'), tuple([self.modified] + inv + [self.name]))
else: else:
frappe.throw(_("Please enter atleast 1 invoice in the table")) frappe.throw(_("Please enter atleast 1 invoice in the table"))
@ -60,7 +56,7 @@ class CForm(Document):
def get_invoice_details(self, invoice_no): def get_invoice_details(self, invoice_no):
""" Pull details from invoices for referrence """ """ 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) ["posting_date", "territory", "net_total", "grand_total"], as_dict=True)
return { return {
'invoice_date' : inv.posting_date, 'invoice_date' : inv.posting_date,
@ -72,9 +68,9 @@ class CForm(Document):
def get_invoice_nos(doctype, txt, searchfield, start, page_len, filters): def get_invoice_nos(doctype, txt, searchfield, start, page_len, filters):
from erpnext.utilities import build_filter_conditions from erpnext.utilities import build_filter_conditions
conditions, filter_values = build_filter_conditions(filters) conditions, filter_values = build_filter_conditions(filters)
return frappe.db.sql("""select name from `tabSales Invoice` where docstatus = 1 return frappe.db.sql("""select name from `tabSales Invoice` where docstatus = 1
and c_form_applicable = 'Yes' and ifnull(c_form_no, '') = '' %s and c_form_applicable = 'Yes' and ifnull(c_form_no, '') = '' %s
and %s like %s order by name limit %s, %s""" % and %s like %s order by name limit %s, %s""" %
(conditions, searchfield, "%s", "%s", "%s"), (conditions, searchfield, "%s", "%s", "%s"),
tuple(filter_values + ["%%%s%%" % txt, start, page_len])) tuple(filter_values + ["%%%s%%" % txt, start, page_len]))

View File

@ -8,13 +8,13 @@ from erpnext.accounts.report.accounts_receivable.accounts_receivable import get_
def execute(filters=None): def execute(filters=None):
if not filters: filters = {} if not filters: filters = {}
columns = get_columns() columns = get_columns()
entries = get_entries(filters) entries = get_entries(filters)
invoice_posting_date_map = get_invoice_posting_date_map(filters) invoice_posting_date_map = get_invoice_posting_date_map(filters)
against_date = "" against_date = ""
outstanding_amount = 0.0 outstanding_amount = 0.0
data = [] data = []
for d in entries: for d in entries:
if d.against_voucher: if d.against_voucher:
@ -23,67 +23,66 @@ def execute(filters=None):
else: else:
against_date = d.against_invoice and invoice_posting_date_map[d.against_invoice] or "" against_date = d.against_invoice and invoice_posting_date_map[d.against_invoice] or ""
outstanding_amount = d.credit or -1*d.debit 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] against_date, d.debit, d.credit, d.cheque_no, d.cheque_date, d.remark]
if d.against_voucher or d.against_invoice: if d.against_voucher or d.against_invoice:
row += get_ageing_data(d.posting_date, against_date, outstanding_amount) row += get_ageing_data(d.posting_date, against_date, outstanding_amount)
else: else:
row += ["", "", "", "", ""] row += ["", "", "", "", ""]
data.append(row) data.append(row)
return columns, data return columns, data
def get_columns(): def get_columns():
return ["Journal Voucher:Link/Journal Voucher:140", "Account:Link/Account:140", return ["Journal Voucher:Link/Journal Voucher:140", "Account:Link/Account:140",
"Posting Date:Date:100", "Against Invoice:Link/Purchase Invoice:130", "Posting Date:Date:100", "Against Invoice:Link/Purchase Invoice:130",
"Against Invoice Posting Date:Date:130", "Debit:Currency:120", "Credit:Currency:120", "Against Invoice Posting Date:Date:130", "Debit:Currency:120", "Credit:Currency:120",
"Reference No::100", "Reference Date:Date:100", "Remarks::150", "Age:Int:40", "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" "0-30:Currency:100", "30-60:Currency:100", "60-90:Currency:100", "90-Above:Currency:100"
] ]
def get_conditions(filters): def get_conditions(filters):
conditions = "" conditions = ""
party_accounts = [] party_accounts = []
if filters.get("account"): if filters.get("account"):
party_accounts = [filters["account"]] party_accounts = [filters["account"]]
else: else:
cond = filters.get("company") and (" and company = '%s'" % cond = filters.get("company") and (" and company = '%s'" %
filters["company"].replace("'", "\'")) or "" filters["company"].replace("'", "\'")) or ""
if filters.get("payment_type") == "Incoming": if filters.get("payment_type") == "Incoming":
cond += " and master_type = 'Customer'" cond += " and master_type = 'Customer'"
else: else:
cond += " and master_type = 'Supplier'" 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) where ifnull(master_name, '')!='' and docstatus < 2 %s""" % cond)
if party_accounts: if party_accounts:
conditions += " and jvd.account in (%s)" % (", ".join(['%s']*len(party_accounts))) conditions += " and jvd.account in (%s)" % (", ".join(['%s']*len(party_accounts)))
else: else:
msgprint(_("No Customer or Supplier Accounts found. Accounts are identified based on \ msgprint(_("No Customer or Supplier Accounts found"), raise_exception=1)
'Master Type' value in account record."), raise_exception=1)
if filters.get("from_date"): conditions += " and jv.posting_date >= '%s'" % filters["from_date"] 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"] if filters.get("to_date"): conditions += " and jv.posting_date <= '%s'" % filters["to_date"]
return conditions, party_accounts return conditions, party_accounts
def get_entries(filters): def get_entries(filters):
conditions, party_accounts = get_conditions(filters) conditions, party_accounts = get_conditions(filters)
entries = frappe.db.sql("""select jv.name, jvd.account, jv.posting_date, entries = frappe.db.sql("""select jv.name, jvd.account, jv.posting_date,
jvd.against_voucher, jvd.against_invoice, jvd.debit, jvd.credit, jvd.against_voucher, jvd.against_invoice, jvd.debit, jvd.credit,
jv.cheque_no, jv.cheque_date, jv.remark jv.cheque_no, jv.cheque_date, jv.remark
from `tabJournal Voucher Detail` jvd, `tabJournal Voucher` jv from `tabJournal Voucher Detail` jvd, `tabJournal Voucher` jv
where jvd.parent = jv.name and jv.docstatus=1 %s order by jv.name DESC""" % where jvd.parent = jv.name and jv.docstatus=1 %s order by jv.name DESC""" %
(conditions), tuple(party_accounts), as_dict=1) (conditions), tuple(party_accounts), as_dict=1)
return entries return entries
def get_invoice_posting_date_map(filters): def get_invoice_posting_date_map(filters):
invoice_posting_date_map = {} invoice_posting_date_map = {}
if filters.get("payment_type") == "Incoming": 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`"""): for t in frappe.db.sql("""select name, posting_date from `tabPurchase Invoice`"""):
invoice_posting_date_map[t[0]] = t[1] invoice_posting_date_map[t[0]] = t[1]
return invoice_posting_date_map return invoice_posting_date_map

View File

@ -147,7 +147,7 @@ class PurchaseCommon(BuyingController):
stopped = frappe.db.sql("""select name from `tab%s` where name = %s and stopped = frappe.db.sql("""select name from `tab%s` where name = %s and
status = 'Stopped'""" % (doctype, '%s'), docname) status = 'Stopped'""" % (doctype, '%s'), docname)
if stopped: 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 = ''): def check_docstatus(self, check, doctype, docname, detail_doctype = ''):
if check == 'Next': if check == 'Next':

View File

@ -9,23 +9,23 @@ erpnext.hr.EmployeeController = frappe.ui.form.Controller.extend({
this.frm.fields_dict.reports_to.get_query = function(doc, cdt, cdn) { this.frm.fields_dict.reports_to.get_query = function(doc, cdt, cdn) {
return { query: "erpnext.controllers.queries.employee_query"} } return { query: "erpnext.controllers.queries.employee_query"} }
}, },
onload: function() { onload: function() {
this.setup_leave_approver_select(); this.setup_leave_approver_select();
this.frm.toggle_display(["esic_card_no", "gratuity_lic_id", "pan_number", "pf_number"], this.frm.toggle_display(["esic_card_no", "gratuity_lic_id", "pan_number", "pf_number"],
frappe.boot.sysdefaults.country==="India"); frappe.boot.sysdefaults.country==="India");
if(this.frm.doc.__islocal) this.frm.set_value("employee_name", ""); if(this.frm.doc.__islocal) this.frm.set_value("employee_name", "");
}, },
refresh: function() { refresh: function() {
var me = this; var me = this;
erpnext.hide_naming_series(); erpnext.hide_naming_series();
if(!this.frm.doc.__islocal) { if(!this.frm.doc.__islocal) {
cur_frm.add_custom_button(__('Make Salary Structure'), function() { cur_frm.add_custom_button(__('Make Salary Structure'), function() {
me.make_salary_structure(this); }); me.make_salary_structure(this); });
} }
}, },
setup_leave_approver_select: function() { setup_leave_approver_select: function() {
var me = this; var me = this;
return this.frm.call({ return this.frm.call({
@ -33,21 +33,21 @@ erpnext.hr.EmployeeController = frappe.ui.form.Controller.extend({
callback: function(r) { callback: function(r) {
var df = frappe.meta.get_docfield("Employee Leave Approver", "leave_approver", var df = frappe.meta.get_docfield("Employee Leave Approver", "leave_approver",
me.frm.doc.name); me.frm.doc.name);
df.options = $.map(r.message, function(user) { df.options = $.map(r.message, function(user) {
return {value: user, label: frappe.user_info(user).fullname}; return {value: user, label: frappe.user_info(user).fullname};
}); });
me.frm.fields_dict.employee_leave_approvers.refresh(); me.frm.fields_dict.employee_leave_approvers.refresh();
} }
}); });
}, },
date_of_birth: function() { date_of_birth: function() {
return cur_frm.call({ return cur_frm.call({
method: "get_retirement_date", method: "get_retirement_date",
args: {date_of_birth: this.frm.doc.date_of_birth} args: {date_of_birth: this.frm.doc.date_of_birth}
}); });
}, },
salutation: function() { salutation: function() {
if(this.frm.doc.salutation) { if(this.frm.doc.salutation) {
this.frm.set_value("gender", { this.frm.set_value("gender", {
@ -56,16 +56,12 @@ erpnext.hr.EmployeeController = frappe.ui.form.Controller.extend({
}[this.frm.doc.salutation]); }[this.frm.doc.salutation]);
} }
}, },
make_salary_structure: function(btn) { make_salary_structure: function(btn) {
var me = this; var me = this;
this.validate_salary_structure(btn, function(r) { this.validate_salary_structure(btn, function(r) {
if(r.message) { if(r.message) {
msgprint(__("Employee {0}:\ msgprint(__("Active Salary Sructure already exists for Employee {0}", [me.frm.doc.name]));
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]));
} else if(!r.exc) { } else if(!r.exc) {
frappe.model.map({ frappe.model.map({
source: me.frm.doc, source: me.frm.doc,
@ -74,7 +70,7 @@ erpnext.hr.EmployeeController = frappe.ui.form.Controller.extend({
} }
}); });
}, },
validate_salary_structure: function(btn, callback) { validate_salary_structure: function(btn, callback) {
var me = this; var me = this;
return this.frm.call({ return this.frm.call({

View File

@ -63,7 +63,7 @@ class LeaveAllocation(Document):
cf = cf and cint(cf[0][0]) or 0 cf = cf and cint(cf[0][0]) or 0
if not cf: if not cf:
frappe.db.set(self,'carry_forward',0) 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): def get_carry_forwarded_leaves(self):
if self.carry_forward: if self.carry_forward:

View File

@ -144,8 +144,7 @@ class LeaveApplication(DocListController):
def validate_max_days(self): def validate_max_days(self):
max_days = frappe.db.get_value("Leave Type", self.leave_type, "max_days_allowed") max_days = frappe.db.get_value("Leave Type", self.leave_type, "max_days_allowed")
if max_days and self.total_leave_days > max_days: if max_days and self.total_leave_days > max_days:
frappe.throw("Sorry ! You cannot apply for %s for more than %s days" % frappe.throw(_("Leave of type {0} cannot be longer than {1}").format(self.leave_type, max_days))
(self.leave_type, max_days))
def validate_leave_approver(self): def validate_leave_approver(self):
employee = frappe.get_doc("Employee", self.employee) employee = frappe.get_doc("Employee", self.employee)

View File

@ -130,7 +130,7 @@ class SalarySlip(TransactionBase):
(self.month, self.fiscal_year, self.employee, self.name)) (self.month, self.fiscal_year, self.employee, self.name))
if ret_exist: if ret_exist:
self.employee = '' 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): def validate(self):
from frappe.utils import money_in_words from frappe.utils import money_in_words

View File

@ -5,7 +5,7 @@ from __future__ import unicode_literals
import frappe import frappe
from frappe.utils import cstr, flt, nowdate from frappe.utils import cstr, flt, nowdate
from frappe import msgprint, _ from frappe import _
class OverProductionError(frappe.ValidationError): pass class OverProductionError(frappe.ValidationError): pass
@ -34,9 +34,7 @@ class ProductionOrder(Document):
and is_active=1 and item=%s""" and is_active=1 and item=%s"""
, (self.bom_no, self.production_item), as_dict =1) , (self.bom_no, self.production_item), as_dict =1)
if not bom: if not bom:
frappe.throw("""Incorrect BOM: %s entered. frappe.throw(_("BOM {0} is not active or not submitted").format(self.bom_no))
May be BOM not exists or inactive or not submitted
or for some other item.""" % cstr(self.bom_no))
def validate_sales_order(self): def validate_sales_order(self):
if self.sales_order: if self.sales_order:
@ -44,7 +42,7 @@ class ProductionOrder(Document):
where name=%s and docstatus = 1""", self.sales_order, as_dict=1)[0] where name=%s and docstatus = 1""", self.sales_order, as_dict=1)[0]
if not so.name: 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: if not self.expected_delivery_date:
self.expected_delivery_date = so.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` stock_entry = frappe.db.sql("""select name from `tabStock Entry`
where production_order = %s and docstatus = 1""", self.name) where production_order = %s and docstatus = 1""", self.name)
if stock_entry: if stock_entry:
frappe.throw("""Submitted Stock Entry %s exists against this production order. frappe.throw(_("Cannot cancel because submitted Stock Entry {0} exists").format(stock_entry[0][0]))
Hence can not be cancelled.""" % stock_entry[0][0])
frappe.db.set(self,'status', 'Cancelled') frappe.db.set(self,'status', 'Cancelled')
self.update_planned_qty(-self.qty) self.update_planned_qty(-self.qty)

View File

@ -152,21 +152,17 @@ class ProductionPlanningTool(Document):
for d in self.get('pp_details'): for d in self.get('pp_details'):
self.validate_bom_no(d) self.validate_bom_no(d)
if not flt(d.planned_qty): if not flt(d.planned_qty):
frappe.throw("Please Enter Planned Qty for item: %s at row no: %s" % frappe.throw(_("Please enter Planned Qty for Item {0} at row {1}").format(d.item_code, d.idx))
(d.item_code, d.idx))
def validate_bom_no(self, d): def validate_bom_no(self, d):
if not d.bom_no: if not d.bom_no:
frappe.throw("Please enter bom no for item: %s at row no: %s" % frappe.throw(_("Please enter BOM for Item {0} at row {1}").format(d.item_code, d.idx))
(d.item_code, d.idx))
else: else:
bom = frappe.db.sql("""select name from `tabBOM` where name = %s and item = %s bom = frappe.db.sql("""select name from `tabBOM` where name = %s and item = %s
and docstatus = 1 and is_active = 1""", and docstatus = 1 and is_active = 1""",
(d.bom_no, d.item_code), as_dict = 1) (d.bom_no, d.item_code), as_dict = 1)
if not bom: if not bom:
frappe.throw("""Incorrect BOM No: %s entered for item: %s at row no: %s frappe.throw(_("Incorrect or Inactive BOM {0} for Item {1} at row {2}").format(d.bom_no, d.item_code, d.idx))
May be BOM is inactive or for other item or does not exists in the system""" %
(d.bom_no, d.item_doce, d.idx))
def raise_production_order(self): def raise_production_order(self):
"""It will raise production order (Draft) for all distinct FG items""" """It will raise production order (Draft) for all distinct FG items"""

View File

@ -10,7 +10,7 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({
if(this.frm.doc.__islocal) { if(this.frm.doc.__islocal) {
var today = get_today(), var today = get_today(),
currency = frappe.defaults.get_user_default("currency"); currency = frappe.defaults.get_user_default("currency");
$.each({ $.each({
posting_date: today, posting_date: today,
due_date: today, due_date: today,
@ -24,18 +24,18 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({
}, function(fieldname, value) { }, function(fieldname, value) {
if(me.frm.fields_dict[fieldname] && !me.frm.doc[fieldname]) if(me.frm.fields_dict[fieldname] && !me.frm.doc[fieldname])
me.frm.set_value(fieldname, value); me.frm.set_value(fieldname, value);
}); });
} }
if(this.other_fname) { if(this.other_fname) {
this[this.other_fname + "_remove"] = this.calculate_taxes_and_totals; this[this.other_fname + "_remove"] = this.calculate_taxes_and_totals;
} }
if(this.fname) { if(this.fname) {
this[this.fname + "_remove"] = this.calculate_taxes_and_totals; this[this.fname + "_remove"] = this.calculate_taxes_and_totals;
} }
}, },
onload_post_render: function() { onload_post_render: function() {
var me = this; var me = this;
if(this.frm.doc.__islocal && this.frm.doc.company && !this.frm.doc.is_pos) { 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() { refresh: function() {
this.frm.clear_custom_buttons(); this.frm.clear_custom_buttons();
erpnext.hide_naming_series(); erpnext.hide_naming_series();
@ -77,7 +77,7 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({
icon = "icon-file-text"; icon = "icon-file-text";
} }
var me = this; var me = this;
this.$pos_btn = this.frm.appframe.add_button(btn_label, function() { this.$pos_btn = this.frm.appframe.add_button(btn_label, function() {
me.toggle_pos(); me.toggle_pos();
}, icon); }, icon);
@ -87,7 +87,7 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({
// Check whether it is Selling or Buying cycle // Check whether it is Selling or Buying cycle
var price_list = frappe.meta.has_field(cur_frm.doc.doctype, "selling_price_list") ? 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; this.frm.doc.selling_price_list : this.frm.doc.buying_price_list;
if (!price_list) if (!price_list)
msgprint(__("Please select Price List")) msgprint(__("Please select Price List"))
else { else {
@ -109,8 +109,8 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({
this.frm.refresh(); this.frm.refresh();
} }
}, },
item_code: function(doc, cdt, cdn) { item_code: function(doc, cdt, cdn) {
var me = this; var me = this;
var item = frappe.get_doc(cdt, cdn); var item = frappe.get_doc(cdt, cdn);
@ -152,7 +152,7 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({
}); });
} }
} }
}, },
serial_no: function(doc, cdt, cdn) { serial_no: function(doc, cdt, cdn) {
var me = this; var me = this;
@ -169,7 +169,7 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({
var serial_nos = item.serial_no.trim().replace(/,/g, '\n'); var serial_nos = item.serial_no.trim().replace(/,/g, '\n');
serial_nos = serial_nos.trim().split('\n'); serial_nos = serial_nos.trim().split('\n');
// Trim each string and push unique string to new list // Trim each string and push unique string to new list
for (var x=0; x<=serial_nos.length - 1; x++) { for (var x=0; x<=serial_nos.length - 1; x++) {
if (serial_nos[x].trim() != "" && sr_no.indexOf(serial_nos[x].trim()) == -1) { 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() { validate: function() {
this.calculate_taxes_and_totals(); this.calculate_taxes_and_totals();
}, },
company: function() { company: function() {
if(this.frm.doc.company && this.frm.fields_dict.currency) { if(this.frm.doc.company && this.frm.fields_dict.currency) {
var company_currency = this.get_company_currency(); var company_currency = this.get_company_currency();
if (!this.frm.doc.currency) { if (!this.frm.doc.currency) {
this.frm.set_value("currency", company_currency); this.frm.set_value("currency", company_currency);
} }
if (this.frm.doc.currency == company_currency) { if (this.frm.doc.currency == company_currency) {
this.frm.set_value("conversion_rate", 1.0); this.frm.set_value("conversion_rate", 1.0);
} }
@ -209,27 +209,27 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({
this.frm.script_manager.trigger("currency"); this.frm.script_manager.trigger("currency");
} }
}, },
get_company_currency: function() { get_company_currency: function() {
return erpnext.get_currency(this.frm.doc.company); return erpnext.get_currency(this.frm.doc.company);
}, },
currency: function() { currency: function() {
var me = this; var me = this;
this.set_dynamic_labels(); this.set_dynamic_labels();
var company_currency = this.get_company_currency(); var company_currency = this.get_company_currency();
if(this.frm.doc.currency !== 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) { function(exchange_rate) {
me.frm.set_value("conversion_rate", exchange_rate); me.frm.set_value("conversion_rate", exchange_rate);
me.conversion_rate(); me.conversion_rate();
}); });
} else { } else {
this.conversion_rate(); this.conversion_rate();
} }
}, },
conversion_rate: function() { conversion_rate: function() {
if(this.frm.doc.currency === this.get_company_currency()) { if(this.frm.doc.currency === this.get_company_currency()) {
this.frm.set_value("conversion_rate", 1.0); 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(); if(flt(this.frm.doc.conversion_rate)>0.0) this.calculate_taxes_and_totals();
}, },
get_price_list_currency: function(buying_or_selling) { get_price_list_currency: function(buying_or_selling) {
var me = this; var me = this;
var fieldname = buying_or_selling.toLowerCase() + "_price_list"; var fieldname = buying_or_selling.toLowerCase() + "_price_list";
if(this.frm.doc[fieldname]) { if(this.frm.doc[fieldname]) {
return this.frm.call({ return this.frm.call({
method: "erpnext.setup.utils.get_price_list_currency", method: "erpnext.setup.utils.get_price_list_currency",
args: { args: {
price_list: this.frm.doc[fieldname], price_list: this.frm.doc[fieldname],
}, },
callback: function(r) { callback: function(r) {
@ -258,7 +258,7 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({
}); });
} }
}, },
get_exchange_rate: function(from_currency, to_currency, callback) { get_exchange_rate: function(from_currency, to_currency, callback) {
var exchange_name = from_currency + "-" + to_currency; var exchange_name = from_currency + "-" + to_currency;
frappe.model.with_doc("Currency Exchange", exchange_name, function(name) { 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); callback(exchange_doc ? flt(exchange_doc.exchange_rate) : 0);
}); });
}, },
price_list_currency: function() { price_list_currency: function() {
var me=this; var me=this;
this.set_dynamic_labels(); this.set_dynamic_labels();
var company_currency = this.get_company_currency(); var company_currency = this.get_company_currency();
if(this.frm.doc.price_list_currency !== 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) { function(exchange_rate) {
if(exchange_rate) { if(exchange_rate) {
me.frm.set_value("plc_conversion_rate", 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(); this.plc_conversion_rate();
} }
}, },
plc_conversion_rate: function() { plc_conversion_rate: function() {
if(this.frm.doc.price_list_currency === this.get_company_currency()) { if(this.frm.doc.price_list_currency === this.get_company_currency()) {
this.frm.set_value("plc_conversion_rate", 1.0); this.frm.set_value("plc_conversion_rate", 1.0);
@ -294,11 +294,11 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({
this.calculate_taxes_and_totals(); this.calculate_taxes_and_totals();
} }
}, },
qty: function(doc, cdt, cdn) { qty: function(doc, cdt, cdn) {
this.calculate_taxes_and_totals(); this.calculate_taxes_and_totals();
}, },
tax_rate: function(doc, cdt, cdn) { tax_rate: function(doc, cdt, cdn) {
this.calculate_taxes_and_totals(); this.calculate_taxes_and_totals();
}, },
@ -314,30 +314,30 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({
throw e; throw e;
} }
}, },
set_dynamic_labels: function() { set_dynamic_labels: function() {
// What TODO? should we make price list system non-mandatory? // What TODO? should we make price list system non-mandatory?
this.frm.toggle_reqd("plc_conversion_rate", this.frm.toggle_reqd("plc_conversion_rate",
!!(this.frm.doc.price_list_name && this.frm.doc.price_list_currency)); !!(this.frm.doc.price_list_name && this.frm.doc.price_list_currency));
var company_currency = this.get_company_currency(); var company_currency = this.get_company_currency();
this.change_form_labels(company_currency); this.change_form_labels(company_currency);
this.change_grid_labels(company_currency); this.change_grid_labels(company_currency);
this.frm.refresh_fields(); this.frm.refresh_fields();
}, },
recalculate: function() { recalculate: function() {
this.calculate_taxes_and_totals(); this.calculate_taxes_and_totals();
}, },
recalculate_values: function() { recalculate_values: function() {
this.calculate_taxes_and_totals(); this.calculate_taxes_and_totals();
}, },
calculate_charges: function() { calculate_charges: function() {
this.calculate_taxes_and_totals(); this.calculate_taxes_and_totals();
}, },
included_in_print_rate: function(doc, cdt, cdn) { included_in_print_rate: function(doc, cdt, cdn) {
var tax = frappe.get_doc(cdt, cdn); var tax = frappe.get_doc(cdt, cdn);
try { try {
@ -350,52 +350,32 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({
throw e; throw e;
} }
}, },
validate_on_previous_row: function(tax) { validate_on_previous_row: function(tax) {
// validate if a valid row id is mentioned in case of // validate if a valid row id is mentioned in case of
// On Previous Row Amount and On Previous Row Total // 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)) { (!tax.row_id || cint(tax.row_id) >= tax.idx)) {
var msg = repl(__("Row") + " # %(idx)s [%(doctype)s]: " + var msg = __("Please specify a valid Row ID for row {0} in table {1}", [tax.idx, __(tax.doctype)])
__("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)
});
frappe.throw(msg); frappe.throw(msg);
} }
}, },
validate_inclusive_tax: function(tax) { validate_inclusive_tax: function(tax) {
if(!this.frm.tax_doclist) this.frm.tax_doclist = this.get_tax_doclist(); if(!this.frm.tax_doclist) this.frm.tax_doclist = this.get_tax_doclist();
var actual_type_error = function() { var actual_type_error = function() {
var msg = repl(__("For row") + " # %(idx)s [%(doctype)s]: " + var msg = __("Actual type tax cannot be included in Item rate in row {0}", [tax.idx])
"%(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
});
frappe.throw(msg); frappe.throw(msg);
}; };
var on_previous_row_error = function(row_range) { var on_previous_row_error = function(row_range) {
var msg = repl(__("For row") + " # %(idx)s [%(doctype)s]: " + var msg = __("For row {0} in {1}. To include {2} in Item rate, rows {3} must also be included",
__("to be included in Item's rate, it is required that: ") + [tax.idx, __(tax.doctype), tax.charge_type, row_range])
" [" + __("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,
});
frappe.throw(msg); frappe.throw(msg);
}; };
if(cint(tax.included_in_print_rate)) { if(cint(tax.included_in_print_rate)) {
if(tax.charge_type == "Actual") { if(tax.charge_type == "Actual") {
// inclusive tax cannot be of 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 // referred row should also be an inclusive tax
on_previous_row_error(tax.row_id); on_previous_row_error(tax.row_id);
} else if(tax.charge_type == "On Previous Row Total") { } 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; }); function(t) { return cint(t.included_in_print_rate) ? null : t; });
if(taxes_not_included.length > 0) { if(taxes_not_included.length > 0) {
// all rows above this tax should be inclusive // 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) { _load_item_tax_rate: function(item_tax_rate) {
return item_tax_rate ? JSON.parse(item_tax_rate) : {}; return item_tax_rate ? JSON.parse(item_tax_rate) : {};
}, },
_get_tax_rate: function(tax, item_tax_map) { _get_tax_rate: function(tax, item_tax_map) {
return (keys(item_tax_map).indexOf(tax.account_head) != -1) ? return (keys(item_tax_map).indexOf(tax.account_head) != -1) ?
flt(item_tax_map[tax.account_head], precision("rate", tax)) : flt(item_tax_map[tax.account_head], precision("rate", tax)) :
tax.rate; tax.rate;
}, },
get_item_wise_taxes_html: function() { get_item_wise_taxes_html: function() {
var item_tax = {}; var item_tax = {};
var tax_accounts = []; var tax_accounts = [];
var company_currency = this.get_company_currency(); var company_currency = this.get_company_currency();
$.each(this.get_tax_doclist(), function(i, tax) { $.each(this.get_tax_doclist(), function(i, tax) {
var tax_amount_precision = precision("tax_amount", tax); var tax_amount_precision = precision("tax_amount", tax);
var tax_rate_precision = precision("rate", 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) { function(item_code, tax_data) {
if(!item_tax[item_code]) item_tax[item_code] = {}; if(!item_tax[item_code]) item_tax[item_code] = {};
if($.isArray(tax_data)) { 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, var tax_amount = format_currency(flt(tax_data[1], tax_amount_precision), company_currency,
tax_amount_precision); tax_amount_precision);
item_tax[item_code][tax.name] = [tax_rate, tax_amount]; item_tax[item_code][tax.name] = [tax_rate, tax_amount];
} else { } else {
item_tax[item_code][tax.name] = [flt(tax_data, tax_rate_precision) + "%", ""]; 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]); 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"); function(head) { return '<th style="min-width: 100px;">' + (head || "") + "</th>" }).join("\n");
var distinct_item_names = []; var distinct_item_names = [];
var distinct_items = []; var distinct_items = [];
$.each(this.get_item_doclist(), function(i, item) { $.each(this.get_item_doclist(), function(i, item) {
@ -465,7 +445,7 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({
distinct_items.push(item); distinct_items.push(item);
} }
}); });
var rows = $.map(distinct_items, function(item) { var rows = $.map(distinct_items, function(item) {
var item_tax_record = item_tax[item.item_code || item.item_name]; var item_tax_record = item_tax[item.item_code || item.item_name];
if(!item_tax_record) { return null; } if(!item_tax_record) { return null; }
@ -478,7 +458,7 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({
}).join("\n") }).join("\n")
}); });
}).join("\n"); }).join("\n");
if(!rows) return ""; if(!rows) return "";
return '<p><a href="#" onclick="$(\'.tax-break-up\').toggleClass(\'hide\'); return false;">Show / Hide tax break-up</a><br><br></p>\ 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">\ <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> \ <tbody>' + rows + '</tbody> \
</table></div>'; </table></div>';
}, },
validate_company_and_party: function() { validate_company_and_party: function() {
var me = this; var me = this;
var valid = true; var valid = true;
$.each(["company", "customer"], function(i, fieldname) { $.each(["company", "customer"], function(i, fieldname) {
if(frappe.meta.has_field(me.frm.doc.doctype, fieldname)) { if(frappe.meta.has_field(me.frm.doc.doctype, fieldname)) {
if (!me.frm.doc[fieldname]) { if (!me.frm.doc[fieldname]) {
msgprint(__("Please specify") + ": " + msgprint(__("Please specify") + ": " +
frappe.meta.get_label(me.frm.doc.doctype, fieldname, me.frm.doc.name) + frappe.meta.get_label(me.frm.doc.doctype, fieldname, me.frm.doc.name) +
". " + __("It is needed to fetch Item Details.")); ". " + __("It is needed to fetch Item Details."));
valid = false; valid = false;
} }
@ -503,25 +483,25 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({
}); });
return valid; return valid;
}, },
get_item_doclist: function() { get_item_doclist: function() {
return this.frm.doc[this.fname] || []; return this.frm.doc[this.fname] || [];
}, },
get_tax_doclist: function() { get_tax_doclist: function() {
return this.frm.doc[this.other_fname] || []; return this.frm.doc[this.other_fname] || [];
}, },
validate_conversion_rate: function() { validate_conversion_rate: function() {
this.frm.doc.conversion_rate = flt(this.frm.doc.conversion_rate, precision("conversion_rate")); 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); this.frm.doc.name);
var company_currency = this.get_company_currency(); var company_currency = this.get_company_currency();
if(!this.frm.doc.conversion_rate) { if(!this.frm.doc.conversion_rate) {
frappe.throw(repl('%(conversion_rate_label)s' + frappe.throw(repl('%(conversion_rate_label)s' +
__(' is mandatory. Maybe Currency Exchange record is not created for ') + __(' is mandatory. Maybe Currency Exchange record is not created for ') +
'%(from_currency)s' + __(" to ") + '%(to_currency)s', '%(from_currency)s' + __(" to ") + '%(to_currency)s',
{ {
"conversion_rate_label": conversion_rate_label, "conversion_rate_label": conversion_rate_label,
"from_currency": this.frm.doc.currency, "from_currency": this.frm.doc.currency,
@ -529,7 +509,7 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({
})); }));
} }
}, },
calculate_taxes_and_totals: function() { calculate_taxes_and_totals: function() {
this.discount_amount_applied = false; this.discount_amount_applied = false;
this._calculate_taxes_and_totals(); this._calculate_taxes_and_totals();
@ -552,13 +532,13 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({
this.show_item_wise_taxes(); this.show_item_wise_taxes();
}, },
initialize_taxes: function() { initialize_taxes: function() {
var me = this; var me = this;
$.each(this.frm.tax_doclist, function(i, tax) { $.each(this.frm.tax_doclist, function(i, tax) {
tax.item_wise_tax_detail = {}; 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_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"]
@ -566,17 +546,17 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({
tax_fields.push("tax_amount"); tax_fields.push("tax_amount");
$.each(tax_fields, function(i, fieldname) { tax[fieldname] = 0.0 }); $.each(tax_fields, function(i, fieldname) { tax[fieldname] = 0.0 });
me.validate_on_previous_row(tax); me.validate_on_previous_row(tax);
me.validate_inclusive_tax(tax); me.validate_inclusive_tax(tax);
frappe.model.round_floats_in(tax); frappe.model.round_floats_in(tax);
}); });
}, },
calculate_taxes: function() { calculate_taxes: function() {
var me = this; var me = this;
var actual_tax_dict = {}; var actual_tax_dict = {};
// maintain actual tax rate based on idx // maintain actual tax rate based on idx
$.each(this.frm.tax_doclist, function(i, tax) { $.each(this.frm.tax_doclist, function(i, tax) {
if (tax.charge_type == "Actual") { 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 // store tax_amount for current item as it will be used for
// charge type = 'On Previous Row Amount' // charge type = 'On Previous Row Amount'
tax.tax_amount_for_current_item = current_tax_amount; tax.tax_amount_for_current_item = current_tax_amount;
// accumulate tax amount into tax.tax_amount // accumulate tax amount into tax.tax_amount
if (!me.discount_amount_applied) if (!me.discount_amount_applied)
tax.tax_amount += current_tax_amount; tax.tax_amount += current_tax_amount;
tax.tax_amount_after_discount_amount += current_tax_amount; tax.tax_amount_after_discount_amount += current_tax_amount;
// for buying // for buying
if(tax.category) { if(tax.category) {
// if just for valuation, do not add the tax amount in total // if just for valuation, do not add the tax amount in total
// hence, setting it as 0 for further steps // hence, setting it as 0 for further steps
current_tax_amount = (tax.category == "Valuation") ? 0.0 : current_tax_amount; current_tax_amount = (tax.category == "Valuation") ? 0.0 : current_tax_amount;
current_tax_amount *= (tax.add_deduct_tax == "Deduct") ? -1.0 : 1.0; current_tax_amount *= (tax.add_deduct_tax == "Deduct") ? -1.0 : 1.0;
} }
// Calculate tax.total viz. grand total till that step // 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 // item's amount, previously applied tax and the current tax on that item
if(i==0) { if(i==0) {
tax.grand_total_for_current_item = flt(item.base_amount + current_tax_amount, tax.grand_total_for_current_item = flt(item.base_amount + current_tax_amount,
precision("total", tax)); precision("total", tax));
} else { } 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, flt(me.frm.tax_doclist[i-1].grand_total_for_current_item + current_tax_amount,
precision("total", tax)); precision("total", tax));
} }
// in tax.total, accumulate grand total for each item // in tax.total, accumulate grand total for each item
tax.total += tax.grand_total_for_current_item; tax.total += tax.grand_total_for_current_item;
@ -648,54 +628,54 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({
round_off_totals: function(tax) { round_off_totals: function(tax) {
tax.total = flt(tax.total, precision("total", tax)); tax.total = flt(tax.total, precision("total", tax));
tax.tax_amount = flt(tax.tax_amount, precision("tax_amount", 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)); precision("tax_amount", tax));
}, },
adjust_discount_amount_loss: function(tax) { adjust_discount_amount_loss: function(tax) {
var discount_amount_loss = this.frm.doc.grand_total - flt(this.frm.doc.discount_amount) - tax.total; 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)); discount_amount_loss, precision("tax_amount", tax));
tax.total = flt(tax.total + discount_amount_loss, precision("total", tax)); tax.total = flt(tax.total + discount_amount_loss, precision("total", tax));
}, },
get_current_tax_amount: function(item, tax, item_tax_map) { get_current_tax_amount: function(item, tax, item_tax_map) {
var tax_rate = this._get_tax_rate(tax, item_tax_map); var tax_rate = this._get_tax_rate(tax, item_tax_map);
var current_tax_amount = 0.0; var current_tax_amount = 0.0;
if(tax.charge_type == "Actual") { if(tax.charge_type == "Actual") {
// distribute the tax amount proportionally to each item row // distribute the tax amount proportionally to each item row
var actual = flt(tax.rate, precision("tax_amount", tax)); var actual = flt(tax.rate, precision("tax_amount", tax));
current_tax_amount = this.frm.doc.net_total ? current_tax_amount = this.frm.doc.net_total ?
((item.base_amount / this.frm.doc.net_total) * actual) : ((item.base_amount / this.frm.doc.net_total) * actual) :
0.0; 0.0;
} else if(tax.charge_type == "On Net Total") { } else if(tax.charge_type == "On Net Total") {
current_tax_amount = (tax_rate / 100.0) * item.base_amount; current_tax_amount = (tax_rate / 100.0) * item.base_amount;
} else if(tax.charge_type == "On Previous Row Amount") { } else if(tax.charge_type == "On Previous Row Amount") {
current_tax_amount = (tax_rate / 100.0) * current_tax_amount = (tax_rate / 100.0) *
this.frm.tax_doclist[cint(tax.row_id) - 1].tax_amount_for_current_item; this.frm.tax_doclist[cint(tax.row_id) - 1].tax_amount_for_current_item;
} else if(tax.charge_type == "On Previous Row Total") { } else if(tax.charge_type == "On Previous Row Total") {
current_tax_amount = (tax_rate / 100.0) * current_tax_amount = (tax_rate / 100.0) *
this.frm.tax_doclist[cint(tax.row_id) - 1].grand_total_for_current_item; 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)); current_tax_amount = flt(current_tax_amount, precision("tax_amount", tax));
// store tax breakup for each item // store tax breakup for each item
tax.item_wise_tax_detail[item.item_code || item.item_name] = [tax_rate, current_tax_amount]; tax.item_wise_tax_detail[item.item_code || item.item_name] = [tax_rate, current_tax_amount];
return current_tax_amount; return current_tax_amount;
}, },
_cleanup: function() { _cleanup: function() {
$.each(this.frm.tax_doclist, function(i, tax) { $.each(this.frm.tax_doclist, function(i, tax) {
$.each(["tax_amount_for_current_item", "grand_total_for_current_item", $.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]; }); function(i, fieldname) { delete tax[fieldname]; });
tax.item_wise_tax_detail = JSON.stringify(tax.item_wise_tax_detail); 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( this.frm.doc.total_advance = flt(frappe.utils.sum(
$.map(advance_doclist, function(adv) { return adv.allocated_amount }) $.map(advance_doclist, function(adv) { return adv.allocated_amount })
), precision("total_advance")); ), precision("total_advance"));
this.calculate_outstanding_amount(); this.calculate_outstanding_amount();
} }
}, },
_set_in_company_currency: function(item, print_field, base_field) { _set_in_company_currency: function(item, print_field, base_field) {
// set values in base currency // set values in base currency
item[base_field] = flt(item[print_field] * this.frm.doc.conversion_rate, item[base_field] = flt(item[print_field] * this.frm.doc.conversion_rate,
precision(base_field, item)); precision(base_field, item));
}, },
get_terms: function() { get_terms: function() {
var me = this; var me = this;
if(this.frm.doc.tc_name) { 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()); .appendTo($(this.frm.fields_dict.other_charges_calculation.wrapper).empty());
} }
}, },
}); });

View File

@ -27,7 +27,7 @@ class Customer(TransactionBase):
def validate_values(self): def validate_values(self):
if frappe.defaults.get_global_default('cust_master_name') == 'Naming Series' and not self.naming_series: 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): def validate(self):
self.validate_values() self.validate_values()
@ -66,7 +66,7 @@ class Customer(TransactionBase):
c.is_primary_contact = 1 c.is_primary_contact = 1
try: try:
c.save() c.save()
except NameError, e: except NameError:
pass pass
def on_update(self): def on_update(self):
@ -86,7 +86,7 @@ class Customer(TransactionBase):
def validate_name_with_customer_group(self): def validate_name_with_customer_group(self):
if frappe.db.exists("Customer Group", self.name): 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): def delete_customer_address(self):
addresses = frappe.db.sql("""select name, lead from `tabAddress` addresses = frappe.db.sql("""select name, lead from `tabAddress`

View File

@ -27,11 +27,11 @@ class Lead(SellingController):
self.set_status() self.set_status()
if self.source == 'Campaign' and not self.campaign_name and session['user'] != 'Guest': 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 self.email_id:
if not validate_email_add(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): def on_update(self):
self.check_email_id_is_unique() self.check_email_id_is_unique()

View File

@ -145,7 +145,7 @@ class SalesOrder(SellingController):
if quotation: if quotation:
doc = frappe.get_doc("Quotation", quotation) doc = frappe.get_doc("Quotation", quotation)
if doc.docstatus==2: if doc.docstatus==2:
frappe.throw(quotation + ": " + frappe._("Quotation is cancelled.")) frappe.throw(_("Quotation {0} is cancelled").format(quotation))
doc.set_status(update=True) doc.set_status(update=True)

View File

@ -17,6 +17,4 @@ class CustomerGroup(NestedSet):
def validate_name_with_customer(self): def validate_name_with_customer(self):
if frappe.db.exists("Customer", self.name): if frappe.db.exists("Customer", self.name):
frappe.msgprint(_("An Customer exists with same name (%s), \ frappe.msgprint(_("An Customer exists with same name"), raise_exception=1)
please change the Customer Group name or rename the Customer") %
self.name, raise_exception=1)

View File

@ -1,241 +1,241 @@
{ {
"creation": "2012-12-20 12:50:49.000000", "creation": "2012-12-20 12:50:49.000000",
"docstatus": 0, "docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"fields": [ "fields": [
{ {
"fieldname": "materials", "fieldname": "materials",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "Materials", "label": "Materials",
"permlevel": 0 "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.", "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", "fieldname": "fs_item_serial_nos",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Item Serial Nos", "label": "Item Serial Nos",
"permlevel": 0 "permlevel": 0
}, },
{ {
"description": "To track items in sales and purchase documents with batch nos<br><b>Preferred Industry: Chemicals etc</b>", "description": "To track items in sales and purchase documents with batch nos<br><b>Preferred Industry: Chemicals etc</b>",
"fieldname": "fs_item_batch_nos", "fieldname": "fs_item_batch_nos",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Item Batch Nos", "label": "Item Batch Nos",
"permlevel": 0 "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", "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", "fieldname": "fs_brands",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Brands", "label": "Brands",
"permlevel": 0 "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.", "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", "fieldname": "fs_item_barcode",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Item Barcode", "label": "Item Barcode",
"permlevel": 0 "permlevel": 0
}, },
{ {
"fieldname": "column_break0", "fieldname": "column_break0",
"fieldtype": "Column Break", "fieldtype": "Column Break",
"permlevel": 0 "permlevel": 0
}, },
{ {
"description": "1. To maintain the customer wise item code and to make them searchable based on their code use this option", "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", "fieldname": "fs_item_advanced",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Item Advanced", "label": "Item Advanced",
"permlevel": 0 "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", "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", "fieldname": "fs_packing_details",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Packing Details", "label": "Packing Details",
"permlevel": 0 "permlevel": 0
}, },
{ {
"description": "To get Item Group in details table", "description": "To get Item Group in details table",
"fieldname": "fs_item_group_in_details", "fieldname": "fs_item_group_in_details",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Item Groups in Details", "label": "Item Groups in Details",
"permlevel": 0 "permlevel": 0
}, },
{ {
"fieldname": "sales_and_purchase", "fieldname": "sales_and_purchase",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "Sales and Purchase", "label": "Sales and Purchase",
"permlevel": 0 "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.", "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", "fieldname": "fs_exports",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Exports", "label": "Exports",
"permlevel": 0 "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.", "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", "fieldname": "fs_imports",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Imports", "label": "Imports",
"permlevel": 0 "permlevel": 0
}, },
{ {
"fieldname": "column_break1", "fieldname": "column_break1",
"fieldtype": "Column Break", "fieldtype": "Column Break",
"permlevel": 0 "permlevel": 0
}, },
{ {
"description": "Field available in Delivery Note, Quotation, Sales Invoice, Sales Order", "description": "Field available in Delivery Note, Quotation, Sales Invoice, Sales Order",
"fieldname": "fs_discounts", "fieldname": "fs_discounts",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Sales Discounts", "label": "Sales Discounts",
"permlevel": 0 "permlevel": 0
}, },
{ {
"description": "Discount Fields will be available in Purchase Order, Purchase Receipt, Purchase Invoice", "description": "Discount Fields will be available in Purchase Order, Purchase Receipt, Purchase Invoice",
"fieldname": "fs_purchase_discounts", "fieldname": "fs_purchase_discounts",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Purchase Discounts", "label": "Purchase Discounts",
"permlevel": 0 "permlevel": 0
}, },
{ {
"description": "To track any installation or commissioning related work after sales", "description": "To track any installation or commissioning related work after sales",
"fieldname": "fs_after_sales_installations", "fieldname": "fs_after_sales_installations",
"fieldtype": "Check", "fieldtype": "Check",
"label": "After Sale Installations", "label": "After Sale Installations",
"permlevel": 0 "permlevel": 0
}, },
{ {
"description": "Available in \nBOM, Delivery Note, Purchase Invoice, Production Order, Purchase Order, Purchase Receipt, Sales Invoice, Sales Order, Stock Entry, Timesheet", "description": "Available in BOM, Delivery Note, Purchase Invoice, Production Order, Purchase Order, Purchase Receipt, Sales Invoice, Sales Order, Stock Entry, Timesheet",
"fieldname": "fs_projects", "fieldname": "fs_projects",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Projects", "label": "Projects",
"permlevel": 0 "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", "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", "fieldname": "fs_sales_extras",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Sales Extras", "label": "Sales Extras",
"permlevel": 0 "permlevel": 0
}, },
{ {
"fieldname": "accounts", "fieldname": "accounts",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "Accounts", "label": "Accounts",
"permlevel": 0 "permlevel": 0
}, },
{ {
"description": "Check if you need automatic recurring invoices. After submitting any sales invoice, Recurring section will be visible.", "description": "Check if you need automatic recurring invoices. After submitting any sales invoice, Recurring section will be visible.",
"fieldname": "fs_recurring_invoice", "fieldname": "fs_recurring_invoice",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Recurring Invoice", "label": "Recurring Invoice",
"permlevel": 0 "permlevel": 0
}, },
{ {
"fieldname": "column_break2", "fieldname": "column_break2",
"fieldtype": "Column Break", "fieldtype": "Column Break",
"permlevel": 0 "permlevel": 0
}, },
{ {
"description": "To enable <b>Point of Sale</b> features", "description": "To enable <b>Point of Sale</b> features",
"fieldname": "fs_pos", "fieldname": "fs_pos",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Point of Sale", "label": "Point of Sale",
"permlevel": 0 "permlevel": 0
}, },
{ {
"description": "To enable <b>Point of Sale</b> view", "description": "To enable <b>Point of Sale</b> view",
"fieldname": "fs_pos_view", "fieldname": "fs_pos_view",
"fieldtype": "Check", "fieldtype": "Check",
"label": "POS View", "label": "POS View",
"permlevel": 0 "permlevel": 0
}, },
{ {
"fieldname": "production", "fieldname": "production",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "Manufacturing", "label": "Manufacturing",
"permlevel": 0 "permlevel": 0
}, },
{ {
"description": "If you involve in manufacturing activity<br>\nEnables item <b>Is Manufactured</b>", "description": "If you involve in manufacturing activity. Enables Item 'Is Manufactured'",
"fieldname": "fs_manufacturing", "fieldname": "fs_manufacturing",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Manufacturing", "label": "Manufacturing",
"permlevel": 0 "permlevel": 0
}, },
{ {
"fieldname": "column_break3", "fieldname": "column_break3",
"fieldtype": "Column Break", "fieldtype": "Column Break",
"permlevel": 0 "permlevel": 0
}, },
{ {
"description": "If you follow Quality Inspection<br>\nEnables item QA Required and QA No in Purchase Receipt", "description": "If you follow Quality Inspection. Enables Item QA Required and QA No in Purchase Receipt",
"fieldname": "fs_quality", "fieldname": "fs_quality",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Quality", "label": "Quality",
"permlevel": 0 "permlevel": 0
}, },
{ {
"fieldname": "miscelleneous", "fieldname": "miscelleneous",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "Miscelleneous", "label": "Miscelleneous",
"permlevel": 0 "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", "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", "fieldname": "fs_page_break",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Page Break", "label": "Page Break",
"permlevel": 0 "permlevel": 0
}, },
{ {
"fieldname": "column_break4", "fieldname": "column_break4",
"fieldtype": "Column Break", "fieldtype": "Column Break",
"permlevel": 0 "permlevel": 0
}, },
{ {
"fieldname": "fs_more_info", "fieldname": "fs_more_info",
"fieldtype": "Check", "fieldtype": "Check",
"label": "More Info", "label": "More Info",
"permlevel": 0 "permlevel": 0
} }
], ],
"icon": "icon-glass", "icon": "icon-glass",
"idx": 1, "idx": 1,
"issingle": 1, "issingle": 1,
"modified": "2013-12-24 11:40:19.000000", "modified": "2013-12-24 11:40:19.000000",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Setup", "module": "Setup",
"name": "Features Setup", "name": "Features Setup",
"name_case": "Title Case", "name_case": "Title Case",
"owner": "Administrator", "owner": "Administrator",
"permissions": [ "permissions": [
{ {
"create": 1, "create": 1,
"email": 1, "email": 1,
"permlevel": 0, "permlevel": 0,
"print": 1, "print": 1,
"read": 1, "read": 1,
"report": 0, "report": 0,
"role": "System Manager", "role": "System Manager",
"submit": 0, "submit": 0,
"write": 1 "write": 1
}, },
{ {
"create": 1, "create": 1,
"email": 1, "email": 1,
"permlevel": 0, "permlevel": 0,
"print": 1, "print": 1,
"read": 1, "read": 1,
"report": 0, "report": 0,
"role": "Administrator", "role": "Administrator",
"submit": 0, "submit": 0,
"write": 1 "write": 1
} }
] ]
} }

View File

@ -15,11 +15,11 @@ class NamingSeries(Document):
return { return {
"transactions": "\n".join([''] + sorted(list(set( "transactions": "\n".join([''] + sorted(list(set(
frappe.db.sql_list("""select parent frappe.db.sql_list("""select parent
from `tabDocField` where fieldname='naming_series'""") from `tabDocField` where fieldname='naming_series'""")
+ frappe.db.sql_list("""select dt from `tabCustom Field` + frappe.db.sql_list("""select dt from `tabCustom Field`
where fieldname='naming_series'""") 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""")]) frappe.db.sql("""select name from tabSeries order by name""")])
} }
@ -86,16 +86,16 @@ class NamingSeries(Document):
dt = DocType() dt = DocType()
parent = list(set( parent = list(set(
frappe.db.sql_list("""select dt.name frappe.db.sql_list("""select dt.name
from `tabDocField` df, `tabDocType` dt from `tabDocField` df, `tabDocType` dt
where dt.name = df.parent and df.fieldname='naming_series' and dt.name != %s""", where dt.name = df.parent and df.fieldname='naming_series' and dt.name != %s""",
self.select_doc_for_series) self.select_doc_for_series)
+ frappe.db.sql_list("""select dt.name + frappe.db.sql_list("""select dt.name
from `tabCustom Field` df, `tabDocType` dt from `tabCustom Field` df, `tabDocType` dt
where dt.name = df.dt and df.fieldname='naming_series' and dt.name != %s""", where dt.name = df.dt and df.fieldname='naming_series' and dt.name != %s""",
self.select_doc_for_series) 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] for p in parent]
options = self.scrub_options_list(self.set_options.split("\n")) options = self.scrub_options_list(self.set_options.split("\n"))
for series in options: for series in options:
@ -104,19 +104,12 @@ class NamingSeries(Document):
if i[0]: if i[0]:
existing_series = [d.split('.')[0] for d in i[0].split("\n")] existing_series = [d.split('.')[0] for d in i[0].split("\n")]
if series.split(".")[0] in existing_series: if series.split(".")[0] in existing_series:
throw("{oops}! {sr} {series} {msg} {existing_series}. {select}".format(**{ frappe.throw(_("Series {0} already used in {1}").format(series,i[1]))
"oops": _("Oops"),
"sr": _("Series Name"),
"series": series,
"msg": _("is already in use in"),
"existing_series": i[1],
"select": _("Please select a new one")
}))
def validate_series_name(self, n): def validate_series_name(self, n):
import re import re
if not re.match("^[a-zA-Z0-9-/.#]*$", n): 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=''): def get_options(self, arg=''):
return frappe.get_meta(self.select_doc_for_series).get_field("naming_series").options 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): def get_current(self, arg=None):
"""get series current""" """get series current"""
if self.prefix: if self.prefix:
self.current_value = frappe.db.get_value("Series", self.current_value = frappe.db.get_value("Series",
self.prefix.split('.')[0], "current") self.prefix.split('.')[0], "current")
def insert_series(self, series): def insert_series(self, series):
@ -136,7 +129,7 @@ class NamingSeries(Document):
if self.prefix: if self.prefix:
prefix = self.prefix.split('.')[0] prefix = self.prefix.split('.')[0]
self.insert_series(prefix) 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)) (self.current_value, prefix))
msgprint(_("Series Updated Successfully")) msgprint(_("Series Updated Successfully"))
else: 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") make_property_setter(doctype, "naming_series", "reqd", 1, "Check")
# set values for mandatory # set values for mandatory
frappe.db.sql("""update `tab{doctype}` set naming_series={s} where frappe.db.sql("""update `tab{doctype}` set naming_series={s} where
ifnull(naming_series, '')=''""".format(doctype=doctype, s="%s"), ifnull(naming_series, '')=''""".format(doctype=doctype, s="%s"),
get_default_naming_series(doctype)) get_default_naming_series(doctype))
if hide_name_field: 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") make_property_setter(doctype, fieldname, "reqd", 1, "Check")
# set values for mandatory # 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)) ifnull({fieldname}, '')=''""".format(doctype=doctype, fieldname=fieldname))
def get_default_naming_series(doctype): def get_default_naming_series(doctype):
naming_series = frappe.get_meta(doctype).get_field("naming_series").options or "" naming_series = frappe.get_meta(doctype).get_field("naming_series").options or ""
naming_series = naming_series.split("\n") naming_series = naming_series.split("\n")
return naming_series[0] or naming_series[1] return naming_series[0] or naming_series[1]

View File

@ -10,19 +10,19 @@ from frappe.utils.nestedset import NestedSet
class SalesPerson(NestedSet): class SalesPerson(NestedSet):
nsm_parent_field = 'parent_sales_person'; nsm_parent_field = 'parent_sales_person';
def validate(self): def validate(self):
for d in self.get('target_details'): for d in self.get('target_details'):
if not flt(d.target_qty) and not flt(d.target_amount): if not flt(d.target_qty) and not flt(d.target_amount):
frappe.throw(_("Either target qty or target amount is mandatory.")) frappe.throw(_("Either target qty or target amount is mandatory."))
def on_update(self): def on_update(self):
super(SalesPerson, self).on_update() super(SalesPerson, self).on_update()
self.validate_one_root() self.validate_one_root()
def get_email_id(self): def get_email_id(self):
if self.employee: if self.employee:
user = frappe.db.get_value("Employee", self.employee, "user_id") user = frappe.db.get_value("Employee", self.employee, "user_id")
if not user: 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: else:
return frappe.db.get_value("User", user, "email") or user return frappe.db.get_value("User", user, "email") or user

View File

@ -11,8 +11,7 @@ def get_company_currency(company):
if not currency: if not currency:
currency = frappe.db.get_default("currency") currency = frappe.db.get_default("currency")
if not currency: if not currency:
throw(_('Please specify Default Currency in Company Master \ throw(_('Please specify Default Currency in Company Master and Global Defaults'))
and Global Defaults'))
return currency return currency

View File

@ -109,8 +109,7 @@ class Item(WebsiteGenerator):
self.is_pro_applicable = "No" self.is_pro_applicable = "No"
if self.is_pro_applicable == 'Yes' and self.is_stock_item == 'No': if self.is_pro_applicable == 'Yes' and self.is_stock_item == 'No':
frappe.throw(_("As Production Order can be made for this item, \ frappe.throw(_("As Production Order can be made for this item, it must be a stock item."))
it must be a stock item."))
if self.has_serial_no == 'Yes' and self.is_stock_item == 'No': 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) 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) and t2.docstatus = 1 and t1.docstatus =1 """, self.name)
if bom_mat and bom_mat[0][0]: if bom_mat and bom_mat[0][0]:
frappe.throw(_("Item must be a purchase item, \ frappe.throw(_("Item must be a purchase item, as it is present in one or many Active BOMs"))
as it is present in one or many Active BOMs"))
if self.is_manufactured_item != "Yes": if self.is_manufactured_item != "Yes":
bom = frappe.db.sql("""select name from `tabBOM` where item = %s bom = frappe.db.sql("""select name from `tabBOM` where item = %s
and is_active = 1""", (self.name,)) and is_active = 1""", (self.name,))
if bom and bom[0][0]: if bom and bom[0][0]:
frappe.throw(_("""Allow Bill of Materials should be 'Yes'. Because one or many \ frappe.throw(_("""Allow Bill of Materials should be 'Yes'. Because one or many active BOMs present for this item"""))
active BOMs present for this item"""))
def fill_customer_code(self): def fill_customer_code(self):
""" Append all the customer codes and insert into "customer_code" field of item table """ """ Append all the customer codes and insert into "customer_code" field of item table """

View File

@ -17,41 +17,27 @@ class ItemPrice(Document):
self.check_duplicate_item() self.check_duplicate_item()
self.update_price_list_details() self.update_price_list_details()
self.update_item_details() self.update_item_details()
def validate_item(self): def validate_item(self):
if not frappe.db.exists("Item", self.item_code): if not frappe.db.exists("Item", self.item_code):
throw("{doctype}: {item} {not_found}".format(**{ throw(_("Item {0} not found").format(self.item_code))
"doctype": _("Item"),
"item": self.item_code,
"not_found": _(" not found")
}))
def validate_price_list(self): def validate_price_list(self):
enabled = frappe.db.get_value("Price List", self.price_list, "enabled") enabled = frappe.db.get_value("Price List", self.price_list, "enabled")
if not enabled: if not enabled:
throw("{message}: {price_list} {disabled}".format(**{ throw(_("Price List {0} is disabled").format(self.price_list))
"message": _("Price List"),
"price_list": self.price_list,
"disabled": _("is disabled.")
}))
def check_duplicate_item(self): def check_duplicate_item(self):
if frappe.db.sql("""select name from `tabItem Price` if frappe.db.sql("""select name from `tabItem Price`
where item_code=%s and price_list=%s and name!=%s""", where item_code=%s and price_list=%s and name!=%s""",
(self.item_code, self.price_list, self.name)): (self.item_code, self.price_list, self.name)):
throw("{duplicate_item}: {item_code}, {already}: {price_list}".format(**{ frappe.throw(_("Item {0} appears multiple times in Price List {1}").format(self.item_code, self.price_list))
"duplicate_item": _("Duplicate Item"),
"item_code": self.item_code,
"already": _("already available in Price List"),
"price_list": self.price_list
}), ItemPriceDuplicateItem)
def update_price_list_details(self): def update_price_list_details(self):
self.buying, self.selling, self.currency = \ 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"]) ["buying", "selling", "currency"])
def update_item_details(self): 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"]) self.item_code, ["item_name", "description"])

View File

@ -8,7 +8,7 @@ from __future__ import unicode_literals
import frappe import frappe
from frappe.utils import cstr, flt from frappe.utils import cstr, flt
from frappe import msgprint, _ from frappe import _
from erpnext.controllers.buying_controller import BuyingController from erpnext.controllers.buying_controller import BuyingController
class MaterialRequest(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 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): 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\ 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))
\n Anyway, you can add more qty in new row for the same item."
% (actual_so_qty - already_indented, item, so_no))
def validate_schedule_date(self): def validate_schedule_date(self):
for d in self.get('indent_details'): for d in self.get('indent_details'):

View File

@ -27,8 +27,7 @@ class SerialNo(StockController):
def validate(self): def validate(self):
if self.get("__islocal") and self.warehouse: if self.get("__islocal") and self.warehouse:
frappe.throw(_("New Serial No cannot have Warehouse. Warehouse must be \ frappe.throw(_("New Serial No cannot have Warehouse. Warehouse must be set by Stock Entry or Purchase Receipt"), SerialNoCannotCreateDirectError)
set by Stock Entry or Purchase Receipt"), SerialNoCannotCreateDirectError)
self.set_maintenance_status() self.set_maintenance_status()
self.validate_warehouse() self.validate_warehouse()
@ -166,8 +165,7 @@ class SerialNo(StockController):
if self.status == 'Delivered': if self.status == 'Delivered':
frappe.throw(_("Delivered Serial No ") + self.name + _(" can not be deleted")) frappe.throw(_("Delivered Serial No ") + self.name + _(" can not be deleted"))
if self.warehouse: if self.warehouse:
frappe.throw(_("Cannot delete Serial No in warehouse. \ frappe.throw(_("Cannot delete Serial No in warehouse. First remove from warehouse, then delete.") + ": " + self.name)
First remove from warehouse, then delete.") + ": " + self.name)
def before_rename(self, old, new, merge=False): def before_rename(self, old, new, merge=False):
if merge: if merge:

View File

@ -14,45 +14,45 @@ class StockReconciliation(StockController):
def setup(self): def setup(self):
self.head_row = ["Item Code", "Warehouse", "Quantity", "Valuation Rate"] self.head_row = ["Item Code", "Warehouse", "Quantity", "Valuation Rate"]
self.entries = [] self.entries = []
def validate(self): def validate(self):
self.validate_data() self.validate_data()
self.validate_expense_account() self.validate_expense_account()
def on_submit(self): def on_submit(self):
self.insert_stock_ledger_entries() self.insert_stock_ledger_entries()
self.make_gl_entries() self.make_gl_entries()
def on_cancel(self): def on_cancel(self):
self.delete_and_repost_sle() self.delete_and_repost_sle()
self.make_cancel_gl_entries() self.make_cancel_gl_entries()
def validate_data(self): def validate_data(self):
if not self.reconciliation_json: if not self.reconciliation_json:
return return
data = json.loads(self.reconciliation_json) data = json.loads(self.reconciliation_json)
# strip out extra columns (if any) # strip out extra columns (if any)
data = [row[:4] for row in data] data = [row[:4] for row in data]
if self.head_row not in data: if self.head_row not in data:
msgprint(_("""Wrong Template: Unable to find head row."""), msgprint(_("""Wrong Template: Unable to find head row."""),
raise_exception=1) raise_exception=1)
# remove the help part and save the json # remove the help part and save the json
head_row_no = 0 head_row_no = 0
if data.index(self.head_row) != 0: if data.index(self.head_row) != 0:
head_row_no = data.index(self.head_row) head_row_no = data.index(self.head_row)
data = data[head_row_no:] data = data[head_row_no:]
self.reconciliation_json = json.dumps(data) self.reconciliation_json = json.dumps(data)
def _get_msg(row_num, msg): def _get_msg(row_num, msg):
return _("Row # ") + ("%d: " % (row_num+head_row_no+2)) + _(msg) return _("Row # ") + ("%d: " % (row_num+head_row_no+2)) + _(msg)
self.validation_messages = [] self.validation_messages = []
item_warehouse_combinations = [] item_warehouse_combinations = []
# validate no of rows # validate no of rows
rows = data[1:] rows = data[1:]
if len(rows) > 100: if len(rows) > 100:
@ -64,69 +64,66 @@ class StockReconciliation(StockController):
self.validation_messages.append(_get_msg(row_num, "Duplicate entry")) self.validation_messages.append(_get_msg(row_num, "Duplicate entry"))
else: else:
item_warehouse_combinations.append([row[0], row[1]]) item_warehouse_combinations.append([row[0], row[1]])
self.validate_item(row[0], row_num+head_row_no+2) self.validate_item(row[0], row_num+head_row_no+2)
# note: warehouse will be validated through link validation # note: warehouse will be validated through link validation
# if both not specified # if both not specified
if row[2] == "" and row[3] == "": if row[2] == "" and row[3] == "":
self.validation_messages.append(_get_msg(row_num, self.validation_messages.append(_get_msg(row_num,
"Please specify either Quantity or Valuation Rate or both")) "Please specify either Quantity or Valuation Rate or both"))
# do not allow negative quantity # do not allow negative quantity
if flt(row[2]) < 0: 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")) "Negative Quantity is not allowed"))
# do not allow negative valuation # do not allow negative valuation
if flt(row[3]) < 0: 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")) "Negative Valuation Rate is not allowed"))
# throw all validation messages # throw all validation messages
if self.validation_messages: if self.validation_messages:
for msg in self.validation_messages: for msg in self.validation_messages:
msgprint(msg) msgprint(msg)
raise frappe.ValidationError raise frappe.ValidationError
def validate_item(self, item_code, row_num): def validate_item(self, item_code, row_num):
from erpnext.stock.doctype.item.item import validate_end_of_life, \ from erpnext.stock.doctype.item.item import validate_end_of_life, \
validate_is_stock_item, validate_cancelled_item validate_is_stock_item, validate_cancelled_item
# using try except to catch all validation msgs and display together # using try except to catch all validation msgs and display together
try: try:
item = frappe.get_doc("Item", item_code) item = frappe.get_doc("Item", item_code)
# end of life and stock item # end of life and stock item
validate_end_of_life(item_code, item.end_of_life, verbose=0) validate_end_of_life(item_code, item.end_of_life, verbose=0)
validate_is_stock_item(item_code, item.is_stock_item, verbose=0) validate_is_stock_item(item_code, item.is_stock_item, verbose=0)
# item should not be serialized # item should not be serialized
if item.has_serial_no == "Yes": if item.has_serial_no == "Yes":
raise frappe.ValidationError, (_("Serialized Item: '") + item_code + raise frappe.ValidationError, _("Serialized Item {0} cannot be updated using Stock Reconciliation").format(item_code)
_("""' can not be managed using Stock Reconciliation.\
You can add/delete Serial No directly, \
to modify stock of this item."""))
# docstatus should be < 2 # docstatus should be < 2
validate_cancelled_item(item_code, item.docstatus, verbose=0) validate_cancelled_item(item_code, item.docstatus, verbose=0)
except Exception, e: except Exception, e:
self.validation_messages.append(_("Row # ") + ("%d: " % (row_num)) + cstr(e)) self.validation_messages.append(_("Row # ") + ("%d: " % (row_num)) + cstr(e))
def insert_stock_ledger_entries(self): def insert_stock_ledger_entries(self):
""" find difference between current and expected entries """ find difference between current and expected entries
and create stock ledger entries based on the difference""" and create stock ledger entries based on the difference"""
from erpnext.stock.utils import get_valuation_method from erpnext.stock.utils import get_valuation_method
from erpnext.stock.stock_ledger import get_previous_sle from erpnext.stock.stock_ledger import get_previous_sle
row_template = ["item_code", "warehouse", "qty", "valuation_rate"] row_template = ["item_code", "warehouse", "qty", "valuation_rate"]
if not self.reconciliation_json: if not self.reconciliation_json:
msgprint(_("""Stock Reconciliation file not uploaded"""), raise_exception=1) msgprint(_("""Stock Reconciliation file not uploaded"""), raise_exception=1)
data = json.loads(self.reconciliation_json) data = json.loads(self.reconciliation_json)
for row_num, row in enumerate(data[data.index(self.head_row)+1:]): for row_num, row in enumerate(data[data.index(self.head_row)+1:]):
row = frappe._dict(zip(row_template, row)) row = frappe._dict(zip(row_template, row))
@ -141,22 +138,22 @@ class StockReconciliation(StockController):
# check valuation rate mandatory # check valuation rate mandatory
if row.qty != "" and not row.valuation_rate and \ if row.qty != "" and not row.valuation_rate and \
flt(previous_sle.get("qty_after_transaction")) <= 0: 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 + _(" at warehouse: ") + row.warehouse +
_(" is less than equals to zero in the system, valuation rate is mandatory for this item")) _(" is less than equals to zero in the system, valuation rate is mandatory for this item"))
change_in_qty = row.qty != "" and \ change_in_qty = row.qty != "" and \
(flt(row.qty) - flt(previous_sle.get("qty_after_transaction"))) (flt(row.qty) - flt(previous_sle.get("qty_after_transaction")))
change_in_rate = row.valuation_rate != "" and \ change_in_rate = row.valuation_rate != "" and \
(flt(row.valuation_rate) - flt(previous_sle.get("valuation_rate"))) (flt(row.valuation_rate) - flt(previous_sle.get("valuation_rate")))
if get_valuation_method(row.item_code) == "Moving Average": if get_valuation_method(row.item_code) == "Moving Average":
self.sle_for_moving_avg(row, previous_sle, change_in_qty, change_in_rate) self.sle_for_moving_avg(row, previous_sle, change_in_qty, change_in_rate)
else: else:
self.sle_for_fifo(row, previous_sle, change_in_qty, change_in_rate) 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): def sle_for_moving_avg(self, row, previous_sle, change_in_qty, change_in_rate):
"""Insert Stock Ledger Entries for Moving Average valuation""" """Insert Stock Ledger Entries for Moving Average valuation"""
def _get_incoming_rate(qty, valuation_rate, previous_qty, previous_valuation_rate): def _get_incoming_rate(qty, valuation_rate, previous_qty, previous_valuation_rate):
@ -167,73 +164,73 @@ class StockReconciliation(StockController):
valuation_rate = previous_valuation_rate valuation_rate = previous_valuation_rate
return (qty * valuation_rate - previous_qty * previous_valuation_rate) \ return (qty * valuation_rate - previous_qty * previous_valuation_rate) \
/ flt(qty - previous_qty) / flt(qty - previous_qty)
if change_in_qty: if change_in_qty:
# if change in qty, irrespective of change in rate # if change in qty, irrespective of change in rate
incoming_rate = _get_incoming_rate(flt(row.qty), flt(row.valuation_rate), incoming_rate = _get_incoming_rate(flt(row.qty), flt(row.valuation_rate),
flt(previous_sle.get("qty_after_transaction")), flt(previous_sle.get("qty_after_transaction")),
flt(previous_sle.get("valuation_rate"))) flt(previous_sle.get("valuation_rate")))
row["voucher_detail_no"] = "Row: " + cstr(row.row_num) + "/Actual Entry" row["voucher_detail_no"] = "Row: " + cstr(row.row_num) + "/Actual Entry"
self.insert_entries({"actual_qty": change_in_qty, "incoming_rate": incoming_rate}, row) 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: 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 # and positive actual stock before this reconciliation
incoming_rate = _get_incoming_rate( incoming_rate = _get_incoming_rate(
flt(previous_sle.get("qty_after_transaction"))+1, flt(row.valuation_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"))) flt(previous_sle.get("valuation_rate")))
# +1 entry # +1 entry
row["voucher_detail_no"] = "Row: " + cstr(row.row_num) + "/Valuation Adjustment +1" row["voucher_detail_no"] = "Row: " + cstr(row.row_num) + "/Valuation Adjustment +1"
self.insert_entries({"actual_qty": 1, "incoming_rate": incoming_rate}, row) self.insert_entries({"actual_qty": 1, "incoming_rate": incoming_rate}, row)
# -1 entry # -1 entry
row["voucher_detail_no"] = "Row: " + cstr(row.row_num) + "/Valuation Adjustment -1" row["voucher_detail_no"] = "Row: " + cstr(row.row_num) + "/Valuation Adjustment -1"
self.insert_entries({"actual_qty": -1}, row) self.insert_entries({"actual_qty": -1}, row)
def sle_for_fifo(self, row, previous_sle, change_in_qty, change_in_rate): def sle_for_fifo(self, row, previous_sle, change_in_qty, change_in_rate):
"""Insert Stock Ledger Entries for FIFO valuation""" """Insert Stock Ledger Entries for FIFO valuation"""
previous_stock_queue = json.loads(previous_sle.get("stock_queue") or "[]") 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_qty = sum((batch[0] for batch in previous_stock_queue))
previous_stock_value = sum((batch[0] * batch[1] for batch in \ previous_stock_value = sum((batch[0] * batch[1] for batch in \
previous_stock_queue)) previous_stock_queue))
def _insert_entries(): def _insert_entries():
if previous_stock_queue != [[row.qty, row.valuation_rate]]: if previous_stock_queue != [[row.qty, row.valuation_rate]]:
# make entry as per attachment # make entry as per attachment
if row.qty: if row.qty:
row["voucher_detail_no"] = "Row: " + cstr(row.row_num) + "/Actual Entry" 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) "incoming_rate": flt(row.valuation_rate)}, row)
# Make reverse entry # Make reverse entry
if previous_stock_qty: if previous_stock_qty:
row["voucher_detail_no"] = "Row: " + cstr(row.row_num) + "/Reverse Entry" row["voucher_detail_no"] = "Row: " + cstr(row.row_num) + "/Reverse Entry"
self.insert_entries({"actual_qty": -1 * previous_stock_qty, self.insert_entries({"actual_qty": -1 * previous_stock_qty,
"incoming_rate": previous_stock_qty < 0 and "incoming_rate": previous_stock_qty < 0 and
flt(row.valuation_rate) or 0}, row) flt(row.valuation_rate) or 0}, row)
if change_in_qty: if change_in_qty:
if row.valuation_rate == "": if row.valuation_rate == "":
# dont want change in valuation # dont want change in valuation
if previous_stock_qty > 0: if previous_stock_qty > 0:
# set valuation_rate as previous valuation_rate # set valuation_rate as previous valuation_rate
row.valuation_rate = previous_stock_value / flt(previous_stock_qty) row.valuation_rate = previous_stock_value / flt(previous_stock_qty)
_insert_entries() _insert_entries()
elif change_in_rate and previous_stock_qty > 0: 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 # and positive actual stock before this reconciliation
row.qty = previous_stock_qty row.qty = previous_stock_qty
_insert_entries() _insert_entries()
def insert_entries(self, opts, row): def insert_entries(self, opts, row):
"""Insert Stock Ledger Entries""" """Insert Stock Ledger Entries"""
args = frappe._dict({ args = frappe._dict({
"doctype": "Stock Ledger Entry", "doctype": "Stock Ledger Entry",
"item_code": row.item_code, "item_code": row.item_code,
@ -253,19 +250,19 @@ class StockReconciliation(StockController):
# append to entries # append to entries
self.entries.append(args) self.entries.append(args)
def delete_and_repost_sle(self): def delete_and_repost_sle(self):
""" Delete Stock Ledger Entries related to this voucher """ Delete Stock Ledger Entries related to this voucher
and repost future Stock Ledger Entries""" and repost future Stock Ledger Entries"""
existing_entries = frappe.db.sql("""select distinct item_code, warehouse existing_entries = frappe.db.sql("""select distinct item_code, warehouse
from `tabStock Ledger Entry` where voucher_type=%s and voucher_no=%s""", from `tabStock Ledger Entry` where voucher_type=%s and voucher_no=%s""",
(self.doctype, self.name), as_dict=1) (self.doctype, self.name), as_dict=1)
# delete entries # 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)) where voucher_type=%s and voucher_no=%s""", (self.doctype, self.name))
# repost future entries for selected item_code, warehouse # repost future entries for selected item_code, warehouse
for entries in existing_entries: for entries in existing_entries:
update_entries_after({ update_entries_after({
@ -274,29 +271,26 @@ class StockReconciliation(StockController):
"posting_date": self.posting_date, "posting_date": self.posting_date,
"posting_time": self.posting_time "posting_time": self.posting_time
}) })
def get_gl_entries(self, warehouse_account=None): def get_gl_entries(self, warehouse_account=None):
if not self.cost_center: if not self.cost_center:
msgprint(_("Please enter Cost Center"), raise_exception=1) 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) self.expense_account, self.cost_center)
def validate_expense_account(self): def validate_expense_account(self):
if not cint(frappe.defaults.get_global_default("auto_accounting_for_stock")): if not cint(frappe.defaults.get_global_default("auto_accounting_for_stock")):
return return
if not self.expense_account: if not self.expense_account:
msgprint(_("Please enter Expense Account"), raise_exception=1) msgprint(_("Please enter Expense Account"), raise_exception=1)
elif not frappe.db.sql("""select * from `tabStock Ledger Entry`"""): 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": "report_type") == "Profit and Loss":
msgprint(_("""Expense Account can not be a PL Account, as this stock \ frappe.throw(_("'Profit and Loss' type Account {0} used be set for Opening Entry").format(self.expense_account))
reconciliation is an opening entry. \
Please select 'Temporary Account (Liabilities)' or relevant account"""),
raise_exception=1)
@frappe.whitelist() @frappe.whitelist()
def upload(): def upload():
from frappe.utils.datautils import read_csv_content_from_uploaded_file 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()

View File

@ -65,8 +65,7 @@ class Warehouse(Document):
if parent_account: if parent_account:
self.create_account_under = parent_account self.create_account_under = parent_account
else: else:
frappe.throw(_("Please enter account group under which account \ frappe.throw(_("Please enter parent account group for warehouse account"))
for warehouse ") + self.name +_(" will be created"))
def on_trash(self): def on_trash(self):
# delete bin # delete bin
@ -75,8 +74,7 @@ class Warehouse(Document):
for d in bins: for d in bins:
if d['actual_qty'] or d['reserved_qty'] or d['ordered_qty'] or \ if d['actual_qty'] or d['reserved_qty'] or d['ordered_qty'] or \
d['indented_qty'] or d['projected_qty'] or d['planned_qty']: d['indented_qty'] or d['projected_qty'] or d['planned_qty']:
throw("""Warehouse: %s can not be deleted as qty exists for item: %s""" throw(_("Warehouse {0} can not be deleted as quantity exists for Item {1}").format(self.name, d['item_code']))
% (self.name, d['item_code']))
else: else:
frappe.db.sql("delete from `tabBin` where name = %s", d['name']) 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` if frappe.db.sql("""select name from `tabStock Ledger Entry`
where warehouse = %s""", self.name): where warehouse = %s""", self.name):
throw(_("""Warehouse can not be deleted as stock ledger entry throw(_("Warehouse can not be deleted as stock ledger entry exists for this warehouse."))
exists for this warehouse."""))
def before_rename(self, olddn, newdn, merge=False): def before_rename(self, olddn, newdn, merge=False):
# Add company abbr if not provided # Add company abbr if not provided

View File

@ -11,19 +11,19 @@ from erpnext.utilities.transaction_base import TransactionBase, delete_events
from erpnext.stock.utils import get_valid_serial_nos from erpnext.stock.utils import get_valid_serial_nos
class MaintenanceSchedule(TransactionBase): class MaintenanceSchedule(TransactionBase):
def get_item_details(self, item_code): 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) where name=%s""", (item_code), as_dict=1)
ret = { ret = {
'item_name': item and item[0]['item_name'] or '', 'item_name': item and item[0]['item_name'] or '',
'description' : item and item[0]['description'] or '' 'description' : item and item[0]['description'] or ''
} }
return ret return ret
def generate_schedule(self): def generate_schedule(self):
self.set('maintenance_schedule_detail', []) 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)) where parent=%s""", (self.name))
count = 1 count = 1
for d in self.get('item_maintenance_detail'): for d in self.get('item_maintenance_detail'):
@ -41,7 +41,7 @@ class MaintenanceSchedule(TransactionBase):
count = count + 1 count = count + 1
child.sales_person = d.sales_person child.sales_person = d.sales_person
child.save(1) child.save(1)
self.on_update() self.on_update()
def on_submit(self): def on_submit(self):
@ -61,8 +61,8 @@ class MaintenanceSchedule(TransactionBase):
sp = frappe.get_doc("Sales Person", d.sales_person) sp = frappe.get_doc("Sales Person", d.sales_person)
email_map[d.sales_person] = sp.get_email_id() email_map[d.sales_person] = sp.get_email_id()
scheduled_date = frappe.db.sql("""select scheduled_date from scheduled_date = frappe.db.sql("""select scheduled_date from
`tabMaintenance Schedule Detail` where sales_person=%s and item_code=%s and `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) parent=%s""", (d.sales_person, d.item_code, self.name), as_dict=1)
for key in scheduled_date: for key in scheduled_date:
@ -80,10 +80,10 @@ class MaintenanceSchedule(TransactionBase):
"ref_name": self.name "ref_name": self.name
}).insert(ignore_permissions=1) }).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): def create_schedule_list(self, start_date, end_date, no_of_visit, sales_person):
schedule_list = [] schedule_list = []
start_date_copy = start_date start_date_copy = start_date
date_diff = (getdate(end_date) - getdate(start_date)).days date_diff = (getdate(end_date) - getdate(start_date)).days
add_by = date_diff / no_of_visit add_by = date_diff / no_of_visit
@ -92,7 +92,7 @@ class MaintenanceSchedule(TransactionBase):
if (getdate(start_date_copy) < getdate(end_date)): if (getdate(start_date_copy) < getdate(end_date)):
start_date_copy = add_days(start_date_copy, add_by) start_date_copy = add_days(start_date_copy, add_by)
if len(schedule_list) < no_of_visit: 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) sales_person)
if schedule_date > getdate(end_date): if schedule_date > getdate(end_date):
schedule_date = getdate(end_date) schedule_date = getdate(end_date)
@ -112,17 +112,17 @@ class MaintenanceSchedule(TransactionBase):
if fy_details and fy_details[0]: if fy_details and fy_details[0]:
# check holiday list in employee master # check holiday list in employee master
holiday_list = frappe.db.sql_list("""select h.holiday_date from `tabEmployee` emp, holiday_list = frappe.db.sql_list("""select h.holiday_date from `tabEmployee` emp,
`tabSales Person` sp, `tabHoliday` h, `tabHoliday List` hl `tabSales Person` sp, `tabHoliday` h, `tabHoliday List` hl
where sp.name=%s and emp.name=sp.employee where sp.name=%s and emp.name=sp.employee
and hl.name=emp.holiday_list and and hl.name=emp.holiday_list and
h.parent=hl.name and h.parent=hl.name and
hl.fiscal_year=%s""", (sales_person, fy_details[0])) hl.fiscal_year=%s""", (sales_person, fy_details[0]))
if not holiday_list: if not holiday_list:
# check global holiday list # check global holiday list
holiday_list = frappe.db.sql("""select h.holiday_date from holiday_list = frappe.db.sql("""select h.holiday_date from
`tabHoliday` h, `tabHoliday List` hl `tabHoliday` h, `tabHoliday List` hl
where h.parent=hl.name and ifnull(hl.is_default, 0) = 1 where h.parent=hl.name and ifnull(hl.is_default, 0) = 1
and hl.fiscal_year=%s""", fy_details[0]) and hl.fiscal_year=%s""", fy_details[0])
if not validated and holiday_list: if not validated and holiday_list:
@ -140,14 +140,14 @@ class MaintenanceSchedule(TransactionBase):
period = (getdate(args['end_date']) - getdate(args['start_date'])).days + 1 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: args['periodicity'] == 'Quarterly') and period < 365:
throw(cstr(args['periodicity']) + " periodicity can be set for period of atleast 1 year or more only") throw(cstr(args['periodicity']) + " periodicity can be set for period of atleast 1 year or more only")
elif args['periodicity'] == 'Monthly' and period < 30: elif args['periodicity'] == 'Monthly' and period < 30:
throw("Monthly periodicity can be set for period of atleast 1 month or more") throw("Monthly periodicity can be set for period of atleast 1 month or more")
elif args['periodicity'] == 'Weekly' and period < 7: elif args['periodicity'] == 'Weekly' and period < 7:
throw("Weekly periodicity can be set for period of atleast 1 week or more") throw("Weekly periodicity can be set for period of atleast 1 week or more")
def get_no_of_visits(self, arg): def get_no_of_visits(self, arg):
args = eval(arg) args = eval(arg)
self.validate_period(arg) self.validate_period(arg)
@ -159,19 +159,19 @@ class MaintenanceSchedule(TransactionBase):
elif args['periodicity'] == 'Monthly': elif args['periodicity'] == 'Monthly':
count = period/30 count = period/30
elif args['periodicity'] == 'Quarterly': elif args['periodicity'] == 'Quarterly':
count = period/91 count = period/91
elif args['periodicity'] == 'Half Yearly': elif args['periodicity'] == 'Half Yearly':
count = period/182 count = period/182
elif args['periodicity'] == 'Yearly': elif args['periodicity'] == 'Yearly':
count = period/365 count = period/365
ret = {'no_of_visits' : count} ret = {'no_of_visits' : count}
return ret return ret
def validate_maintenance_detail(self): def validate_maintenance_detail(self):
if not self.get('item_maintenance_detail'): if not self.get('item_maintenance_detail'):
throw(_("Please enter Maintaince Details first")) throw(_("Please enter Maintaince Details first"))
for d in self.get('item_maintenance_detail'): for d in self.get('item_maintenance_detail'):
if not d.item_code: if not d.item_code:
throw(_("Please select item code")) throw(_("Please select item code"))
@ -181,23 +181,23 @@ class MaintenanceSchedule(TransactionBase):
throw(_("Please mention no of visits required")) throw(_("Please mention no of visits required"))
elif not d.sales_person: elif not d.sales_person:
throw(_("Please select Incharge Person's name")) throw(_("Please select Incharge Person's name"))
if getdate(d.start_date) >= getdate(d.end_date): if getdate(d.start_date) >= getdate(d.end_date):
throw(_("Start date should be less than end date for item") + " " + d.item_code) throw(_("Start date should be less than end date for item") + " " + d.item_code)
def validate_sales_order(self): def validate_sales_order(self):
for d in self.get('item_maintenance_detail'): for d in self.get('item_maintenance_detail'):
if d.prevdoc_docname: if d.prevdoc_docname:
chk = frappe.db.sql("""select ms.name from `tabMaintenance Schedule` ms, chk = frappe.db.sql("""select ms.name from `tabMaintenance Schedule` ms,
`tabMaintenance Schedule Item` msi where msi.parent=ms.name and `tabMaintenance Schedule Item` msi where msi.parent=ms.name and
msi.prevdoc_docname=%s and ms.docstatus=1""", d.prevdoc_docname) msi.prevdoc_docname=%s and ms.docstatus=1""", d.prevdoc_docname)
if chk: if chk:
throw("Maintenance Schedule against " + d.prevdoc_docname + " already exist") throw("Maintenance Schedule against " + d.prevdoc_docname + " already exist")
def validate(self): def validate(self):
self.validate_maintenance_detail() self.validate_maintenance_detail()
self.validate_sales_order() self.validate_sales_order()
def on_update(self): def on_update(self):
frappe.db.set(self, 'status', 'Draft') frappe.db.set(self, 'status', 'Draft')
@ -209,21 +209,20 @@ class MaintenanceSchedule(TransactionBase):
def validate_serial_no(self, serial_nos, amc_start_date): def validate_serial_no(self, serial_nos, amc_start_date):
for serial_no in serial_nos: 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) ["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: 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)) 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: 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. throw("""Serial No: %s is already under AMC upto %s.
Please check AMC Start Date.""" % (serial_no, sr_details.amc_expiry_date)) Please check AMC Start Date.""" % (serial_no, sr_details.amc_expiry_date))
if sr_details.status=="Delivered" and sr_details.delivery_date and \ if sr_details.status=="Delivered" and sr_details.delivery_date and \
sr_details.delivery_date >= amc_start_date: sr_details.delivery_date >= amc_start_date:
throw(_("Maintenance start date can not be before \ throw(_("Maintenance start date can not be before delivery date for Serial No {0}").format(serial_no))
delivery date for serial no: ") + serial_no)
def validate_schedule(self): def validate_schedule(self):
item_lst1 =[] item_lst1 =[]
@ -231,26 +230,25 @@ class MaintenanceSchedule(TransactionBase):
for d in self.get('item_maintenance_detail'): for d in self.get('item_maintenance_detail'):
if d.item_code not in item_lst1: if d.item_code not in item_lst1:
item_lst1.append(d.item_code) item_lst1.append(d.item_code)
for m in self.get('maintenance_schedule_detail'): for m in self.get('maintenance_schedule_detail'):
if m.item_code not in item_lst2: if m.item_code not in item_lst2:
item_lst2.append(m.item_code) item_lst2.append(m.item_code)
if len(item_lst1) != len(item_lst2): if len(item_lst1) != len(item_lst2):
throw(_("Maintenance Schedule is not generated for all the items. \ throw(_("Maintenance Schedule is not generated for all the items. Please click on 'Generate Schedule'"))
Please click on 'Generate Schedule'"))
else: else:
for x in item_lst1: for x in item_lst1:
if x not in item_lst2: 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'")) _(". Please click on 'Generate Schedule'"))
def check_serial_no_added(self): def check_serial_no_added(self):
serial_present =[] serial_present =[]
for d in self.get('item_maintenance_detail'): for d in self.get('item_maintenance_detail'):
if d.serial_no: if d.serial_no:
serial_present.append(d.item_code) serial_present.append(d.item_code)
for m in self.get('maintenance_schedule_detail'): for m in self.get('maintenance_schedule_detail'):
if serial_present: if serial_present:
if m.item_code in serial_present and not m.serial_no: if m.item_code in serial_present and not m.serial_no:
@ -270,13 +268,13 @@ class MaintenanceSchedule(TransactionBase):
@frappe.whitelist() @frappe.whitelist()
def make_maintenance_visit(source_name, target_doc=None): def make_maintenance_visit(source_name, target_doc=None):
from frappe.model.mapper import get_mapped_doc from frappe.model.mapper import get_mapped_doc
def update_status(source, target, parent): def update_status(source, target, parent):
target.maintenance_type = "Scheduled" target.maintenance_type = "Scheduled"
doclist = get_mapped_doc("Maintenance Schedule", source_name, { doclist = get_mapped_doc("Maintenance Schedule", source_name, {
"Maintenance Schedule": { "Maintenance Schedule": {
"doctype": "Maintenance Visit", "doctype": "Maintenance Visit",
"field_map": { "field_map": {
"name": "maintenance_schedule" "name": "maintenance_schedule"
}, },
@ -284,15 +282,15 @@ def make_maintenance_visit(source_name, target_doc=None):
"docstatus": ["=", 1] "docstatus": ["=", 1]
}, },
"postprocess": update_status "postprocess": update_status
}, },
"Maintenance Schedule Item": { "Maintenance Schedule Item": {
"doctype": "Maintenance Visit Purpose", "doctype": "Maintenance Visit Purpose",
"field_map": { "field_map": {
"parent": "prevdoc_docname", "parent": "prevdoc_docname",
"parenttype": "prevdoc_doctype", "parenttype": "prevdoc_doctype",
"sales_person": "service_person" "sales_person": "service_person"
} }
} }
}, target_doc) }, target_doc)
return doclist return doclist