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

View File

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

View File

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

View File

@ -147,7 +147,7 @@ class PurchaseCommon(BuyingController):
stopped = frappe.db.sql("""select name from `tab%s` where name = %s and
status = 'Stopped'""" % (doctype, '%s'), docname)
if stopped:
frappe.throw("{0} {1} status is 'Stopped'".format(doctype, docname), frappe.InvalidStatusError)
frappe.throw(_("{0} {1} status is 'Stopped'").format(doctype, docname), frappe.InvalidStatusError)
def check_docstatus(self, check, doctype, docname, detail_doctype = ''):
if check == 'Next':

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

View File

@ -63,7 +63,7 @@ class LeaveAllocation(Document):
cf = cf and cint(cf[0][0]) or 0
if not cf:
frappe.db.set(self,'carry_forward',0)
frappe.throw("Cannot carry forward {0}".format(self.leave_type))
frappe.throw(_("Cannot carry forward {0}").format(self.leave_type))
def get_carry_forwarded_leaves(self):
if self.carry_forward:

View File

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

View File

@ -130,7 +130,7 @@ class SalarySlip(TransactionBase):
(self.month, self.fiscal_year, self.employee, self.name))
if ret_exist:
self.employee = ''
frappe.throw("Salary Slip of employee {0} already created for this month".format(self.employee))
frappe.throw(_("Salary Slip of employee {0} already created for this month").format(self.employee))
def validate(self):
from frappe.utils import money_in_words

View File

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

View File

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

View File

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

View File

@ -27,7 +27,7 @@ class Customer(TransactionBase):
def validate_values(self):
if frappe.defaults.get_global_default('cust_master_name') == 'Naming Series' and not self.naming_series:
frappe.throw("Series is Mandatory.", frappe.MandatoryError)
frappe.throw(_("Series is mandatory"), frappe.MandatoryError)
def validate(self):
self.validate_values()
@ -66,7 +66,7 @@ class Customer(TransactionBase):
c.is_primary_contact = 1
try:
c.save()
except NameError, e:
except NameError:
pass
def on_update(self):
@ -86,7 +86,7 @@ class Customer(TransactionBase):
def validate_name_with_customer_group(self):
if frappe.db.exists("Customer Group", self.name):
frappe.throw("A Customer Group exists with same name please change the Customer name or rename the Customer Group")
frappe.throw(_("A Customer Group exists with same name please change the Customer name or rename the Customer Group"))
def delete_customer_address(self):
addresses = frappe.db.sql("""select name, lead from `tabAddress`

View File

@ -27,11 +27,11 @@ class Lead(SellingController):
self.set_status()
if self.source == 'Campaign' and not self.campaign_name and session['user'] != 'Guest':
frappe.throw("Please specify campaign name")
frappe.throw(_("Campaign Name is required"))
if self.email_id:
if not validate_email_add(self.email_id):
frappe.throw('Please enter valid email id.')
frappe.throw(_('{0} is not a valid email id').format(self.email_id))
def on_update(self):
self.check_email_id_is_unique()

View File

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

View File

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

View File

@ -1,241 +1,241 @@
{
"creation": "2012-12-20 12:50:49.000000",
"docstatus": 0,
"doctype": "DocType",
"creation": "2012-12-20 12:50:49.000000",
"docstatus": 0,
"doctype": "DocType",
"fields": [
{
"fieldname": "materials",
"fieldtype": "Section Break",
"label": "Materials",
"fieldname": "materials",
"fieldtype": "Section Break",
"label": "Materials",
"permlevel": 0
},
},
{
"description": "To track item in sales and purchase documents based on their serial nos. This is can also used to track warranty details of the product.",
"fieldname": "fs_item_serial_nos",
"fieldtype": "Check",
"label": "Item Serial Nos",
"description": "To track item in sales and purchase documents based on their serial nos. This is can also used to track warranty details of the product.",
"fieldname": "fs_item_serial_nos",
"fieldtype": "Check",
"label": "Item Serial Nos",
"permlevel": 0
},
},
{
"description": "To track items in sales and purchase documents with batch nos<br><b>Preferred Industry: Chemicals etc</b>",
"fieldname": "fs_item_batch_nos",
"fieldtype": "Check",
"label": "Item Batch Nos",
"description": "To track items in sales and purchase documents with batch nos<br><b>Preferred Industry: Chemicals etc</b>",
"fieldname": "fs_item_batch_nos",
"fieldtype": "Check",
"label": "Item Batch Nos",
"permlevel": 0
},
},
{
"description": "To track brand name in the following documents<br>\nDelivery Note, Enuiry, Material Request, Item, Purchase Order, Purchase Voucher, Purchaser Receipt, Quotation, Sales Invoice, Sales BOM, Sales Order, Serial No",
"fieldname": "fs_brands",
"fieldtype": "Check",
"label": "Brands",
"description": "To track brand name in the following documents Delivery Note, Opportunity, Material Request, Item, Purchase Order, Purchase Voucher, Purchaser Receipt, Quotation, Sales Invoice, Sales BOM, Sales Order, Serial No",
"fieldname": "fs_brands",
"fieldtype": "Check",
"label": "Brands",
"permlevel": 0
},
},
{
"description": "To track items using barcode. You will be able to enter items in Delivery Note and Sales Invoice by scanning barcode of item.",
"fieldname": "fs_item_barcode",
"fieldtype": "Check",
"label": "Item Barcode",
"description": "To track items using barcode. You will be able to enter items in Delivery Note and Sales Invoice by scanning barcode of item.",
"fieldname": "fs_item_barcode",
"fieldtype": "Check",
"label": "Item Barcode",
"permlevel": 0
},
},
{
"fieldname": "column_break0",
"fieldtype": "Column Break",
"fieldname": "column_break0",
"fieldtype": "Column Break",
"permlevel": 0
},
},
{
"description": "1. To maintain the customer wise item code and to make them searchable based on their code use this option",
"fieldname": "fs_item_advanced",
"fieldtype": "Check",
"label": "Item Advanced",
"description": "1. To maintain the customer wise item code and to make them searchable based on their code use this option",
"fieldname": "fs_item_advanced",
"fieldtype": "Check",
"label": "Item Advanced",
"permlevel": 0
},
},
{
"description": "If Sale BOM is defined, the actual BOM of the Pack is displayed as table.\nAvailable in Delivery Note and Sales Order",
"fieldname": "fs_packing_details",
"fieldtype": "Check",
"label": "Packing Details",
"description": "If Sale BOM is defined, the actual BOM of the Pack is displayed as table. Available in Delivery Note and Sales Order",
"fieldname": "fs_packing_details",
"fieldtype": "Check",
"label": "Packing Details",
"permlevel": 0
},
},
{
"description": "To get Item Group in details table",
"fieldname": "fs_item_group_in_details",
"fieldtype": "Check",
"label": "Item Groups in Details",
"description": "To get Item Group in details table",
"fieldname": "fs_item_group_in_details",
"fieldtype": "Check",
"label": "Item Groups in Details",
"permlevel": 0
},
},
{
"fieldname": "sales_and_purchase",
"fieldtype": "Section Break",
"label": "Sales and Purchase",
"fieldname": "sales_and_purchase",
"fieldtype": "Section Break",
"label": "Sales and Purchase",
"permlevel": 0
},
},
{
"description": "All export related fields like currency, conversion rate, export total, export grand total etc are available in <br>\nDelivery Note, POS, Quotation, Sales Invoice, Sales Order etc.",
"fieldname": "fs_exports",
"fieldtype": "Check",
"label": "Exports",
"description": "All export related fields like currency, conversion rate, export total, export grand total etc are available in Delivery Note, POS, Quotation, Sales Invoice, Sales Order etc.",
"fieldname": "fs_exports",
"fieldtype": "Check",
"label": "Exports",
"permlevel": 0
},
},
{
"description": "All import related fields like currency, conversion rate, import total, import grand total etc are available in <br>\nPurchase Receipt, Supplier Quotation, Purchase Invoice, Purchase Order etc.",
"fieldname": "fs_imports",
"fieldtype": "Check",
"label": "Imports",
"description": "All import related fields like currency, conversion rate, import total, import grand total etc are available in Purchase Receipt, Supplier Quotation, Purchase Invoice, Purchase Order etc.",
"fieldname": "fs_imports",
"fieldtype": "Check",
"label": "Imports",
"permlevel": 0
},
},
{
"fieldname": "column_break1",
"fieldtype": "Column Break",
"fieldname": "column_break1",
"fieldtype": "Column Break",
"permlevel": 0
},
},
{
"description": "Field available in Delivery Note, Quotation, Sales Invoice, Sales Order",
"fieldname": "fs_discounts",
"fieldtype": "Check",
"label": "Sales Discounts",
"description": "Field available in Delivery Note, Quotation, Sales Invoice, Sales Order",
"fieldname": "fs_discounts",
"fieldtype": "Check",
"label": "Sales Discounts",
"permlevel": 0
},
},
{
"description": "Discount Fields will be available in Purchase Order, Purchase Receipt, Purchase Invoice",
"fieldname": "fs_purchase_discounts",
"fieldtype": "Check",
"label": "Purchase Discounts",
"description": "Discount Fields will be available in Purchase Order, Purchase Receipt, Purchase Invoice",
"fieldname": "fs_purchase_discounts",
"fieldtype": "Check",
"label": "Purchase Discounts",
"permlevel": 0
},
},
{
"description": "To track any installation or commissioning related work after sales",
"fieldname": "fs_after_sales_installations",
"fieldtype": "Check",
"label": "After Sale Installations",
"description": "To track any installation or commissioning related work after sales",
"fieldname": "fs_after_sales_installations",
"fieldtype": "Check",
"label": "After Sale Installations",
"permlevel": 0
},
},
{
"description": "Available in \nBOM, Delivery Note, Purchase Invoice, Production Order, Purchase Order, Purchase Receipt, Sales Invoice, Sales Order, Stock Entry, Timesheet",
"fieldname": "fs_projects",
"fieldtype": "Check",
"label": "Projects",
"description": "Available in BOM, Delivery Note, Purchase Invoice, Production Order, Purchase Order, Purchase Receipt, Sales Invoice, Sales Order, Stock Entry, Timesheet",
"fieldname": "fs_projects",
"fieldtype": "Check",
"label": "Projects",
"permlevel": 0
},
},
{
"description": "If you have Sales Team and Sale Partners (Channel Partners) they can be tagged and maintain their contribution in the sales activity",
"fieldname": "fs_sales_extras",
"fieldtype": "Check",
"label": "Sales Extras",
"description": "If you have Sales Team and Sale Partners (Channel Partners) they can be tagged and maintain their contribution in the sales activity",
"fieldname": "fs_sales_extras",
"fieldtype": "Check",
"label": "Sales Extras",
"permlevel": 0
},
},
{
"fieldname": "accounts",
"fieldtype": "Section Break",
"label": "Accounts",
"fieldname": "accounts",
"fieldtype": "Section Break",
"label": "Accounts",
"permlevel": 0
},
},
{
"description": "Check if you need automatic recurring invoices. After submitting any sales invoice, Recurring section will be visible.",
"fieldname": "fs_recurring_invoice",
"fieldtype": "Check",
"label": "Recurring Invoice",
"description": "Check if you need automatic recurring invoices. After submitting any sales invoice, Recurring section will be visible.",
"fieldname": "fs_recurring_invoice",
"fieldtype": "Check",
"label": "Recurring Invoice",
"permlevel": 0
},
},
{
"fieldname": "column_break2",
"fieldtype": "Column Break",
"fieldname": "column_break2",
"fieldtype": "Column Break",
"permlevel": 0
},
},
{
"description": "To enable <b>Point of Sale</b> features",
"fieldname": "fs_pos",
"fieldtype": "Check",
"label": "Point of Sale",
"description": "To enable <b>Point of Sale</b> features",
"fieldname": "fs_pos",
"fieldtype": "Check",
"label": "Point of Sale",
"permlevel": 0
},
},
{
"description": "To enable <b>Point of Sale</b> view",
"fieldname": "fs_pos_view",
"fieldtype": "Check",
"label": "POS View",
"description": "To enable <b>Point of Sale</b> view",
"fieldname": "fs_pos_view",
"fieldtype": "Check",
"label": "POS View",
"permlevel": 0
},
},
{
"fieldname": "production",
"fieldtype": "Section Break",
"label": "Manufacturing",
"fieldname": "production",
"fieldtype": "Section Break",
"label": "Manufacturing",
"permlevel": 0
},
},
{
"description": "If you involve in manufacturing activity<br>\nEnables item <b>Is Manufactured</b>",
"fieldname": "fs_manufacturing",
"fieldtype": "Check",
"label": "Manufacturing",
"description": "If you involve in manufacturing activity. Enables Item 'Is Manufactured'",
"fieldname": "fs_manufacturing",
"fieldtype": "Check",
"label": "Manufacturing",
"permlevel": 0
},
},
{
"fieldname": "column_break3",
"fieldtype": "Column Break",
"fieldname": "column_break3",
"fieldtype": "Column Break",
"permlevel": 0
},
},
{
"description": "If you follow Quality Inspection<br>\nEnables item QA Required and QA No in Purchase Receipt",
"fieldname": "fs_quality",
"fieldtype": "Check",
"label": "Quality",
"description": "If you follow Quality Inspection. Enables Item QA Required and QA No in Purchase Receipt",
"fieldname": "fs_quality",
"fieldtype": "Check",
"label": "Quality",
"permlevel": 0
},
},
{
"fieldname": "miscelleneous",
"fieldtype": "Section Break",
"label": "Miscelleneous",
"fieldname": "miscelleneous",
"fieldtype": "Section Break",
"label": "Miscelleneous",
"permlevel": 0
},
},
{
"description": "If you have long print formats, this feature can be used to split the page to be printed on multiple pages with all headers and footers on each page",
"fieldname": "fs_page_break",
"fieldtype": "Check",
"label": "Page Break",
"description": "If you have long print formats, this feature can be used to split the page to be printed on multiple pages with all headers and footers on each page",
"fieldname": "fs_page_break",
"fieldtype": "Check",
"label": "Page Break",
"permlevel": 0
},
},
{
"fieldname": "column_break4",
"fieldtype": "Column Break",
"fieldname": "column_break4",
"fieldtype": "Column Break",
"permlevel": 0
},
},
{
"fieldname": "fs_more_info",
"fieldtype": "Check",
"label": "More Info",
"fieldname": "fs_more_info",
"fieldtype": "Check",
"label": "More Info",
"permlevel": 0
}
],
"icon": "icon-glass",
"idx": 1,
"issingle": 1,
"modified": "2013-12-24 11:40:19.000000",
"modified_by": "Administrator",
"module": "Setup",
"name": "Features Setup",
"name_case": "Title Case",
"owner": "Administrator",
],
"icon": "icon-glass",
"idx": 1,
"issingle": 1,
"modified": "2013-12-24 11:40:19.000000",
"modified_by": "Administrator",
"module": "Setup",
"name": "Features Setup",
"name_case": "Title Case",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"email": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 0,
"role": "System Manager",
"submit": 0,
"create": 1,
"email": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 0,
"role": "System Manager",
"submit": 0,
"write": 1
},
},
{
"create": 1,
"email": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 0,
"role": "Administrator",
"submit": 0,
"create": 1,
"email": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 0,
"role": "Administrator",
"submit": 0,
"write": 1
}
]
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -8,7 +8,7 @@ from __future__ import unicode_literals
import frappe
from frappe.utils import cstr, flt
from frappe import msgprint, _
from frappe import _
from erpnext.controllers.buying_controller import BuyingController
class MaterialRequest(BuyingController):
@ -43,9 +43,7 @@ class MaterialRequest(BuyingController):
actual_so_qty = actual_so_qty and flt(actual_so_qty[0][0]) or 0
if actual_so_qty and (flt(so_items[so_no][item]) + already_indented > actual_so_qty):
frappe.throw("You can raise indent of maximum qty: %s for item: %s against sales order: %s\
\n Anyway, you can add more qty in new row for the same item."
% (actual_so_qty - already_indented, item, so_no))
frappe.throw(_("Material Request of maximum {0} can be made for Item {1} against Sales Order {2}").format(actual_so_qty - already_indented, item, so_no))
def validate_schedule_date(self):
for d in self.get('indent_details'):

View File

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

View File

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

View File

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

View File

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