update translation strings #1403

This commit is contained in:
Rushabh Mehta
2014-04-14 19:20:45 +05:30
parent 7da01d6007
commit 9f0d625300
70 changed files with 1325 additions and 1639 deletions

View File

@@ -12,17 +12,17 @@ class BankReconciliation(Document):
if not (self.bank_account and self.from_date and self.to_date): if not (self.bank_account and self.from_date and self.to_date):
msgprint("Bank Account, From Date and To Date are Mandatory") msgprint("Bank Account, From Date and To Date are Mandatory")
return return
dl = frappe.db.sql("""select t1.name, t1.cheque_no, t1.cheque_date, t2.debit, dl = frappe.db.sql("""select t1.name, t1.cheque_no, t1.cheque_date, t2.debit,
t2.credit, t1.posting_date, t2.against_account t2.credit, t1.posting_date, t2.against_account
from from
`tabJournal Voucher` t1, `tabJournal Voucher Detail` t2 `tabJournal Voucher` t1, `tabJournal Voucher Detail` t2
where where
t2.parent = t1.name and t2.account = %s t2.parent = t1.name and t2.account = %s
and (clearance_date is null or clearance_date = '0000-00-00' or clearance_date = '') and (clearance_date is null or clearance_date = '0000-00-00' or clearance_date = '')
and t1.posting_date >= %s and t1.posting_date <= %s and t1.docstatus=1""", and t1.posting_date >= %s and t1.posting_date <= %s and t1.docstatus=1""",
(self.bank_account, self.from_date, self.to_date)) (self.bank_account, self.from_date, self.to_date))
self.set('entries', []) self.set('entries', [])
self.total_amount = 0.0 self.total_amount = 0.0
@@ -43,13 +43,13 @@ class BankReconciliation(Document):
if d.clearance_date: if d.clearance_date:
if d.cheque_date and getdate(d.clearance_date) < getdate(d.cheque_date): if d.cheque_date and getdate(d.clearance_date) < getdate(d.cheque_date):
frappe.throw("Clearance Date can not be before Cheque Date (Row #%s)" % d.idx) frappe.throw("Clearance Date can not be before Cheque Date (Row #%s)" % d.idx)
frappe.db.set_value("Journal Voucher", d.voucher_id, "clearance_date", d.clearance_date) frappe.db.set_value("Journal Voucher", d.voucher_id, "clearance_date", d.clearance_date)
frappe.db.sql("""update `tabJournal Voucher` set clearance_date = %s, modified = %s frappe.db.sql("""update `tabJournal Voucher` set clearance_date = %s, modified = %s
where name=%s""", (d.clearance_date, nowdate(), d.voucher_id)) where name=%s""", (d.clearance_date, nowdate(), d.voucher_id))
vouchers.append(d.voucher_id) vouchers.append(d.voucher_id)
if vouchers: if vouchers:
msgprint("Clearance Date updated in %s" % ", ".join(vouchers)) msgprint("Clearance Date updated in: {0}".format(", ".join(vouchers)))
else: else:
msgprint(_("Clearance Date not mentioned")) msgprint(_("Clearance Date not mentioned"))

View File

@@ -10,33 +10,33 @@ from frappe.utils.nestedset import NestedSet
class CostCenter(NestedSet): class CostCenter(NestedSet):
nsm_parent_field = 'parent_cost_center' nsm_parent_field = 'parent_cost_center'
def autoname(self): def autoname(self):
self.name = self.cost_center_name.strip() + ' - ' + \ self.name = self.cost_center_name.strip() + ' - ' + \
frappe.db.get_value("Company", self.company, "abbr") frappe.db.get_value("Company", self.company, "abbr")
def validate_mandatory(self): def validate_mandatory(self):
if not self.group_or_ledger: if not self.group_or_ledger:
msgprint("Please select Group or Ledger value", raise_exception=1) msgprint(_("Please select Group or Ledger value"), raise_exception=1)
if self.cost_center_name != self.company and not self.parent_cost_center: if self.cost_center_name != self.company and not self.parent_cost_center:
msgprint("Please enter parent cost center", raise_exception=1) msgprint(_("Please enter parent cost center"), raise_exception=1)
elif self.cost_center_name == self.company and self.parent_cost_center: elif self.cost_center_name == self.company and self.parent_cost_center:
msgprint(_("Root cannot have a parent cost center"), raise_exception=1) msgprint(_("Root cannot have a parent cost center"), raise_exception=1)
def convert_group_to_ledger(self): def convert_group_to_ledger(self):
if self.check_if_child_exists(): if self.check_if_child_exists():
msgprint("Cost Center: %s has existing child. You can not convert this cost center to ledger" % (self.name), raise_exception=1) msgprint(_("Cannot convert Cost Center to ledger as it has child nodes"), raise_exception=1)
elif self.check_gle_exists(): elif self.check_gle_exists():
msgprint("Cost Center with existing transaction can not be converted to ledger.", raise_exception=1) msgprint(_("Cost Center with existing transactions can not be converted to ledger"), raise_exception=1)
else: else:
self.group_or_ledger = 'Ledger' self.group_or_ledger = 'Ledger'
self.save() self.save()
return 1 return 1
def convert_ledger_to_group(self): def convert_ledger_to_group(self):
if self.check_gle_exists(): if self.check_gle_exists():
msgprint("Cost Center with existing transaction can not be converted to group.", raise_exception=1) msgprint(_("Cost Center with existing transactions can not be converted to group"), raise_exception=1)
else: else:
self.group_or_ledger = 'Group' self.group_or_ledger = 'Group'
self.save() self.save()
@@ -44,7 +44,7 @@ class CostCenter(NestedSet):
def check_gle_exists(self): def check_gle_exists(self):
return frappe.db.get_value("GL Entry", {"cost_center": self.name}) return frappe.db.get_value("GL Entry", {"cost_center": self.name})
def check_if_child_exists(self): def check_if_child_exists(self):
return frappe.db.sql("select name from `tabCost Center` where \ return frappe.db.sql("select name from `tabCost Center` where \
parent_cost_center = %s and docstatus != 2", self.name) parent_cost_center = %s and docstatus != 2", self.name)
@@ -53,11 +53,11 @@ class CostCenter(NestedSet):
check_acc_list = [] check_acc_list = []
for d in self.get('budget_details'): for d in self.get('budget_details'):
if self.group_or_ledger=="Group": if self.group_or_ledger=="Group":
msgprint("Budget cannot be set for Group Cost Centers", raise_exception=1) msgprint(_("Budget cannot be set for Group Cost Centers"), raise_exception=1)
if [d.account, d.fiscal_year] in check_acc_list: if [d.account, d.fiscal_year] in check_acc_list:
msgprint("Account " + d.account + "has been entered more than once for fiscal year " + d.fiscal_year, raise_exception=1) msgprint(_("Account {0} has been entered more than once for fiscal year {1}").format(d.account, d.fiscal_year), raise_exception=1)
else: else:
check_acc_list.append([d.account, d.fiscal_year]) check_acc_list.append([d.account, d.fiscal_year])
def validate(self): def validate(self):
@@ -65,25 +65,25 @@ class CostCenter(NestedSet):
Cost Center name must be unique Cost Center name must be unique
""" """
if (self.get("__islocal") or not self.name) and frappe.db.sql("select name from `tabCost Center` where cost_center_name = %s and company=%s", (self.cost_center_name, self.company)): if (self.get("__islocal") or not self.name) and frappe.db.sql("select name from `tabCost Center` where cost_center_name = %s and company=%s", (self.cost_center_name, self.company)):
msgprint("Cost Center Name already exists, please rename", raise_exception=1) msgprint(_("Cost Center Name already exists"), raise_exception=1)
self.validate_mandatory() self.validate_mandatory()
self.validate_budget_details() self.validate_budget_details()
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
from erpnext.setup.doctype.company.company import get_name_with_abbr from erpnext.setup.doctype.company.company import get_name_with_abbr
new_cost_center = get_name_with_abbr(newdn, self.company) new_cost_center = get_name_with_abbr(newdn, self.company)
# Validate properties before merging # Validate properties before merging
super(CostCenter, self).before_rename(olddn, new_cost_center, merge, "group_or_ledger") super(CostCenter, self).before_rename(olddn, new_cost_center, merge, "group_or_ledger")
return new_cost_center return new_cost_center
def after_rename(self, olddn, newdn, merge=False): def after_rename(self, olddn, newdn, merge=False):
if not merge: if not merge:
frappe.db.set_value("Cost Center", newdn, "cost_center_name", frappe.db.set_value("Cost Center", newdn, "cost_center_name",
" - ".join(newdn.split(" - ")[:-1])) " - ".join(newdn.split(" - ")[:-1]))
else: else:
super(CostCenter, self).after_rename(olddn, newdn, merge) super(CostCenter, self).after_rename(olddn, newdn, merge)

View File

@@ -9,19 +9,18 @@ from frappe.utils import getdate
from frappe.model.document import Document from frappe.model.document import Document
class FiscalYear(Document): class FiscalYear(Document):
def set_as_default(self): def set_as_default(self):
frappe.db.set_value("Global Defaults", None, "current_fiscal_year", self.name) frappe.db.set_value("Global Defaults", None, "current_fiscal_year", self.name)
frappe.get_doc("Global Defaults").on_update() frappe.get_doc("Global Defaults").on_update()
# clear cache # clear cache
frappe.clear_cache() frappe.clear_cache()
msgprint(self.name + _(""" is now the default Fiscal Year. \ msgprint(_("{0} is now the default Fiscal Year. Please refresh your browser for the change to take effect.").format(self.name))
Please refresh your browser for the change to take effect."""))
def validate(self): def validate(self):
year_start_end_dates = frappe.db.sql("""select year_start_date, year_end_date year_start_end_dates = frappe.db.sql("""select year_start_date, year_end_date
from `tabFiscal Year` where name=%s""", (self.name)) from `tabFiscal Year` where name=%s""", (self.name))
if year_start_end_dates: if year_start_end_dates:
@@ -36,10 +35,10 @@ class FiscalYear(Document):
if (getdate(self.year_end_date) - getdate(self.year_start_date)).days > 366: if (getdate(self.year_end_date) - getdate(self.year_start_date)).days > 366:
frappe.throw(_("Year Start Date and Year End Date are not within Fiscal Year.")) frappe.throw(_("Year Start Date and Year End Date are not within Fiscal Year."))
year_start_end_dates = frappe.db.sql("""select name, year_start_date, year_end_date year_start_end_dates = frappe.db.sql("""select name, year_start_date, year_end_date
from `tabFiscal Year` where name!=%s""", (self.name)) from `tabFiscal Year` where name!=%s""", (self.name))
for fiscal_year, ysd, yed in year_start_end_dates: for fiscal_year, ysd, yed in year_start_end_dates:
if (getdate(self.year_start_date) == ysd and getdate(self.year_end_date) == yed) \ if (getdate(self.year_start_date) == ysd and getdate(self.year_end_date) == yed) \
and (not frappe.flags.in_test): and (not frappe.flags.in_test):
frappe.throw(_("Year Start Date and Year End Date are already set in Fiscal Year: ") + fiscal_year) frappe.throw(_("Year Start Date and Year End Date are already set in Fiscal Year: ") + fiscal_year)

View File

@@ -57,17 +57,16 @@ class JournalVoucher(AccountsController):
def validate_debit_credit(self): def validate_debit_credit(self):
for d in self.get('entries'): for d in self.get('entries'):
if d.debit and d.credit: if d.debit and d.credit:
msgprint("You cannot credit and debit same account at the same time.", msgprint(_("You cannot credit and debit same account at the same time."), raise_exception=1)
raise_exception=1)
def validate_cheque_info(self): def validate_cheque_info(self):
if self.voucher_type in ['Bank Voucher']: if self.voucher_type in ['Bank Voucher']:
if not self.cheque_no or not self.cheque_date: if not self.cheque_no or not self.cheque_date:
msgprint("Reference No & Reference Date is required for %s" % msgprint(_("Reference No & Reference Date is required for {0}").format(self.voucher_type),
self.voucher_type, raise_exception=1) raise_exception=1)
if self.cheque_date and not self.cheque_no: if self.cheque_date and not self.cheque_no:
msgprint("Reference No is mandatory if you entered Reference Date", raise_exception=1) msgprint(_("Reference No is mandatory if you entered Reference Date"), raise_exception=1)
def validate_entries_for_advance(self): def validate_entries_for_advance(self):
for d in self.get('entries'): for d in self.get('entries'):
@@ -76,19 +75,17 @@ class JournalVoucher(AccountsController):
master_type = frappe.db.get_value("Account", d.account, "master_type") master_type = frappe.db.get_value("Account", d.account, "master_type")
if (master_type == 'Customer' and flt(d.credit) > 0) or \ if (master_type == 'Customer' and flt(d.credit) > 0) or \
(master_type == 'Supplier' and flt(d.debit) > 0): (master_type == 'Supplier' and flt(d.debit) > 0):
msgprint("Message: Please check Is Advance as 'Yes' against \ msgprint(_("Please check 'Is Advance' against Account {0} if this is an advance entry.").format(d.account))
Account %s if this is an advance entry." % d.account)
def validate_against_jv(self): def validate_against_jv(self):
for d in self.get('entries'): for d in self.get('entries'):
if d.against_jv: if d.against_jv:
if d.against_jv == self.name: if d.against_jv == self.name:
msgprint("You can not enter current voucher in 'Against JV' column", msgprint(_("You can not enter current voucher in 'Against Journal Voucher' column"), raise_exception=1)
raise_exception=1)
elif not frappe.db.sql("""select name from `tabJournal Voucher Detail` elif not frappe.db.sql("""select name from `tabJournal Voucher Detail`
where account = %s and docstatus = 1 and parent = %s""", where account = %s and docstatus = 1 and parent = %s""",
(d.account, d.against_jv)): (d.account, d.against_jv)):
msgprint("Against JV: %s is not valid." % d.against_jv, raise_exception=1) msgprint(_("Journal Voucher {0} does not have account {1}.").format(d.against_jv, d.account), raise_exception=1)
def set_against_account(self): def set_against_account(self):
# Debit = Credit # Debit = Credit
@@ -104,8 +101,8 @@ class JournalVoucher(AccountsController):
self.total_credit = credit self.total_credit = credit
if abs(self.total_debit-self.total_credit) > 0.001: if abs(self.total_debit-self.total_credit) > 0.001:
msgprint("Debit must be equal to Credit. The difference is %s" % msgprint(_("Debit must equal Credit. The difference is {0}").format(self.total_debit-self.total_credit),
(self.total_debit-self.total_credit), raise_exception=1) raise_exception=1)
# update against account # update against account
for d in self.get('entries'): for d in self.get('entries'):
@@ -116,10 +113,9 @@ class JournalVoucher(AccountsController):
r = [] r = []
if self.cheque_no: if self.cheque_no:
if self.cheque_date: if self.cheque_date:
r.append('Via Reference #%s dated %s' % r.append(_('Reference #{0} dated {1}').fomrat(self.cheque_no, formatdate(self.cheque_date)))
(self.cheque_no, formatdate(self.cheque_date)))
else : else :
msgprint("Please enter Reference date", raise_exception=1) msgprint(_("Please enter Reference date"), raise_exception=1)
for d in self.get('entries'): for d in self.get('entries'):
if d.against_invoice and d.credit: if d.against_invoice and d.credit:
@@ -137,12 +133,12 @@ class JournalVoucher(AccountsController):
bill_no[0][1] and formatdate(bill_no[0][1].strftime('%Y-%m-%d')) or '')) bill_no[0][1] and formatdate(bill_no[0][1].strftime('%Y-%m-%d')) or ''))
if self.user_remark: if self.user_remark:
r.append("User Remark : %s"%self.user_remark) r.append(_("Note: {0}").format(self.user_remark))
if r: if r:
self.remark = ("\n").join(r) self.remark = ("\n").join(r)
else: else:
frappe.msgprint("User Remarks is mandatory", raise_exception=1) frappe.msgprint(_("User Remarks is mandatory"), raise_exception=1)
def set_aging_date(self): def set_aging_date(self):
if self.is_opening != 'Yes': if self.is_opening != 'Yes':
@@ -158,7 +154,7 @@ class JournalVoucher(AccountsController):
# If customer/supplier account, aging date is mandatory # If customer/supplier account, aging date is mandatory
if exists and not self.aging_date: if exists and not self.aging_date:
msgprint("Aging Date is mandatory for opening entry", raise_exception=1) msgprint(_("Aging Date is mandatory for opening entry"), raise_exception=1)
else: else:
self.aging_date = self.posting_date self.aging_date = self.posting_date
@@ -195,8 +191,8 @@ class JournalVoucher(AccountsController):
credit_days = self.get_credit_days_for(d.account) credit_days = self.get_credit_days_for(d.account)
# Check credit days # Check credit days
if credit_days > 0 and not self.get_authorized_user() and cint(date_diff) > credit_days: if credit_days > 0 and not self.get_authorized_user() and cint(date_diff) > credit_days:
msgprint("Credit Not Allowed: Cannot allow a check that is dated \ msgprint(_("Maximum allowed credit is {0} days after posting date").format(credit_days),
more than %s days after the posting date" % credit_days, raise_exception=1) raise_exception=1)
def get_credit_days_for(self, ac): def get_credit_days_for(self, ac):
if not self.credit_days_for.has_key(ac): if not self.credit_days_for.has_key(ac):
@@ -272,7 +268,7 @@ class JournalVoucher(AccountsController):
def get_balance(self): def get_balance(self):
if not self.get('entries'): if not self.get('entries'):
msgprint("Please enter atleast 1 entry in 'GL Entries' table") msgprint(_("'Entries' cannot be empty"), raise_exception=True)
else: else:
flag, self.total_debit, self.total_credit = 0, 0, 0 flag, self.total_debit, self.total_credit = 0, 0, 0
diff = flt(self.difference, 2) diff = flt(self.difference, 2)
@@ -390,7 +386,7 @@ def get_payment_entry(doc):
jv.company = doc.company jv.company = doc.company
jv.fiscal_year = doc.fiscal_year jv.fiscal_year = doc.fiscal_year
d1 = jv.append("entries") jv.append("entries")
d2 = jv.append("entries") d2 = jv.append("entries")
if bank_account: if bank_account:

View File

@@ -12,23 +12,23 @@ from frappe.model.document import Document
class PaymenttoInvoiceMatchingTool(Document): class PaymenttoInvoiceMatchingTool(Document):
def get_voucher_details(self): def get_voucher_details(self):
total_amount = frappe.db.sql("""select sum(ifnull(debit, 0)) - sum(ifnull(credit, 0)) total_amount = frappe.db.sql("""select sum(ifnull(debit, 0)) - sum(ifnull(credit, 0))
from `tabGL Entry` from `tabGL Entry`
where voucher_type = %s and voucher_no = %s where voucher_type = %s and voucher_no = %s
and account = %s""", (self.voucher_type, self.voucher_no, self.account)) and account = %s""", (self.voucher_type, self.voucher_no, self.account))
total_amount = total_amount and flt(total_amount[0][0]) or 0 total_amount = total_amount and flt(total_amount[0][0]) or 0
reconciled_payment = frappe.db.sql(""" reconciled_payment = frappe.db.sql("""
select abs(sum(ifnull(debit, 0)) - sum(ifnull(credit, 0))) from `tabGL Entry` where select abs(sum(ifnull(debit, 0)) - sum(ifnull(credit, 0))) from `tabGL Entry` where
against_voucher = %s and voucher_no != %s against_voucher = %s and voucher_no != %s
and account = %s""", (self.voucher_no, self.voucher_no, self.account)) and account = %s""", (self.voucher_no, self.voucher_no, self.account))
reconciled_payment = reconciled_payment and flt(reconciled_payment[0][0]) or 0 reconciled_payment = reconciled_payment and flt(reconciled_payment[0][0]) or 0
ret = { ret = {
'total_amount': total_amount, 'total_amount': total_amount,
'pending_amt_to_reconcile': total_amount - reconciled_payment 'pending_amt_to_reconcile': total_amount - reconciled_payment
} }
return ret return ret
def get_payment_entries(self): def get_payment_entries(self):
@@ -37,16 +37,16 @@ class PaymenttoInvoiceMatchingTool(Document):
Payment entry will be decided based on account type (Dr/Cr) Payment entry will be decided based on account type (Dr/Cr)
""" """
self.set('ir_payment_details', []) self.set('ir_payment_details', [])
gle = self.get_gl_entries() gle = self.get_gl_entries()
self.create_payment_table(gle) self.create_payment_table(gle)
def get_gl_entries(self): def get_gl_entries(self):
self.validate_mandatory() self.validate_mandatory()
cond = self.from_date and " and t1.posting_date >= '" + self.from_date + "'" or "" cond = self.from_date and " and t1.posting_date >= '" + self.from_date + "'" or ""
cond += self.to_date and " and t1.posting_date <= '" + self.to_date + "'"or "" cond += self.to_date and " and t1.posting_date <= '" + self.to_date + "'"or ""
if self.amt_greater_than: if self.amt_greater_than:
cond += ' and abs(ifnull(t2.debit, 0) - ifnull(t2.credit, 0)) >= ' + \ cond += ' and abs(ifnull(t2.debit, 0) - ifnull(t2.credit, 0)) >= ' + \
self.amt_greater_than self.amt_greater_than
@@ -55,13 +55,13 @@ class PaymenttoInvoiceMatchingTool(Document):
self.amt_less_than self.amt_less_than
gle = frappe.db.sql(""" gle = frappe.db.sql("""
select t1.name as voucher_no, t1.posting_date, t1.total_debit as total_amt, select t1.name as voucher_no, t1.posting_date, t1.total_debit as total_amt,
abs(sum(ifnull(t2.credit, 0)) - sum(ifnull(t2.debit, 0))) as amt_due, t1.remark, abs(sum(ifnull(t2.credit, 0)) - sum(ifnull(t2.debit, 0))) as amt_due, t1.remark,
t2.against_account, t2.name as voucher_detail_no t2.against_account, t2.name as voucher_detail_no
from `tabJournal Voucher` t1, `tabJournal Voucher Detail` t2 from `tabJournal Voucher` t1, `tabJournal Voucher Detail` t2
where t1.name = t2.parent and t1.docstatus = 1 and t2.account = %s where t1.name = t2.parent and t1.docstatus = 1 and t2.account = %s
and ifnull(t2.against_voucher, '')='' and ifnull(t2.against_invoice, '')='' and ifnull(t2.against_voucher, '')='' and ifnull(t2.against_invoice, '')=''
and ifnull(t2.against_jv, '')='' and t1.name != %s %s group by t1.name, t2.name """ % and ifnull(t2.against_jv, '')='' and t1.name != %s %s group by t1.name, t2.name """ %
('%s', '%s', cond), (self.account, self.voucher_no), as_dict=1) ('%s', '%s', cond), (self.account, self.voucher_no), as_dict=1)
return gle return gle
@@ -76,11 +76,11 @@ class PaymenttoInvoiceMatchingTool(Document):
ch.against_account = d.get('against_account') ch.against_account = d.get('against_account')
ch.remarks = d.get('remark') ch.remarks = d.get('remark')
ch.voucher_detail_no = d.get('voucher_detail_no') ch.voucher_detail_no = d.get('voucher_detail_no')
def validate_mandatory(self): def validate_mandatory(self):
if not self.account: if not self.account:
msgprint("Please select Account first", raise_exception=1) msgprint(_("Please select Account first"), raise_exception=1)
def reconcile(self): def reconcile(self):
""" """
Links booking and payment voucher Links booking and payment voucher
@@ -88,59 +88,59 @@ class PaymenttoInvoiceMatchingTool(Document):
2. split into multiple rows if partially adjusted, assign against voucher 2. split into multiple rows if partially adjusted, assign against voucher
3. submit payment voucher 3. submit payment voucher
""" """
if not self.voucher_no or not frappe.db.sql("""select name from `tab%s` if not self.voucher_no or not frappe.db.sql("""select name from `tab%s`
where name = %s""" % (self.voucher_type, '%s'), self.voucher_no): where name = %s""" % (self.voucher_type, '%s'), self.voucher_no):
frappe.throw(_("Please select valid Voucher No to proceed")) frappe.throw(_("Please select valid Voucher No to proceed"))
lst = [] lst = []
for d in self.get('ir_payment_details'): for d in self.get('ir_payment_details'):
if flt(d.amt_to_be_reconciled) > 0: if flt(d.amt_to_be_reconciled) > 0:
args = { args = {
'voucher_no' : d.voucher_no, 'voucher_no' : d.voucher_no,
'voucher_detail_no' : d.voucher_detail_no, 'voucher_detail_no' : d.voucher_detail_no,
'against_voucher_type' : self.voucher_type, 'against_voucher_type' : self.voucher_type,
'against_voucher' : self.voucher_no, 'against_voucher' : self.voucher_no,
'account' : self.account, 'account' : self.account,
'is_advance' : 'No', 'is_advance' : 'No',
# 'dr_or_cr' : self.account_type=='debit' and 'credit' or 'debit', # 'dr_or_cr' : self.account_type=='debit' and 'credit' or 'debit',
'unadjusted_amt' : flt(d.amt_due), 'unadjusted_amt' : flt(d.amt_due),
'allocated_amt' : flt(d.amt_to_be_reconciled) 'allocated_amt' : flt(d.amt_to_be_reconciled)
} }
lst.append(args) lst.append(args)
if lst: if lst:
from erpnext.accounts.utils import reconcile_against_document from erpnext.accounts.utils import reconcile_against_document
reconcile_against_document(lst) reconcile_against_document(lst)
msgprint("Successfully allocated.") msgprint(_("Successfully allocated"))
else: else:
msgprint("No amount allocated.", raise_exception=1) msgprint(_("No amount allocated"), raise_exception=1)
def gl_entry_details(doctype, txt, searchfield, start, page_len, filters): def gl_entry_details(doctype, txt, searchfield, start, page_len, filters):
from erpnext.controllers.queries import get_match_cond from erpnext.controllers.queries import get_match_cond
return frappe.db.sql("""select gle.voucher_no, gle.posting_date, return frappe.db.sql("""select gle.voucher_no, gle.posting_date,
gle.debit, gle.credit from `tabGL Entry` gle gle.debit, gle.credit from `tabGL Entry` gle
where gle.account = '%(acc)s' where gle.account = '%(acc)s'
and gle.voucher_type = '%(dt)s' and gle.voucher_type = '%(dt)s'
and gle.voucher_no like '%(txt)s' and gle.voucher_no like '%(txt)s'
and (ifnull(gle.against_voucher, '') = '' and (ifnull(gle.against_voucher, '') = ''
or ifnull(gle.against_voucher, '') = gle.voucher_no ) or ifnull(gle.against_voucher, '') = gle.voucher_no )
and (select ifnull(abs(sum(ifnull(debit, 0)) - sum(ifnull(credit, 0))), 0) and (select ifnull(abs(sum(ifnull(debit, 0)) - sum(ifnull(credit, 0))), 0)
from `tabGL Entry` from `tabGL Entry`
where account = '%(acc)s' where account = '%(acc)s'
and against_voucher_type = '%(dt)s' and against_voucher_type = '%(dt)s'
and against_voucher = gle.voucher_no and against_voucher = gle.voucher_no
and voucher_no != gle.voucher_no) and voucher_no != gle.voucher_no)
!= abs(ifnull(gle.debit, 0) - ifnull(gle.credit, 0)) != abs(ifnull(gle.debit, 0) - ifnull(gle.credit, 0))
and if(gle.voucher_type='Sales Invoice', ifnull((select is_pos from `tabSales Invoice` and if(gle.voucher_type='Sales Invoice', ifnull((select is_pos from `tabSales Invoice`
where name=gle.voucher_no), 0), 0)=0 where name=gle.voucher_no), 0), 0)=0
%(mcond)s %(mcond)s
ORDER BY gle.posting_date desc, gle.voucher_no desc ORDER BY gle.posting_date desc, gle.voucher_no desc
limit %(start)s, %(page_len)s""" % { limit %(start)s, %(page_len)s""" % {
"dt":filters["dt"], "dt":filters["dt"],
"acc":filters["acc"], "acc":filters["acc"],
'mcond':get_match_cond(doctype), 'mcond':get_match_cond(doctype),
'txt': "%%%s%%" % txt, 'txt': "%%%s%%" % txt,
"start": start, "start": start,
"page_len": page_len "page_len": page_len
}) })

View File

@@ -16,18 +16,18 @@ class POSSetting(Document):
self.check_for_duplicate() self.check_for_duplicate()
self.validate_expense_account() self.validate_expense_account()
self.validate_all_link_fields() self.validate_all_link_fields()
def check_for_duplicate(self): def check_for_duplicate(self):
res = frappe.db.sql("""select name, user from `tabPOS Setting` res = frappe.db.sql("""select name, user from `tabPOS Setting`
where ifnull(user, '') = %s and name != %s and company = %s""", where ifnull(user, '') = %s and name != %s and company = %s""",
(self.user, self.name, self.company)) (self.user, self.name, self.company))
if res: if res:
if res[0][1]: if res[0][1]:
msgprint("POS Setting '%s' already created for user: '%s' and company: '%s'" % msgprint(_("POS Setting {0} already created for user: {1} and company {2}").format(res[0][0],
(res[0][0], res[0][1], self.company), raise_exception=1) res[0][1], self.company), raise_exception=1)
else: else:
msgprint("Global POS Setting already created - %s for this company: '%s'" % msgprint(_("Global POS Setting {0} already created for company {1}").format(res[0][0],
(res[0][0], self.company), raise_exception=1) self.company), raise_exception=1)
def validate_expense_account(self): def validate_expense_account(self):
if cint(frappe.defaults.get_global_default("auto_accounting_for_stock")) \ if cint(frappe.defaults.get_global_default("auto_accounting_for_stock")) \
@@ -35,13 +35,13 @@ class POSSetting(Document):
msgprint(_("Expense Account is mandatory"), raise_exception=1) msgprint(_("Expense Account is mandatory"), raise_exception=1)
def validate_all_link_fields(self): def validate_all_link_fields(self):
accounts = {"Account": [self.cash_bank_account, self.income_account, accounts = {"Account": [self.cash_bank_account, self.income_account,
self.expense_account], "Cost Center": [self.cost_center], self.expense_account], "Cost Center": [self.cost_center],
"Warehouse": [self.warehouse]} "Warehouse": [self.warehouse]}
for link_dt, dn_list in accounts.items(): for link_dt, dn_list in accounts.items():
for link_dn in dn_list: for link_dn in dn_list:
if link_dn and not frappe.db.exists({"doctype": link_dt, if link_dn and not frappe.db.exists({"doctype": link_dt,
"company": self.company, "name": link_dn}): "company": self.company, "name": link_dn}):
frappe.throw(link_dn +_(" does not belong to ") + self.company) frappe.throw(link_dn +_(" does not belong to ") + self.company)
@@ -53,17 +53,17 @@ class POSSetting(Document):
def set_defaults(self, include_current_pos=True): def set_defaults(self, include_current_pos=True):
frappe.defaults.clear_default("is_pos") frappe.defaults.clear_default("is_pos")
if not include_current_pos: if not include_current_pos:
condition = " where name != '%s'" % self.name.replace("'", "\'") condition = " where name != '%s'" % self.name.replace("'", "\'")
else: else:
condition = "" condition = ""
pos_view_users = frappe.db.sql_list("""select user pos_view_users = frappe.db.sql_list("""select user
from `tabPOS Setting` {0}""".format(condition)) from `tabPOS Setting` {0}""".format(condition))
for user in pos_view_users: for user in pos_view_users:
if user: if user:
frappe.defaults.set_user_default("is_pos", 1, user) frappe.defaults.set_user_default("is_pos", 1, user)
else: else:
frappe.defaults.set_global_default("is_pos", 1) frappe.defaults.set_global_default("is_pos", 1)

View File

@@ -6,7 +6,7 @@ import frappe
from frappe.utils import cint, cstr, flt, formatdate from frappe.utils import cint, cstr, flt, formatdate
from frappe import msgprint, _ from frappe import msgprint, _, throw
from erpnext.setup.utils import get_company_currency from erpnext.setup.utils import get_company_currency
import frappe.defaults import frappe.defaults
@@ -73,22 +73,15 @@ class PurchaseInvoice(BuyingController):
def check_active_purchase_items(self): def check_active_purchase_items(self):
for d in self.get('entries'): for d in self.get('entries'):
if d.item_code: # extra condn coz item_code is not mandatory in PV if d.item_code: # extra condn coz item_code is not mandatory in PV
valid_item = frappe.db.sql("select docstatus,is_purchase_item from tabItem where name = %s",d.item_code) if frappe.db.get_value("Item", d.item_code, "is_purchase_item") != 'Yes':
if valid_item[0][0] == 2: msgprint(_("Item {0} is not Purchase Item").format(d.item_code), raise_exception=True)
msgprint("Item : '%s' is Inactive, you can restore it from Trash" %(d.item_code))
raise Exception
if not valid_item[0][1] == 'Yes':
msgprint("Item : '%s' is not Purchase Item"%(d.item_code))
raise Exception
def check_conversion_rate(self): def check_conversion_rate(self):
default_currency = get_company_currency(self.company) default_currency = get_company_currency(self.company)
if not default_currency: if not default_currency:
msgprint('Message: Please enter default currency in Company Master') throw(_('Please enter default currency in Company Master'))
raise Exception
if (self.currency == default_currency and flt(self.conversion_rate) != 1.00) or not self.conversion_rate or (self.currency != default_currency and flt(self.conversion_rate) == 1.00): if (self.currency == default_currency and flt(self.conversion_rate) != 1.00) or not self.conversion_rate or (self.currency != default_currency and flt(self.conversion_rate) == 1.00):
msgprint("Message: Please Enter Appropriate Conversion Rate.") throw(_("Conversion rate cannot be 0 or 1"))
raise Exception
def validate_bill_no(self): def validate_bill_no(self):
if self.bill_no and self.bill_no.lower().strip() \ if self.bill_no and self.bill_no.lower().strip() \
@@ -97,13 +90,12 @@ class PurchaseInvoice(BuyingController):
where bill_no = %s and credit_to = %s and docstatus = 1 and name != %s""", where bill_no = %s and credit_to = %s and docstatus = 1 and name != %s""",
(self.bill_no, self.credit_to, self.name)) (self.bill_no, self.credit_to, self.name))
if b_no and cstr(b_no[0][2]) == cstr(self.is_opening): if b_no and cstr(b_no[0][2]) == cstr(self.is_opening):
msgprint("Please check you have already booked expense against Bill No. %s \ throw(_("Bill No {0} already booked in Purchase Invoice {1}").format(cstr(b_no[0][0]),
in Purchase Invoice %s" % (cstr(b_no[0][0]), cstr(b_no[0][1])), cstr(b_no[0][1])))
raise_exception=1)
if not self.remarks and self.bill_date: if not self.remarks and self.bill_date:
self.remarks = (self.remarks or '') + "\n" + ("Against Bill %s dated %s" self.remarks = (self.remarks or '') + "\n" \
% (self.bill_no, formatdate(self.bill_date))) + _("Against Bill %s dated %s").format(self.bill_no, formatdate(self.bill_date))
if not self.remarks: if not self.remarks:
self.remarks = "No Remarks" self.remarks = "No Remarks"
@@ -130,7 +122,7 @@ class PurchaseInvoice(BuyingController):
check_list.append(d.purchase_order) check_list.append(d.purchase_order)
stopped = frappe.db.sql("select name from `tabPurchase Order` where status = 'Stopped' and name = %s", d.purchase_order) stopped = frappe.db.sql("select name from `tabPurchase Order` where status = 'Stopped' and name = %s", d.purchase_order)
if stopped: if stopped:
msgprint("One cannot do any transaction against 'Purchase Order' : %s, it's status is 'Stopped'" % (d.purhcase_order)) throw(_("Purchase Order {0} is 'Stopped'").format(d.purchase_order))
raise Exception raise Exception
def validate_with_previous_doc(self): def validate_with_previous_doc(self):
@@ -176,7 +168,7 @@ class PurchaseInvoice(BuyingController):
if self.is_opening != 'Yes': if self.is_opening != 'Yes':
self.aging_date = self.posting_date self.aging_date = self.posting_date
elif not self.aging_date: elif not self.aging_date:
msgprint("Aging Date is mandatory for opening entry") msgprint(_("Ageing date is mandatory for opening entry"))
raise Exception raise Exception
def set_against_expense_account(self): def set_against_expense_account(self):
@@ -199,8 +191,7 @@ class PurchaseInvoice(BuyingController):
against_accounts.append(stock_not_billed_account) against_accounts.append(stock_not_billed_account)
elif not item.expense_account: elif not item.expense_account:
msgprint(_("Expense account is mandatory for item") + ": " + throw(_("Expense account is mandatory for item {0}").format(item.item_code or item.item_name))
(item.item_code or item.item_name), raise_exception=1)
elif item.expense_account not in against_accounts: elif item.expense_account not in against_accounts:
# if no auto_accounting_for_stock or not a stock item # if no auto_accounting_for_stock or not a stock item
@@ -212,19 +203,18 @@ class PurchaseInvoice(BuyingController):
if frappe.db.get_value("Buying Settings", None, "po_required") == 'Yes': if frappe.db.get_value("Buying Settings", None, "po_required") == 'Yes':
for d in self.get('entries'): for d in self.get('entries'):
if not d.purchase_order: if not d.purchase_order:
msgprint("Purchse Order No. required against item %s"%d.item_code) throw(_("Purchse Order number required for Item {0}").format(d.item_code))
raise Exception
def pr_required(self): def pr_required(self):
if frappe.db.get_value("Buying Settings", None, "pr_required") == 'Yes': if frappe.db.get_value("Buying Settings", None, "pr_required") == 'Yes':
for d in self.get('entries'): for d in self.get('entries'):
if not d.purchase_receipt: if not d.purchase_receipt:
msgprint("Purchase Receipt No. required against item %s"%d.item_code) throw(_("Purchase Receipt number required for Item {0}").format(d.item_code))
raise Exception raise Exception
def validate_write_off_account(self): def validate_write_off_account(self):
if self.write_off_amount and not self.write_off_account: if self.write_off_amount and not self.write_off_account:
msgprint("Please enter Write Off Account", raise_exception=1) throw(_("Please enter Write Off Account"))
def check_prev_docstatus(self): def check_prev_docstatus(self):
for d in self.get('entries'): for d in self.get('entries'):

View File

@@ -6,9 +6,9 @@ import frappe
import frappe.defaults import frappe.defaults
from frappe.utils import add_days, cint, cstr, date_diff, flt, getdate, nowdate, \ from frappe.utils import add_days, cint, cstr, date_diff, flt, getdate, nowdate, \
get_first_day, get_last_day, comma_and get_first_day, get_last_day
from frappe.model.naming import make_autoname from frappe.model.naming import make_autoname
from frappe import _, msgprint from frappe import _, msgprint, throw
from erpnext.accounts.party import get_party_account, get_due_date from erpnext.accounts.party import get_party_account, get_due_date
from erpnext.controllers.stock_controller import update_gl_entries_after from erpnext.controllers.stock_controller import update_gl_entries_after
@@ -165,8 +165,7 @@ class SalesInvoice(SellingController):
if d.time_log_batch: if d.time_log_batch:
status = frappe.db.get_value("Time Log Batch", d.time_log_batch, "status") status = frappe.db.get_value("Time Log Batch", d.time_log_batch, "status")
if status!="Submitted": if status!="Submitted":
frappe.msgprint(_("Time Log Batch status must be 'Submitted'") + ":" + d.time_log_batch, frappe.throw(_("Time Log Batch {0} must be 'Submitted'").format(d.time_log_batch))
raise_exception=True)
def set_pos_fields(self, for_validate=False): def set_pos_fields(self, for_validate=False):
"""Set retail related fields from pos settings""" """Set retail related fields from pos settings"""
@@ -263,10 +262,8 @@ class SalesInvoice(SellingController):
where name = %s and (ifnull(end_of_life,'')='' or end_of_life > now())""", d.item_code) where name = %s and (ifnull(end_of_life,'')='' or end_of_life > now())""", d.item_code)
acc = frappe.db.sql("""select account_type from `tabAccount` acc = frappe.db.sql("""select account_type from `tabAccount`
where name = %s and docstatus != 2""", d.income_account) where name = %s and docstatus != 2""", d.income_account)
if not acc: if item and item[0][1] == 'Yes' and not acc[0][0] == 'Fixed Asset':
msgprint("Account: "+d.income_account+" does not exist in the system", raise_exception=True) msgprint(_("Account {0} must be of type 'Fixed Asset' as Item {1} is an Asset Item").format(d.item_code), raise_exception=True)
elif item and item[0][1] == 'Yes' and not acc[0][0] == 'Fixed Asset':
msgprint("Please select income head with account type 'Fixed Asset' as Item %s is an asset item" % d.item_code, raise_exception=True)
def validate_with_previous_doc(self): def validate_with_previous_doc(self):
super(SalesInvoice, self).validate_with_previous_doc(self.tname, { super(SalesInvoice, self).validate_with_previous_doc(self.tname, {
@@ -302,7 +299,7 @@ class SalesInvoice(SellingController):
if self.is_opening != 'Yes': if self.is_opening != 'Yes':
self.aging_date = self.posting_date self.aging_date = self.posting_date
elif not self.aging_date: elif not self.aging_date:
msgprint("Aging Date is mandatory for opening entry") msgprint(_("Ageing Date is mandatory for opening entry"))
raise Exception raise Exception
@@ -327,7 +324,7 @@ class SalesInvoice(SellingController):
for d in self.get('entries'): for d in self.get('entries'):
if frappe.db.get_value('Item', d.item_code, 'is_stock_item') == 'Yes' \ if frappe.db.get_value('Item', d.item_code, 'is_stock_item') == 'Yes' \
and not d.get(i.lower().replace(' ','_')): and not d.get(i.lower().replace(' ','_')):
msgprint("%s is mandatory for stock item which is not mentioed against item: %s"%(i,d.item_code), raise_exception=1) msgprint(_("{0} is mandatory for Item {1}").format(i,d.item_code), raise_exception=1)
def validate_proj_cust(self): def validate_proj_cust(self):
@@ -337,34 +334,32 @@ class SalesInvoice(SellingController):
where name = %s and (customer = %s or where name = %s and (customer = %s or
ifnull(customer,'')='')""", (self.project_name, self.customer)) ifnull(customer,'')='')""", (self.project_name, self.customer))
if not res: if not res:
msgprint("Customer - %s does not belong to project - %s. \n\nIf you want to use project for multiple customers then please make customer details blank in that project."%(self.customer,self.project_name)) msgprint(_("Customer {0} does not belong to project {1}").format(self.customer,self.project_name))
raise Exception raise Exception
def validate_pos(self): def validate_pos(self):
if not self.cash_bank_account and flt(self.paid_amount): if not self.cash_bank_account and flt(self.paid_amount):
msgprint("Cash/Bank Account is mandatory for POS, for making payment entry") msgprint(_("Cash or Bank Account is mandatory for making payment entry"))
raise Exception raise Exception
if flt(self.paid_amount) + flt(self.write_off_amount) \ if flt(self.paid_amount) + flt(self.write_off_amount) \
- flt(self.grand_total) > 1/(10**(self.precision("grand_total") + 1)): - flt(self.grand_total) > 1/(10**(self.precision("grand_total") + 1)):
frappe.throw(_("""(Paid amount + Write Off Amount) can not be \ frappe.throw(_("""Paid amount + Write Off Amount can not be greater than Grand Total"""))
greater than Grand Total"""))
def validate_item_code(self): def validate_item_code(self):
for d in self.get('entries'): for d in self.get('entries'):
if not d.item_code: if not d.item_code:
msgprint("Please enter Item Code at line no : %s to update stock or remove check from Update Stock in Basic Info Tab." % (d.idx), msgprint(_("Item Code required at Row No {0}").format(d.idx), raise_exception=True)
raise_exception=True)
def validate_delivery_note(self): def validate_delivery_note(self):
for d in self.get("entries"): for d in self.get("entries"):
if d.delivery_note: if d.delivery_note:
msgprint("""Stock update can not be made against Delivery Note""", raise_exception=1) msgprint(_("Stock cannot be updated against Delivery Note {0}").format(d.delivery_note), raise_exception=1)
def validate_write_off_account(self): def validate_write_off_account(self):
if flt(self.write_off_amount) and not self.write_off_account: if flt(self.write_off_amount) and not self.write_off_account:
msgprint("Please enter Write Off Account", raise_exception=1) msgprint(_("Please enter Write Off Account"), raise_exception=1)
def validate_c_form(self): def validate_c_form(self):
@@ -396,9 +391,9 @@ class SalesInvoice(SellingController):
ps = frappe.db.sql("""select name, warehouse from `tabPOS Setting` ps = frappe.db.sql("""select name, warehouse from `tabPOS Setting`
where ifnull(user,'') = '' and company = %s""", self.company) where ifnull(user,'') = '' and company = %s""", self.company)
if not ps: if not ps:
msgprint("To make POS entry, please create POS Setting from Accounts --> POS Setting page and refresh the system.", raise_exception=True) msgprint(_("POS Setting required to make POS Entry"), raise_exception=True)
elif not ps[0][1]: elif not ps[0][1]:
msgprint("Please enter warehouse in POS Setting") msgprint(_("Warehouse required in POS Setting"))
else: else:
w = ps[0][1] w = ps[0][1]
return w return w
@@ -426,7 +421,7 @@ class SalesInvoice(SellingController):
else: else:
# show message that the amount is not paid # show message that the amount is not paid
frappe.db.set(self,'paid_amount',0) frappe.db.set(self,'paid_amount',0)
frappe.msgprint("Note: Payment Entry will not be created since 'Cash/Bank Account' was not specified.") frappe.msgprint(_("Note: Payment Entry will not be created since 'Cash or Bank Account' was not specified"))
else: else:
frappe.db.set(self,'paid_amount',0) frappe.db.set(self,'paid_amount',0)
@@ -436,15 +431,15 @@ class SalesInvoice(SellingController):
submitted = frappe.db.sql("""select name from `tabSales Order` submitted = frappe.db.sql("""select name from `tabSales Order`
where docstatus = 1 and name = %s""", d.sales_order) where docstatus = 1 and name = %s""", d.sales_order)
if not submitted: if not submitted:
msgprint("Sales Order : "+ cstr(d.sales_order) +" is not submitted") msgprint(_("Sales Order {0} is not submitted").format(d.sales_order))
raise Exception , "Validation Error." raise Exception
if d.delivery_note: if d.delivery_note:
submitted = frappe.db.sql("""select name from `tabDelivery Note` submitted = frappe.db.sql("""select name from `tabDelivery Note`
where docstatus = 1 and name = %s""", d.delivery_note) where docstatus = 1 and name = %s""", d.delivery_note)
if not submitted: if not submitted:
msgprint("Delivery Note : "+ cstr(d.delivery_note) +" is not submitted") msgprint(_("Delivery Note {0} is not submitted").format(d.delivery_note))
raise Exception , "Validation Error." raise Exception
def update_stock_ledger(self): def update_stock_ledger(self):
sl_entries = [] sl_entries = []
@@ -594,15 +589,12 @@ class SalesInvoice(SellingController):
self.validate_notification_email_id() self.validate_notification_email_id()
if not self.recurring_type: if not self.recurring_type:
msgprint(_("Please select: ") + self.meta.get_label("recurring_type"), msgprint(_("Please select {0}").format(self.meta.get_label("recurring_type")),
raise_exception=1) raise_exception=1)
elif not (self.invoice_period_from_date and \ elif not (self.invoice_period_from_date and \
self.invoice_period_to_date): self.invoice_period_to_date):
msgprint(comma_and([self.meta.get_label("invoice_period_from_date"), throw(_("Invoice Period From and Invoice Period To dates mandatory for recurring invoice"))
self.meta.get_label("invoice_period_to_date")])
+ _(": Mandatory for a Recurring Invoice."),
raise_exception=True)
def convert_to_recurring(self): def convert_to_recurring(self):
if self.convert_into_recurring_invoice: if self.convert_into_recurring_invoice:
@@ -625,20 +617,15 @@ class SalesInvoice(SellingController):
from frappe.utils import validate_email_add from frappe.utils import validate_email_add
for email in email_list: for email in email_list:
if not validate_email_add(email): if not validate_email_add(email):
msgprint(self.meta.get_label("notification_email_address") \ throw(_("{0} is an invalid email address in 'Notification Email Address'").format(email))
+ " - " + _("Invalid Email Address") + ": \"%s\"" % email,
raise_exception=1)
else: else:
msgprint("Notification Email Addresses not specified for recurring invoice", throw(_("'Notification Email Addresses' not specified for recurring invoice"))
raise_exception=1)
def set_next_date(self): def set_next_date(self):
""" Set next date on which auto invoice will be created""" """ Set next date on which auto invoice will be created"""
if not self.repeat_on_day_of_month: if not self.repeat_on_day_of_month:
msgprint("""Please enter 'Repeat on Day of Month' field value. msgprint(_("Please enter 'Repeat on Day of Month' field value"), raise_exception=1)
The day of the month on which auto invoice
will be generated e.g. 05, 28 etc.""", raise_exception=1)
next_date = get_next_date(self.posting_date, next_date = get_next_date(self.posting_date,
month_map[self.recurring_type], cint(self.repeat_on_day_of_month)) month_map[self.recurring_type], cint(self.repeat_on_day_of_month))
@@ -765,7 +752,7 @@ def assign_task_to_owner(inv, msg, users):
def get_bank_cash_account(mode_of_payment): def get_bank_cash_account(mode_of_payment):
val = frappe.db.get_value("Mode of Payment", mode_of_payment, "default_account") val = frappe.db.get_value("Mode of Payment", mode_of_payment, "default_account")
if not val: if not val:
frappe.msgprint("Default Bank / Cash Account not set in Mode of Payment: %s. Please add a Default Account in Mode of Payment master." % mode_of_payment) frappe.msgprint(_("Please set default Cash or Bank account in Mode of Payment {0}").format(mode_of_payment))
return { return {
"cash_bank_account": val "cash_bank_account": val
} }

View File

@@ -5,7 +5,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe import _, msgprint from frappe import _, msgprint, throw
from frappe.utils import flt, fmt_money from frappe.utils import flt, fmt_money
from frappe.model.controller import DocListController from frappe.model.controller import DocListController
from erpnext.setup.utils import get_company_currency from erpnext.setup.utils import get_company_currency
@@ -15,36 +15,34 @@ class FromGreaterThanToError(frappe.ValidationError): pass
class ManyBlankToValuesError(frappe.ValidationError): pass class ManyBlankToValuesError(frappe.ValidationError): pass
class ShippingRule(DocListController): class ShippingRule(DocListController):
def validate(self): def validate(self):
self.validate_value("calculate_based_on", "in", ["Net Total", "Net Weight"]) self.validate_value("calculate_based_on", "in", ["Net Total", "Net Weight"])
self.shipping_rule_conditions = self.get("shipping_rule_conditions") self.shipping_rule_conditions = self.get("shipping_rule_conditions")
self.validate_from_to_values() self.validate_from_to_values()
self.sort_shipping_rule_conditions() self.sort_shipping_rule_conditions()
self.validate_overlapping_shipping_rule_conditions() self.validate_overlapping_shipping_rule_conditions()
def validate_from_to_values(self): def validate_from_to_values(self):
zero_to_values = [] zero_to_values = []
for d in self.get("shipping_rule_conditions"): for d in self.get("shipping_rule_conditions"):
self.round_floats_in(d) self.round_floats_in(d)
# values cannot be negative # values cannot be negative
self.validate_value("from_value", ">=", 0.0, d) self.validate_value("from_value", ">=", 0.0, d)
self.validate_value("to_value", ">=", 0.0, d) self.validate_value("to_value", ">=", 0.0, d)
if not d.to_value: if not d.to_value:
zero_to_values.append(d) zero_to_values.append(d)
elif d.from_value >= d.to_value: elif d.from_value >= d.to_value:
msgprint(_("Error") + ": " + _("Row") + " # %d: " % d.idx + throw(_("From value must be less than to value in row {0}").format(d.idx), FromGreaterThanToError)
_("From Value should be less than To Value"),
raise_exception=FromGreaterThanToError)
# check if more than two or more rows has To Value = 0 # check if more than two or more rows has To Value = 0
if len(zero_to_values) >= 2: if len(zero_to_values) >= 2:
msgprint(_('''There can only be one Shipping Rule Condition with 0 or blank value for "To Value"'''), throw(_('There can only be one Shipping Rule Condition with 0 or blank value for "To Value"'),
raise_exception=ManyBlankToValuesError) ManyBlankToValuesError)
def sort_shipping_rule_conditions(self): def sort_shipping_rule_conditions(self):
"""Sort Shipping Rule Conditions based on increasing From Value""" """Sort Shipping Rule Conditions based on increasing From Value"""
self.shipping_rules_conditions = sorted(self.shipping_rule_conditions, key=lambda d: flt(d.from_value)) self.shipping_rules_conditions = sorted(self.shipping_rule_conditions, key=lambda d: flt(d.from_value))
@@ -61,7 +59,7 @@ class ShippingRule(DocListController):
""" """
separate = (x1 <= x2 <= y1 <= y2) or (y1 <= y2 <= x1 <= x2) separate = (x1 <= x2 <= y1 <= y2) or (y1 <= y2 <= x1 <= x2)
return (not separate) return (not separate)
overlaps = [] overlaps = []
for i in xrange(0, len(self.shipping_rule_conditions)): for i in xrange(0, len(self.shipping_rule_conditions)):
for j in xrange(i+1, len(self.shipping_rule_conditions)): for j in xrange(i+1, len(self.shipping_rule_conditions)):
@@ -72,13 +70,13 @@ class ShippingRule(DocListController):
range_b = (d2.from_value, d2.to_value or d2.from_value) range_b = (d2.from_value, d2.to_value or d2.from_value)
if overlap_exists_between(range_a, range_b): if overlap_exists_between(range_a, range_b):
overlaps.append([d1, d2]) overlaps.append([d1, d2])
if overlaps: if overlaps:
company_currency = get_company_currency(self.company) company_currency = get_company_currency(self.company)
msgprint(_("Error") + ": " + _("Overlapping Conditions found between") + ":") msgprint(_("Overlapping conditions found between:"))
messages = [] messages = []
for d1, d2 in overlaps: for d1, d2 in overlaps:
messages.append("%s-%s = %s " % (d1.from_value, d1.to_value, fmt_money(d1.shipping_amount, currency=company_currency)) + messages.append("%s-%s = %s " % (d1.from_value, d1.to_value, fmt_money(d1.shipping_amount, currency=company_currency)) +
_("and") + " %s-%s = %s" % (d2.from_value, d2.to_value, fmt_money(d2.shipping_amount, currency=company_currency))) _("and") + " %s-%s = %s" % (d2.from_value, d2.to_value, fmt_money(d2.shipping_amount, currency=company_currency)))
msgprint("\n".join(messages), raise_exception=OverlappingConditionError) msgprint("\n".join(messages), raise_exception=OverlappingConditionError)

View File

@@ -180,4 +180,4 @@ def create_party_account(party, party_type, company):
"report_type": "Balance Sheet" "report_type": "Balance Sheet"
}).insert(ignore_permissions=True) }).insert(ignore_permissions=True)
frappe.msgprint(_("Account Created") + ": " + account.name) frappe.msgprint(_("Account Created: {0}").format(account.name))

View File

@@ -5,7 +5,7 @@ from __future__ import unicode_literals
import frappe import frappe
from frappe.utils import nowdate, cstr, flt, now, getdate, add_months from frappe.utils import nowdate, cstr, flt, now, getdate, add_months
from frappe import msgprint, throw, _ from frappe import throw, _
from frappe.utils import formatdate from frappe.utils import formatdate
from erpnext.utilities import build_filter_conditions from erpnext.utilities import build_filter_conditions
@@ -29,9 +29,7 @@ def get_fiscal_years(date=None, fiscal_year=None, label="Date", verbose=1):
from `tabFiscal Year` where %s order by year_start_date desc""" % cond) from `tabFiscal Year` where %s order by year_start_date desc""" % cond)
if not fy: if not fy:
error_msg = """%s %s not in any Fiscal Year""" % (label, formatdate(date)) error_msg = _("""{0} {1} not in any Fiscal Year""").format(label, formatdate(date))
error_msg = """{msg}: {date}""".format(msg=_("Fiscal Year does not exist for date"),
date=formatdate(date))
if verbose: frappe.msgprint(error_msg) if verbose: frappe.msgprint(error_msg)
raise FiscalYearError, error_msg raise FiscalYearError, error_msg
@@ -40,12 +38,7 @@ def get_fiscal_years(date=None, fiscal_year=None, label="Date", verbose=1):
def validate_fiscal_year(date, fiscal_year, label="Date"): def validate_fiscal_year(date, fiscal_year, label="Date"):
years = [f[0] for f in get_fiscal_years(date, label=label)] years = [f[0] for f in get_fiscal_years(date, label=label)]
if fiscal_year not in years: if fiscal_year not in years:
throw(("%(label)s '%(posting_date)s': " + _("not within Fiscal Year") + \ throw(_("{0} '{1}' not in Fiscal Year {2}").format(label, formatdate(date), fiscal_year))
": '%(fiscal_year)s'") % {
"label": label,
"posting_date": formatdate(date),
"fiscal_year": fiscal_year
})
@frappe.whitelist() @frappe.whitelist()
def get_balance_on(account=None, date=None): def get_balance_on(account=None, date=None):
@@ -62,7 +55,7 @@ def get_balance_on(account=None, date=None):
try: try:
year_start_date = get_fiscal_year(date, verbose=0)[1] year_start_date = get_fiscal_year(date, verbose=0)[1]
except FiscalYearError, e: except FiscalYearError:
if getdate(date) > getdate(nowdate()): if getdate(date) > getdate(nowdate()):
# if fiscal year not found and the date is greater than today # if fiscal year not found and the date is greater than today
# get fiscal year for today's date and its corresponding year start date # get fiscal year for today's date and its corresponding year start date
@@ -232,9 +225,7 @@ def remove_against_link_from_jv(ref_type, ref_no, against_field):
and voucher_no != ifnull(against_voucher, '')""", and voucher_no != ifnull(against_voucher, '')""",
(now(), frappe.session.user, ref_type, ref_no)) (now(), frappe.session.user, ref_type, ref_no))
frappe.msgprint("{msg} {linked_jv}".format(msg = _("""Following linked Journal Vouchers \ frappe.msgprint(_("Following Journal Vouchers Unlinked: {0}".format("\n".join(linked_jv))))
made against this transaction has been unlinked. You can link them again with other \
transactions via Payment Reconciliation Tool."""), linked_jv="\n".join(linked_jv)))
@frappe.whitelist() @frappe.whitelist()

View File

@@ -4,7 +4,7 @@
from __future__ import unicode_literals 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 msgprint, _
from erpnext.controllers.buying_controller import BuyingController from erpnext.controllers.buying_controller import BuyingController
class PurchaseOrder(BuyingController): class PurchaseOrder(BuyingController):
@@ -128,8 +128,8 @@ class PurchaseOrder(BuyingController):
date_diff = frappe.db.sql("select TIMEDIFF('%s', '%s')" % ( mod_db[0][0],cstr(self.modified))) date_diff = frappe.db.sql("select TIMEDIFF('%s', '%s')" % ( mod_db[0][0],cstr(self.modified)))
if date_diff and date_diff[0][0]: if date_diff and date_diff[0][0]:
msgprint(cstr(self.doctype) +" => "+ cstr(self.name) +" has been modified. Please Refresh. ") msgprint(_("{0} {1} has been modified. Please refresh").format(self.doctype, self.name),
raise Exception raise_exception=True)
def update_status(self, status): def update_status(self, status):
self.check_modified_date() self.check_modified_date()
@@ -140,7 +140,7 @@ class PurchaseOrder(BuyingController):
self.update_bin(is_submit = (status == 'Submitted') and 1 or 0, is_stopped = 1) self.update_bin(is_submit = (status == 'Submitted') and 1 or 0, is_stopped = 1)
# step 3:=> Acknowledge user # step 3:=> Acknowledge user
msgprint(self.doctype + ": " + self.name + " has been %s." % ((status == 'Submitted') and 'Unstopped' or cstr(status))) msgprint(_("Status of {0} {1} is now {2}").format(self.doctype, self.name, status))
def on_submit(self): def on_submit(self):
purchase_controller = frappe.get_doc("Purchase Common") purchase_controller = frappe.get_doc("Purchase Common")
@@ -163,12 +163,12 @@ class PurchaseOrder(BuyingController):
pc_obj.check_docstatus(check = 'Next', doctype = 'Purchase Receipt', docname = self.name, detail_doctype = 'Purchase Receipt Item') pc_obj.check_docstatus(check = 'Next', doctype = 'Purchase Receipt', docname = self.name, detail_doctype = 'Purchase Receipt Item')
# Check if Purchase Invoice has been submitted against current Purchase Order # Check if Purchase Invoice has been submitted against current Purchase Order
submitted = frappe.db.sql("""select t1.name submitted = frappe.db.sql_list("""select t1.name
from `tabPurchase Invoice` t1,`tabPurchase Invoice Item` t2 from `tabPurchase Invoice` t1,`tabPurchase Invoice Item` t2
where t1.name = t2.parent and t2.purchase_order = %s and t1.docstatus = 1""", where t1.name = t2.parent and t2.purchase_order = %s and t1.docstatus = 1""",
self.name) self.name)
if submitted: if submitted:
msgprint("Purchase Invoice : " + cstr(submitted[0][0]) + " has already been submitted !") msgprint(_("Purchase Invoice {0} is already submitted").format(", ".join(submitted)))
raise Exception raise Exception
frappe.db.set(self,'status','Cancelled') frappe.db.set(self,'status','Cancelled')

View File

@@ -55,7 +55,7 @@ class Supplier(TransactionBase):
#validation for Naming Series mandatory field... #validation for Naming Series mandatory field...
if frappe.defaults.get_global_default('supp_master_name') == 'Naming Series': if frappe.defaults.get_global_default('supp_master_name') == 'Naming Series':
if not self.naming_series: if not self.naming_series:
msgprint("Series is Mandatory.", raise_exception=1) msgprint(_("Series is mandatory"), raise_exception=1)
def get_contacts(self,nm): def get_contacts(self,nm):
if nm: if nm:
@@ -106,7 +106,7 @@ class Supplier(TransactionBase):
@frappe.whitelist() @frappe.whitelist()
def get_dashboard_info(supplier): def get_dashboard_info(supplier):
if not frappe.has_permission("Supplier", "read", supplier): if not frappe.has_permission("Supplier", "read", supplier):
frappe.msgprint("No Permission", raise_exception=True) frappe.throw(_("No permission"))
out = {} out = {}
for doctype in ["Supplier Quotation", "Purchase Order", "Purchase Receipt", "Purchase Invoice"]: for doctype in ["Supplier Quotation", "Purchase Order", "Purchase Receipt", "Purchase Invoice"]:

View File

@@ -417,8 +417,7 @@ class AccountsController(TransactionBase):
ref_amt = flt(frappe.db.get_value(ref_dt + " Item", ref_amt = flt(frappe.db.get_value(ref_dt + " Item",
item.get(item_ref_dn), based_on), self.precision(based_on, item)) item.get(item_ref_dn), based_on), self.precision(based_on, item))
if not ref_amt: if not ref_amt:
frappe.msgprint(_("As amount for item") + ": " + item.item_code + _(" in ") + frappe.msgprint(_("Warning: System will not check overbilling since amount for Item {0} in {1} is zero").format(item.item_code, ref_dt))
ref_dt + _(" is zero, system will not check for over-billed"))
else: else:
already_billed = frappe.db.sql("""select sum(%s) from `tab%s` already_billed = frappe.db.sql("""select sum(%s) from `tab%s`
where %s=%s and docstatus=1 and parent != %s""" % where %s=%s and docstatus=1 and parent != %s""" %

View File

@@ -14,22 +14,22 @@ class BuyingController(StockController):
def onload_post_render(self): def onload_post_render(self):
# contact, address, item details # contact, address, item details
self.set_missing_values() self.set_missing_values()
def validate(self): def validate(self):
super(BuyingController, self).validate() super(BuyingController, self).validate()
if getattr(self, "supplier", None) and not self.supplier_name: if getattr(self, "supplier", None) and not self.supplier_name:
self.supplier_name = frappe.db.get_value("Supplier", self.supplier_name = frappe.db.get_value("Supplier",
self.supplier, "supplier_name") self.supplier, "supplier_name")
self.is_item_table_empty() self.is_item_table_empty()
self.validate_stock_or_nonstock_items() self.validate_stock_or_nonstock_items()
self.validate_warehouse() self.validate_warehouse()
def set_missing_values(self, for_validate=False): def set_missing_values(self, for_validate=False):
super(BuyingController, self).set_missing_values(for_validate) super(BuyingController, self).set_missing_values(for_validate)
self.set_supplier_from_item_default() self.set_supplier_from_item_default()
self.set_price_list_currency("Buying") self.set_price_list_currency("Buying")
# set contact and address details for supplier, if they are not mentioned # set contact and address details for supplier, if they are not mentioned
if getattr(self, "supplier", None): if getattr(self, "supplier", None):
self.update_if_missing(get_party_details(self.supplier, party_type="Supplier")) self.update_if_missing(get_party_details(self.supplier, party_type="Supplier"))
@@ -45,24 +45,24 @@ class BuyingController(StockController):
if supplier: if supplier:
self.supplier = supplier self.supplier = supplier
break break
def validate_warehouse(self): def validate_warehouse(self):
from erpnext.stock.utils import validate_warehouse_company from erpnext.stock.utils import validate_warehouse_company
warehouses = list(set([d.warehouse for d in warehouses = list(set([d.warehouse for d in
self.get(self.fname) if getattr(d, "warehouse", None)])) self.get(self.fname) if getattr(d, "warehouse", None)]))
for w in warehouses: for w in warehouses:
validate_warehouse_company(w, self.company) validate_warehouse_company(w, self.company)
def validate_stock_or_nonstock_items(self): def validate_stock_or_nonstock_items(self):
if not self.get_stock_items(): if not self.get_stock_items():
tax_for_valuation = [d.account_head for d in tax_for_valuation = [d.account_head for d in
self.get("other_charges") self.get("other_charges")
if d.category in ["Valuation", "Valuation and Total"]] if d.category in ["Valuation", "Valuation and Total"]]
if tax_for_valuation: if tax_for_valuation:
frappe.msgprint(_("""Tax Category can not be 'Valuation' or 'Valuation and Total' as all items are non-stock items"""), raise_exception=1) frappe.throw(_("Tax Category can not be 'Valuation' or 'Valuation and Total' as all items are non-stock items"))
def set_total_in_words(self): def set_total_in_words(self):
from frappe.utils import money_in_words from frappe.utils import money_in_words
company_currency = get_company_currency(self.company) company_currency = get_company_currency(self.company)
@@ -71,12 +71,12 @@ class BuyingController(StockController):
if self.meta.get_field("in_words_import"): if self.meta.get_field("in_words_import"):
self.in_words_import = money_in_words(self.grand_total_import, self.in_words_import = money_in_words(self.grand_total_import,
self.currency) self.currency)
def calculate_taxes_and_totals(self): def calculate_taxes_and_totals(self):
self.other_fname = "other_charges" self.other_fname = "other_charges"
super(BuyingController, self).calculate_taxes_and_totals() super(BuyingController, self).calculate_taxes_and_totals()
self.calculate_total_advance("Purchase Invoice", "advance_allocation_details") self.calculate_total_advance("Purchase Invoice", "advance_allocation_details")
def calculate_item_values(self): def calculate_item_values(self):
for item in self.item_doclist: for item in self.item_doclist:
self.round_floats_in(item) self.round_floats_in(item)
@@ -86,7 +86,7 @@ class BuyingController(StockController):
elif not item.rate: elif not item.rate:
item.rate = flt(item.price_list_rate * (1.0 - (item.discount_percentage / 100.0)), item.rate = flt(item.price_list_rate * (1.0 - (item.discount_percentage / 100.0)),
self.precision("rate", item)) self.precision("rate", item))
item.amount = flt(item.rate * item.qty, item.amount = flt(item.rate * item.qty,
self.precision("amount", item)) self.precision("amount", item))
item.item_tax_amount = 0.0; item.item_tax_amount = 0.0;
@@ -94,19 +94,19 @@ class BuyingController(StockController):
self._set_in_company_currency(item, "amount", "base_amount") self._set_in_company_currency(item, "amount", "base_amount")
self._set_in_company_currency(item, "price_list_rate", "base_price_list_rate") self._set_in_company_currency(item, "price_list_rate", "base_price_list_rate")
self._set_in_company_currency(item, "rate", "base_rate") self._set_in_company_currency(item, "rate", "base_rate")
def calculate_net_total(self): def calculate_net_total(self):
self.net_total = self.net_total_import = 0.0 self.net_total = self.net_total_import = 0.0
for item in self.item_doclist: for item in self.item_doclist:
self.net_total += item.base_amount self.net_total += item.base_amount
self.net_total_import += item.amount self.net_total_import += item.amount
self.round_floats_in(self, ["net_total", "net_total_import"]) self.round_floats_in(self, ["net_total", "net_total_import"])
def calculate_totals(self): def calculate_totals(self):
self.grand_total = flt(self.tax_doclist[-1].total if self.tax_doclist self.grand_total = flt(self.tax_doclist[-1].total if self.tax_doclist
else self.net_total, self.precision("grand_total")) else self.net_total, self.precision("grand_total"))
self.grand_total_import = flt(self.grand_total / self.conversion_rate, self.grand_total_import = flt(self.grand_total / self.conversion_rate,
self.precision("grand_total_import")) self.precision("grand_total_import"))
@@ -116,28 +116,28 @@ class BuyingController(StockController):
if self.meta.get_field("rounded_total"): if self.meta.get_field("rounded_total"):
self.rounded_total = _round(self.grand_total) self.rounded_total = _round(self.grand_total)
if self.meta.get_field("rounded_total_import"): if self.meta.get_field("rounded_total_import"):
self.rounded_total_import = _round(self.grand_total_import) self.rounded_total_import = _round(self.grand_total_import)
if self.meta.get_field("other_charges_added"): if self.meta.get_field("other_charges_added"):
self.other_charges_added = flt(sum([flt(d.tax_amount) for d in self.tax_doclist self.other_charges_added = flt(sum([flt(d.tax_amount) for d in self.tax_doclist
if d.add_deduct_tax=="Add" and d.category in ["Valuation and Total", "Total"]]), if d.add_deduct_tax=="Add" and d.category in ["Valuation and Total", "Total"]]),
self.precision("other_charges_added")) self.precision("other_charges_added"))
if self.meta.get_field("other_charges_deducted"): if self.meta.get_field("other_charges_deducted"):
self.other_charges_deducted = flt(sum([flt(d.tax_amount) for d in self.tax_doclist self.other_charges_deducted = flt(sum([flt(d.tax_amount) for d in self.tax_doclist
if d.add_deduct_tax=="Deduct" and d.category in ["Valuation and Total", "Total"]]), if d.add_deduct_tax=="Deduct" and d.category in ["Valuation and Total", "Total"]]),
self.precision("other_charges_deducted")) self.precision("other_charges_deducted"))
if self.meta.get_field("other_charges_added_import"): if self.meta.get_field("other_charges_added_import"):
self.other_charges_added_import = flt(self.other_charges_added / self.other_charges_added_import = flt(self.other_charges_added /
self.conversion_rate, self.precision("other_charges_added_import")) self.conversion_rate, self.precision("other_charges_added_import"))
if self.meta.get_field("other_charges_deducted_import"): if self.meta.get_field("other_charges_deducted_import"):
self.other_charges_deducted_import = flt(self.other_charges_deducted / self.other_charges_deducted_import = flt(self.other_charges_deducted /
self.conversion_rate, self.precision("other_charges_deducted_import")) self.conversion_rate, self.precision("other_charges_deducted_import"))
def calculate_outstanding_amount(self): def calculate_outstanding_amount(self):
if self.doctype == "Purchase Invoice" and self.docstatus == 0: if self.doctype == "Purchase Invoice" and self.docstatus == 0:
self.total_advance = flt(self.total_advance, self.total_advance = flt(self.total_advance,
@@ -146,17 +146,17 @@ class BuyingController(StockController):
self.precision("write_off_amount")), self.precision("total_amount_to_pay")) self.precision("write_off_amount")), self.precision("total_amount_to_pay"))
self.outstanding_amount = flt(self.total_amount_to_pay - self.total_advance, self.outstanding_amount = flt(self.total_amount_to_pay - self.total_advance,
self.precision("outstanding_amount")) self.precision("outstanding_amount"))
# update valuation rate # update valuation rate
def update_valuation_rate(self, parentfield): def update_valuation_rate(self, parentfield):
""" """
item_tax_amount is the total tax amount applied on that item item_tax_amount is the total tax amount applied on that item
stored for valuation stored for valuation
TODO: rename item_tax_amount to valuation_tax_amount TODO: rename item_tax_amount to valuation_tax_amount
""" """
stock_items = self.get_stock_items() stock_items = self.get_stock_items()
stock_items_qty, stock_items_amount = 0, 0 stock_items_qty, stock_items_amount = 0, 0
last_stock_item_idx = 1 last_stock_item_idx = 1
for d in self.get(parentfield): for d in self.get(parentfield):
@@ -164,47 +164,45 @@ class BuyingController(StockController):
stock_items_qty += flt(d.qty) stock_items_qty += flt(d.qty)
stock_items_amount += flt(d.base_amount) stock_items_amount += flt(d.base_amount)
last_stock_item_idx = d.idx last_stock_item_idx = d.idx
total_valuation_amount = sum([flt(d.tax_amount) for d in total_valuation_amount = sum([flt(d.tax_amount) for d in
self.get("other_charges") self.get("other_charges")
if d.category in ["Valuation", "Valuation and Total"]]) if d.category in ["Valuation", "Valuation and Total"]])
valuation_amount_adjustment = total_valuation_amount valuation_amount_adjustment = total_valuation_amount
for i, item in enumerate(self.get(parentfield)): for i, item in enumerate(self.get(parentfield)):
if item.item_code and item.qty and item.item_code in stock_items: if item.item_code and item.qty and item.item_code in stock_items:
item_proportion = flt(item.base_amount) / stock_items_amount if stock_items_amount \ item_proportion = flt(item.base_amount) / stock_items_amount if stock_items_amount \
else flt(item.qty) / stock_items_qty else flt(item.qty) / stock_items_qty
if i == (last_stock_item_idx - 1): if i == (last_stock_item_idx - 1):
item.item_tax_amount = flt(valuation_amount_adjustment, item.item_tax_amount = flt(valuation_amount_adjustment,
self.precision("item_tax_amount", item)) self.precision("item_tax_amount", item))
else: else:
item.item_tax_amount = flt(item_proportion * total_valuation_amount, item.item_tax_amount = flt(item_proportion * total_valuation_amount,
self.precision("item_tax_amount", item)) self.precision("item_tax_amount", item))
valuation_amount_adjustment -= item.item_tax_amount valuation_amount_adjustment -= item.item_tax_amount
self.round_floats_in(item) self.round_floats_in(item)
item.conversion_factor = item.conversion_factor or flt(frappe.db.get_value( item.conversion_factor = item.conversion_factor or flt(frappe.db.get_value(
"UOM Conversion Detail", {"parent": item.item_code, "uom": item.uom}, "UOM Conversion Detail", {"parent": item.item_code, "uom": item.uom},
"conversion_factor")) or 1 "conversion_factor")) or 1
qty_in_stock_uom = flt(item.qty * item.conversion_factor) qty_in_stock_uom = flt(item.qty * item.conversion_factor)
item.valuation_rate = ((item.base_amount + item.item_tax_amount + item.rm_supp_cost) item.valuation_rate = ((item.base_amount + item.item_tax_amount + item.rm_supp_cost)
/ qty_in_stock_uom) / qty_in_stock_uom)
else: else:
item.valuation_rate = 0.0 item.valuation_rate = 0.0
def validate_for_subcontracting(self): def validate_for_subcontracting(self):
if not self.is_subcontracted and self.sub_contracted_items: if not self.is_subcontracted and self.sub_contracted_items:
frappe.msgprint(_("""Please enter whether %s is made for subcontracting or purchasing, frappe.throw(_("Please enter 'Is Subcontracted' as Yes or No"))
in 'Is Subcontracted' field""" % self.doctype), raise_exception=1)
if self.doctype == "Purchase Receipt" and self.is_subcontracted=="Yes" \ if self.doctype == "Purchase Receipt" and self.is_subcontracted=="Yes" \
and not self.supplier_warehouse: and not self.supplier_warehouse:
frappe.msgprint(_("Supplier Warehouse mandatory subcontracted purchase receipt"), frappe.throw(_("Supplier Warehouse mandatory for sub-contracted Purchase Receipt"))
raise_exception=1)
def update_raw_materials_supplied(self, raw_material_table): def update_raw_materials_supplied(self, raw_material_table):
self.set(raw_material_table, []) self.set(raw_material_table, [])
if self.is_subcontracted=="Yes": if self.is_subcontracted=="Yes":
@@ -234,30 +232,30 @@ class BuyingController(StockController):
"consumed_qty": required_qty, "consumed_qty": required_qty,
"description": item.description, "description": item.description,
}) })
self.append(raw_material_table, rm_doclist) self.append(raw_material_table, rm_doclist)
raw_materials_cost += required_qty * flt(item.rate) raw_materials_cost += required_qty * flt(item.rate)
if self.doctype == "Purchase Receipt": if self.doctype == "Purchase Receipt":
d.rm_supp_cost = raw_materials_cost d.rm_supp_cost = raw_materials_cost
def get_items_from_default_bom(self, item_code): def get_items_from_default_bom(self, item_code):
bom_items = frappe.db.sql("""select t2.item_code, t2.qty_consumed_per_unit, bom_items = frappe.db.sql("""select t2.item_code, t2.qty_consumed_per_unit,
t2.rate, t2.stock_uom, t2.name, t2.description t2.rate, t2.stock_uom, t2.name, t2.description
from `tabBOM` t1, `tabBOM Item` t2 from `tabBOM` t1, `tabBOM Item` t2
where t2.parent = t1.name and t1.item = %s and t1.is_default = 1 where t2.parent = t1.name and t1.item = %s and t1.is_default = 1
and t1.docstatus = 1 and t1.is_active = 1""", item_code, as_dict=1) and t1.docstatus = 1 and t1.is_active = 1""", item_code, as_dict=1)
if not bom_items: if not bom_items:
msgprint(_("No default BOM exists for item: ") + item_code, raise_exception=1) msgprint(_("No default BOM exists for Item {0}").format(item_code), raise_exception=1)
return bom_items return bom_items
@property @property
def sub_contracted_items(self): def sub_contracted_items(self):
if not hasattr(self, "_sub_contracted_items"): if not hasattr(self, "_sub_contracted_items"):
self._sub_contracted_items = [] self._sub_contracted_items = []
item_codes = list(set(item.item_code for item in item_codes = list(set(item.item_code for item in
self.get(self.fname))) self.get(self.fname)))
if item_codes: if item_codes:
self._sub_contracted_items = [r[0] for r in frappe.db.sql("""select name self._sub_contracted_items = [r[0] for r in frappe.db.sql("""select name
@@ -265,12 +263,12 @@ class BuyingController(StockController):
(", ".join((["%s"]*len(item_codes))),), item_codes)] (", ".join((["%s"]*len(item_codes))),), item_codes)]
return self._sub_contracted_items return self._sub_contracted_items
@property @property
def purchase_items(self): def purchase_items(self):
if not hasattr(self, "_purchase_items"): if not hasattr(self, "_purchase_items"):
self._purchase_items = [] self._purchase_items = []
item_codes = list(set(item.item_code for item in item_codes = list(set(item.item_code for item in
self.get(self.fname))) self.get(self.fname)))
if item_codes: if item_codes:
self._purchase_items = [r[0] for r in frappe.db.sql("""select name self._purchase_items = [r[0] for r in frappe.db.sql("""select name
@@ -282,4 +280,4 @@ class BuyingController(StockController):
def is_item_table_empty(self): def is_item_table_empty(self):
if not len(self.get(self.fname)): if not len(self.get(self.fname)):
frappe.throw(_("Item table can not be blank")) frappe.throw(_("Item table can not be blank"))

View File

@@ -3,9 +3,9 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe.utils import cint, flt, comma_or, _round, cstr from frappe.utils import cint, flt, _round, cstr
from erpnext.setup.utils import get_company_currency from erpnext.setup.utils import get_company_currency
from frappe import msgprint, _ from frappe import _, throw
from erpnext.controllers.stock_controller import StockController from erpnext.controllers.stock_controller import StockController
@@ -233,8 +233,7 @@ class SellingController(StockController):
if self.meta.get_field("commission_rate"): if self.meta.get_field("commission_rate"):
self.round_floats_in(self, ["net_total", "commission_rate"]) self.round_floats_in(self, ["net_total", "commission_rate"])
if self.commission_rate > 100.0: if self.commission_rate > 100.0:
msgprint(_(self.meta.get_label("commission_rate")) + " " + throw(_("Commission rate cannot be greater than 100"))
_("cannot be greater than 100"), raise_exception=True)
self.total_commission = flt(self.net_total * self.commission_rate / 100.0, self.total_commission = flt(self.net_total * self.commission_rate / 100.0,
self.precision("total_commission")) self.precision("total_commission"))
@@ -252,17 +251,14 @@ class SellingController(StockController):
total += sales_person.allocated_percentage total += sales_person.allocated_percentage
if sales_team and total != 100.0: if sales_team and total != 100.0:
msgprint(_("Total") + " " + throw(_("Total allocated percentage for sales team should be 100"))
_(self.meta.get_label("allocated_percentage", parentfield="sales_team")) +
" " + _("should be 100%"), raise_exception=True)
def validate_order_type(self): def validate_order_type(self):
valid_types = ["Sales", "Maintenance", "Shopping Cart"] valid_types = ["Sales", "Maintenance", "Shopping Cart"]
if not self.order_type: if not self.order_type:
self.order_type = "Sales" self.order_type = "Sales"
elif self.order_type not in valid_types: elif self.order_type not in valid_types:
msgprint(_(self.meta.get_label("order_type")) + " " + throw(_("Order Type must be one of {1}").comma_or(valid_types))
_("must be one of") + ": " + comma_or(valid_types), raise_exception=True)
def check_credit(self, grand_total): def check_credit(self, grand_total):
customer_account = frappe.db.get_value("Account", {"company": self.company, customer_account = frappe.db.get_value("Account", {"company": self.company,

View File

@@ -3,8 +3,8 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe.utils import flt, cstr from frappe.utils import flt
from frappe import msgprint from frappe import msgprint, _, throw
from frappe.model.controller import DocListController from frappe.model.controller import DocListController
@@ -129,19 +129,13 @@ class StatusUpdater(DocListController):
item['target_ref_field'] = args['target_ref_field'].replace('_', ' ') item['target_ref_field'] = args['target_ref_field'].replace('_', ' ')
if not item[args['target_ref_field']]: if not item[args['target_ref_field']]:
msgprint("""As %(target_ref_field)s for item: %(item_code)s in \ msgprint(_("Note: System will not check over-delivery and over-booking for Item {0} as quantity or amount is 0").format(item.item_code))
%(parenttype)s: %(parent)s is zero, system will not check \
over-delivery or over-billed""" % item)
elif args.get('no_tolerance'): elif args.get('no_tolerance'):
item['reduce_by'] = item[args['target_field']] - \ item['reduce_by'] = item[args['target_field']] - \
item[args['target_ref_field']] item[args['target_ref_field']]
if item['reduce_by'] > .01: if item['reduce_by'] > .01:
msgprint(""" msgprint(_("Allowance for over-delivery / over-billing crossed for Item {0}").format(item.item_code))
Row #%(idx)s: Max %(target_ref_field)s allowed for <b>Item \ throw(_("{0} must be less than or equal to {1}").format(_(item.target_ref_field), item[args["target_ref_field"]]))
%(item_code)s</b> against <b>%(parenttype)s %(parent)s</b> \
is <b>""" % item + cstr(item[args['target_ref_field']]) +
"""</b>.<br>You must reduce the %(target_ref_field)s by \
%(reduce_by)s""" % item, raise_exception=1)
else: else:
self.check_overflow_with_tolerance(item, args) self.check_overflow_with_tolerance(item, args)
@@ -161,18 +155,8 @@ class StatusUpdater(DocListController):
item['max_allowed'] = flt(item[args['target_ref_field']] * (100+tolerance)/100) item['max_allowed'] = flt(item[args['target_ref_field']] * (100+tolerance)/100)
item['reduce_by'] = item[args['target_field']] - item['max_allowed'] item['reduce_by'] = item[args['target_field']] - item['max_allowed']
msgprint(""" msgprint(_("Allowance for over-delivery / over-billing crossed for Item {0}").format(item["item_code"]))
Row #%(idx)s: Max %(target_ref_field)s allowed for <b>Item %(item_code)s</b> \ throw(_("{0} must be less than or equal to {1}").format(_(item["target_ref_field"]), item[args["max_allowed"]]))
against <b>%(parenttype)s %(parent)s</b> is <b>%(max_allowed)s</b>.
If you want to increase your overflow tolerance, please increase tolerance %% in \
Global Defaults or Item master.
Or, you must reduce the %(target_ref_field)s by %(reduce_by)s
Also, please check if the order item has already been billed in the Sales Order""" %
item, raise_exception=1)
def update_qty(self, change_modified=True): def update_qty(self, change_modified=True):
""" """

View File

@@ -65,7 +65,7 @@ class StockController(AccountsController):
warehouse_with_no_account.append(sle.warehouse) warehouse_with_no_account.append(sle.warehouse)
if warehouse_with_no_account: if warehouse_with_no_account:
msgprint(_("No accounting entries for following warehouses") + ": \n" + msgprint(_("No accounting entries for the following warehouses") + ": \n" +
"\n".join(warehouse_with_no_account)) "\n".join(warehouse_with_no_account))
return process_gl_map(gl_list) return process_gl_map(gl_list)
@@ -231,12 +231,10 @@ class StockController(AccountsController):
def check_expense_account(self, item): def check_expense_account(self, item):
if item.meta.get_field("expense_account") and not item.expense_account: if item.meta.get_field("expense_account") and not item.expense_account:
msgprint(_("""Expense/Difference account is mandatory for item: """) + item.item_code, frappe.throw(_("Expense or Difference account is mandatory for Item {0}").format(item.item_code))
raise_exception=1)
if item.meta.get_field("expense_account") and not item.cost_center: if item.meta.get_field("expense_account") and not item.cost_center:
msgprint(_("""Cost Center is mandatory for item: """) + item.item_code, frappe.throw(_("""Cost Center is mandatory for item {0}""").format(item.item_code))
raise_exception=1)
def get_sl_entries(self, d, args): def get_sl_entries(self, d, args):
sl_dict = { sl_dict = {

View File

@@ -3,12 +3,12 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe.utils import add_days, add_months, cstr, getdate from frappe.utils import getdate
from frappe import _ from frappe import _
def get_columns(filters, trans): def get_columns(filters, trans):
validate_filters(filters) validate_filters(filters)
# get conditions for based_on filter cond # get conditions for based_on filter cond
based_on_details = based_wise_colums_query(filters.get("based_on"), trans) based_on_details = based_wise_colums_query(filters.get("based_on"), trans)
# get conditions for periodic filter cond # get conditions for periodic filter cond
@@ -17,11 +17,11 @@ def get_columns(filters, trans):
group_by_cols = group_wise_column(filters.get("group_by")) group_by_cols = group_wise_column(filters.get("group_by"))
columns = based_on_details["based_on_cols"] + period_cols + ["Total(Qty):Float:120", "Total(Amt):Currency:120"] columns = based_on_details["based_on_cols"] + period_cols + ["Total(Qty):Float:120", "Total(Amt):Currency:120"]
if group_by_cols: if group_by_cols:
columns = based_on_details["based_on_cols"] + group_by_cols + period_cols + \ columns = based_on_details["based_on_cols"] + group_by_cols + period_cols + \
["Total(Qty):Float:120", "Total(Amt):Currency:120"] ["Total(Qty):Float:120", "Total(Amt):Currency:120"]
conditions = {"based_on_select": based_on_details["based_on_select"], "period_wise_select": period_select, conditions = {"based_on_select": based_on_details["based_on_select"], "period_wise_select": period_select,
"columns": columns, "group_by": based_on_details["based_on_group_by"], "grbc": group_by_cols, "trans": trans, "columns": columns, "group_by": based_on_details["based_on_group_by"], "grbc": group_by_cols, "trans": trans,
"addl_tables": based_on_details["addl_tables"]} "addl_tables": based_on_details["addl_tables"]}
@@ -30,16 +30,16 @@ def get_columns(filters, trans):
def validate_filters(filters): def validate_filters(filters):
for f in ["Fiscal Year", "Based On", "Period", "Company"]: for f in ["Fiscal Year", "Based On", "Period", "Company"]:
if not filters.get(f.lower().replace(" ", "_")): if not filters.get(f.lower().replace(" ", "_")):
frappe.msgprint(f + _(" is mandatory"), raise_exception=1) frappe.throw(_("{0} is mandatory").format(f))
if filters.get("based_on") == filters.get("group_by"): if filters.get("based_on") == filters.get("group_by"):
frappe.msgprint("'Based On' and 'Group By' can not be same", raise_exception=1) frappe.throw(_("'Based On' and 'Group By' can not be same"))
def get_data(filters, conditions): def get_data(filters, conditions):
data = [] data = []
inc, cond= '','' inc, cond= '',''
query_details = conditions["based_on_select"] + conditions["period_wise_select"] query_details = conditions["based_on_select"] + conditions["period_wise_select"]
if conditions["based_on_select"] in ["t1.project_name,", "t2.project_name,"]: if conditions["based_on_select"] in ["t1.project_name,", "t2.project_name,"]:
cond = 'and '+ conditions["based_on_select"][:-1] +' IS Not NULL' cond = 'and '+ conditions["based_on_select"][:-1] +' IS Not NULL'
@@ -59,55 +59,55 @@ def get_data(filters, conditions):
else : else :
inc = 1 inc = 1
data1 = frappe.db.sql(""" select %s from `tab%s` t1, `tab%s Item` t2 %s data1 = frappe.db.sql(""" select %s from `tab%s` t1, `tab%s Item` t2 %s
where t2.parent = t1.name and t1.company = %s and t1.fiscal_year = %s and where t2.parent = t1.name and t1.company = %s and t1.fiscal_year = %s and
t1.docstatus = 1 %s t1.docstatus = 1 %s
group by %s group by %s
""" % (query_details, conditions["trans"], conditions["trans"], conditions["addl_tables"], "%s", """ % (query_details, conditions["trans"], conditions["trans"], conditions["addl_tables"], "%s",
"%s", cond, conditions["group_by"]), (filters.get("company"), "%s", cond, conditions["group_by"]), (filters.get("company"),
filters["fiscal_year"]),as_list=1) filters["fiscal_year"]),as_list=1)
for d in range(len(data1)): for d in range(len(data1)):
#to add blanck column #to add blanck column
dt = data1[d] dt = data1[d]
dt.insert(ind,'') dt.insert(ind,'')
data.append(dt) data.append(dt)
#to get distinct value of col specified by group_by in filter #to get distinct value of col specified by group_by in filter
row = frappe.db.sql("""select DISTINCT(%s) from `tab%s` t1, `tab%s Item` t2 %s row = frappe.db.sql("""select DISTINCT(%s) from `tab%s` t1, `tab%s Item` t2 %s
where t2.parent = t1.name and t1.company = %s and t1.fiscal_year = %s where t2.parent = t1.name and t1.company = %s and t1.fiscal_year = %s
and t1.docstatus = 1 and %s = %s and t1.docstatus = 1 and %s = %s
""" % """ %
(sel_col, conditions["trans"], conditions["trans"], conditions["addl_tables"], (sel_col, conditions["trans"], conditions["trans"], conditions["addl_tables"],
"%s", "%s", conditions["group_by"], "%s"), "%s", "%s", conditions["group_by"], "%s"),
(filters.get("company"), filters.get("fiscal_year"), data1[d][0]), as_list=1) (filters.get("company"), filters.get("fiscal_year"), data1[d][0]), as_list=1)
for i in range(len(row)): for i in range(len(row)):
des = ['' for q in range(len(conditions["columns"]))] des = ['' for q in range(len(conditions["columns"]))]
#get data for group_by filter #get data for group_by filter
row1 = frappe.db.sql(""" select %s , %s from `tab%s` t1, `tab%s Item` t2 %s row1 = frappe.db.sql(""" select %s , %s from `tab%s` t1, `tab%s Item` t2 %s
where t2.parent = t1.name and t1.company = %s and t1.fiscal_year = %s where t2.parent = t1.name and t1.company = %s and t1.fiscal_year = %s
and t1.docstatus = 1 and %s = %s and %s = %s and t1.docstatus = 1 and %s = %s and %s = %s
""" % """ %
(sel_col, conditions["period_wise_select"], conditions["trans"], (sel_col, conditions["period_wise_select"], conditions["trans"],
conditions["trans"], conditions["addl_tables"], "%s", "%s", sel_col, conditions["trans"], conditions["addl_tables"], "%s", "%s", sel_col,
"%s", conditions["group_by"], "%s"), "%s", conditions["group_by"], "%s"),
(filters.get("company"), filters.get("fiscal_year"), row[i][0], (filters.get("company"), filters.get("fiscal_year"), row[i][0],
data1[d][0]), as_list=1) data1[d][0]), as_list=1)
des[ind] = row[i] des[ind] = row[i]
for j in range(1,len(conditions["columns"])-inc): for j in range(1,len(conditions["columns"])-inc):
des[j+inc] = row1[0][j] des[j+inc] = row1[0][j]
data.append(des) data.append(des)
else: else:
data = frappe.db.sql(""" select %s from `tab%s` t1, `tab%s Item` t2 %s data = frappe.db.sql(""" select %s from `tab%s` t1, `tab%s Item` t2 %s
where t2.parent = t1.name and t1.company = %s and t1.fiscal_year = %s and where t2.parent = t1.name and t1.company = %s and t1.fiscal_year = %s and
t1.docstatus = 1 %s t1.docstatus = 1 %s
group by %s group by %s
""" % """ %
(query_details, conditions["trans"], conditions["trans"], conditions["addl_tables"], (query_details, conditions["trans"], conditions["trans"], conditions["addl_tables"],
"%s", "%s", cond,conditions["group_by"]), "%s", "%s", cond,conditions["group_by"]),
(filters.get("company"), filters.get("fiscal_year")), as_list=1) (filters.get("company"), filters.get("fiscal_year")), as_list=1)
return data return data
@@ -124,13 +124,13 @@ def period_wise_colums_query(filters, trans):
trans_date = 'posting_date' trans_date = 'posting_date'
else: else:
trans_date = 'transaction_date' trans_date = 'transaction_date'
if filters.get("period") != 'Yearly': if filters.get("period") != 'Yearly':
for dt in bet_dates: for dt in bet_dates:
get_period_wise_columns(dt, filters.get("period"), pwc) get_period_wise_columns(dt, filters.get("period"), pwc)
query_details = get_period_wise_query(dt, trans_date, query_details) query_details = get_period_wise_query(dt, trans_date, query_details)
else: else:
pwc = [filters.get("fiscal_year") + " (Qty):Float:120", pwc = [filters.get("fiscal_year") + " (Qty):Float:120",
filters.get("fiscal_year") + " (Amt):Currency:120"] filters.get("fiscal_year") + " (Amt):Currency:120"]
query_details = " SUM(t2.qty), SUM(t1.grand_total)," query_details = " SUM(t2.qty), SUM(t1.grand_total),"
@@ -139,14 +139,14 @@ def period_wise_colums_query(filters, trans):
def get_period_wise_columns(bet_dates, period, pwc): def get_period_wise_columns(bet_dates, period, pwc):
if period == 'Monthly': if period == 'Monthly':
pwc += [get_mon(bet_dates[0]) + " (Qty):Float:120", pwc += [get_mon(bet_dates[0]) + " (Qty):Float:120",
get_mon(bet_dates[0]) + " (Amt):Currency:120"] get_mon(bet_dates[0]) + " (Amt):Currency:120"]
else: else:
pwc += [get_mon(bet_dates[0]) + "-" + get_mon(bet_dates[1]) + " (Qty):Float:120", pwc += [get_mon(bet_dates[0]) + "-" + get_mon(bet_dates[1]) + " (Qty):Float:120",
get_mon(bet_dates[0]) + "-" + get_mon(bet_dates[1]) + " (Amt):Currency:120"] get_mon(bet_dates[0]) + "-" + get_mon(bet_dates[1]) + " (Amt):Currency:120"]
def get_period_wise_query(bet_dates, trans_date, query_details): def get_period_wise_query(bet_dates, trans_date, query_details):
query_details += """SUM(IF(t1.%(trans_date)s BETWEEN '%(sd)s' AND '%(ed)s', t2.qty, NULL)), query_details += """SUM(IF(t1.%(trans_date)s BETWEEN '%(sd)s' AND '%(ed)s', t2.qty, NULL)),
SUM(IF(t1.%(trans_date)s BETWEEN '%(sd)s' AND '%(ed)s', t1.grand_total, NULL)), SUM(IF(t1.%(trans_date)s BETWEEN '%(sd)s' AND '%(ed)s', t1.grand_total, NULL)),
""" % {"trans_date": trans_date, "sd": bet_dates[0],"ed": bet_dates[1]} """ % {"trans_date": trans_date, "sd": bet_dates[0],"ed": bet_dates[1]}
return query_details return query_details
@@ -156,7 +156,7 @@ def get_period_date_ranges(period, fiscal_year=None, year_start_date=None):
from dateutil.relativedelta import relativedelta from dateutil.relativedelta import relativedelta
if not year_start_date: if not year_start_date:
year_start_date, year_end_date = frappe.db.get_value("Fiscal Year", year_start_date, year_end_date = frappe.db.get_value("Fiscal Year",
fiscal_year, ["year_start_date", "year_end_date"]) fiscal_year, ["year_start_date", "year_end_date"])
increment = { increment = {
@@ -197,13 +197,13 @@ def based_wise_colums_query(based_on, trans):
# based_on_cols, based_on_select, based_on_group_by, addl_tables # based_on_cols, based_on_select, based_on_group_by, addl_tables
if based_on == "Item": if based_on == "Item":
based_on_details["based_on_cols"] = ["Item:Link/Item:120", "Item Name:Data:120"] based_on_details["based_on_cols"] = ["Item:Link/Item:120", "Item Name:Data:120"]
based_on_details["based_on_select"] = "t2.item_code, t2.item_name," based_on_details["based_on_select"] = "t2.item_code, t2.item_name,"
based_on_details["based_on_group_by"] = 't2.item_code' based_on_details["based_on_group_by"] = 't2.item_code'
based_on_details["addl_tables"] = '' based_on_details["addl_tables"] = ''
elif based_on == "Item Group": elif based_on == "Item Group":
based_on_details["based_on_cols"] = ["Item Group:Link/Item Group:120"] based_on_details["based_on_cols"] = ["Item Group:Link/Item Group:120"]
based_on_details["based_on_select"] = "t2.item_group," based_on_details["based_on_select"] = "t2.item_group,"
based_on_details["based_on_group_by"] = 't2.item_group' based_on_details["based_on_group_by"] = 't2.item_group'
based_on_details["addl_tables"] = '' based_on_details["addl_tables"] = ''
@@ -224,7 +224,7 @@ def based_wise_colums_query(based_on, trans):
based_on_details["based_on_select"] = "t1.supplier, t3.supplier_type," based_on_details["based_on_select"] = "t1.supplier, t3.supplier_type,"
based_on_details["based_on_group_by"] = 't1.supplier' based_on_details["based_on_group_by"] = 't1.supplier'
based_on_details["addl_tables"] = ',`tabSupplier` t3' based_on_details["addl_tables"] = ',`tabSupplier` t3'
elif based_on == 'Supplier Type': elif based_on == 'Supplier Type':
based_on_details["based_on_cols"] = ["Supplier Type:Link/Supplier Type:140"] based_on_details["based_on_cols"] = ["Supplier Type:Link/Supplier Type:140"]
based_on_details["based_on_select"] = "t3.supplier_type," based_on_details["based_on_select"] = "t3.supplier_type,"
@@ -249,7 +249,7 @@ def based_wise_colums_query(based_on, trans):
based_on_details["based_on_group_by"] = 't2.project_name' based_on_details["based_on_group_by"] = 't2.project_name'
based_on_details["addl_tables"] = '' based_on_details["addl_tables"] = ''
else: else:
frappe.msgprint("Project-wise data is not available for Quotation", raise_exception=1) frappe.throw(_("Project-wise data is not available for Quotation"))
return based_on_details return based_on_details
@@ -257,4 +257,4 @@ def group_wise_column(group_by):
if group_by: if group_by:
return [group_by+":Link/"+group_by+":120"] return [group_by+":Link/"+group_by+":120"]
else: else:
return [] return []

View File

@@ -22,22 +22,22 @@ class Appraisal(Document):
def get_employee_name(self): def get_employee_name(self):
self.employee_name = frappe.db.get_value("Employee", self.employee, "employee_name") self.employee_name = frappe.db.get_value("Employee", self.employee, "employee_name")
return self.employee_name return self.employee_name
def validate_dates(self): def validate_dates(self):
if getdate(self.start_date) > getdate(self.end_date): if getdate(self.start_date) > getdate(self.end_date):
frappe.throw(_("End Date can not be less than Start Date")) frappe.throw(_("End Date can not be less than Start Date"))
def validate_existing_appraisal(self): def validate_existing_appraisal(self):
chk = frappe.db.sql("""select name from `tabAppraisal` where employee=%s chk = frappe.db.sql("""select name from `tabAppraisal` where employee=%s
and (status='Submitted' or status='Completed') and (status='Submitted' or status='Completed')
and ((start_date>=%s and start_date<=%s) and ((start_date>=%s and start_date<=%s)
or (end_date>=%s and end_date<=%s))""", or (end_date>=%s and end_date<=%s))""",
(self.employee,self.start_date,self.end_date,self.start_date,self.end_date)) (self.employee,self.start_date,self.end_date,self.start_date,self.end_date))
if chk: if chk:
frappe.throw("You have already created Appraisal "\ frappe.throw("You have already created Appraisal "\
+cstr(chk[0][0])+" in the current date range for employee "\ +cstr(chk[0][0])+" in the current date range for employee "\
+cstr(self.employee_name)) +cstr(self.employee_name))
def calculate_total(self): def calculate_total(self):
total, total_w = 0, 0 total, total_w = 0, 0
for d in self.get('appraisal_details'): for d in self.get('appraisal_details'):
@@ -47,30 +47,29 @@ class Appraisal(Document):
total_w += flt(d.per_weightage) total_w += flt(d.per_weightage)
if int(total_w) != 100: if int(total_w) != 100:
msgprint("Total weightage assigned should be 100%. It is :" + str(total_w) + "%", frappe.throw(_("Total weightage assigned should be 100%. It is {0}").format(str(total_w) + "%"))
raise_exception=1)
if frappe.db.get_value("Employee", self.employee, "user_id") != \ if frappe.db.get_value("Employee", self.employee, "user_id") != \
frappe.session.user and total == 0: frappe.session.user and total == 0:
msgprint("Total can't be zero. You must atleast give some points!", raise_exception=1) frappe.throw(_("Total cannot be zero"))
self.total_score = total self.total_score = total
def on_submit(self): def on_submit(self):
frappe.db.set(self, 'status', 'Submitted') frappe.db.set(self, 'status', 'Submitted')
def on_cancel(self): def on_cancel(self):
frappe.db.set(self, 'status', 'Cancelled') frappe.db.set(self, 'status', 'Cancelled')
@frappe.whitelist() @frappe.whitelist()
def fetch_appraisal_template(source_name, target_doc=None): def fetch_appraisal_template(source_name, target_doc=None):
target_doc = get_mapped_doc("Appraisal Template", source_name, { target_doc = get_mapped_doc("Appraisal Template", source_name, {
"Appraisal Template": { "Appraisal Template": {
"doctype": "Appraisal", "doctype": "Appraisal",
}, },
"Appraisal Template Goal": { "Appraisal Template Goal": {
"doctype": "Appraisal Goal", "doctype": "Appraisal Goal",
} }
}, target_doc) }, target_doc)
return target_doc return target_doc

View File

@@ -12,8 +12,6 @@ class AppraisalTemplate(Document):
self.total_points = 0 self.total_points = 0
for d in self.get("kra_sheet"): for d in self.get("kra_sheet"):
self.total_points += int(d.per_weightage or 0) self.total_points += int(d.per_weightage or 0)
if int(self.total_points) != 100: if int(self.total_points) != 100:
frappe.msgprint(_("Total (sum of) points distribution for all goals should be 100.") \ frappe.throw(_("Total points for all goals should be 100. It is {0}").format(self.total_points))
+ " " + _("Not") + " " + str(self.total_points),
raise_exception=True)

View File

@@ -5,44 +5,41 @@ from __future__ import unicode_literals
import frappe import frappe
from frappe.utils import getdate, nowdate from frappe.utils import getdate, nowdate
from frappe import msgprint, _ from frappe import _
from frappe.model.document import Document from frappe.model.document import Document
class Attendance(Document): class Attendance(Document):
def validate_duplicate_record(self): def validate_duplicate_record(self):
res = frappe.db.sql("""select name from `tabAttendance` where employee = %s and att_date = %s res = frappe.db.sql("""select name from `tabAttendance` where employee = %s and att_date = %s
and name != %s and docstatus = 1""", and name != %s and docstatus = 1""",
(self.employee, self.att_date, self.name)) (self.employee, self.att_date, self.name))
if res: if res:
msgprint(_("Attendance for the employee: ") + self.employee + frappe.throw(_("Attendance for employee {0} is already marked").format(self.employee))
_(" already marked"), raise_exception=1)
def check_leave_record(self): def check_leave_record(self):
if self.status == 'Present': if self.status == 'Present':
leave = frappe.db.sql("""select name from `tabLeave Application` leave = frappe.db.sql("""select name from `tabLeave Application`
where employee = %s and %s between from_date and to_date and status = 'Approved' where employee = %s and %s between from_date and to_date and status = 'Approved'
and docstatus = 1""", (self.employee, self.att_date)) and docstatus = 1""", (self.employee, self.att_date))
if leave: if leave:
frappe.msgprint(_("Employee: ") + self.employee + _(" was on leave on ") frappe.throw(_("Employee {0} was on leave on {1}. Cannot mark attendance.").format(self.employee,
+ self.att_date + _(". You can not mark his attendance as 'Present'"), self.att_date))
raise_exception=1)
def validate_fiscal_year(self): def validate_fiscal_year(self):
from erpnext.accounts.utils import validate_fiscal_year from erpnext.accounts.utils import validate_fiscal_year
validate_fiscal_year(self.att_date, self.fiscal_year) validate_fiscal_year(self.att_date, self.fiscal_year)
def validate_att_date(self): def validate_att_date(self):
if getdate(self.att_date) > getdate(nowdate()): if getdate(self.att_date) > getdate(nowdate()):
msgprint(_("Attendance can not be marked for future dates"), raise_exception=1) frappe.throw(_("Attendance can not be marked for future dates"))
def validate_employee(self): def validate_employee(self):
emp = frappe.db.sql("select name from `tabEmployee` where name = %s and status = 'Active'", emp = frappe.db.sql("select name from `tabEmployee` where name = %s and status = 'Active'",
self.employee) self.employee)
if not emp: if not emp:
msgprint(_("Employee: ") + self.employee + frappe.throw(_("Employee {0} is not active or does not exist").format(self.employee))
_(" not active or does not exists in the system"), raise_exception=1)
def validate(self): def validate(self):
from erpnext.utilities import validate_status from erpnext.utilities import validate_status
validate_status(self.status, ["Present", "Absent", "Half Day"]) validate_status(self.status, ["Present", "Absent", "Half Day"])
@@ -50,9 +47,9 @@ class Attendance(Document):
self.validate_att_date() self.validate_att_date()
self.validate_duplicate_record() self.validate_duplicate_record()
self.check_leave_record() self.check_leave_record()
def on_update(self): def on_update(self):
# this is done because sometimes user entered wrong employee name # this is done because sometimes user entered wrong employee name
# while uploading employee attendance # while uploading employee attendance
employee_name = frappe.db.get_value("Employee", self.employee, "employee_name") employee_name = frappe.db.get_value("Employee", self.employee, "employee_name")
frappe.db.set(self, 'employee_name', employee_name) frappe.db.set(self, 'employee_name', employee_name)

View File

@@ -3,25 +3,22 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe import msgprint from frappe import _
from frappe.model.document import Document from frappe.model.document import Document
class ExpenseClaim(Document): class ExpenseClaim(Document):
def validate(self): def validate(self):
self.validate_fiscal_year() self.validate_fiscal_year()
self.validate_exp_details() self.validate_exp_details()
def on_submit(self): def on_submit(self):
if self.approval_status=="Draft": if self.approval_status=="Draft":
frappe.msgprint("""Please set Approval Status to 'Approved' or \ frappe.throw(_("""Approval Status must be 'Approved' or 'Rejected'"""))
'Rejected' before submitting""", raise_exception=1)
def validate_fiscal_year(self): def validate_fiscal_year(self):
from erpnext.accounts.utils import validate_fiscal_year from erpnext.accounts.utils import validate_fiscal_year
validate_fiscal_year(self.posting_date, self.fiscal_year, "Posting Date") validate_fiscal_year(self.posting_date, self.fiscal_year, "Posting Date")
def validate_exp_details(self): def validate_exp_details(self):
if not self.get('expense_voucher_details'): if not self.get('expense_voucher_details'):
msgprint("Please add expense voucher details") frappe.throw(_("Please add expense voucher details"))
raise Exception

View File

@@ -4,20 +4,19 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe.utils import add_days, add_years, cint, getdate from frappe.utils import cint
from frappe.model.naming import make_autoname from frappe.model.naming import make_autoname
from frappe import msgprint, throw, _ from frappe import throw, _
import datetime
from frappe.model.document import Document from frappe.model.document import Document
class HolidayList(Document): class HolidayList(Document):
def autoname(self): def autoname(self):
self.name = make_autoname(self.fiscal_year + "/" + self.holiday_list_name + "/.###") self.name = make_autoname(self.fiscal_year + "/" + self.holiday_list_name + "/.###")
def validate(self): def validate(self):
self.update_default_holiday_list() self.update_default_holiday_list()
def get_weekly_off_dates(self): def get_weekly_off_dates(self):
self.validate_values() self.validate_values()
yr_start_date, yr_end_date = self.get_fy_start_end_dates() yr_start_date, yr_end_date = self.get_fy_start_end_dates()
@@ -42,24 +41,24 @@ class HolidayList(Document):
def get_weekly_off_date_list(self, year_start_date, year_end_date): def get_weekly_off_date_list(self, year_start_date, year_end_date):
from frappe.utils import getdate from frappe.utils import getdate
year_start_date, year_end_date = getdate(year_start_date), getdate(year_end_date) year_start_date, year_end_date = getdate(year_start_date), getdate(year_end_date)
from dateutil import relativedelta from dateutil import relativedelta
from datetime import timedelta from datetime import timedelta
import calendar import calendar
date_list = [] date_list = []
weekday = getattr(calendar, (self.weekly_off).upper()) weekday = getattr(calendar, (self.weekly_off).upper())
reference_date = year_start_date + relativedelta.relativedelta(weekday=weekday) reference_date = year_start_date + relativedelta.relativedelta(weekday=weekday)
while reference_date <= year_end_date: while reference_date <= year_end_date:
date_list.append(reference_date) date_list.append(reference_date)
reference_date += timedelta(days=7) reference_date += timedelta(days=7)
return date_list return date_list
def clear_table(self): def clear_table(self):
self.set('holiday_list_details', []) self.set('holiday_list_details', [])
def update_default_holiday_list(self): def update_default_holiday_list(self):
frappe.db.sql("""update `tabHoliday List` set is_default = 0 frappe.db.sql("""update `tabHoliday List` set is_default = 0
where ifnull(is_default, 0) = 1 and fiscal_year = %s""", (self.fiscal_year,)) where ifnull(is_default, 0) = 1 and fiscal_year = %s""", (self.fiscal_year,))

View File

@@ -4,67 +4,46 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe.utils import cint, flt from frappe.utils import cint, flt
from frappe import msgprint from frappe import _
from frappe.model.document import Document from frappe.model.document import Document
class LeaveAllocation(Document): class LeaveAllocation(Document):
def validate(self): def validate(self):
self.validate_new_leaves_allocated_value() self.validate_new_leaves_allocated_value()
self.check_existing_leave_allocation() self.check_existing_leave_allocation()
self.validate_new_leaves_allocated()
def on_update_after_submit(self): def on_update_after_submit(self):
self.validate_new_leaves_allocated_value() self.validate_new_leaves_allocated_value()
self.validate_new_leaves_allocated()
def on_update(self): def on_update(self):
self.get_total_allocated_leaves() self.get_total_allocated_leaves()
def on_cancel(self): def on_cancel(self):
self.check_for_leave_application() self.check_for_leave_application()
def validate_new_leaves_allocated_value(self): def validate_new_leaves_allocated_value(self):
"""validate that leave allocation is in multiples of 0.5""" """validate that leave allocation is in multiples of 0.5"""
if flt(self.new_leaves_allocated) % 0.5: if flt(self.new_leaves_allocated) % 0.5:
guess = round(flt(self.new_leaves_allocated) * 2.0) / 2.0 frappe.throw(_("Leaves must be allocated in multiples of 0.5"))
msgprint("""New Leaves Allocated should be a multiple of 0.5.
Perhaps you should enter %s or %s""" % (guess, guess + 0.5),
raise_exception=1)
def check_existing_leave_allocation(self): def check_existing_leave_allocation(self):
"""check whether leave for same type is already allocated or not""" """check whether leave for same type is already allocated or not"""
leave_allocation = frappe.db.sql("""select name from `tabLeave Allocation` leave_allocation = frappe.db.sql("""select name from `tabLeave Allocation`
where employee=%s and leave_type=%s and fiscal_year=%s and docstatus=1""", where employee=%s and leave_type=%s and fiscal_year=%s and docstatus=1""",
(self.employee, self.leave_type, self.fiscal_year)) (self.employee, self.leave_type, self.fiscal_year))
if leave_allocation: if leave_allocation:
msgprint("""%s is already allocated to Employee: %s for Fiscal Year: %s. frappe.msgprint(_("Leaves for type {0} already allocated for Employee {1} for Fiscal Year {0}").format(self.leave_type,
Please refere Leave Allocation: \ self.employee, self.fiscal_year))
<a href="#Form/Leave Allocation/%s">%s</a>""" % \ frappe.throw('<a href="#Form/Leave Allocation/{0}">{0}</a>'.format(leave_allocation[0][0]))
(self.leave_type, self.employee, self.fiscal_year,
leave_allocation[0][0], leave_allocation[0][0]), raise_exception=1)
def validate_new_leaves_allocated(self):
"""check if Total Leaves Allocated >= Leave Applications"""
self.total_leaves_allocated = flt(self.carry_forwarded_leaves) + \
flt(self.new_leaves_allocated)
leaves_applied = self.get_leaves_applied(self.fiscal_year)
if leaves_applied > self.total_leaves_allocated:
expected_new_leaves = flt(self.new_leaves_allocated) + \
(leaves_applied - self.total_leaves_allocated)
msgprint("""Employee: %s has already applied for %s leaves.
Hence, New Leaves Allocated should be atleast %s""" % \
(self.employee, leaves_applied, expected_new_leaves),
raise_exception=1)
def get_leave_bal(self, prev_fyear): def get_leave_bal(self, prev_fyear):
return self.get_leaves_allocated(prev_fyear) - self.get_leaves_applied(prev_fyear) return self.get_leaves_allocated(prev_fyear) - self.get_leaves_applied(prev_fyear)
def get_leaves_applied(self, fiscal_year): def get_leaves_applied(self, fiscal_year):
leaves_applied = frappe.db.sql("""select SUM(ifnull(total_leave_days, 0)) leaves_applied = frappe.db.sql("""select SUM(ifnull(total_leave_days, 0))
from `tabLeave Application` where employee=%s and leave_type=%s from `tabLeave Application` where employee=%s and leave_type=%s
and fiscal_year=%s and docstatus=1""", and fiscal_year=%s and docstatus=1""",
(self.employee, self.leave_type, fiscal_year)) (self.employee, self.leave_type, fiscal_year))
return leaves_applied and flt(leaves_applied[0][0]) or 0 return leaves_applied and flt(leaves_applied[0][0]) or 0
@@ -74,7 +53,7 @@ class LeaveAllocation(Document):
and fiscal_year=%s and docstatus=1 and name!=%s""", and fiscal_year=%s and docstatus=1 and name!=%s""",
(self.employee, self.leave_type, fiscal_year, self.name)) (self.employee, self.leave_type, fiscal_year, self.name))
return leaves_allocated and flt(leaves_allocated[0][0]) or 0 return leaves_allocated and flt(leaves_allocated[0][0]) or 0
def allow_carry_forward(self): def allow_carry_forward(self):
"""check whether carry forward is allowed or not for this leave type""" """check whether carry forward is allowed or not for this leave type"""
cf = frappe.db.sql("""select is_carry_forward from `tabLeave Type` where name = %s""", cf = frappe.db.sql("""select is_carry_forward from `tabLeave Type` where name = %s""",
@@ -82,15 +61,14 @@ 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)
msgprint("Sorry! You cannot carry forward %s" % (self.leave_type), frappe.throw("Cannot carry forward {0}".format(self.leave_type))
raise_exception=1)
def get_carry_forwarded_leaves(self): def get_carry_forwarded_leaves(self):
if self.carry_forward: if self.carry_forward:
self.allow_carry_forward() self.allow_carry_forward()
prev_fiscal_year = frappe.db.sql("""select name from `tabFiscal Year` prev_fiscal_year = frappe.db.sql("""select name from `tabFiscal Year`
where year_start_date = (select date_add(year_start_date, interval -1 year) where year_start_date = (select date_add(year_start_date, interval -1 year)
from `tabFiscal Year` where name=%s) from `tabFiscal Year` where name=%s)
order by name desc limit 1""", self.fiscal_year) order by name desc limit 1""", self.fiscal_year)
prev_fiscal_year = prev_fiscal_year and prev_fiscal_year[0][0] or '' prev_fiscal_year = prev_fiscal_year and prev_fiscal_year[0][0] or ''
prev_bal = 0 prev_bal = 0
@@ -112,10 +90,7 @@ class LeaveAllocation(Document):
where employee=%s and leave_type=%s and fiscal_year=%s and docstatus=1""", where employee=%s and leave_type=%s and fiscal_year=%s and docstatus=1""",
(self.employee, self.leave_type, self.fiscal_year)) (self.employee, self.leave_type, self.fiscal_year))
if exists: if exists:
msgprint("""Cannot cancel this Leave Allocation as \ frappe.msgprint(_("Cannot cancel because Employee {0} is already approved for {1}").format(self.employee,
Employee : %s has already applied for %s. self.leave_type))
Please check Leave Application: \ frappe.throw('<a href="#Form/Leave Application/{0}">{0}</a>'.format(exists[0][0]))
<a href="#Form/Leave Application/%s">%s</a>""" % \
(self.employee, self.leave_type, exists[0][0], exists[0][0]))
raise Exception

View File

@@ -13,7 +13,7 @@ class LeaveDayBlockedError(frappe.ValidationError): pass
class OverlapError(frappe.ValidationError): pass class OverlapError(frappe.ValidationError): pass
class InvalidLeaveApproverError(frappe.ValidationError): pass class InvalidLeaveApproverError(frappe.ValidationError): pass
class LeaveApproverIdentityError(frappe.ValidationError): pass class LeaveApproverIdentityError(frappe.ValidationError): pass
from frappe.model.controller import DocListController from frappe.model.controller import DocListController
class LeaveApplication(DocListController): class LeaveApplication(DocListController):
def setup(self): def setup(self):
@@ -21,7 +21,7 @@ class LeaveApplication(DocListController):
self.previous_doc = frappe.db.get_value(self.doctype, self.name, "*", as_dict=True) self.previous_doc = frappe.db.get_value(self.doctype, self.name, "*", as_dict=True)
else: else:
self.previous_doc = None self.previous_doc = None
def validate(self): def validate(self):
self.validate_to_date() self.validate_to_date()
self.validate_balance_leaves() self.validate_balance_leaves()
@@ -30,7 +30,7 @@ class LeaveApplication(DocListController):
self.show_block_day_warning() self.show_block_day_warning()
self.validate_block_days() self.validate_block_days()
self.validate_leave_approver() self.validate_leave_approver()
def on_update(self): def on_update(self):
if (not self.previous_doc and self.leave_approver) or (self.previous_doc and \ if (not self.previous_doc and self.leave_approver) or (self.previous_doc and \
self.status == "Open" and self.previous_doc.leave_approver != self.leave_approver): self.status == "Open" and self.previous_doc.leave_approver != self.leave_approver):
@@ -40,25 +40,24 @@ class LeaveApplication(DocListController):
self.previous_doc.status == "Open" and self.status == "Rejected": self.previous_doc.status == "Open" and self.status == "Rejected":
# notify employee about rejection # notify employee about rejection
self.notify_employee(self.status) self.notify_employee(self.status)
def on_submit(self): def on_submit(self):
if self.status != "Approved": if self.status != "Approved":
frappe.msgprint("""Only Leave Applications with status 'Approved' can be Submitted.""", frappe.throw(_("Only Leave Applications with status 'Approved' can be submitted"))
raise_exception=True)
# notify leave applier about approval # notify leave applier about approval
self.notify_employee(self.status) self.notify_employee(self.status)
def on_cancel(self): def on_cancel(self):
# notify leave applier about cancellation # notify leave applier about cancellation
self.notify_employee("cancelled") self.notify_employee("cancelled")
def show_block_day_warning(self): def show_block_day_warning(self):
from erpnext.hr.doctype.leave_block_list.leave_block_list import get_applicable_block_dates from erpnext.hr.doctype.leave_block_list.leave_block_list import get_applicable_block_dates
block_dates = get_applicable_block_dates(self.from_date, self.to_date, block_dates = get_applicable_block_dates(self.from_date, self.to_date,
self.employee, self.company, all_lists=True) self.employee, self.company, all_lists=True)
if block_dates: if block_dates:
frappe.msgprint(_("Warning: Leave application contains following block dates") + ":") frappe.msgprint(_("Warning: Leave application contains following block dates") + ":")
for d in block_dates: for d in block_dates:
@@ -67,20 +66,20 @@ class LeaveApplication(DocListController):
def validate_block_days(self): def validate_block_days(self):
from erpnext.hr.doctype.leave_block_list.leave_block_list import get_applicable_block_dates from erpnext.hr.doctype.leave_block_list.leave_block_list import get_applicable_block_dates
block_dates = get_applicable_block_dates(self.from_date, self.to_date, block_dates = get_applicable_block_dates(self.from_date, self.to_date,
self.employee, self.company) self.employee, self.company)
if block_dates: if block_dates:
if self.status == "Approved": if self.status == "Approved":
frappe.msgprint(_("Cannot approve leave as you are not authorized to approve leaves on Block Dates.")) frappe.msgprint(_("Cannot approve leave as you are not authorized to approve leaves on Block Dates"))
raise LeaveDayBlockedError raise LeaveDayBlockedError
def get_holidays(self): def get_holidays(self):
tot_hol = frappe.db.sql("""select count(*) from `tabHoliday` h1, `tabHoliday List` h2, `tabEmployee` e1 tot_hol = frappe.db.sql("""select count(*) from `tabHoliday` h1, `tabHoliday List` h2, `tabEmployee` e1
where e1.name = %s and h1.parent = h2.name and e1.holiday_list = h2.name where e1.name = %s and h1.parent = h2.name and e1.holiday_list = h2.name
and h1.holiday_date between %s and %s""", (self.employee, self.from_date, self.to_date)) and h1.holiday_date between %s and %s""", (self.employee, self.from_date, self.to_date))
if not tot_hol: if not tot_hol:
tot_hol = frappe.db.sql("""select count(*) from `tabHoliday` h1, `tabHoliday List` h2 tot_hol = frappe.db.sql("""select count(*) from `tabHoliday` h1, `tabHoliday List` h2
where h1.parent = h2.name and h1.holiday_date between %s and %s where h1.parent = h2.name and h1.holiday_date between %s and %s
and ifnull(h2.is_default,0) = 1 and h2.fiscal_year = %s""", and ifnull(h2.is_default,0) = 1 and h2.fiscal_year = %s""",
(self.from_date, self.to_date, self.fiscal_year)) (self.from_date, self.to_date, self.fiscal_year))
@@ -100,17 +99,15 @@ class LeaveApplication(DocListController):
def validate_to_date(self): def validate_to_date(self):
if self.from_date and self.to_date and \ if self.from_date and self.to_date and \
(getdate(self.to_date) < getdate(self.from_date)): (getdate(self.to_date) < getdate(self.from_date)):
msgprint("To date cannot be before from date") frappe.throw(_("To date cannot be before from date"))
raise Exception
def validate_balance_leaves(self): def validate_balance_leaves(self):
if self.from_date and self.to_date: if self.from_date and self.to_date:
self.total_leave_days = self.get_total_leave_days()["total_leave_days"] self.total_leave_days = self.get_total_leave_days()["total_leave_days"]
if self.total_leave_days == 0: if self.total_leave_days == 0:
msgprint(_("The day(s) on which you are applying for leave coincide with holiday(s). You need not apply for leave."), frappe.throw(_("The day(s) on which you are applying for leave are holiday. You need not apply for leave."))
raise_exception=1)
if not is_lwp(self.leave_type): if not is_lwp(self.leave_type):
self.leave_balance = get_leave_balance(self.employee, self.leave_balance = get_leave_balance(self.employee,
self.leave_type, self.fiscal_year)["leave_balance"] self.leave_type, self.fiscal_year)["leave_balance"]
@@ -118,94 +115,96 @@ class LeaveApplication(DocListController):
if self.status != "Rejected" \ if self.status != "Rejected" \
and self.leave_balance - self.total_leave_days < 0: and self.leave_balance - self.total_leave_days < 0:
#check if this leave type allow the remaining balance to be in negative. If yes then warn the user and continue to save else warn the user and don't save. #check if this leave type allow the remaining balance to be in negative. If yes then warn the user and continue to save else warn the user and don't save.
msgprint("There is not enough leave balance for Leave Type: %s" % \ if frappe.db.get_value("Leave Type", self.leave_type, "allow_negative"):
(self.leave_type,), frappe.msgprint(_("Note: There is not enough leave balance for Leave Type {0}").format(self.leave_type))
raise_exception=not(frappe.db.get_value("Leave Type", self.leave_type,"allow_negative") or None)) else:
frappe.throw(_("There is not enough leave balance for Leave Type {0}").format(self.leave_type))
def validate_leave_overlap(self): def validate_leave_overlap(self):
if not self.name: if not self.name:
self.name = "New Leave Application" self.name = "New Leave Application"
for d in frappe.db.sql("""select name, leave_type, posting_date, for d in frappe.db.sql("""select name, leave_type, posting_date,
from_date, to_date from_date, to_date
from `tabLeave Application` from `tabLeave Application`
where where
employee = %(employee)s employee = %(employee)s
and docstatus < 2 and docstatus < 2
and status in ("Open", "Approved") and status in ("Open", "Approved")
and (from_date between %(from_date)s and %(to_date)s and (from_date between %(from_date)s and %(to_date)s
or to_date between %(from_date)s and %(to_date)s or to_date between %(from_date)s and %(to_date)s
or %(from_date)s between from_date and to_date) or %(from_date)s between from_date and to_date)
and name != %(name)s""", self.as_dict(), as_dict = 1): and name != %(name)s""", self.as_dict(), as_dict = 1):
msgprint("Employee : %s has already applied for %s between %s and %s on %s. Please refer Leave Application : <a href=\"#Form/Leave Application/%s\">%s</a>" % (self.employee, cstr(d['leave_type']), formatdate(d['from_date']), formatdate(d['to_date']), formatdate(d['posting_date']), d['name'], d['name']), raise_exception = OverlapError) frappe.msgprint(_("Employee {0} has already applied for {1} between {2} and {3}").format(self.employee,
cstr(d['leave_type']), formatdate(d['from_date']), formatdate(d['to_date'])))
frappe.throw('<a href="#Form/Leave Application/{0}">{0}</a>'.format(d["name"]), OverlapError)
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("Sorry ! You cannot apply for %s for more than %s days" %
(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)
leave_approvers = [l.leave_approver for l in leave_approvers = [l.leave_approver for l in
employee.get("employee_leave_approvers")] employee.get("employee_leave_approvers")]
if len(leave_approvers) and self.leave_approver not in leave_approvers: if len(leave_approvers) and self.leave_approver not in leave_approvers:
msgprint(("[" + _("For Employee") + ' "' + self.employee + '"] ' frappe.throw(_("Leave approver must be one of {0}").format(comma_or(leave_approvers)), InvalidLeaveApproverError)
+ _("Leave Approver can be one of") + ": "
+ comma_or(leave_approvers)), raise_exception=InvalidLeaveApproverError) elif self.leave_approver and not frappe.db.sql("""select name from `tabUserRole`
elif self.leave_approver and not frappe.db.sql("""select name from `tabUserRole`
where parent=%s and role='Leave Approver'""", self.leave_approver): where parent=%s and role='Leave Approver'""", self.leave_approver):
msgprint(get_fullname(self.leave_approver) + ": " \ frappe.throw(_("{0} must have role 'Leave Approver'").format(get_fullname(self.leave_approver)),
+ _("does not have role 'Leave Approver'"), raise_exception=InvalidLeaveApproverError) InvalidLeaveApproverError)
elif self.docstatus==1 and len(leave_approvers) and self.leave_approver != frappe.session.user: elif self.docstatus==1 and len(leave_approvers) and self.leave_approver != frappe.session.user:
msgprint(_("Only the selected Leave Approver can submit this Leave Application"), msgprint(_("Only the selected Leave Approver can submit this Leave Application"),
raise_exception=LeaveApproverIdentityError) raise_exception=LeaveApproverIdentityError)
def notify_employee(self, status): def notify_employee(self, status):
employee = frappe.get_doc("Employee", self.employee) employee = frappe.get_doc("Employee", self.employee)
if not employee.user_id: if not employee.user_id:
return return
def _get_message(url=False): def _get_message(url=False):
if url: if url:
name = get_url_to_form(self.doctype, self.name) name = get_url_to_form(self.doctype, self.name)
else: else:
name = self.name name = self.name
return (_("Leave Application") + ": %s - %s") % (name, _(status)) return (_("Leave Application") + ": %s - %s") % (name, _(status))
self.notify({ self.notify({
# for post in messages # for post in messages
"message": _get_message(url=True), "message": _get_message(url=True),
"message_to": employee.user_id, "message_to": employee.user_id,
"subject": _get_message(), "subject": _get_message(),
}) })
def notify_leave_approver(self): def notify_leave_approver(self):
employee = frappe.get_doc("Employee", self.employee) employee = frappe.get_doc("Employee", self.employee)
def _get_message(url=False): def _get_message(url=False):
name = self.name name = self.name
employee_name = cstr(employee.employee_name) employee_name = cstr(employee.employee_name)
if url: if url:
name = get_url_to_form(self.doctype, self.name) name = get_url_to_form(self.doctype, self.name)
employee_name = get_url_to_form("Employee", self.employee, label=employee_name) employee_name = get_url_to_form("Employee", self.employee, label=employee_name)
return (_("New Leave Application") + ": %s - " + _("Employee") + ": %s") % (name, employee_name) return (_("New Leave Application") + ": %s - " + _("Employee") + ": %s") % (name, employee_name)
self.notify({ self.notify({
# for post in messages # for post in messages
"message": _get_message(url=True), "message": _get_message(url=True),
"message_to": self.leave_approver, "message_to": self.leave_approver,
# for email # for email
"subject": _get_message() "subject": _get_message()
}) })
def notify(self, args): def notify(self, args):
args = frappe._dict(args) args = frappe._dict(args)
from frappe.core.page.messages.messages import post from frappe.core.page.messages.messages import post
@@ -213,62 +212,62 @@ class LeaveApplication(DocListController):
"notify": cint(self.follow_via_email)}) "notify": cint(self.follow_via_email)})
@frappe.whitelist() @frappe.whitelist()
def get_leave_balance(employee, leave_type, fiscal_year): def get_leave_balance(employee, leave_type, fiscal_year):
leave_all = frappe.db.sql("""select total_leaves_allocated leave_all = frappe.db.sql("""select total_leaves_allocated
from `tabLeave Allocation` where employee = %s and leave_type = %s from `tabLeave Allocation` where employee = %s and leave_type = %s
and fiscal_year = %s and docstatus = 1""", (employee, and fiscal_year = %s and docstatus = 1""", (employee,
leave_type, fiscal_year)) leave_type, fiscal_year))
leave_all = leave_all and flt(leave_all[0][0]) or 0 leave_all = leave_all and flt(leave_all[0][0]) or 0
leave_app = frappe.db.sql("""select SUM(total_leave_days) leave_app = frappe.db.sql("""select SUM(total_leave_days)
from `tabLeave Application` from `tabLeave Application`
where employee = %s and leave_type = %s and fiscal_year = %s where employee = %s and leave_type = %s and fiscal_year = %s
and status="Approved" and docstatus = 1""", (employee, leave_type, fiscal_year)) and status="Approved" and docstatus = 1""", (employee, leave_type, fiscal_year))
leave_app = leave_app and flt(leave_app[0][0]) or 0 leave_app = leave_app and flt(leave_app[0][0]) or 0
ret = {'leave_balance': leave_all - leave_app} ret = {'leave_balance': leave_all - leave_app}
return ret return ret
def is_lwp(leave_type): def is_lwp(leave_type):
lwp = frappe.db.sql("select is_lwp from `tabLeave Type` where name = %s", leave_type) lwp = frappe.db.sql("select is_lwp from `tabLeave Type` where name = %s", leave_type)
return lwp and cint(lwp[0][0]) or 0 return lwp and cint(lwp[0][0]) or 0
@frappe.whitelist() @frappe.whitelist()
def get_events(start, end): def get_events(start, end):
events = [] events = []
employee = frappe.db.get_default("employee", frappe.session.user) employee = frappe.db.get_default("employee", frappe.session.user)
company = frappe.db.get_default("company", frappe.session.user) company = frappe.db.get_default("company", frappe.session.user)
from frappe.widgets.reportview import build_match_conditions from frappe.widgets.reportview import build_match_conditions
match_conditions = build_match_conditions("Leave Application") match_conditions = build_match_conditions("Leave Application")
# show department leaves for employee # show department leaves for employee
if "Employee" in frappe.get_roles(): if "Employee" in frappe.get_roles():
add_department_leaves(events, start, end, employee, company) add_department_leaves(events, start, end, employee, company)
add_leaves(events, start, end, employee, company, match_conditions) add_leaves(events, start, end, employee, company, match_conditions)
add_block_dates(events, start, end, employee, company) add_block_dates(events, start, end, employee, company)
add_holidays(events, start, end, employee, company) add_holidays(events, start, end, employee, company)
return events return events
def add_department_leaves(events, start, end, employee, company): def add_department_leaves(events, start, end, employee, company):
department = frappe.db.get_value("Employee", employee, "department") department = frappe.db.get_value("Employee", employee, "department")
if not department: if not department:
return return
# department leaves # department leaves
department_employees = frappe.db.sql_list("""select name from tabEmployee where department=%s department_employees = frappe.db.sql_list("""select name from tabEmployee where department=%s
and company=%s""", (department, company)) and company=%s""", (department, company))
match_conditions = "employee in (\"%s\")" % '", "'.join(department_employees) match_conditions = "employee in (\"%s\")" % '", "'.join(department_employees)
add_leaves(events, start, end, employee, company, match_conditions=match_conditions) add_leaves(events, start, end, employee, company, match_conditions=match_conditions)
def add_leaves(events, start, end, employee, company, match_conditions=None): def add_leaves(events, start, end, employee, company, match_conditions=None):
query = """select name, from_date, to_date, employee_name, half_day, query = """select name, from_date, to_date, employee_name, half_day,
status, employee, docstatus status, employee, docstatus
from `tabLeave Application` where from `tabLeave Application` where
(from_date between %s and %s or to_date between %s and %s) (from_date between %s and %s or to_date between %s and %s)
@@ -276,7 +275,7 @@ def add_leaves(events, start, end, employee, company, match_conditions=None):
and status!="Rejected" """ and status!="Rejected" """
if match_conditions: if match_conditions:
query += " and " + match_conditions query += " and " + match_conditions
for d in frappe.db.sql(query, (start, end, start, end), as_dict=True): for d in frappe.db.sql(query, (start, end, start, end), as_dict=True):
e = { e = {
"name": d.name, "name": d.name,
@@ -311,9 +310,9 @@ def add_holidays(events, start, end, employee, company):
applicable_holiday_list = frappe.db.get_value("Employee", employee, "holiday_list") applicable_holiday_list = frappe.db.get_value("Employee", employee, "holiday_list")
if not applicable_holiday_list: if not applicable_holiday_list:
return return
for holiday in frappe.db.sql("""select name, holiday_date, description for holiday in frappe.db.sql("""select name, holiday_date, description
from `tabHoliday` where parent=%s and holiday_date between %s and %s""", from `tabHoliday` where parent=%s and holiday_date between %s and %s""",
(applicable_holiday_list, start, end), as_dict=True): (applicable_holiday_list, start, end), as_dict=True):
events.append({ events.append({
"doctype": "Holiday", "doctype": "Holiday",
@@ -325,24 +324,24 @@ def add_holidays(events, start, end, employee, company):
@frappe.whitelist() @frappe.whitelist()
def query_for_permitted_employees(doctype, txt, searchfield, start, page_len, filters): def query_for_permitted_employees(doctype, txt, searchfield, start, page_len, filters):
txt = "%" + cstr(txt) + "%" txt = "%" + cstr(txt) + "%"
if "Leave Approver" in frappe.user.get_roles(): if "Leave Approver" in frappe.user.get_roles():
user = frappe.session.user.replace('"', '\"') user = frappe.session.user.replace('"', '\"')
condition = """and (exists(select ela.name from `tabEmployee Leave Approver` ela condition = """and (exists(select ela.name from `tabEmployee Leave Approver` ela
where ela.parent=`tabEmployee`.name and ela.leave_approver= "%s") or where ela.parent=`tabEmployee`.name and ela.leave_approver= "%s") or
not exists(select ela.name from `tabEmployee Leave Approver` ela not exists(select ela.name from `tabEmployee Leave Approver` ela
where ela.parent=`tabEmployee`.name) where ela.parent=`tabEmployee`.name)
or user_id = "%s")""" % (user, user) or user_id = "%s")""" % (user, user)
else: else:
from frappe.widgets.reportview import build_match_conditions from frappe.widgets.reportview import build_match_conditions
condition = build_match_conditions("Employee") condition = build_match_conditions("Employee")
condition = ("and " + condition) if condition else "" condition = ("and " + condition) if condition else ""
return frappe.db.sql("""select name, employee_name from `tabEmployee` return frappe.db.sql("""select name, employee_name from `tabEmployee`
where status = 'Active' and docstatus < 2 and where status = 'Active' and docstatus < 2 and
(`%s` like %s or employee_name like %s) %s (`%s` like %s or employee_name like %s) %s
order by order by
case when name like %s then 0 else 1 end, case when name like %s then 0 else 1 end,
case when employee_name like %s then 0 else 1 end, case when employee_name like %s then 0 else 1 end,
name limit %s, %s""" % tuple([searchfield] + ["%s"]*2 + [condition] + ["%s"]*4), name limit %s, %s""" % tuple([searchfield] + ["%s"]*2 + [condition] + ["%s"]*4),
(txt, txt, txt, txt, start, page_len)) (txt, txt, txt, txt, start, page_len))

View File

@@ -4,16 +4,16 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe.utils import cint, cstr, flt, nowdate from frappe.utils import cint, cstr, flt, nowdate, comma_and
from frappe import msgprint, _ from frappe import msgprint, _
from frappe.model.document import Document from frappe.model.document import Document
class LeaveControlPanel(Document): class LeaveControlPanel(Document):
def get_employees(self): def get_employees(self):
lst1 = [[self.employee_type,"employment_type"],[self.branch,"branch"],[self.designation,"designation"],[self.department, "department"],[self.grade,"grade"]] lst1 = [[self.employee_type,"employment_type"],[self.branch,"branch"],[self.designation,"designation"],[self.department, "department"],[self.grade,"grade"]]
condition = "where " condition = "where "
flag = 0 flag = 0
@@ -26,7 +26,7 @@ class LeaveControlPanel(Document):
flag = 1 flag = 1
emp_query = "select name from `tabEmployee` " emp_query = "select name from `tabEmployee` "
if flag == 1: if flag == 1:
emp_query += condition emp_query += condition
e = frappe.db.sql(emp_query) e = frappe.db.sql(emp_query)
return e return e
@@ -41,7 +41,7 @@ class LeaveControlPanel(Document):
employees = self.get_employees() employees = self.get_employees()
if not employees: if not employees:
frappe.throw(_("No employee found")) frappe.throw(_("No employee found"))
for d in self.get_employees(): for d in self.get_employees():
try: try:
la = frappe.get_doc('Leave Allocation') la = frappe.get_doc('Leave Allocation')
@@ -59,4 +59,4 @@ class LeaveControlPanel(Document):
except: except:
pass pass
if leave_allocated_for: if leave_allocated_for:
msgprint("Leaves Allocated Successfully for " + ", ".join(leave_allocated_for)) msgprint(_("Leaves Allocated Successfully for {0}").format(comma_and(leave_allocated_for)))

View File

@@ -4,57 +4,56 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe.utils import cint, flt from frappe.utils import cint, flt
from frappe import msgprint from frappe import _
from frappe.model.document import Document from frappe.model.document import Document
class SalaryManager(Document): class SalaryManager(Document):
def get_emp_list(self): def get_emp_list(self):
""" """
Returns list of active employees based on selected criteria Returns list of active employees based on selected criteria
and for which salary structure exists and for which salary structure exists
""" """
cond = self.get_filter_condition() cond = self.get_filter_condition()
cond += self.get_joining_releiving_condition() cond += self.get_joining_releiving_condition()
emp_list = frappe.db.sql(""" emp_list = frappe.db.sql("""
select t1.name select t1.name
from `tabEmployee` t1, `tabSalary Structure` t2 from `tabEmployee` t1, `tabSalary Structure` t2
where t1.docstatus!=2 and t2.docstatus != 2 where t1.docstatus!=2 and t2.docstatus != 2
and t1.name = t2.employee and t1.name = t2.employee
%s """% cond) %s """% cond)
return emp_list return emp_list
def get_filter_condition(self): def get_filter_condition(self):
self.check_mandatory() self.check_mandatory()
cond = '' cond = ''
for f in ['company', 'branch', 'department', 'designation', 'grade']: for f in ['company', 'branch', 'department', 'designation', 'grade']:
if self.get(f): if self.get(f):
cond += " and t1." + f + " = '" + self.get(f).replace("'", "\'") + "'" cond += " and t1." + f + " = '" + self.get(f).replace("'", "\'") + "'"
return cond return cond
def get_joining_releiving_condition(self): def get_joining_releiving_condition(self):
m = self.get_month_details(self.fiscal_year, self.month) m = self.get_month_details(self.fiscal_year, self.month)
cond = """ cond = """
and ifnull(t1.date_of_joining, '0000-00-00') <= '%(month_end_date)s' and ifnull(t1.date_of_joining, '0000-00-00') <= '%(month_end_date)s'
and ifnull(t1.relieving_date, '2199-12-31') >= '%(month_start_date)s' and ifnull(t1.relieving_date, '2199-12-31') >= '%(month_start_date)s'
""" % m """ % m
return cond return cond
def check_mandatory(self): def check_mandatory(self):
for f in ['company', 'month', 'fiscal_year']: for f in ['company', 'month', 'fiscal_year']:
if not self.get(f): if not self.get(f):
msgprint("Please select %s to proceed" % f, raise_exception=1) frappe.throw(_("Please set {0}").format(f))
def get_month_details(self, year, month): def get_month_details(self, year, month):
ysd = frappe.db.get_value("Fiscal Year", year, "year_start_date") ysd = frappe.db.get_value("Fiscal Year", year, "year_start_date")
if ysd: if ysd:
@@ -67,22 +66,22 @@ class SalaryManager(Document):
month_days = cint(calendar.monthrange(cint(msd.year) ,cint(month))[1]) # days in month month_days = cint(calendar.monthrange(cint(msd.year) ,cint(month))[1]) # days in month
med = datetime.date(msd.year, cint(month), month_days) # month end date med = datetime.date(msd.year, cint(month), month_days) # month end date
return { return {
'year': msd.year, 'year': msd.year,
'month_start_date': msd, 'month_start_date': msd,
'month_end_date': med, 'month_end_date': med,
'month_days': month_days 'month_days': month_days
} }
def create_sal_slip(self): def create_sal_slip(self):
""" """
Creates salary slip for selected employees if already not created Creates salary slip for selected employees if already not created
""" """
emp_list = self.get_emp_list() emp_list = self.get_emp_list()
ss_list = [] ss_list = []
for emp in emp_list: for emp in emp_list:
if not frappe.db.sql("""select name from `tabSalary Slip` if not frappe.db.sql("""select name from `tabSalary Slip`
where docstatus!= 2 and employee = %s and month = %s and fiscal_year = %s and company = %s where docstatus!= 2 and employee = %s and month = %s and fiscal_year = %s and company = %s
""", (emp[0], self.month, self.fiscal_year, self.company)): """, (emp[0], self.month, self.fiscal_year, self.company)):
ss = frappe.get_doc({ ss = frappe.get_doc({
@@ -95,18 +94,18 @@ class SalaryManager(Document):
}) })
ss.insert() ss.insert()
ss_list.append(ss.name) ss_list.append(ss.name)
return self.create_log(ss_list) return self.create_log(ss_list)
def create_log(self, ss_list): def create_log(self, ss_list):
log = "<b>No employee for the above selected criteria OR salary slip already created</b>" log = "<b>No employee for the above selected criteria OR salary slip already created</b>"
if ss_list: if ss_list:
log = "<b>Created Salary Slip has been created: </b>\ log = "<b>Created Salary Slip has been created: </b>\
<br><br>%s" % '<br>'.join(ss_list) <br><br>%s" % '<br>'.join(ss_list)
return log return log
def get_sal_slip_list(self): def get_sal_slip_list(self):
""" """
Returns list of salary slips based on selected criteria Returns list of salary slips based on selected criteria
@@ -114,17 +113,17 @@ class SalaryManager(Document):
""" """
cond = self.get_filter_condition() cond = self.get_filter_condition()
ss_list = frappe.db.sql(""" ss_list = frappe.db.sql("""
select t1.name from `tabSalary Slip` t1 select t1.name from `tabSalary Slip` t1
where t1.docstatus = 0 and month = %s and fiscal_year = %s %s where t1.docstatus = 0 and month = %s and fiscal_year = %s %s
""" % ('%s', '%s', cond), (self.month, self.fiscal_year)) """ % ('%s', '%s', cond), (self.month, self.fiscal_year))
return ss_list return ss_list
def submit_salary_slip(self): def submit_salary_slip(self):
""" """
Submit all salary slips based on selected criteria Submit all salary slips based on selected criteria
""" """
ss_list = self.get_sal_slip_list() ss_list = self.get_sal_slip_list()
not_submitted_ss = [] not_submitted_ss = []
for ss in ss_list: for ss in ss_list:
ss_obj = frappe.get_doc("Salary Slip",ss[0]) ss_obj = frappe.get_doc("Salary Slip",ss[0])
@@ -132,31 +131,31 @@ class SalaryManager(Document):
frappe.db.set(ss_obj, 'email_check', cint(self.send_mail)) frappe.db.set(ss_obj, 'email_check', cint(self.send_mail))
if cint(self.send_email) == 1: if cint(self.send_email) == 1:
ss_obj.send_mail_funct() ss_obj.send_mail_funct()
frappe.db.set(ss_obj, 'docstatus', 1) frappe.db.set(ss_obj, 'docstatus', 1)
except Exception,e: except Exception,e:
not_submitted_ss.append(ss[0]) not_submitted_ss.append(ss[0])
msgprint(e) frappe.msgprint(e)
continue continue
return self.create_submit_log(ss_list, not_submitted_ss) return self.create_submit_log(ss_list, not_submitted_ss)
def create_submit_log(self, all_ss, not_submitted_ss): def create_submit_log(self, all_ss, not_submitted_ss):
log = '' log = ''
if not all_ss: if not all_ss:
log = "No salary slip found to submit for the above selected criteria" log = "No salary slip found to submit for the above selected criteria"
else: else:
all_ss = [d[0] for d in all_ss] all_ss = [d[0] for d in all_ss]
submitted_ss = list(set(all_ss) - set(not_submitted_ss)) submitted_ss = list(set(all_ss) - set(not_submitted_ss))
if submitted_ss: if submitted_ss:
mail_sent_msg = self.send_email and " (Mail has been sent to the employee)" or "" mail_sent_msg = self.send_email and " (Mail has been sent to the employee)" or ""
log = """ log = """
<b>Submitted Salary Slips%s:</b>\ <b>Submitted Salary Slips%s:</b>\
<br><br> %s <br><br> <br><br> %s <br><br>
""" % (mail_sent_msg, '<br>'.join(submitted_ss)) """ % (mail_sent_msg, '<br>'.join(submitted_ss))
if not_submitted_ss: if not_submitted_ss:
log += """ log += """
<b>Not Submitted Salary Slips: </b>\ <b>Not Submitted Salary Slips: </b>\
@@ -167,33 +166,33 @@ class SalaryManager(Document):
send mail, uncheck 'Send Email' checkbox. <br>\ send mail, uncheck 'Send Email' checkbox. <br>\
Then try to submit Salary Slip again. Then try to submit Salary Slip again.
"""% ('<br>'.join(not_submitted_ss)) """% ('<br>'.join(not_submitted_ss))
return log return log
def get_total_salary(self): def get_total_salary(self):
""" """
Get total salary amount from submitted salary slip based on selected criteria Get total salary amount from submitted salary slip based on selected criteria
""" """
cond = self.get_filter_condition() cond = self.get_filter_condition()
tot = frappe.db.sql(""" tot = frappe.db.sql("""
select sum(rounded_total) from `tabSalary Slip` t1 select sum(rounded_total) from `tabSalary Slip` t1
where t1.docstatus = 1 and month = %s and fiscal_year = %s %s where t1.docstatus = 1 and month = %s and fiscal_year = %s %s
""" % ('%s', '%s', cond), (self.month, self.fiscal_year)) """ % ('%s', '%s', cond), (self.month, self.fiscal_year))
return flt(tot[0][0]) return flt(tot[0][0])
def get_acc_details(self): def get_acc_details(self):
""" """
get default bank account,default salary acount from company get default bank account,default salary acount from company
""" """
amt = self.get_total_salary() amt = self.get_total_salary()
default_bank_account = frappe.db.get_value("Company", self.company, default_bank_account = frappe.db.get_value("Company", self.company,
"default_bank_account") "default_bank_account")
if not default_bank_account: if not default_bank_account:
msgprint("You can set Default Bank Account in Company master.") frappe.msgprint(_("You can set Default Bank Account in Company master"))
return { return {
'default_bank_account' : default_bank_account, 'default_bank_account' : default_bank_account,
'amount' : amt 'amount' : amt
} }

View File

@@ -10,12 +10,12 @@ from frappe.model.naming import make_autoname
from frappe import msgprint, _ from frappe import msgprint, _
from erpnext.setup.utils import get_company_currency from erpnext.setup.utils import get_company_currency
from erpnext.utilities.transaction_base import TransactionBase from erpnext.utilities.transaction_base import TransactionBase
class SalarySlip(TransactionBase): class SalarySlip(TransactionBase):
def autoname(self): def autoname(self):
self.name = make_autoname('Sal Slip/' +self.employee + '/.#####') self.name = make_autoname('Sal Slip/' +self.employee + '/.#####')
def get_emp_and_leave_details(self): def get_emp_and_leave_details(self):
if self.employee: if self.employee:
@@ -25,19 +25,19 @@ class SalarySlip(TransactionBase):
self.pull_sal_struct(struct) self.pull_sal_struct(struct)
def check_sal_struct(self): def check_sal_struct(self):
struct = frappe.db.sql("""select name from `tabSalary Structure` struct = frappe.db.sql("""select name from `tabSalary Structure`
where employee=%s and is_active = 'Yes'""", self.employee) where employee=%s and is_active = 'Yes'""", self.employee)
if not struct: if not struct:
msgprint("Please create Salary Structure for employee '%s'" % self.employee) msgprint(_("Please create Salary Structure for employee {0}").format(self.employee))
self.employee = None self.employee = None
return struct and struct[0][0] or '' return struct and struct[0][0] or ''
def pull_sal_struct(self, struct): def pull_sal_struct(self, struct):
from erpnext.hr.doctype.salary_structure.salary_structure import get_mapped_doc from erpnext.hr.doctype.salary_structure.salary_structure import get_mapped_doc
self.update(get_mapped_doc(struct, self)) self.update(get_mapped_doc(struct, self))
def pull_emp_details(self): def pull_emp_details(self):
emp = frappe.db.get_value("Employee", self.employee, emp = frappe.db.get_value("Employee", self.employee,
["bank_name", "bank_ac_no", "esic_card_no", "pf_number"], as_dict=1) ["bank_name", "bank_ac_no", "esic_card_no", "pf_number"], as_dict=1)
if emp: if emp:
self.bank_name = emp.bank_name self.bank_name = emp.bank_name
@@ -50,39 +50,36 @@ class SalarySlip(TransactionBase):
self.fiscal_year = frappe.get_default("fiscal_year") self.fiscal_year = frappe.get_default("fiscal_year")
if not self.month: if not self.month:
self.month = "%02d" % getdate(nowdate()).month self.month = "%02d" % getdate(nowdate()).month
m = frappe.get_doc('Salary Manager').get_month_details(self.fiscal_year, self.month) m = frappe.get_doc('Salary Manager').get_month_details(self.fiscal_year, self.month)
holidays = self.get_holidays_for_employee(m) holidays = self.get_holidays_for_employee(m)
if not cint(frappe.db.get_value("HR Settings", "HR Settings", if not cint(frappe.db.get_value("HR Settings", "HR Settings",
"include_holidays_in_total_working_days")): "include_holidays_in_total_working_days")):
m["month_days"] -= len(holidays) m["month_days"] -= len(holidays)
if m["month_days"] < 0: if m["month_days"] < 0:
msgprint(_("Bummer! There are more holidays than working days this month."), frappe.throw(_("There are more holidays than working days this month."))
raise_exception=True)
if not lwp: if not lwp:
lwp = self.calculate_lwp(holidays, m) lwp = self.calculate_lwp(holidays, m)
self.total_days_in_month = m['month_days'] self.total_days_in_month = m['month_days']
self.leave_without_pay = lwp self.leave_without_pay = lwp
payment_days = flt(self.get_payment_days(m)) - flt(lwp) payment_days = flt(self.get_payment_days(m)) - flt(lwp)
self.payment_days = payment_days > 0 and payment_days or 0 self.payment_days = payment_days > 0 and payment_days or 0
def get_payment_days(self, m): def get_payment_days(self, m):
payment_days = m['month_days'] payment_days = m['month_days']
emp = frappe.db.sql("select date_of_joining, relieving_date from `tabEmployee` \ emp = frappe.db.sql("select date_of_joining, relieving_date from `tabEmployee` \
where name = %s", self.employee, as_dict=1)[0] where name = %s", self.employee, as_dict=1)[0]
if emp['relieving_date']: if emp['relieving_date']:
if getdate(emp['relieving_date']) > m['month_start_date'] and \ if getdate(emp['relieving_date']) > m['month_start_date'] and \
getdate(emp['relieving_date']) < m['month_end_date']: getdate(emp['relieving_date']) < m['month_end_date']:
payment_days = getdate(emp['relieving_date']).day payment_days = getdate(emp['relieving_date']).day
elif getdate(emp['relieving_date']) < m['month_start_date']: elif getdate(emp['relieving_date']) < m['month_start_date']:
frappe.msgprint(_("Relieving Date of employee is ") + cstr(emp['relieving_date'] frappe.throw(_("Employee relieved on {0} must be set as 'Left'").format(emp["relieving_date"]))
+ _(". Please set status of the employee as 'Left'")), raise_exception=1)
if emp['date_of_joining']: if emp['date_of_joining']:
if getdate(emp['date_of_joining']) > m['month_start_date'] and \ if getdate(emp['date_of_joining']) > m['month_start_date'] and \
getdate(emp['date_of_joining']) < m['month_end_date']: getdate(emp['date_of_joining']) < m['month_end_date']:
@@ -91,19 +88,19 @@ class SalarySlip(TransactionBase):
payment_days = 0 payment_days = 0
return payment_days return payment_days
def get_holidays_for_employee(self, m): def get_holidays_for_employee(self, m):
holidays = frappe.db.sql("""select t1.holiday_date holidays = frappe.db.sql("""select t1.holiday_date
from `tabHoliday` t1, tabEmployee t2 from `tabHoliday` t1, tabEmployee t2
where t1.parent = t2.holiday_list and t2.name = %s where t1.parent = t2.holiday_list and t2.name = %s
and t1.holiday_date between %s and %s""", and t1.holiday_date between %s and %s""",
(self.employee, m['month_start_date'], m['month_end_date'])) (self.employee, m['month_start_date'], m['month_end_date']))
if not holidays: if not holidays:
holidays = frappe.db.sql("""select t1.holiday_date holidays = frappe.db.sql("""select t1.holiday_date
from `tabHoliday` t1, `tabHoliday List` t2 from `tabHoliday` t1, `tabHoliday List` t2
where t1.parent = t2.name and ifnull(t2.is_default, 0) = 1 where t1.parent = t2.name and ifnull(t2.is_default, 0) = 1
and t2.fiscal_year = %s and t2.fiscal_year = %s
and t1.holiday_date between %s and %s""", (self.fiscal_year, and t1.holiday_date between %s and %s""", (self.fiscal_year,
m['month_start_date'], m['month_end_date'])) m['month_start_date'], m['month_end_date']))
holidays = [cstr(i[0]) for i in holidays] holidays = [cstr(i[0]) for i in holidays]
return holidays return holidays
@@ -115,10 +112,10 @@ class SalarySlip(TransactionBase):
if dt not in holidays: if dt not in holidays:
leave = frappe.db.sql(""" leave = frappe.db.sql("""
select t1.name, t1.half_day select t1.name, t1.half_day
from `tabLeave Application` t1, `tabLeave Type` t2 from `tabLeave Application` t1, `tabLeave Type` t2
where t2.name = t1.leave_type where t2.name = t1.leave_type
and ifnull(t2.is_lwp, 0) = 1 and ifnull(t2.is_lwp, 0) = 1
and t1.docstatus = 1 and t1.docstatus = 1
and t1.employee = %s and t1.employee = %s
and %s between from_date and to_date and %s between from_date and to_date
""", (self.employee, dt)) """, (self.employee, dt))
@@ -127,21 +124,19 @@ class SalarySlip(TransactionBase):
return lwp return lwp
def check_existing(self): def check_existing(self):
ret_exist = frappe.db.sql("""select name from `tabSalary Slip` ret_exist = frappe.db.sql("""select name from `tabSalary Slip`
where month = %s and fiscal_year = %s and docstatus != 2 where month = %s and fiscal_year = %s and docstatus != 2
and employee = %s and name != %s""", and employee = %s and name != %s""",
(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 = ''
msgprint("Salary Slip of employee '%s' already created for this month" frappe.throw("Salary Slip of employee {0} already created for this month".format(self.employee))
% self.employee, raise_exception=1)
def validate(self): def validate(self):
from frappe.utils import money_in_words from frappe.utils import money_in_words
self.check_existing() self.check_existing()
if not (len(self.get("earning_details")) or if not (len(self.get("earning_details")) or
len(self.get("deduction_details"))): len(self.get("deduction_details"))):
self.get_emp_and_leave_details() self.get_emp_and_leave_details()
else: else:
@@ -149,7 +144,7 @@ class SalarySlip(TransactionBase):
if not self.net_pay: if not self.net_pay:
self.calculate_net_pay() self.calculate_net_pay()
company_currency = get_company_currency(self.company) company_currency = get_company_currency(self.company)
self.total_in_words = money_in_words(self.rounded_total, company_currency) self.total_in_words = money_in_words(self.rounded_total, company_currency)
@@ -164,46 +159,46 @@ class SalarySlip(TransactionBase):
else: else:
d.e_modified_amount = d.e_amount d.e_modified_amount = d.e_amount
self.gross_pay += flt(d.e_modified_amount) self.gross_pay += flt(d.e_modified_amount)
def calculate_ded_total(self): def calculate_ded_total(self):
self.total_deduction = 0 self.total_deduction = 0
for d in self.get('deduction_details'): for d in self.get('deduction_details'):
if cint(d.d_depends_on_lwp) == 1: if cint(d.d_depends_on_lwp) == 1:
d.d_modified_amount = _round(flt(d.d_amount) * flt(self.payment_days) d.d_modified_amount = _round(flt(d.d_amount) * flt(self.payment_days)
/ cint(self.total_days_in_month), 2) / cint(self.total_days_in_month), 2)
elif not self.payment_days: elif not self.payment_days:
d.d_modified_amount = 0 d.d_modified_amount = 0
else: else:
d.d_modified_amount = d.d_amount d.d_modified_amount = d.d_amount
self.total_deduction += flt(d.d_modified_amount) self.total_deduction += flt(d.d_modified_amount)
def calculate_net_pay(self): def calculate_net_pay(self):
self.calculate_earning_total() self.calculate_earning_total()
self.calculate_ded_total() self.calculate_ded_total()
self.net_pay = flt(self.gross_pay) - flt(self.total_deduction) self.net_pay = flt(self.gross_pay) - flt(self.total_deduction)
self.rounded_total = _round(self.net_pay) self.rounded_total = _round(self.net_pay)
def on_submit(self): def on_submit(self):
if(self.email_check == 1): if(self.email_check == 1):
self.send_mail_funct() self.send_mail_funct()
def send_mail_funct(self):
def send_mail_funct(self):
from frappe.utils.email_lib import sendmail from frappe.utils.email_lib import sendmail
receiver = frappe.db.get_value("Employee", self.employee, "company_email") receiver = frappe.db.get_value("Employee", self.employee, "company_email")
if receiver: if receiver:
subj = 'Salary Slip - ' + cstr(self.month) +'/'+cstr(self.fiscal_year) subj = 'Salary Slip - ' + cstr(self.month) +'/'+cstr(self.fiscal_year)
earn_ret=frappe.db.sql("""select e_type, e_modified_amount from `tabSalary Slip Earning` earn_ret=frappe.db.sql("""select e_type, e_modified_amount from `tabSalary Slip Earning`
where parent = %s""", self.name) where parent = %s""", self.name)
ded_ret=frappe.db.sql("""select d_type, d_modified_amount from `tabSalary Slip Deduction` ded_ret=frappe.db.sql("""select d_type, d_modified_amount from `tabSalary Slip Deduction`
where parent = %s""", self.name) where parent = %s""", self.name)
earn_table = '' earn_table = ''
ded_table = '' ded_table = ''
if earn_ret: if earn_ret:
earn_table += "<table cellspacing=5px cellpadding=5px width='100%%'>" earn_table += "<table cellspacing=5px cellpadding=5px width='100%%'>"
for e in earn_ret: for e in earn_ret:
if not e[1]: if not e[1]:
earn_table += '<tr><td>%s</td><td align="right">0.00</td></tr>' % cstr(e[0]) earn_table += '<tr><td>%s</td><td align="right">0.00</td></tr>' % cstr(e[0])
@@ -211,11 +206,11 @@ class SalarySlip(TransactionBase):
earn_table += '<tr><td>%s</td><td align="right">%s</td></tr>' \ earn_table += '<tr><td>%s</td><td align="right">%s</td></tr>' \
% (cstr(e[0]), cstr(e[1])) % (cstr(e[0]), cstr(e[1]))
earn_table += '</table>' earn_table += '</table>'
if ded_ret: if ded_ret:
ded_table += "<table cellspacing=5px cellpadding=5px width='100%%'>" ded_table += "<table cellspacing=5px cellpadding=5px width='100%%'>"
for d in ded_ret: for d in ded_ret:
if not d[1]: if not d[1]:
ded_table +='<tr><td">%s</td><td align="right">0.00</td></tr>' % cstr(d[0]) ded_table +='<tr><td">%s</td><td align="right">0.00</td></tr>' % cstr(d[0])
@@ -223,10 +218,10 @@ class SalarySlip(TransactionBase):
ded_table +='<tr><td>%s</td><td align="right">%s</td></tr>' \ ded_table +='<tr><td>%s</td><td align="right">%s</td></tr>' \
% (cstr(d[0]), cstr(d[1])) % (cstr(d[0]), cstr(d[1]))
ded_table += '</table>' ded_table += '</table>'
letter_head = frappe.db.get_value("Letter Head", {"is_default": 1, "disabled": 0}, letter_head = frappe.db.get_value("Letter Head", {"is_default": 1, "disabled": 0},
"content") "content")
msg = '''<div> %s <br> msg = '''<div> %s <br>
<table cellspacing= "5" cellpadding="5" width = "100%%"> <table cellspacing= "5" cellpadding="5" width = "100%%">
<tr> <tr>
@@ -248,15 +243,15 @@ class SalarySlip(TransactionBase):
<td width = "50%%">Designation : %s</td> <td width = "50%%">Designation : %s</td>
<td width = "50%%">Grade : %s</td> <td width = "50%%">Grade : %s</td>
</tr> </tr>
<tr> <tr>
<td width = "50%%">Bank Account No. : %s</td> <td width = "50%%">Bank Account No. : %s</td>
<td width = "50%%">Bank Name : %s</td> <td width = "50%%">Bank Name : %s</td>
</tr> </tr>
<tr> <tr>
<td width = "50%%">Arrear Amount : <b>%s</b></td> <td width = "50%%">Arrear Amount : <b>%s</b></td>
<td width = "50%%">Payment days : %s</td> <td width = "50%%">Payment days : %s</td>
</tr> </tr>
</table> </table>
<table border="1px solid #CCC" width="100%%" cellpadding="0px" cellspacing="0px"> <table border="1px solid #CCC" width="100%%" cellpadding="0px" cellspacing="0px">
@@ -287,14 +282,14 @@ class SalarySlip(TransactionBase):
<td width='25%%'><b>Net Pay(in words) : </td> <td width='25%%'><b>Net Pay(in words) : </td>
<td colspan = '3' width = '50%%'>%s</b></td> <td colspan = '3' width = '50%%'>%s</b></td>
</tr> </tr>
</table></div>''' % (cstr(letter_head), cstr(self.employee), </table></div>''' % (cstr(letter_head), cstr(self.employee),
cstr(self.employee_name), cstr(self.month), cstr(self.fiscal_year), cstr(self.employee_name), cstr(self.month), cstr(self.fiscal_year),
cstr(self.department), cstr(self.branch), cstr(self.designation), cstr(self.department), cstr(self.branch), cstr(self.designation),
cstr(self.grade), cstr(self.bank_account_no), cstr(self.bank_name), cstr(self.grade), cstr(self.bank_account_no), cstr(self.bank_name),
cstr(self.arrear_amount), cstr(self.payment_days), earn_table, ded_table, cstr(self.arrear_amount), cstr(self.payment_days), earn_table, ded_table,
cstr(flt(self.gross_pay)), cstr(flt(self.total_deduction)), cstr(flt(self.gross_pay)), cstr(flt(self.total_deduction)),
cstr(flt(self.net_pay)), cstr(self.total_in_words)) cstr(flt(self.net_pay)), cstr(self.total_in_words))
sendmail([receiver], subject=subj, msg = msg) sendmail([receiver], subject=subj, msg = msg)
else: else:
msgprint("Company Email ID not found, hence mail not sent") msgprint(_("Company Email ID not found, hence mail not sent"))

View File

@@ -6,7 +6,7 @@ import frappe
from frappe.utils import cstr, flt from frappe.utils import cstr, flt
from frappe.model.naming import make_autoname from frappe.model.naming import make_autoname
from frappe import msgprint, _ from frappe import _
from frappe.model.document import Document from frappe.model.document import Document
@@ -17,7 +17,7 @@ class SalaryStructure(Document):
def get_employee_details(self): def get_employee_details(self):
ret = {} ret = {}
det = frappe.db.sql("""select employee_name, branch, designation, department, grade det = frappe.db.sql("""select employee_name, branch, designation, department, grade
from `tabEmployee` where name = %s""", self.employee) from `tabEmployee` where name = %s""", self.employee)
if det: if det:
ret = { ret = {
@@ -31,7 +31,7 @@ class SalaryStructure(Document):
return ret return ret
def get_ss_values(self,employee): def get_ss_values(self,employee):
basic_info = frappe.db.sql("""select bank_name, bank_ac_no, esic_card_no, pf_number basic_info = frappe.db.sql("""select bank_name, bank_ac_no, esic_card_no, pf_number
from `tabEmployee` where name =%s""", employee) from `tabEmployee` where name =%s""", employee)
ret = {'bank_name': basic_info and basic_info[0][0] or '', ret = {'bank_name': basic_info and basic_info[0][0] or '',
'bank_ac_no': basic_info and basic_info[0][1] or '', 'bank_ac_no': basic_info and basic_info[0][1] or '',
@@ -49,33 +49,32 @@ class SalaryStructure(Document):
elif(tab_fname == 'deduction_details'): elif(tab_fname == 'deduction_details'):
child.d_type = cstr(li[0]) child.d_type = cstr(li[0])
child.d_modified_amt = 0 child.d_modified_amt = 0
def make_earn_ded_table(self): def make_earn_ded_table(self):
self.make_table('Earning Type','earning_details','Salary Structure Earning') self.make_table('Earning Type','earning_details','Salary Structure Earning')
self.make_table('Deduction Type','deduction_details', 'Salary Structure Deduction') self.make_table('Deduction Type','deduction_details', 'Salary Structure Deduction')
def check_existing(self): def check_existing(self):
ret = frappe.db.sql("""select name from `tabSalary Structure` where is_active = 'Yes' ret = frappe.db.sql("""select name from `tabSalary Structure` where is_active = 'Yes'
and employee = %s and name!=%s""", (self.employee,self.name)) and employee = %s and name!=%s""", (self.employee,self.name))
if ret and self.is_active=='Yes': if ret and self.is_active=='Yes':
msgprint(_("""Another Salary Structure '%s' is active for employee '%s'. Please make its status 'Inactive' to proceed.""") % frappe.throw(_("Another Salary Structure {0} is active for employee {0}. Please make its status 'Inactive' to proceed.").format(cstr(ret), self.employee))
(cstr(ret), self.employee), raise_exception=1)
def validate_amount(self): def validate_amount(self):
if flt(self.net_pay) < 0: if flt(self.net_pay) < 0:
msgprint(_("Net pay can not be negative"), raise_exception=1) frappe.throw(_("Net pay cannot be negative"))
def validate(self): def validate(self):
self.check_existing() self.check_existing()
self.validate_amount() self.validate_amount()
@frappe.whitelist() @frappe.whitelist()
def make_salary_slip(source_name, target_doc=None): def make_salary_slip(source_name, target_doc=None):
return get_mapped_doc(source_name, target_doc).as_dict() return get_mapped_doc(source_name, target_doc).as_dict()
def get_mapped_doc(source_name, target_doc=None): def get_mapped_doc(source_name, target_doc=None):
from frappe.model.mapper import get_mapped_doc from frappe.model.mapper import get_mapped_doc
def postprocess(source, target): def postprocess(source, target):
sal_slip = frappe.get_doc(target) sal_slip = frappe.get_doc(target)
sal_slip.run_method("pull_emp_details") sal_slip.run_method("pull_emp_details")
@@ -84,24 +83,24 @@ def get_mapped_doc(source_name, target_doc=None):
doc = get_mapped_doc("Salary Structure", source_name, { doc = get_mapped_doc("Salary Structure", source_name, {
"Salary Structure": { "Salary Structure": {
"doctype": "Salary Slip", "doctype": "Salary Slip",
"field_map": { "field_map": {
"total_earning": "gross_pay" "total_earning": "gross_pay"
} }
}, },
"Salary Structure Deduction": { "Salary Structure Deduction": {
"doctype": "Salary Slip Deduction", "doctype": "Salary Slip Deduction",
"field_map": [ "field_map": [
["depend_on_lwp", "d_depends_on_lwp"], ["depend_on_lwp", "d_depends_on_lwp"],
["d_modified_amt", "d_amount"], ["d_modified_amt", "d_amount"],
["d_modified_amt", "d_modified_amount"] ["d_modified_amt", "d_modified_amount"]
], ],
"add_if_empty": True "add_if_empty": True
}, },
"Salary Structure Earning": { "Salary Structure Earning": {
"doctype": "Salary Slip Earning", "doctype": "Salary Slip Earning",
"field_map": [ "field_map": [
["depend_on_lwp", "e_depends_on_lwp"], ["depend_on_lwp", "e_depends_on_lwp"],
["modified_value", "e_modified_amount"], ["modified_value", "e_modified_amount"],
["modified_value", "e_amount"] ["modified_value", "e_amount"]
], ],

View File

@@ -6,7 +6,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe.utils import cstr, add_days, date_diff from frappe.utils import cstr, add_days, date_diff
from frappe import msgprint, _ from frappe import _
from frappe.utils.datautils import UnicodeWriter from frappe.utils.datautils import UnicodeWriter
from frappe.model.document import Document from frappe.model.document import Document
@@ -17,32 +17,32 @@ class UploadAttendance(Document):
def get_template(): def get_template():
if not frappe.has_permission("Attendance", "create"): if not frappe.has_permission("Attendance", "create"):
raise frappe.PermissionError raise frappe.PermissionError
args = frappe.local.form_dict args = frappe.local.form_dict
w = UnicodeWriter() w = UnicodeWriter()
w = add_header(w) w = add_header(w)
w = add_data(w, args) w = add_data(w, args)
# write out response as a type csv # write out response as a type csv
frappe.response['result'] = cstr(w.getvalue()) frappe.response['result'] = cstr(w.getvalue())
frappe.response['type'] = 'csv' frappe.response['type'] = 'csv'
frappe.response['doctype'] = "Attendance" frappe.response['doctype'] = "Attendance"
def add_header(w): def add_header(w):
status = ", ".join((frappe.get_meta("Attendance").get_field("status").options or "").strip().split("\n")) status = ", ".join((frappe.get_meta("Attendance").get_field("status").options or "").strip().split("\n"))
w.writerow(["Notes:"]) w.writerow(["Notes:"])
w.writerow(["Please do not change the template headings"]) w.writerow(["Please do not change the template headings"])
w.writerow(["Status should be one of these values: " + status]) w.writerow(["Status should be one of these values: " + status])
w.writerow(["If you are overwriting existing attendance records, 'ID' column mandatory"]) w.writerow(["If you are overwriting existing attendance records, 'ID' column mandatory"])
w.writerow(["ID", "Employee", "Employee Name", "Date", "Status", w.writerow(["ID", "Employee", "Employee Name", "Date", "Status",
"Fiscal Year", "Company", "Naming Series"]) "Fiscal Year", "Company", "Naming Series"])
return w return w
def add_data(w, args): def add_data(w, args):
from erpnext.accounts.utils import get_fiscal_year from erpnext.accounts.utils import get_fiscal_year
dates = get_dates(args) dates = get_dates(args)
employees = get_active_employees() employees = get_active_employees()
existing_attendance_records = get_existing_attendance_records(args) existing_attendance_records = get_existing_attendance_records(args)
@@ -54,9 +54,9 @@ def add_data(w, args):
existing_attendance = existing_attendance_records[tuple([date, employee.name])] existing_attendance = existing_attendance_records[tuple([date, employee.name])]
row = [ row = [
existing_attendance and existing_attendance.name or "", existing_attendance and existing_attendance.name or "",
employee.name, employee.employee_name, date, employee.name, employee.employee_name, date,
existing_attendance and existing_attendance.status or "", existing_attendance and existing_attendance.status or "",
get_fiscal_year(date)[0], employee.company, get_fiscal_year(date)[0], employee.company,
existing_attendance and existing_attendance.naming_series or get_naming_series(), existing_attendance and existing_attendance.naming_series or get_naming_series(),
] ]
w.writerow(row) w.writerow(row)
@@ -67,28 +67,27 @@ def get_dates(args):
no_of_days = date_diff(add_days(args["to_date"], 1), args["from_date"]) no_of_days = date_diff(add_days(args["to_date"], 1), args["from_date"])
dates = [add_days(args["from_date"], i) for i in range(0, no_of_days)] dates = [add_days(args["from_date"], i) for i in range(0, no_of_days)]
return dates return dates
def get_active_employees(): def get_active_employees():
employees = frappe.db.sql("""select name, employee_name, company employees = frappe.db.sql("""select name, employee_name, company
from tabEmployee where docstatus < 2 and status = 'Active'""", as_dict=1) from tabEmployee where docstatus < 2 and status = 'Active'""", as_dict=1)
return employees return employees
def get_existing_attendance_records(args): def get_existing_attendance_records(args):
attendance = frappe.db.sql("""select name, att_date, employee, status, naming_series attendance = frappe.db.sql("""select name, att_date, employee, status, naming_series
from `tabAttendance` where att_date between %s and %s and docstatus < 2""", from `tabAttendance` where att_date between %s and %s and docstatus < 2""",
(args["from_date"], args["to_date"]), as_dict=1) (args["from_date"], args["to_date"]), as_dict=1)
existing_attendance = {} existing_attendance = {}
for att in attendance: for att in attendance:
existing_attendance[tuple([att.att_date, att.employee])] = att existing_attendance[tuple([att.att_date, att.employee])] = att
return existing_attendance return existing_attendance
def get_naming_series(): def get_naming_series():
series = frappe.get_meta("Attendance").get_field("naming_series").options.strip().split("\n") series = frappe.get_meta("Attendance").get_field("naming_series").options.strip().split("\n")
if not series: if not series:
msgprint("""Please create naming series for Attendance \ frappe.throw(_("Please setup numbering series for Attendance via Setup > Numbering Series"))
through Setup -> Numbering Series.""", raise_exception=1)
return series[0] return series[0]
@@ -96,10 +95,10 @@ def get_naming_series():
def upload(): def upload():
if not frappe.has_permission("Attendance", "create"): if not frappe.has_permission("Attendance", "create"):
raise frappe.PermissionError raise frappe.PermissionError
from frappe.utils.datautils import read_csv_content_from_uploaded_file from frappe.utils.datautils import read_csv_content_from_uploaded_file
from frappe.modules import scrub from frappe.modules import scrub
rows = read_csv_content_from_uploaded_file() rows = read_csv_content_from_uploaded_file()
if not rows: if not rows:
msg = [_("Please select a csv file")] msg = [_("Please select a csv file")]
@@ -109,10 +108,9 @@ def upload():
columns[3] = "att_date" columns[3] = "att_date"
ret = [] ret = []
error = False error = False
from frappe.utils.datautils import check_record, import_doc from frappe.utils.datautils import check_record, import_doc
doctype_dl = frappe.get_meta("Attendance")
for i, row in enumerate(rows[5:]): for i, row in enumerate(rows[5:]):
if not row: continue if not row: continue
row_idx = i + 5 row_idx = i + 5
@@ -120,18 +118,18 @@ def upload():
d["doctype"] = "Attendance" d["doctype"] = "Attendance"
if d.name: if d.name:
d["docstatus"] = frappe.db.get_value("Attendance", d.name, "docstatus") d["docstatus"] = frappe.db.get_value("Attendance", d.name, "docstatus")
try: try:
check_record(d) check_record(d)
ret.append(import_doc(d, "Attendance", 1, row_idx, submit=True)) ret.append(import_doc(d, "Attendance", 1, row_idx, submit=True))
except Exception, e: except Exception, e:
error = True error = True
ret.append('Error for row (#%d) %s : %s' % (row_idx, ret.append('Error for row (#%d) %s : %s' % (row_idx,
len(row)>1 and row[1] or "", cstr(e))) len(row)>1 and row[1] or "", cstr(e)))
frappe.errprint(frappe.get_traceback()) frappe.errprint(frappe.get_traceback())
if error: if error:
frappe.db.rollback() frappe.db.rollback()
else: else:
frappe.db.commit() frappe.db.commit()
return {"messages": ret, "error": error} return {"messages": ret, "error": error}

View File

@@ -10,8 +10,8 @@ def get_leave_approver_list():
roles = [r[0] for r in frappe.db.sql("""select distinct parent from `tabUserRole` roles = [r[0] for r in frappe.db.sql("""select distinct parent from `tabUserRole`
where role='Leave Approver'""")] where role='Leave Approver'""")]
if not roles: if not roles:
frappe.msgprint(_("No Leave Approvers. Please assign 'Leave Approver' Role to atleast one user.")) frappe.msgprint(_("No Leave Approvers. Please assign 'Leave Approver' Role to atleast one user"))
return roles return roles
@@ -20,6 +20,5 @@ def get_expense_approver_list():
roles = [r[0] for r in frappe.db.sql("""select distinct parent from `tabUserRole` roles = [r[0] for r in frappe.db.sql("""select distinct parent from `tabUserRole`
where role='Expense Approver'""")] where role='Expense Approver'""")]
if not roles: if not roles:
frappe.msgprint("No Expense Approvers. Please assign 'Expense Approver' \ frappe.msgprint(_("No Expense Approvers. Please assign 'Expense Approver' Role to atleast one user"))
Role to atleast one user.")
return roles return roles

View File

@@ -5,10 +5,7 @@ from __future__ import unicode_literals
import frappe import frappe
from frappe.utils import cint, cstr, flt, now, nowdate from frappe.utils import cint, cstr, flt, now, nowdate
from frappe import msgprint, _ from frappe import _
from frappe.model.document import Document from frappe.model.document import Document
class BOM(Document): class BOM(Document):
@@ -64,11 +61,7 @@ class BOM(Document):
def validate_rm_item(self, item): def validate_rm_item(self, item):
if item[0]['name'] == self.item: if item[0]['name'] == self.item:
msgprint("Item_code: %s in materials tab cannot be same as FG Item", frappe.throw(_("Raw material cannot be same as main Item"))
item[0]['name'], raise_exception=1)
if not item or item[0]['docstatus'] == 2:
msgprint("Item %s does not exist in system" % item[0]['item_code'], raise_exception = 1)
def set_bom_material_details(self): def set_bom_material_details(self):
for item in self.get("bom_materials"): for item in self.get("bom_materials"):
@@ -188,12 +181,10 @@ class BOM(Document):
""" Validate main FG item""" """ Validate main FG item"""
item = self.get_item_det(self.item) item = self.get_item_det(self.item)
if not item: if not item:
msgprint("Item %s does not exists in the system or expired." % frappe.throw(_("Item {0} does not exists in the system or has expired").format(self.item))
self.item, raise_exception = 1)
elif item[0]['is_manufactured_item'] != 'Yes' \ elif item[0]['is_manufactured_item'] != 'Yes' \
and item[0]['is_sub_contracted_item'] != 'Yes': and item[0]['is_sub_contracted_item'] != 'Yes':
msgprint("""As Item: %s is not a manufactured / sub-contracted item, \ frappe.throw(_("Item {0} must be manufactured or sub-contracted").format(self.item))
you can not make BOM for it""" % self.item, raise_exception = 1)
else: else:
ret = frappe.db.get_value("Item", self.item, ["description", "stock_uom"]) ret = frappe.db.get_value("Item", self.item, ["description", "stock_uom"])
self.description = ret[0] self.description = ret[0]
@@ -204,8 +195,7 @@ class BOM(Document):
self.op = [] self.op = []
for d in self.get('bom_operations'): for d in self.get('bom_operations'):
if cstr(d.operation_no) in self.op: if cstr(d.operation_no) in self.op:
msgprint("Operation no: %s is repeated in Operations Table" % frappe.throw(_("Operation {0} is repeated in Operations Table").format(d.operation_no))
d.operation_no, raise_exception=1)
else: else:
# add operation in op list # add operation in op list
self.op.append(cstr(d.operation_no)) self.op.append(cstr(d.operation_no))
@@ -216,26 +206,20 @@ class BOM(Document):
for m in self.get('bom_materials'): for m in self.get('bom_materials'):
# check if operation no not in op table # check if operation no not in op table
if self.with_operations and cstr(m.operation_no) not in self.op: if self.with_operations and cstr(m.operation_no) not in self.op:
msgprint("""Operation no: %s against item: %s at row no: %s \ frappe.throw(_("Operation {0} not present in Operations Table").format(m.operation_no))
is not present at Operations table""" %
(m.operation_no, m.item_code, m.idx), raise_exception = 1)
item = self.get_item_det(m.item_code) item = self.get_item_det(m.item_code)
if item[0]['is_manufactured_item'] == 'Yes': if item[0]['is_manufactured_item'] == 'Yes':
if not m.bom_no: if not m.bom_no:
msgprint("Please enter BOM No aginst item: %s at row no: %s" % frappe.throw(_("BOM number is required for manufactured Item {0} in row {1}").format(m.item, m.idx))
(m.item_code, m.idx), raise_exception=1)
else: else:
self.validate_bom_no(m.item_code, m.bom_no, m.idx) self.validate_bom_no(m.item_code, m.bom_no, m.idx)
elif m.bom_no: elif m.bom_no:
msgprint("""As Item %s is not a manufactured / sub-contracted item, \ frappe.throw(_("BOM number not allowed for non-manufactured Item {0} in row {1}").format(m.item_code, m.idx))
you can not enter BOM against it (Row No: %s).""" %
(m.item_code, m.idx), raise_exception = 1)
if flt(m.qty) <= 0: if flt(m.qty) <= 0:
msgprint("Please enter qty against raw material: %s at row no: %s" % frappe.throw(_("Quantity required for Item {0} in row {1}").format(m.item_code, m.idx))
(m.item_code, m.idx), raise_exception = 1)
self.check_if_item_repeated(m.item_code, m.operation_no, check_list) self.check_if_item_repeated(m.item_code, m.operation_no, check_list)
@@ -245,14 +229,11 @@ class BOM(Document):
and is_active=1 and docstatus=1""", and is_active=1 and docstatus=1""",
(bom_no, item), as_dict =1) (bom_no, item), as_dict =1)
if not bom: if not bom:
msgprint("""Incorrect BOM No: %s against item: %s at row no: %s. frappe.throw(_("BOM {0} for Item {1} in row {2} is inactive or not submitted").format(bom_no, item, idx))
It may be inactive or not submitted or does not belong to this item.""" %
(bom_no, item, idx), raise_exception = 1)
def check_if_item_repeated(self, item, op, check_list): def check_if_item_repeated(self, item, op, check_list):
if [cstr(item), cstr(op)] in check_list: if [cstr(item), cstr(op)] in check_list:
msgprint(_("Item") + " %s " % (item,) + _("has been entered atleast twice") frappe.throw(_("Item {0} has been entered multiple times against same operation").format(item))
+ (cstr(op) and _(" against same operation") or ""), raise_exception=1)
else: else:
check_list.append([cstr(item), cstr(op)]) check_list.append([cstr(item), cstr(op)])
@@ -268,8 +249,7 @@ class BOM(Document):
count = count + 1 count = count + 1
for b in boms: for b in boms:
if b[0] == self.name: if b[0] == self.name:
msgprint("""Recursion Occured => '%s' cannot be '%s' of '%s'. frappe.throw(_("BOM recursion: {0} cannot be parent or child of {2}").format(b[0], self.name))
""" % (cstr(b[0]), cstr(d[2]), self.name), raise_exception = 1)
if b[0]: if b[0]:
bom_list.append(b[0]) bom_list.append(b[0])
@@ -389,12 +369,9 @@ class BOM(Document):
and docstatus = 1 and is_active = 1)""", self.name) and docstatus = 1 and is_active = 1)""", self.name)
if act_pbom and act_pbom[0][0]: if act_pbom and act_pbom[0][0]:
action = self.docstatus < 2 and _("deactivate") or _("cancel") frappe.throw(_("Cannot deactive or cancle BOM as it is linked with other BOMs"))
msgprint(_("Cannot ") + action + _(": It is linked to other active BOM(s)"),
raise_exception=1)
def get_bom_items_as_dict(bom, qty=1, fetch_exploded=1): def get_bom_items_as_dict(bom, qty=1, fetch_exploded=1):
import json
item_dict = {} item_dict = {}
query = """select query = """select

View File

@@ -4,8 +4,8 @@
from __future__ import unicode_literals 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 frappe.model.document import Document from frappe.model.document import Document
class BOMReplaceTool(Document): class BOMReplaceTool(Document):
@@ -17,22 +17,22 @@ class BOMReplaceTool(Document):
for bom in bom_list: for bom in bom_list:
bom_obj = frappe.get_doc("BOM", bom) bom_obj = frappe.get_doc("BOM", bom)
updated_bom = bom_obj.update_cost_and_exploded_items(updated_bom) updated_bom = bom_obj.update_cost_and_exploded_items(updated_bom)
frappe.msgprint(_("BOM replaced")) frappe.msgprint(_("BOM replaced"))
def validate_bom(self): def validate_bom(self):
if cstr(self.current_bom) == cstr(self.new_bom): if cstr(self.current_bom) == cstr(self.new_bom):
msgprint("Current BOM and New BOM can not be same", raise_exception=1) frappe.throw(_("Current BOM and New BOM can not be same"))
def update_new_bom(self): def update_new_bom(self):
current_bom_unitcost = frappe.db.sql("""select total_cost/quantity current_bom_unitcost = frappe.db.sql("""select total_cost/quantity
from `tabBOM` where name = %s""", self.current_bom) from `tabBOM` where name = %s""", self.current_bom)
current_bom_unitcost = current_bom_unitcost and flt(current_bom_unitcost[0][0]) or 0 current_bom_unitcost = current_bom_unitcost and flt(current_bom_unitcost[0][0]) or 0
frappe.db.sql("""update `tabBOM Item` set bom_no=%s, frappe.db.sql("""update `tabBOM Item` set bom_no=%s,
rate=%s, amount=qty*%s where bom_no = %s and docstatus < 2""", rate=%s, amount=qty*%s where bom_no = %s and docstatus < 2""",
(self.new_bom, current_bom_unitcost, current_bom_unitcost, self.current_bom)) (self.new_bom, current_bom_unitcost, current_bom_unitcost, self.current_bom))
def get_parent_boms(self): def get_parent_boms(self):
return [d[0] for d in frappe.db.sql("""select distinct parent return [d[0] for d in frappe.db.sql("""select distinct parent
from `tabBOM Item` where ifnull(bom_no, '') = %s and docstatus < 2""", from `tabBOM Item` where ifnull(bom_no, '') = %s and docstatus < 2""",
self.new_bom)] self.new_bom)]

View File

@@ -16,31 +16,31 @@ class ProductionOrder(Document):
def validate(self): def validate(self):
if self.docstatus == 0: if self.docstatus == 0:
self.status = "Draft" self.status = "Draft"
from erpnext.utilities import validate_status from erpnext.utilities import validate_status
validate_status(self.status, ["Draft", "Submitted", "Stopped", validate_status(self.status, ["Draft", "Submitted", "Stopped",
"In Process", "Completed", "Cancelled"]) "In Process", "Completed", "Cancelled"])
self.validate_bom_no() self.validate_bom_no()
self.validate_sales_order() self.validate_sales_order()
self.validate_warehouse() self.validate_warehouse()
from erpnext.utilities.transaction_base import validate_uom_is_integer from erpnext.utilities.transaction_base import validate_uom_is_integer
validate_uom_is_integer(self, "stock_uom", ["qty", "produced_qty"]) validate_uom_is_integer(self, "stock_uom", ["qty", "produced_qty"])
def validate_bom_no(self): def validate_bom_no(self):
if self.bom_no: if self.bom_no:
bom = frappe.db.sql("""select name from `tabBOM` where name=%s and docstatus=1 bom = frappe.db.sql("""select name from `tabBOM` where name=%s and docstatus=1
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("""Incorrect BOM: %s entered.
May be BOM not exists or inactive or not submitted May be BOM not exists or inactive or not submitted
or for some other item.""" % cstr(self.bom_no)) 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:
so = frappe.db.sql("""select name, delivery_date from `tabSales Order` so = frappe.db.sql("""select name, delivery_date from `tabSales Order`
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:
@@ -48,39 +48,39 @@ class ProductionOrder(Document):
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
self.validate_production_order_against_so() self.validate_production_order_against_so()
def validate_warehouse(self): def validate_warehouse(self):
from erpnext.stock.utils import validate_warehouse_company from erpnext.stock.utils import validate_warehouse_company
for w in [self.fg_warehouse, self.wip_warehouse]: for w in [self.fg_warehouse, self.wip_warehouse]:
validate_warehouse_company(w, self.company) validate_warehouse_company(w, self.company)
def validate_production_order_against_so(self): def validate_production_order_against_so(self):
# already ordered qty # already ordered qty
ordered_qty_against_so = frappe.db.sql("""select sum(qty) from `tabProduction Order` ordered_qty_against_so = frappe.db.sql("""select sum(qty) from `tabProduction Order`
where production_item = %s and sales_order = %s and docstatus < 2 and name != %s""", where production_item = %s and sales_order = %s and docstatus < 2 and name != %s""",
(self.production_item, self.sales_order, self.name))[0][0] (self.production_item, self.sales_order, self.name))[0][0]
total_qty = flt(ordered_qty_against_so) + flt(self.qty) total_qty = flt(ordered_qty_against_so) + flt(self.qty)
# get qty from Sales Order Item table # get qty from Sales Order Item table
so_item_qty = frappe.db.sql("""select sum(qty) from `tabSales Order Item` so_item_qty = frappe.db.sql("""select sum(qty) from `tabSales Order Item`
where parent = %s and item_code = %s""", where parent = %s and item_code = %s""",
(self.sales_order, self.production_item))[0][0] (self.sales_order, self.production_item))[0][0]
# get qty from Packing Item table # get qty from Packing Item table
dnpi_qty = frappe.db.sql("""select sum(qty) from `tabPacked Item` dnpi_qty = frappe.db.sql("""select sum(qty) from `tabPacked Item`
where parent = %s and parenttype = 'Sales Order' and item_code = %s""", where parent = %s and parenttype = 'Sales Order' and item_code = %s""",
(self.sales_order, self.production_item))[0][0] (self.sales_order, self.production_item))[0][0]
# total qty in SO # total qty in SO
so_qty = flt(so_item_qty) + flt(dnpi_qty) so_qty = flt(so_item_qty) + flt(dnpi_qty)
if total_qty > so_qty: if total_qty > so_qty:
frappe.throw(_("Total production order qty for item") + ": " + frappe.throw(_("Total production order qty for item") + ": " +
cstr(self.production_item) + _(" against sales order") + ": " + cstr(self.production_item) + _(" against sales order") + ": " +
cstr(self.sales_order) + _(" will be ") + cstr(total_qty) + ", " + cstr(self.sales_order) + _(" will be ") + cstr(total_qty) + ", " +
_("which is greater than sales order qty ") + "(" + cstr(so_qty) + ")" + _("which is greater than sales order qty ") + "(" + cstr(so_qty) + ")" +
_("Please reduce qty."), exc=OverProductionError) _("Please reduce qty."), exc=OverProductionError)
def stop_unstop(self, status): def stop_unstop(self, status):
@@ -88,7 +88,7 @@ class ProductionOrder(Document):
self.update_status(status) self.update_status(status)
qty = (flt(self.qty)-flt(self.produced_qty)) * ((status == 'Stopped') and -1 or 1) qty = (flt(self.qty)-flt(self.produced_qty)) * ((status == 'Stopped') and -1 or 1)
self.update_planned_qty(qty) self.update_planned_qty(qty)
msgprint("Production Order has been %s" % status) frappe.msgprint(_("Production Order status is {0}").format(status))
def update_status(self, status): def update_status(self, status):
@@ -108,14 +108,14 @@ class ProductionOrder(Document):
frappe.throw(_("WIP Warehouse required before Submit")) frappe.throw(_("WIP Warehouse required before Submit"))
frappe.db.set(self,'status', 'Submitted') frappe.db.set(self,'status', 'Submitted')
self.update_planned_qty(self.qty) self.update_planned_qty(self.qty)
def on_cancel(self): def on_cancel(self):
# Check whether any stock entry exists against this Production Order # Check whether any stock entry exists against this Production Order
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("""Submitted Stock Entry %s exists against this production order.
Hence can not be cancelled.""" % 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')
@@ -132,27 +132,27 @@ class ProductionOrder(Document):
from erpnext.stock.utils import update_bin from erpnext.stock.utils import update_bin
update_bin(args) update_bin(args)
@frappe.whitelist() @frappe.whitelist()
def get_item_details(item): def get_item_details(item):
res = frappe.db.sql("""select stock_uom, description res = frappe.db.sql("""select stock_uom, description
from `tabItem` where (ifnull(end_of_life, "")="" or end_of_life > now()) from `tabItem` where (ifnull(end_of_life, "")="" or end_of_life > now())
and name=%s""", item, as_dict=1) and name=%s""", item, as_dict=1)
if not res: if not res:
return {} return {}
res = res[0] res = res[0]
bom = frappe.db.sql("""select name from `tabBOM` where item=%s bom = frappe.db.sql("""select name from `tabBOM` where item=%s
and ifnull(is_default, 0)=1""", item) and ifnull(is_default, 0)=1""", item)
if bom: if bom:
res.bom_no = bom[0][0] res.bom_no = bom[0][0]
return res return res
@frappe.whitelist() @frappe.whitelist()
def make_stock_entry(production_order_id, purpose): def make_stock_entry(production_order_id, purpose):
production_order = frappe.get_doc("Production Order", production_order_id) production_order = frappe.get_doc("Production Order", production_order_id)
stock_entry = frappe.new_doc("Stock Entry") stock_entry = frappe.new_doc("Stock Entry")
stock_entry.purpose = purpose stock_entry.purpose = purpose
stock_entry.production_order = production_order_id stock_entry.production_order = production_order_id
@@ -160,12 +160,12 @@ def make_stock_entry(production_order_id, purpose):
stock_entry.bom_no = production_order.bom_no stock_entry.bom_no = production_order.bom_no
stock_entry.use_multi_level_bom = production_order.use_multi_level_bom stock_entry.use_multi_level_bom = production_order.use_multi_level_bom
stock_entry.fg_completed_qty = flt(production_order.qty) - flt(production_order.produced_qty) stock_entry.fg_completed_qty = flt(production_order.qty) - flt(production_order.produced_qty)
if purpose=="Material Transfer": if purpose=="Material Transfer":
stock_entry.to_warehouse = production_order.wip_warehouse stock_entry.to_warehouse = production_order.wip_warehouse
else: else:
stock_entry.from_warehouse = production_order.wip_warehouse stock_entry.from_warehouse = production_order.wip_warehouse
stock_entry.to_warehouse = production_order.fg_warehouse stock_entry.to_warehouse = production_order.fg_warehouse
stock_entry.run_method("get_items") stock_entry.run_method("get_items")
return stock_entry.as_dict() return stock_entry.as_dict()

View File

@@ -3,7 +3,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe.utils import cstr, flt, cint, nowdate, add_days from frappe.utils import cstr, flt, cint, nowdate, add_days, comma_and
from frappe import msgprint, _ from frappe import msgprint, _
@@ -16,19 +16,19 @@ class ProductionPlanningTool(Document):
def get_so_details(self, so): def get_so_details(self, so):
"""Pull other details from so""" """Pull other details from so"""
so = frappe.db.sql("""select transaction_date, customer, grand_total so = frappe.db.sql("""select transaction_date, customer, grand_total
from `tabSales Order` where name = %s""", so, as_dict = 1) from `tabSales Order` where name = %s""", so, as_dict = 1)
ret = { ret = {
'sales_order_date': so and so[0]['transaction_date'] or '', 'sales_order_date': so and so[0]['transaction_date'] or '',
'customer' : so[0]['customer'] or '', 'customer' : so[0]['customer'] or '',
'grand_total': so[0]['grand_total'] 'grand_total': so[0]['grand_total']
} }
return ret return ret
def get_item_details(self, item_code): def get_item_details(self, item_code):
""" Pull other item details from item master""" """ Pull other item details from item master"""
item = frappe.db.sql("""select description, stock_uom, default_bom item = frappe.db.sql("""select description, stock_uom, default_bom
from `tabItem` where name = %s""", item_code, as_dict =1) from `tabItem` where name = %s""", item_code, as_dict =1)
ret = { ret = {
'description' : item and item[0]['description'], 'description' : item and item[0]['description'],
@@ -42,7 +42,7 @@ class ProductionPlanningTool(Document):
def clear_item_table(self): def clear_item_table(self):
self.set('pp_details', []) self.set('pp_details', [])
def validate_company(self): def validate_company(self):
if not self.company: if not self.company:
frappe.throw(_("Please enter Company")) frappe.throw(_("Please enter Company"))
@@ -56,10 +56,10 @@ class ProductionPlanningTool(Document):
so_filter += ' and so.transaction_date <= "' + self.to_date + '"' so_filter += ' and so.transaction_date <= "' + self.to_date + '"'
if self.customer: if self.customer:
so_filter += ' and so.customer = "' + self.customer + '"' so_filter += ' and so.customer = "' + self.customer + '"'
if self.fg_item: if self.fg_item:
item_filter += ' and item.name = "' + self.fg_item + '"' item_filter += ' and item.name = "' + self.fg_item + '"'
open_so = frappe.db.sql(""" open_so = frappe.db.sql("""
select distinct so.name, so.transaction_date, so.customer, so.grand_total select distinct so.name, so.transaction_date, so.customer, so.grand_total
from `tabSales Order` so, `tabSales Order Item` so_item from `tabSales Order` so, `tabSales Order Item` so_item
@@ -68,15 +68,15 @@ class ProductionPlanningTool(Document):
and so.company = %s and so.company = %s
and ifnull(so_item.qty, 0) > ifnull(so_item.delivered_qty, 0) %s and ifnull(so_item.qty, 0) > ifnull(so_item.delivered_qty, 0) %s
and (exists (select name from `tabItem` item where item.name=so_item.item_code and (exists (select name from `tabItem` item where item.name=so_item.item_code
and (ifnull(item.is_pro_applicable, 'No') = 'Yes' and (ifnull(item.is_pro_applicable, 'No') = 'Yes'
or ifnull(item.is_sub_contracted_item, 'No') = 'Yes') %s) or ifnull(item.is_sub_contracted_item, 'No') = 'Yes') %s)
or exists (select name from `tabPacked Item` pi or exists (select name from `tabPacked Item` pi
where pi.parent = so.name and pi.parent_item = so_item.item_code where pi.parent = so.name and pi.parent_item = so_item.item_code
and exists (select name from `tabItem` item where item.name=pi.item_code and exists (select name from `tabItem` item where item.name=pi.item_code
and (ifnull(item.is_pro_applicable, 'No') = 'Yes' and (ifnull(item.is_pro_applicable, 'No') = 'Yes'
or ifnull(item.is_sub_contracted_item, 'No') = 'Yes') %s))) or ifnull(item.is_sub_contracted_item, 'No') = 'Yes') %s)))
""" % ('%s', so_filter, item_filter, item_filter), self.company, as_dict=1) """ % ('%s', so_filter, item_filter, item_filter), self.company, as_dict=1)
self.add_so_in_table(open_so) self.add_so_in_table(open_so)
def add_so_in_table(self, open_so): def add_so_in_table(self, open_so):
@@ -94,7 +94,7 @@ class ProductionPlanningTool(Document):
def get_items_from_so(self): def get_items_from_so(self):
""" Pull items from Sales Order, only proction item """ Pull items from Sales Order, only proction item
and subcontracted item will be pulled from Packing item and subcontracted item will be pulled from Packing item
and add items in the table and add items in the table
""" """
items = self.get_items() items = self.get_items()
@@ -105,36 +105,36 @@ class ProductionPlanningTool(Document):
if not so_list: if not so_list:
msgprint(_("Please enter sales order in the above table")) msgprint(_("Please enter sales order in the above table"))
return [] return []
items = frappe.db.sql("""select distinct parent, item_code, warehouse, items = frappe.db.sql("""select distinct parent, item_code, warehouse,
(qty - ifnull(delivered_qty, 0)) as pending_qty (qty - ifnull(delivered_qty, 0)) as pending_qty
from `tabSales Order Item` so_item from `tabSales Order Item` so_item
where parent in (%s) and docstatus = 1 and ifnull(qty, 0) > ifnull(delivered_qty, 0) where parent in (%s) and docstatus = 1 and ifnull(qty, 0) > ifnull(delivered_qty, 0)
and exists (select * from `tabItem` item where item.name=so_item.item_code and exists (select * from `tabItem` item where item.name=so_item.item_code
and (ifnull(item.is_pro_applicable, 'No') = 'Yes' and (ifnull(item.is_pro_applicable, 'No') = 'Yes'
or ifnull(item.is_sub_contracted_item, 'No') = 'Yes'))""" % \ or ifnull(item.is_sub_contracted_item, 'No') = 'Yes'))""" % \
(", ".join(["%s"] * len(so_list))), tuple(so_list), as_dict=1) (", ".join(["%s"] * len(so_list))), tuple(so_list), as_dict=1)
packed_items = frappe.db.sql("""select distinct pi.parent, pi.item_code, pi.warehouse as reserved_warhouse, packed_items = frappe.db.sql("""select distinct pi.parent, pi.item_code, pi.warehouse as reserved_warhouse,
(((so_item.qty - ifnull(so_item.delivered_qty, 0)) * pi.qty) / so_item.qty) (((so_item.qty - ifnull(so_item.delivered_qty, 0)) * pi.qty) / so_item.qty)
as pending_qty as pending_qty
from `tabSales Order Item` so_item, `tabPacked Item` pi from `tabSales Order Item` so_item, `tabPacked Item` pi
where so_item.parent = pi.parent and so_item.docstatus = 1 where so_item.parent = pi.parent and so_item.docstatus = 1
and pi.parent_item = so_item.item_code and pi.parent_item = so_item.item_code
and so_item.parent in (%s) and ifnull(so_item.qty, 0) > ifnull(so_item.delivered_qty, 0) and so_item.parent in (%s) and ifnull(so_item.qty, 0) > ifnull(so_item.delivered_qty, 0)
and exists (select * from `tabItem` item where item.name=pi.item_code and exists (select * from `tabItem` item where item.name=pi.item_code
and (ifnull(item.is_pro_applicable, 'No') = 'Yes' and (ifnull(item.is_pro_applicable, 'No') = 'Yes'
or ifnull(item.is_sub_contracted_item, 'No') = 'Yes'))""" % \ or ifnull(item.is_sub_contracted_item, 'No') = 'Yes'))""" % \
(", ".join(["%s"] * len(so_list))), tuple(so_list), as_dict=1) (", ".join(["%s"] * len(so_list))), tuple(so_list), as_dict=1)
return items + packed_items return items + packed_items
def add_items(self, items): def add_items(self, items):
self.clear_item_table() self.clear_item_table()
for p in items: for p in items:
item_details = frappe.db.sql("""select description, stock_uom, default_bom item_details = frappe.db.sql("""select description, stock_uom, default_bom
from tabItem where name=%s""", p['item_code']) from tabItem where name=%s""", p['item_code'])
pi = self.append('pp_details', {}) pi = self.append('pp_details', {})
pi.sales_order = p['parent'] pi.sales_order = p['parent']
@@ -145,27 +145,27 @@ class ProductionPlanningTool(Document):
pi.bom_no = item_details and item_details[0][2] or '' pi.bom_no = item_details and item_details[0][2] or ''
pi.so_pending_qty = flt(p['pending_qty']) pi.so_pending_qty = flt(p['pending_qty'])
pi.planned_qty = flt(p['pending_qty']) pi.planned_qty = flt(p['pending_qty'])
def validate_data(self): def validate_data(self):
self.validate_company() self.validate_company()
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: %s at row no: %s" %
(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 no for item: %s at row no: %s" %
(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 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""" % May be BOM is inactive or for other item or does not exists in the system""" %
(d.bom_no, d.item_doce, d.idx)) (d.bom_no, d.item_doce, d.idx))
def raise_production_order(self): def raise_production_order(self):
@@ -180,9 +180,9 @@ class ProductionPlanningTool(Document):
if pro: if pro:
pro = ["""<a href="#Form/Production Order/%s" target="_blank">%s</a>""" % \ pro = ["""<a href="#Form/Production Order/%s" target="_blank">%s</a>""" % \
(p, p) for p in pro] (p, p) for p in pro]
msgprint(_("Production Order(s) created:\n\n") + '\n'.join(pro)) msgprint(_("{0} created").format(comma_and(pro)))
else : else :
msgprint(_("No Production Order created.")) msgprint(_("No Production Orders created"))
def get_distinct_items_and_boms(self): def get_distinct_items_and_boms(self):
""" Club similar BOM and item for processing """ Club similar BOM and item for processing
@@ -191,7 +191,7 @@ class ProductionPlanningTool(Document):
} }
""" """
item_dict, bom_dict = {}, {} item_dict, bom_dict = {}, {}
for d in self.get("pp_details"): for d in self.get("pp_details"):
bom_dict.setdefault(d.bom_no, []).append([d.sales_order, flt(d.planned_qty)]) bom_dict.setdefault(d.bom_no, []).append([d.sales_order, flt(d.planned_qty)])
item_dict[(d.item_code, d.sales_order, d.warehouse)] = { item_dict[(d.item_code, d.sales_order, d.warehouse)] = {
"production_item" : d.item_code, "production_item" : d.item_code,
@@ -207,7 +207,7 @@ class ProductionPlanningTool(Document):
"status" : "Draft", "status" : "Draft",
} }
return bom_dict, item_dict return bom_dict, item_dict
def create_production_order(self, items): def create_production_order(self, items):
"""Create production order. Called from Production Planning Tool""" """Create production order. Called from Production Planning Tool"""
from erpnext.manufacturing.doctype.production_order.production_order import OverProductionError from erpnext.manufacturing.doctype.production_order.production_order import OverProductionError
@@ -216,16 +216,16 @@ class ProductionPlanningTool(Document):
for key in items: for key in items:
pro = frappe.new_doc("Production Order") pro = frappe.new_doc("Production Order")
pro.update(items[key]) pro.update(items[key])
frappe.flags.mute_messages = True frappe.flags.mute_messages = True
try: try:
pro.insert() pro.insert()
pro_list.append(pro.name) pro_list.append(pro.name)
except OverProductionError, e: except OverProductionError:
pass pass
frappe.flags.mute_messages = False frappe.flags.mute_messages = False
return pro_list return pro_list
def download_raw_materials(self): def download_raw_materials(self):
@@ -236,41 +236,41 @@ class ProductionPlanningTool(Document):
return self.get_csv() return self.get_csv()
def get_raw_materials(self, bom_dict): def get_raw_materials(self, bom_dict):
""" Get raw materials considering sub-assembly items """ Get raw materials considering sub-assembly items
{ {
"item_code": [qty_required, description, stock_uom, min_order_qty] "item_code": [qty_required, description, stock_uom, min_order_qty]
} }
""" """
item_list = [] item_list = []
for bom, so_wise_qty in bom_dict.items(): for bom, so_wise_qty in bom_dict.items():
bom_wise_item_details = {} bom_wise_item_details = {}
if self.use_multi_level_bom: if self.use_multi_level_bom:
# get all raw materials with sub assembly childs # get all raw materials with sub assembly childs
for d in frappe.db.sql("""select fb.item_code, for d in frappe.db.sql("""select fb.item_code,
ifnull(sum(fb.qty_consumed_per_unit), 0) as qty, ifnull(sum(fb.qty_consumed_per_unit), 0) as qty,
fb.description, fb.stock_uom, it.min_order_qty fb.description, fb.stock_uom, it.min_order_qty
from `tabBOM Explosion Item` fb,`tabItem` it from `tabBOM Explosion Item` fb,`tabItem` it
where it.name = fb.item_code and ifnull(it.is_pro_applicable, 'No') = 'No' where it.name = fb.item_code and ifnull(it.is_pro_applicable, 'No') = 'No'
and ifnull(it.is_sub_contracted_item, 'No') = 'No' and ifnull(it.is_sub_contracted_item, 'No') = 'No'
and fb.docstatus<2 and fb.parent=%s and fb.docstatus<2 and fb.parent=%s
group by item_code, stock_uom""", bom, as_dict=1): group by item_code, stock_uom""", bom, as_dict=1):
bom_wise_item_details.setdefault(d.item_code, d) bom_wise_item_details.setdefault(d.item_code, d)
else: else:
# Get all raw materials considering SA items as raw materials, # Get all raw materials considering SA items as raw materials,
# so no childs of SA items # so no childs of SA items
for d in frappe.db.sql("""select bom_item.item_code, for d in frappe.db.sql("""select bom_item.item_code,
ifnull(sum(bom_item.qty_consumed_per_unit), 0) as qty, ifnull(sum(bom_item.qty_consumed_per_unit), 0) as qty,
bom_item.description, bom_item.stock_uom, item.min_order_qty bom_item.description, bom_item.stock_uom, item.min_order_qty
from `tabBOM Item` bom_item, tabItem item from `tabBOM Item` bom_item, tabItem item
where bom_item.parent = %s and bom_item.docstatus < 2 where bom_item.parent = %s and bom_item.docstatus < 2
and bom_item.item_code = item.name and bom_item.item_code = item.name
group by item_code""", bom, as_dict=1): group by item_code""", bom, as_dict=1):
bom_wise_item_details.setdefault(d.item_code, d) bom_wise_item_details.setdefault(d.item_code, d)
for item, item_details in bom_wise_item_details.items(): for item, item_details in bom_wise_item_details.items():
for so_qty in so_wise_qty: for so_qty in so_wise_qty:
item_list.append([item, flt(item_details.qty) * so_qty[1], item_details.description, item_list.append([item, flt(item_details.qty) * so_qty[1], item_details.description,
item_details.stock_uom, item_details.min_order_qty, so_qty[0]]) item_details.stock_uom, item_details.min_order_qty, so_qty[0]])
self.make_items_dict(item_list) self.make_items_dict(item_list)
@@ -286,18 +286,18 @@ class ProductionPlanningTool(Document):
total_qty = sum([flt(d[0]) for d in self.item_dict[item]]) total_qty = sum([flt(d[0]) for d in self.item_dict[item]])
for item_details in self.item_dict[item]: for item_details in self.item_dict[item]:
item_list.append([item, item_details[1], item_details[2], item_details[0]]) item_list.append([item, item_details[1], item_details[2], item_details[0]])
item_qty = frappe.db.sql("""select warehouse, indented_qty, ordered_qty, actual_qty item_qty = frappe.db.sql("""select warehouse, indented_qty, ordered_qty, actual_qty
from `tabBin` where item_code = %s""", item, as_dict=1) from `tabBin` where item_code = %s""", item, as_dict=1)
i_qty, o_qty, a_qty = 0, 0, 0 i_qty, o_qty, a_qty = 0, 0, 0
for w in item_qty: for w in item_qty:
i_qty, o_qty, a_qty = i_qty + flt(w.indented_qty), o_qty + flt(w.ordered_qty), a_qty + flt(w.actual_qty) i_qty, o_qty, a_qty = i_qty + flt(w.indented_qty), o_qty + flt(w.ordered_qty), a_qty + flt(w.actual_qty)
item_list.append(['', '', '', '', w.warehouse, flt(w.indented_qty), item_list.append(['', '', '', '', w.warehouse, flt(w.indented_qty),
flt(w.ordered_qty), flt(w.actual_qty)]) flt(w.ordered_qty), flt(w.actual_qty)])
if item_qty: if item_qty:
item_list.append(['', '', '', '', 'Total', i_qty, o_qty, a_qty]) item_list.append(['', '', '', '', 'Total', i_qty, o_qty, a_qty])
return item_list return item_list
def raise_purchase_request(self): def raise_purchase_request(self):
""" """
Raise Material Request if projected qty is less than qty required Raise Material Request if projected qty is less than qty required
@@ -306,10 +306,10 @@ class ProductionPlanningTool(Document):
self.validate_data() self.validate_data()
if not self.purchase_request_for_warehouse: if not self.purchase_request_for_warehouse:
frappe.throw(_("Please enter Warehouse for which Material Request will be raised")) frappe.throw(_("Please enter Warehouse for which Material Request will be raised"))
bom_dict = self.get_distinct_items_and_boms()[0] bom_dict = self.get_distinct_items_and_boms()[0]
self.get_raw_materials(bom_dict) self.get_raw_materials(bom_dict)
if self.item_dict: if self.item_dict:
self.insert_purchase_request() self.insert_purchase_request()
@@ -348,15 +348,15 @@ class ProductionPlanningTool(Document):
items_to_be_requested[item]["No Sales Order"] += requested_qty items_to_be_requested[item]["No Sales Order"] += requested_qty
return items_to_be_requested return items_to_be_requested
def get_projected_qty(self): def get_projected_qty(self):
items = self.item_dict.keys() items = self.item_dict.keys()
item_projected_qty = frappe.db.sql("""select item_code, sum(projected_qty) item_projected_qty = frappe.db.sql("""select item_code, sum(projected_qty)
from `tabBin` where item_code in (%s) group by item_code""" % from `tabBin` where item_code in (%s) group by item_code""" %
(", ".join(["%s"]*len(items)),), tuple(items)) (", ".join(["%s"]*len(items)),), tuple(items))
return dict(item_projected_qty) return dict(item_projected_qty)
def insert_purchase_request(self): def insert_purchase_request(self):
items_to_be_requested = self.get_requested_items() items_to_be_requested = self.get_requested_items()
@@ -397,11 +397,10 @@ class ProductionPlanningTool(Document):
pr_doc.ignore_permissions = 1 pr_doc.ignore_permissions = 1
pr_doc.submit() pr_doc.submit()
purchase_request_list.append(pr_doc.name) purchase_request_list.append(pr_doc.name)
if purchase_request_list: if purchase_request_list:
pur_req = ["""<a href="#Form/Material Request/%s" target="_blank">%s</a>""" % \ pur_req = ["""<a href="#Form/Material Request/%s" target="_blank">%s</a>""" % \
(p, p) for p in purchase_request_list] (p, p) for p in purchase_request_list]
msgprint("Material Request(s) created: \n%s" % msgprint(_("Material Requests {0} created").format(comma_and(pur_req)))
"\n".join(pur_req))
else: else:
msgprint(_("Nothing to request")) msgprint(_("Nothing to request"))

View File

@@ -5,13 +5,13 @@ from __future__ import unicode_literals
import frappe import frappe
from frappe.utils import flt, getdate from frappe.utils import flt, getdate
from frappe import msgprint from frappe import _
from erpnext.utilities.transaction_base import delete_events from erpnext.utilities.transaction_base import delete_events
from frappe.model.document import Document from frappe.model.document import Document
class Project(Document): class Project(Document):
def get_gross_profit(self): def get_gross_profit(self):
pft, per_pft =0, 0 pft, per_pft =0, 0
pft = flt(self.project_value) - flt(self.est_material_cost) pft = flt(self.project_value) - flt(self.est_material_cost)
@@ -19,19 +19,18 @@ class Project(Document):
per_pft = (flt(pft) / flt(self.project_value)) * 100 per_pft = (flt(pft) / flt(self.project_value)) * 100
ret = {'gross_margin_value': pft, 'per_gross_margin': per_pft} ret = {'gross_margin_value': pft, 'per_gross_margin': per_pft}
return ret return ret
def validate(self): def validate(self):
"""validate start date before end date""" """validate start date before end date"""
if self.project_start_date and self.completion_date: if self.project_start_date and self.completion_date:
if getdate(self.completion_date) < getdate(self.project_start_date): if getdate(self.completion_date) < getdate(self.project_start_date):
msgprint("Expected Completion Date can not be less than Project Start Date") frappe.throw(_("Expected Completion Date can not be less than Project Start Date"))
raise Exception
def on_update(self): def on_update(self):
self.add_calendar_event() self.add_calendar_event()
def update_percent_complete(self): def update_percent_complete(self):
total = frappe.db.sql("""select count(*) from tabTask where project=%s""", total = frappe.db.sql("""select count(*) from tabTask where project=%s""",
self.name)[0][0] self.name)[0][0]
if total: if total:
completed = frappe.db.sql("""select count(*) from tabTask where completed = frappe.db.sql("""select count(*) from tabTask where
@@ -42,7 +41,7 @@ class Project(Document):
def add_calendar_event(self): def add_calendar_event(self):
# delete any earlier event for this project # delete any earlier event for this project
delete_events(self.doctype, self.name) delete_events(self.doctype, self.name)
# add events # add events
for milestone in self.get("project_milestones"): for milestone in self.get("project_milestones"):
if milestone.milestone_date: if milestone.milestone_date:
@@ -57,6 +56,6 @@ class Project(Document):
"ref_type": self.doctype, "ref_type": self.doctype,
"ref_name": self.name "ref_name": self.name
}).insert() }).insert()
def on_trash(self): def on_trash(self):
delete_events(self.doctype, self.name) delete_events(self.doctype, self.name)

View File

@@ -5,7 +5,7 @@ from __future__ import unicode_literals
import frappe, json import frappe, json
from frappe.utils import getdate, today from frappe.utils import getdate, today
from frappe import msgprint from frappe import _
from frappe.model.document import Document from frappe.model.document import Document
@@ -15,32 +15,30 @@ class Task(Document):
return { return {
"project": self.project "project": self.project
} }
def get_customer_details(self): def get_customer_details(self):
cust = frappe.db.sql("select customer_name from `tabCustomer` where name=%s", self.customer) cust = frappe.db.sql("select customer_name from `tabCustomer` where name=%s", self.customer)
if cust: if cust:
ret = {'customer_name': cust and cust[0][0] or ''} ret = {'customer_name': cust and cust[0][0] or ''}
return ret return ret
def validate(self): def validate(self):
if self.exp_start_date and self.exp_end_date and getdate(self.exp_start_date) > getdate(self.exp_end_date): if self.exp_start_date and self.exp_end_date and getdate(self.exp_start_date) > getdate(self.exp_end_date):
msgprint("'Expected Start Date' can not be greater than 'Expected End Date'") frappe.throw(_("'Expected Start Date' can not be greater than 'Expected End Date'"))
raise Exception
if self.act_start_date and self.act_end_date and getdate(self.act_start_date) > getdate(self.act_end_date): if self.act_start_date and self.act_end_date and getdate(self.act_start_date) > getdate(self.act_end_date):
msgprint("'Actual Start Date' can not be greater than 'Actual End Date'") frappe.throw(_("'Actual Start Date' can not be greater than 'Actual End Date'"))
raise Exception
self.update_status() self.update_status()
def update_status(self): def update_status(self):
status = frappe.db.get_value("Task", self.name, "status") status = frappe.db.get_value("Task", self.name, "status")
if self.status=="Working" and status !="Working" and not self.act_start_date: if self.status=="Working" and status !="Working" and not self.act_start_date:
self.act_start_date = today() self.act_start_date = today()
if self.status=="Closed" and status != "Closed" and not self.act_end_date: if self.status=="Closed" and status != "Closed" and not self.act_end_date:
self.act_end_date = today() self.act_end_date = today()
def on_update(self): def on_update(self):
"""update percent complete in project""" """update percent complete in project"""
if self.project: if self.project:
@@ -55,14 +53,14 @@ def get_events(start, end, filters=None):
conditions = build_match_conditions("Task") conditions = build_match_conditions("Task")
conditions and (" and " + conditions) or "" conditions and (" and " + conditions) or ""
if filters: if filters:
filters = json.loads(filters) filters = json.loads(filters)
for key in filters: for key in filters:
if filters[key]: if filters[key]:
conditions += " and " + key + ' = "' + filters[key].replace('"', '\"') + '"' conditions += " and " + key + ' = "' + filters[key].replace('"', '\"') + '"'
data = frappe.db.sql("""select name, exp_start_date, exp_end_date, data = frappe.db.sql("""select name, exp_start_date, exp_end_date,
subject, status, project from `tabTask` subject, status, project from `tabTask`
where ((exp_start_date between '%(start)s' and '%(end)s') \ where ((exp_start_date between '%(start)s' and '%(end)s') \
or (exp_end_date between '%(start)s' and '%(end)s')) or (exp_end_date between '%(start)s' and '%(end)s'))
@@ -79,7 +77,7 @@ def get_project(doctype, txt, searchfield, start, page_len, filters):
return frappe.db.sql(""" select name from `tabProject` return frappe.db.sql(""" select name from `tabProject`
where %(key)s like "%(txt)s" where %(key)s like "%(txt)s"
%(mcond)s %(mcond)s
order by name order by name
limit %(start)s, %(page_len)s """ % {'key': searchfield, limit %(start)s, %(page_len)s """ % {'key': searchfield,
'txt': "%%%s%%" % txt, 'mcond':get_match_cond(doctype), 'txt': "%%%s%%" % txt, 'mcond':get_match_cond(doctype),
'start': start, 'page_len': page_len}) 'start': start, 'page_len': page_len})

View File

@@ -6,7 +6,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe import _ from frappe import _
from frappe.utils import cstr from frappe.utils import cstr, comma_and
class OverlapError(frappe.ValidationError): pass class OverlapError(frappe.ValidationError): pass
@@ -14,12 +14,12 @@ class OverlapError(frappe.ValidationError): pass
from frappe.model.document import Document from frappe.model.document import Document
class TimeLog(Document): class TimeLog(Document):
def validate(self): def validate(self):
self.set_status() self.set_status()
self.validate_overlap() self.validate_overlap()
self.calculate_total_hours() self.calculate_total_hours()
def calculate_total_hours(self): def calculate_total_hours(self):
from frappe.utils import time_diff_in_hours from frappe.utils import time_diff_in_hours
self.hours = time_diff_in_hours(self.to_time, self.from_time) self.hours = time_diff_in_hours(self.to_time, self.from_time)
@@ -30,36 +30,35 @@ class TimeLog(Document):
1: "Submitted", 1: "Submitted",
2: "Cancelled" 2: "Cancelled"
}[self.docstatus or 0] }[self.docstatus or 0]
if self.time_log_batch: if self.time_log_batch:
self.status="Batched for Billing" self.status="Batched for Billing"
if self.sales_invoice: if self.sales_invoice:
self.status="Billed" self.status="Billed"
def validate_overlap(self): def validate_overlap(self):
existing = frappe.db.sql_list("""select name from `tabTime Log` where owner=%s and existing = frappe.db.sql_list("""select name from `tabTime Log` where owner=%s and
( (
(from_time between %s and %s) or (from_time between %s and %s) or
(to_time between %s and %s) or (to_time between %s and %s) or
(%s between from_time and to_time)) (%s between from_time and to_time))
and name!=%s and name!=%s
and ifnull(task, "")=%s and ifnull(task, "")=%s
and docstatus < 2""", and docstatus < 2""",
(self.owner, self.from_time, self.to_time, self.from_time, (self.owner, self.from_time, self.to_time, self.from_time,
self.to_time, self.from_time, self.name or "No Name", self.to_time, self.from_time, self.name or "No Name",
cstr(self.task))) cstr(self.task)))
if existing: if existing:
frappe.msgprint(_("This Time Log conflicts with") + ":" + ', '.join(existing), frappe.throw(_("This Time Log conflicts with {0}").format(comma_and(existing)), OverlapError)
raise_exception=OverlapError)
def before_cancel(self): def before_cancel(self):
self.set_status() self.set_status()
def before_update_after_submit(self): def before_update_after_submit(self):
self.set_status() self.set_status()
@frappe.whitelist() @frappe.whitelist()
def get_events(start, end): def get_events(start, end):
from frappe.widgets.reportview import build_match_conditions from frappe.widgets.reportview import build_match_conditions
@@ -67,7 +66,7 @@ def get_events(start, end):
frappe.msgprint(_("No Permission"), raise_exception=1) frappe.msgprint(_("No Permission"), raise_exception=1)
match = build_match_conditions("Time Log") match = build_match_conditions("Time Log")
data = frappe.db.sql("""select name, from_time, to_time, data = frappe.db.sql("""select name, from_time, to_time,
activity_type, task, project from `tabTime Log` activity_type, task, project from `tabTime Log`
where from_time between '%(start)s' and '%(end)s' or to_time between '%(start)s' and '%(end)s' where from_time between '%(start)s' and '%(end)s' or to_time between '%(start)s' and '%(end)s'
%(match)s""" % { %(match)s""" % {
@@ -75,12 +74,12 @@ def get_events(start, end):
"end": end, "end": end,
"match": match and (" and " + match) or "" "match": match and (" and " + match) or ""
}, as_dict=True, update={"allDay": 0}) }, as_dict=True, update={"allDay": 0})
for d in data: for d in data:
d.title = d.name + ": " + (d.activity_type or "[Activity Type not set]") d.title = d.name + ": " + (d.activity_type or "[Activity Type not set]")
if d.task: if d.task:
d.title += " for Task: " + d.task d.title += " for Task: " + d.task
if d.project: if d.project:
d.title += " for Project: " + d.project d.title += " for Project: " + d.project
return data return data

View File

@@ -29,8 +29,7 @@ class TimeLogBatch(Document):
def validate_time_log_is_submitted(self, tl): def validate_time_log_is_submitted(self, tl):
if tl.status != "Submitted" and self.docstatus == 0: if tl.status != "Submitted" and self.docstatus == 0:
frappe.msgprint(_("Time Log must have status 'Submitted'") + \ frappe.throw(_("Time Log {0} must be 'Submitted'").format(tl.name))
" :" + tl.name + " (" + _(tl.status) + ")", raise_exception=True)
def set_status(self): def set_status(self):
self.status = { self.status = {

View File

@@ -6,7 +6,7 @@ import frappe
from frappe.utils import cstr, getdate from frappe.utils import cstr, getdate
from frappe import msgprint from frappe import _
from erpnext.stock.utils import get_valid_serial_nos from erpnext.stock.utils import get_valid_serial_nos
from erpnext.utilities.transaction_base import TransactionBase from erpnext.utilities.transaction_base import TransactionBase
@@ -46,15 +46,14 @@ class InstallationNote(TransactionBase):
def is_serial_no_added(self, item_code, serial_no): def is_serial_no_added(self, item_code, serial_no):
ar_required = frappe.db.get_value("Item", item_code, "has_serial_no") ar_required = frappe.db.get_value("Item", item_code, "has_serial_no")
if ar_required == 'Yes' and not serial_no: if ar_required == 'Yes' and not serial_no:
msgprint("Serial No is mandatory for item: " + item_code, raise_exception=1) frappe.throw(_("Serial No is mandatory for Item {0}").format(item_code))
elif ar_required != 'Yes' and cstr(serial_no).strip(): elif ar_required != 'Yes' and cstr(serial_no).strip():
msgprint("If serial no required, please select 'Yes' in 'Has Serial No' in Item :" + frappe.throw(_("Item {0} is not a serialized Item").format(item_code))
item_code, raise_exception=1)
def is_serial_no_exist(self, item_code, serial_no): def is_serial_no_exist(self, item_code, serial_no):
for x in serial_no: for x in serial_no:
if not frappe.db.exists("Serial No", x): if not frappe.db.exists("Serial No", x):
msgprint("Serial No " + x + " does not exist in the system", raise_exception=1) frappe.throw(_("Serial No {0} does not exist").format(x))
def is_serial_no_installed(self,cur_s_no,item_code): def is_serial_no_installed(self,cur_s_no,item_code):
for x in cur_s_no: for x in cur_s_no:
@@ -62,8 +61,7 @@ class InstallationNote(TransactionBase):
status = status and status[0][0] or '' status = status and status[0][0] or ''
if status == 'Installed': if status == 'Installed':
msgprint("Item "+item_code+" with serial no. " + x + " already installed", frappe.throw(_("Item {0} with Serial No {1} is already installed").format(item_code, x))
raise_exception=1)
def get_prevdoc_serial_no(self, prevdoc_detail_docname): def get_prevdoc_serial_no(self, prevdoc_detail_docname):
serial_nos = frappe.db.get_value("Delivery Note Item", serial_nos = frappe.db.get_value("Delivery Note Item",
@@ -73,8 +71,7 @@ class InstallationNote(TransactionBase):
def is_serial_no_match(self, cur_s_no, prevdoc_s_no, prevdoc_docname): def is_serial_no_match(self, cur_s_no, prevdoc_s_no, prevdoc_docname):
for sr in cur_s_no: for sr in cur_s_no:
if sr not in prevdoc_s_no: if sr not in prevdoc_s_no:
msgprint("Serial No. " + sr + " is not matching with the Delivery Note " + frappe.throw(_("Serial No {0} does not belong to Delivery Note {1}").format(sr, prevdoc_docname))
prevdoc_docname, raise_exception = 1)
def validate_serial_no(self): def validate_serial_no(self):
cur_s_no, prevdoc_s_no, sr_list = [], [], [] cur_s_no, prevdoc_s_no, sr_list = [], [], []
@@ -95,12 +92,11 @@ class InstallationNote(TransactionBase):
if d.prevdoc_docname: if d.prevdoc_docname:
d_date = frappe.db.get_value("Delivery Note", d.prevdoc_docname, "posting_date") d_date = frappe.db.get_value("Delivery Note", d.prevdoc_docname, "posting_date")
if d_date > getdate(self.inst_date): if d_date > getdate(self.inst_date):
msgprint("Installation Date can not be before Delivery Date " + cstr(d_date) + frappe.throw(_("Installation date cannot be before delivery date for Item {0}").format(d.item_code))
" for item "+d.item_code, raise_exception=1)
def check_item_table(self): def check_item_table(self):
if not(self.get('installed_item_details')): if not(self.get('installed_item_details')):
msgprint("Please fetch items from Delivery Note selected", raise_exception=1) frappe.throw(_("Please pull items from Delivery Note"))
def on_update(self): def on_update(self):
frappe.db.set(self, 'status', 'Draft') frappe.db.set(self, 'status', 'Draft')

View File

@@ -4,10 +4,10 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe import _ from frappe import _
from frappe.utils import cstr, validate_email_add, cint from frappe.utils import cstr, validate_email_add, cint, comma_and
from frappe import session from frappe import session
from erpnext.controllers.selling_controller import SellingController from erpnext.controllers.selling_controller import SellingController
class Lead(SellingController): class Lead(SellingController):
@@ -15,7 +15,7 @@ class Lead(SellingController):
customer = frappe.db.get_value("Customer", {"lead_name": self.name}) customer = frappe.db.get_value("Customer", {"lead_name": self.name})
if customer: if customer:
self.set("__is_customer", customer) self.set("__is_customer", customer)
def validate(self): def validate(self):
self._prev = frappe._dict({ self._prev = frappe._dict({
"contact_date": frappe.db.get_value("Lead", self.name, "contact_date") if \ "contact_date": frappe.db.get_value("Lead", self.name, "contact_date") if \
@@ -23,20 +23,20 @@ class Lead(SellingController):
"contact_by": frappe.db.get_value("Lead", self.name, "contact_by") if \ "contact_by": frappe.db.get_value("Lead", self.name, "contact_by") if \
(not cint(self.get("__islocal"))) else None, (not cint(self.get("__islocal"))) else None,
}) })
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("Please specify campaign name")
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('Please enter valid email id.')
def on_update(self): def on_update(self):
self.check_email_id_is_unique() self.check_email_id_is_unique()
self.add_calendar_event() self.add_calendar_event()
def add_calendar_event(self, opts=None, force=False): def add_calendar_event(self, opts=None, force=False):
super(Lead, self).add_calendar_event({ super(Lead, self).add_calendar_event({
"owner": self.lead_owner, "owner": self.lead_owner,
@@ -48,22 +48,21 @@ class Lead(SellingController):
def check_email_id_is_unique(self): def check_email_id_is_unique(self):
if self.email_id: if self.email_id:
# validate email is unique # validate email is unique
email_list = frappe.db.sql("""select name from tabLead where email_id=%s""", email_list = frappe.db.sql("""select name from tabLead where email_id=%s""",
self.email_id) self.email_id)
if len(email_list) > 1: if len(email_list) > 1:
items = [e[0] for e in email_list if e[0]!=self.name] items = [e[0] for e in email_list if e[0]!=self.name]
frappe.msgprint(_("""Email Id must be unique, already exists for: """) + \ frappe.throw(_("Email id must be unique, already exists for {0}").format(comma_and(items)))
", ".join(items), raise_exception=True)
def on_trash(self): def on_trash(self):
frappe.db.sql("""update `tabSupport Ticket` set lead='' where lead=%s""", frappe.db.sql("""update `tabSupport Ticket` set lead='' where lead=%s""",
self.name) self.name)
self.delete_events() self.delete_events()
def has_customer(self): def has_customer(self):
return frappe.db.get_value("Customer", {"lead_name": self.name}) return frappe.db.get_value("Customer", {"lead_name": self.name})
def has_opportunity(self): def has_opportunity(self):
return frappe.db.get_value("Opportunity", {"lead": self.name, "docstatus": 1, return frappe.db.get_value("Opportunity", {"lead": self.name, "docstatus": 1,
"status": ["!=", "Lost"]}) "status": ["!=", "Lost"]})
@@ -74,7 +73,7 @@ def make_customer(source_name, target_doc=None):
def _make_customer(source_name, target_doc=None, ignore_permissions=False): def _make_customer(source_name, target_doc=None, ignore_permissions=False):
from frappe.model.mapper import get_mapped_doc from frappe.model.mapper import get_mapped_doc
def set_missing_values(source, target): def set_missing_values(source, target):
if source.company_name: if source.company_name:
target.customer_type = "Company" target.customer_type = "Company"
@@ -82,10 +81,10 @@ def _make_customer(source_name, target_doc=None, ignore_permissions=False):
else: else:
target.customer_type = "Individual" target.customer_type = "Individual"
target.customer_name = source.lead_name target.customer_name = source.lead_name
target.customer_group = frappe.db.get_default("customer_group") target.customer_group = frappe.db.get_default("customer_group")
doclist = get_mapped_doc("Lead", source_name, doclist = get_mapped_doc("Lead", source_name,
{"Lead": { {"Lead": {
"doctype": "Customer", "doctype": "Customer",
"field_map": { "field_map": {
@@ -95,14 +94,14 @@ def _make_customer(source_name, target_doc=None, ignore_permissions=False):
"fax": "fax_1" "fax": "fax_1"
} }
}}, target_doc, set_missing_values, ignore_permissions=ignore_permissions) }}, target_doc, set_missing_values, ignore_permissions=ignore_permissions)
return doclist return doclist
@frappe.whitelist() @frappe.whitelist()
def make_opportunity(source_name, target_doc=None): def make_opportunity(source_name, target_doc=None):
from frappe.model.mapper import get_mapped_doc from frappe.model.mapper import get_mapped_doc
doclist = get_mapped_doc("Lead", source_name, doclist = get_mapped_doc("Lead", source_name,
{"Lead": { {"Lead": {
"doctype": "Opportunity", "doctype": "Opportunity",
"field_map": { "field_map": {
@@ -115,19 +114,19 @@ def make_opportunity(source_name, target_doc=None):
"mobile_no": "contact_mobile" "mobile_no": "contact_mobile"
} }
}}, target_doc) }}, target_doc)
return doclist return doclist
@frappe.whitelist() @frappe.whitelist()
def get_lead_details(lead): def get_lead_details(lead):
if not lead: return {} if not lead: return {}
from erpnext.accounts.party import set_address_details from erpnext.accounts.party import set_address_details
out = frappe._dict() out = frappe._dict()
lead_doc = frappe.get_doc("Lead", lead) lead_doc = frappe.get_doc("Lead", lead)
lead = lead_doc lead = lead_doc
out.update({ out.update({
"territory": lead.territory, "territory": lead.territory,
"customer_name": lead.company_name or lead.lead_name, "customer_name": lead.company_name or lead.lead_name,
@@ -136,7 +135,7 @@ def get_lead_details(lead):
"contact_mobile": lead.mobile_no, "contact_mobile": lead.mobile_no,
"contact_phone": lead.phone, "contact_phone": lead.phone,
}) })
set_address_details(out, lead, "Lead") set_address_details(out, lead, "Lead")
return out return out

View File

@@ -8,13 +8,13 @@ from frappe.utils import cstr, cint
from frappe import msgprint, _ from frappe import msgprint, _
from erpnext.utilities.transaction_base import TransactionBase from erpnext.utilities.transaction_base import TransactionBase
class Opportunity(TransactionBase): class Opportunity(TransactionBase):
fname = 'enq_details' fname = 'enq_details'
tname = 'Opportunity Item' tname = 'Opportunity Item'
def get_item_details(self, item_code): def get_item_details(self, item_code):
item = frappe.db.sql("""select item_name, stock_uom, description_html, description, item_group, brand item = frappe.db.sql("""select item_name, stock_uom, description_html, description, item_group, brand
from `tabItem` where name = %s""", item_code, as_dict=1) from `tabItem` where name = %s""", item_code, as_dict=1)
@@ -28,7 +28,7 @@ class Opportunity(TransactionBase):
return ret return ret
def get_cust_address(self,name): def get_cust_address(self,name):
details = frappe.db.sql("""select customer_name, address, territory, customer_group details = frappe.db.sql("""select customer_name, address, territory, customer_group
from `tabCustomer` where name = %s and docstatus != 2""", (name), as_dict = 1) from `tabCustomer` where name = %s and docstatus != 2""", (name), as_dict = 1)
if details: if details:
ret = { ret = {
@@ -39,28 +39,28 @@ class Opportunity(TransactionBase):
} }
# ********** get primary contact details (this is done separately coz. , in case there is no primary contact thn it would not be able to fetch customer details in case of join query) # ********** get primary contact details (this is done separately coz. , in case there is no primary contact thn it would not be able to fetch customer details in case of join query)
contact_det = frappe.db.sql("""select contact_name, contact_no, email_id contact_det = frappe.db.sql("""select contact_name, contact_no, email_id
from `tabContact` where customer = %s and is_customer = 1 from `tabContact` where customer = %s and is_customer = 1
and is_primary_contact = 'Yes' and docstatus != 2""", name, as_dict = 1) and is_primary_contact = 'Yes' and docstatus != 2""", name, as_dict = 1)
ret['contact_person'] = contact_det and contact_det[0]['contact_name'] or '' ret['contact_person'] = contact_det and contact_det[0]['contact_name'] or ''
ret['contact_no'] = contact_det and contact_det[0]['contact_no'] or '' ret['contact_no'] = contact_det and contact_det[0]['contact_no'] or ''
ret['email_id'] = contact_det and contact_det[0]['email_id'] or '' ret['email_id'] = contact_det and contact_det[0]['email_id'] or ''
return ret return ret
else: else:
msgprint("Customer : %s does not exist in system." % (name)) msgprint("Customer : %s does not exist in system." % (name))
raise Exception raise Exception
def on_update(self): def on_update(self):
self.add_calendar_event() self.add_calendar_event()
def add_calendar_event(self, opts=None, force=False): def add_calendar_event(self, opts=None, force=False):
if not opts: if not opts:
opts = frappe._dict() opts = frappe._dict()
opts.description = "" opts.description = ""
if self.customer: if self.customer:
if self.contact_person: if self.contact_person:
opts.description = 'Contact '+cstr(self.contact_person) opts.description = 'Contact '+cstr(self.contact_person)
@@ -71,23 +71,22 @@ class Opportunity(TransactionBase):
opts.description = 'Contact '+cstr(self.contact_display) opts.description = 'Contact '+cstr(self.contact_display)
else: else:
opts.description = 'Contact lead '+cstr(self.lead) opts.description = 'Contact lead '+cstr(self.lead)
opts.subject = opts.description opts.subject = opts.description
opts.description += '. By : ' + cstr(self.contact_by) opts.description += '. By : ' + cstr(self.contact_by)
if self.to_discuss: if self.to_discuss:
opts.description += ' To Discuss : ' + cstr(self.to_discuss) opts.description += ' To Discuss : ' + cstr(self.to_discuss)
super(Opportunity, self).add_calendar_event(opts, force) super(Opportunity, self).add_calendar_event(opts, force)
def validate_item_details(self): def validate_item_details(self):
if not self.get('enquiry_details'): if not self.get('enquiry_details'):
msgprint("Please select items for which enquiry needs to be made") frappe.throw(_("Items required"))
raise Exception
def validate_lead_cust(self): def validate_lead_cust(self):
if self.enquiry_from == 'Lead' and not self.lead: if self.enquiry_from == 'Lead' and not self.lead:
msgprint("Lead Id is mandatory if 'Opportunity From' is selected as Lead", raise_exception=1) frappe.throw(_("Lead must be set if Opportunity is made from Lead"))
elif self.enquiry_from == 'Customer' and not self.customer: elif self.enquiry_from == 'Customer' and not self.customer:
msgprint("Customer is mandatory if 'Opportunity From' is selected as Customer", raise_exception=1) msgprint("Customer is mandatory if 'Opportunity From' is selected as Customer", raise_exception=1)
@@ -98,24 +97,24 @@ class Opportunity(TransactionBase):
"contact_by": frappe.db.get_value("Opportunity", self.name, "contact_by") if \ "contact_by": frappe.db.get_value("Opportunity", self.name, "contact_by") if \
(not cint(self.get("__islocal"))) else None, (not cint(self.get("__islocal"))) else None,
}) })
self.set_status() self.set_status()
self.validate_item_details() self.validate_item_details()
self.validate_uom_is_integer("uom", "qty") self.validate_uom_is_integer("uom", "qty")
self.validate_lead_cust() self.validate_lead_cust()
from erpnext.accounts.utils import validate_fiscal_year from erpnext.accounts.utils import validate_fiscal_year
validate_fiscal_year(self.transaction_date, self.fiscal_year, "Opportunity Date") validate_fiscal_year(self.transaction_date, self.fiscal_year, "Opportunity Date")
def on_submit(self): def on_submit(self):
if self.lead: if self.lead:
frappe.get_doc("Lead", self.lead).set_status(update=True) frappe.get_doc("Lead", self.lead).set_status(update=True)
def on_cancel(self): def on_cancel(self):
if self.has_quotation(): if self.has_quotation():
frappe.throw(_("Cannot Cancel Opportunity as Quotation Exists")) frappe.throw(_("Cannot Cancel Opportunity as Quotation Exists"))
self.set_status(update=True) self.set_status(update=True)
def declare_enquiry_lost(self,arg): def declare_enquiry_lost(self,arg):
if not self.has_quotation(): if not self.has_quotation():
frappe.db.set(self, 'status', 'Lost') frappe.db.set(self, 'status', 'Lost')
@@ -125,40 +124,40 @@ class Opportunity(TransactionBase):
def on_trash(self): def on_trash(self):
self.delete_events() self.delete_events()
def has_quotation(self): def has_quotation(self):
return frappe.db.get_value("Quotation Item", {"prevdoc_docname": self.name, "docstatus": 1}) return frappe.db.get_value("Quotation Item", {"prevdoc_docname": self.name, "docstatus": 1})
@frappe.whitelist() @frappe.whitelist()
def make_quotation(source_name, target_doc=None): def make_quotation(source_name, target_doc=None):
from frappe.model.mapper import get_mapped_doc from frappe.model.mapper import get_mapped_doc
def set_missing_values(source, target): def set_missing_values(source, target):
quotation = frappe.get_doc(target) quotation = frappe.get_doc(target)
quotation.run_method("onload_post_render") quotation.run_method("onload_post_render")
quotation.run_method("calculate_taxes_and_totals") quotation.run_method("calculate_taxes_and_totals")
doclist = get_mapped_doc("Opportunity", source_name, { doclist = get_mapped_doc("Opportunity", source_name, {
"Opportunity": { "Opportunity": {
"doctype": "Quotation", "doctype": "Quotation",
"field_map": { "field_map": {
"enquiry_from": "quotation_to", "enquiry_from": "quotation_to",
"enquiry_type": "order_type", "enquiry_type": "order_type",
"name": "enq_no", "name": "enq_no",
}, },
"validation": { "validation": {
"docstatus": ["=", 1] "docstatus": ["=", 1]
} }
}, },
"Opportunity Item": { "Opportunity Item": {
"doctype": "Quotation Item", "doctype": "Quotation Item",
"field_map": { "field_map": {
"parent": "prevdoc_docname", "parent": "prevdoc_docname",
"parenttype": "prevdoc_doctype", "parenttype": "prevdoc_doctype",
"uom": "stock_uom" "uom": "stock_uom"
}, },
"add_if_empty": True "add_if_empty": True
} }
}, target_doc, set_missing_values) }, target_doc, set_missing_values)
return doclist return doclist

View File

@@ -5,7 +5,7 @@ from __future__ import unicode_literals
import frappe import frappe
from frappe.utils import cstr from frappe.utils import cstr
from frappe import _, msgprint from frappe import _
from erpnext.controllers.selling_controller import SellingController from erpnext.controllers.selling_controller import SellingController
@@ -20,31 +20,28 @@ class Quotation(SellingController):
chk_dupl_itm = [] chk_dupl_itm = []
for d in self.get('quotation_details'): for d in self.get('quotation_details'):
if [cstr(d.item_code),cstr(d.description)] in chk_dupl_itm: if [cstr(d.item_code),cstr(d.description)] in chk_dupl_itm:
msgprint("Item %s has been entered twice. Please change description atleast to continue" % d.item_code) frappe.throw(_("Item {0} with same description entered twice").format(d.item_code))
raise Exception
else: else:
chk_dupl_itm.append([cstr(d.item_code),cstr(d.description)]) chk_dupl_itm.append([cstr(d.item_code),cstr(d.description)])
def validate_order_type(self): def validate_order_type(self):
super(Quotation, self).validate_order_type() super(Quotation, self).validate_order_type()
if self.order_type in ['Maintenance', 'Service']: if self.order_type in ['Maintenance', 'Service']:
for d in self.get('quotation_details'): for d in self.get('quotation_details'):
is_service_item = frappe.db.sql("select is_service_item from `tabItem` where name=%s", d.item_code) is_service_item = frappe.db.sql("select is_service_item from `tabItem` where name=%s", d.item_code)
is_service_item = is_service_item and is_service_item[0][0] or 'No' is_service_item = is_service_item and is_service_item[0][0] or 'No'
if is_service_item == 'No': if is_service_item == 'No':
msgprint("You can not select non service item "+d.item_code+" in Maintenance Quotation") frappe.throw(_("Item {0} must be Service Item").format(d.item_code))
raise Exception
else: else:
for d in self.get('quotation_details'): for d in self.get('quotation_details'):
is_sales_item = frappe.db.sql("select is_sales_item from `tabItem` where name=%s", d.item_code) is_sales_item = frappe.db.sql("select is_sales_item from `tabItem` where name=%s", d.item_code)
is_sales_item = is_sales_item and is_sales_item[0][0] or 'No' is_sales_item = is_sales_item and is_sales_item[0][0] or 'No'
if is_sales_item == 'No': if is_sales_item == 'No':
msgprint("You can not select non sales item "+d.item_code+" in Sales Quotation") frappe.throw(_("Item {0} must be Sales Item").format(d.item_code))
raise Exception
def validate(self): def validate(self):
super(Quotation, self).validate() super(Quotation, self).validate()
self.set_status() self.set_status()
@@ -56,7 +53,7 @@ class Quotation(SellingController):
for opportunity in list(set([d.prevdoc_docname for d in self.get("quotation_details")])): for opportunity in list(set([d.prevdoc_docname for d in self.get("quotation_details")])):
if opportunity: if opportunity:
frappe.get_doc("Opportunity", opportunity).set_status(update=True) frappe.get_doc("Opportunity", opportunity).set_status(update=True)
def declare_order_lost(self, arg): def declare_order_lost(self, arg):
if not self.has_sales_order(): if not self.has_sales_order():
frappe.db.set(self, 'status', 'Lost') frappe.db.set(self, 'status', 'Lost')
@@ -64,26 +61,25 @@ class Quotation(SellingController):
self.update_opportunity() self.update_opportunity()
else: else:
frappe.throw(_("Cannot set as Lost as Sales Order is made.")) frappe.throw(_("Cannot set as Lost as Sales Order is made."))
def check_item_table(self): def check_item_table(self):
if not self.get('quotation_details'): if not self.get('quotation_details'):
msgprint("Please enter item details") frappe.throw(_("Please enter item details"))
raise Exception
def on_submit(self): def on_submit(self):
self.check_item_table() self.check_item_table()
# Check for Approving Authority # Check for Approving Authority
frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype, self.company, self.grand_total, self) frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype, self.company, self.grand_total, self)
#update enquiry status #update enquiry status
self.update_opportunity() self.update_opportunity()
def on_cancel(self): def on_cancel(self):
#update enquiry status #update enquiry status
self.set_status() self.set_status()
self.update_opportunity() self.update_opportunity()
def print_other_charges(self,docname): def print_other_charges(self,docname):
print_lst = [] print_lst = []
for d in self.get('other_charges'): for d in self.get('other_charges'):
@@ -92,51 +88,51 @@ class Quotation(SellingController):
lst1.append(d.total) lst1.append(d.total)
print_lst.append(lst1) print_lst.append(lst1)
return print_lst return print_lst
@frappe.whitelist() @frappe.whitelist()
def make_sales_order(source_name, target_doc=None): def make_sales_order(source_name, target_doc=None):
return _make_sales_order(source_name, target_doc) return _make_sales_order(source_name, target_doc)
def _make_sales_order(source_name, target_doc=None, ignore_permissions=False): def _make_sales_order(source_name, target_doc=None, ignore_permissions=False):
from frappe.model.mapper import get_mapped_doc from frappe.model.mapper import get_mapped_doc
customer = _make_customer(source_name, ignore_permissions) customer = _make_customer(source_name, ignore_permissions)
def set_missing_values(source, target): def set_missing_values(source, target):
if customer: if customer:
target.customer = customer.name target.customer = customer.name
target.customer_name = customer.customer_name target.customer_name = customer.customer_name
si = frappe.get_doc(target) si = frappe.get_doc(target)
si.ignore_permissions = ignore_permissions si.ignore_permissions = ignore_permissions
si.run_method("onload_post_render") si.run_method("onload_post_render")
doclist = get_mapped_doc("Quotation", source_name, { doclist = get_mapped_doc("Quotation", source_name, {
"Quotation": { "Quotation": {
"doctype": "Sales Order", "doctype": "Sales Order",
"validation": { "validation": {
"docstatus": ["=", 1] "docstatus": ["=", 1]
} }
}, },
"Quotation Item": { "Quotation Item": {
"doctype": "Sales Order Item", "doctype": "Sales Order Item",
"field_map": { "field_map": {
"parent": "prevdoc_docname" "parent": "prevdoc_docname"
} }
}, },
"Sales Taxes and Charges": { "Sales Taxes and Charges": {
"doctype": "Sales Taxes and Charges", "doctype": "Sales Taxes and Charges",
"add_if_empty": True "add_if_empty": True
}, },
"Sales Team": { "Sales Team": {
"doctype": "Sales Team", "doctype": "Sales Team",
"add_if_empty": True "add_if_empty": True
} }
}, target_doc, set_missing_values, ignore_permissions=ignore_permissions) }, target_doc, set_missing_values, ignore_permissions=ignore_permissions)
# postprocess: fetch shipping address, set missing values # postprocess: fetch shipping address, set missing values
return doclist return doclist
def _make_customer(source_name, ignore_permissions=False): def _make_customer(source_name, ignore_permissions=False):
@@ -152,7 +148,7 @@ def _make_customer(source_name, ignore_permissions=False):
if quotation[1] == "Shopping Cart": if quotation[1] == "Shopping Cart":
customer.customer_group = frappe.db.get_value("Shopping Cart Settings", None, customer.customer_group = frappe.db.get_value("Shopping Cart Settings", None,
"default_customer_group") "default_customer_group")
try: try:
customer.insert() customer.insert()
return customer return customer

View File

@@ -4,6 +4,8 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe import _
from frappe.model.document import Document from frappe.model.document import Document
class SalesBOM(Document): class SalesBOM(Document):
@@ -11,7 +13,7 @@ class SalesBOM(Document):
def autoname(self): def autoname(self):
self.name = self.new_item_code self.name = self.new_item_code
def validate(self): def validate(self):
self.validate_main_item() self.validate_main_item()
@@ -22,23 +24,22 @@ class SalesBOM(Document):
"""main item must have Is Stock Item as No and Is Sales Item as Yes""" """main item must have Is Stock Item as No and Is Sales Item as Yes"""
if not frappe.db.sql("""select name from tabItem where name=%s and if not frappe.db.sql("""select name from tabItem where name=%s and
ifnull(is_stock_item,'')='No' and ifnull(is_sales_item,'')='Yes'""", self.new_item_code): ifnull(is_stock_item,'')='No' and ifnull(is_sales_item,'')='Yes'""", self.new_item_code):
frappe.msgprint("""Parent Item %s is either a Stock Item or a not a Sales Item""", frappe.throw(_("Parent Item {0} must be not Stock Item and must be a Sales Item").format(self.new_item_code))
raise_exception=1)
def get_item_details(self, name): def get_item_details(self, name):
det = frappe.db.sql("""select description, stock_uom from `tabItem` det = frappe.db.sql("""select description, stock_uom from `tabItem`
where name = %s""", name) where name = %s""", name)
return { return {
'description' : det and det[0][0] or '', 'description' : det and det[0][0] or '',
'uom': det and det[0][1] or '' 'uom': det and det[0][1] or ''
} }
def get_new_item_code(doctype, txt, searchfield, start, page_len, filters): def get_new_item_code(doctype, txt, searchfield, start, page_len, filters):
from erpnext.controllers.queries import get_match_cond from erpnext.controllers.queries import get_match_cond
return frappe.db.sql("""select name, item_name, description from tabItem return frappe.db.sql("""select name, item_name, description from tabItem
where is_stock_item="No" and is_sales_item="Yes" where is_stock_item="No" and is_sales_item="Yes"
and name not in (select name from `tabSales BOM`) and %s like %s and name not in (select name from `tabSales BOM`) and %s like %s
%s limit %s, %s""" % (searchfield, "%s", %s limit %s, %s""" % (searchfield, "%s",
get_match_cond(doctype),"%s", "%s"), get_match_cond(doctype),"%s", "%s"),
("%%%s%%" % txt, start, page_len)) ("%%%s%%" % txt, start, page_len))

View File

@@ -5,9 +5,9 @@ from __future__ import unicode_literals
import frappe import frappe
import frappe.utils import frappe.utils
from frappe.utils import cstr, flt, getdate from frappe.utils import cstr, flt, getdate, comma_and
from frappe import msgprint from frappe import _
from frappe.model.mapper import get_mapped_doc from frappe.model.mapper import get_mapped_doc
from erpnext.controllers.selling_controller import SellingController from erpnext.controllers.selling_controller import SellingController
@@ -23,22 +23,19 @@ class SalesOrder(SellingController):
# validate transaction date v/s delivery date # validate transaction date v/s delivery date
if self.delivery_date: if self.delivery_date:
if getdate(self.transaction_date) > getdate(self.delivery_date): if getdate(self.transaction_date) > getdate(self.delivery_date):
msgprint("Expected Delivery Date cannot be before Sales Order Date") frappe.throw(_("Expected Delivery Date cannot be before Sales Order Date"))
raise Exception
def validate_po(self): def validate_po(self):
# validate p.o date v/s delivery date # validate p.o date v/s delivery date
if self.po_date and self.delivery_date and getdate(self.po_date) > getdate(self.delivery_date): if self.po_date and self.delivery_date and getdate(self.po_date) > getdate(self.delivery_date):
msgprint("Expected Delivery Date cannot be before Purchase Order Date") frappe.throw(_("Expected Delivery Date cannot be before Purchase Order Date"))
raise Exception
if self.po_no and self.customer: if self.po_no and self.customer:
so = frappe.db.sql("select name from `tabSales Order` \ so = frappe.db.sql("select name from `tabSales Order` \
where ifnull(po_no, '') = %s and name != %s and docstatus < 2\ where ifnull(po_no, '') = %s and name != %s and docstatus < 2\
and customer = %s", (self.po_no, self.name, self.customer)) and customer = %s", (self.po_no, self.name, self.customer))
if so and so[0][0]: if so and so[0][0]:
msgprint("""Another Sales Order (%s) exists against same PO No and Customer. frappe.msgprint(_("Warning: Sales Order {0} already exists against same Purchase Order number").format(so[0][0]))
Please be sure, you are not making duplicate entry.""" % so[0][0])
def validate_for_items(self): def validate_for_items(self):
check_list, flag = [], 0 check_list, flag = [], 0
@@ -49,16 +46,15 @@ class SalesOrder(SellingController):
if frappe.db.get_value("Item", d.item_code, "is_stock_item") == 'Yes': if frappe.db.get_value("Item", d.item_code, "is_stock_item") == 'Yes':
if not d.warehouse: if not d.warehouse:
msgprint("""Please enter Reserved Warehouse for item %s frappe.throw(_("Reserved warehouse required for stock item {0}").format(d.item_code))
as it is stock Item""" % d.item_code, raise_exception=1)
if e in check_list: if e in check_list:
msgprint("Item %s has been entered twice." % d.item_code) frappe.throw(_("Item {0} has been entered twice").format(d.item_code))
else: else:
check_list.append(e) check_list.append(e)
else: else:
if f in chk_dupl_itm: if f in chk_dupl_itm:
msgprint("Item %s has been entered twice." % d.item_code) frappe.throw(_("Item {0} has been entered twice").format(d.item_code))
else: else:
chk_dupl_itm.append(f) chk_dupl_itm.append(f)
@@ -74,16 +70,14 @@ class SalesOrder(SellingController):
if d.prevdoc_docname: if d.prevdoc_docname:
res = frappe.db.sql("select name from `tabQuotation` where name=%s and order_type = %s", (d.prevdoc_docname, self.order_type)) res = frappe.db.sql("select name from `tabQuotation` where name=%s and order_type = %s", (d.prevdoc_docname, self.order_type))
if not res: if not res:
msgprint("""Order Type (%s) should be same in Quotation: %s \ frappe.msgprint(_("Quotation {0} not of type {1}").format(d.prevdoc_docname, self.order_type))
and current Sales Order""" % (self.order_type, d.prevdoc_docname))
def validate_order_type(self): def validate_order_type(self):
super(SalesOrder, self).validate_order_type() super(SalesOrder, self).validate_order_type()
def validate_delivery_date(self): def validate_delivery_date(self):
if self.order_type == 'Sales' and not self.delivery_date: if self.order_type == 'Sales' and not self.delivery_date:
msgprint("Please enter 'Expected Delivery Date'") frappe.throw(_("Please enter 'Expected Delivery Date'"))
raise Exception
self.validate_sales_mntc_quotation() self.validate_sales_mntc_quotation()
@@ -93,8 +87,7 @@ class SalesOrder(SellingController):
and (customer = %s or ifnull(customer,'')='')""", and (customer = %s or ifnull(customer,'')='')""",
(self.project_name, self.customer)) (self.project_name, self.customer))
if not res: if not res:
msgprint("Customer - %s does not belong to project - %s. \n\nIf you want to use project for multiple customers then please make customer details blank in project - %s."%(self.customer,self.project_name,self.project_name)) frappe.throw(_("Customer {0} does not belong to project {1}").format(self.customer, self.project_name))
raise Exception
def validate(self): def validate(self):
super(SalesOrder, self).validate() super(SalesOrder, self).validate()
@@ -169,8 +162,8 @@ class SalesOrder(SellingController):
def on_cancel(self): def on_cancel(self):
# Cannot cancel stopped SO # Cannot cancel stopped SO
if self.status == 'Stopped': if self.status == 'Stopped':
msgprint("Sales Order : '%s' cannot be cancelled as it is Stopped. Unstop it for any further transactions" %(self.name)) frappe.throw(_("Stopped order cannot be cancelled. Unstop to cancel."))
raise Exception
self.check_nextdoc_docstatus() self.check_nextdoc_docstatus()
self.update_stock_ledger(update_stock = -1) self.update_stock_ledger(update_stock = -1)
@@ -180,55 +173,56 @@ class SalesOrder(SellingController):
def check_nextdoc_docstatus(self): def check_nextdoc_docstatus(self):
# Checks Delivery Note # Checks Delivery Note
submit_dn = frappe.db.sql("select t1.name from `tabDelivery Note` t1,`tabDelivery Note Item` t2 where t1.name = t2.parent and t2.against_sales_order = %s and t1.docstatus = 1", self.name) submit_dn = frappe.db.sql_list("""select t1.name from `tabDelivery Note` t1,`tabDelivery Note Item` t2
where t1.name = t2.parent and t2.against_sales_order = %s and t1.docstatus = 1""", self.name)
if submit_dn: if submit_dn:
msgprint("Delivery Note : " + cstr(submit_dn[0][0]) + " has been submitted against " + cstr(self.doctype) + ". Please cancel Delivery Note : " + cstr(submit_dn[0][0]) + " first and then cancel "+ cstr(self.doctype), raise_exception = 1) frappe.throw(_("Delivery Notes {0} must be cancelled before cancelling this Sales Order").format(comma_and(submit_dn)))
# Checks Sales Invoice # Checks Sales Invoice
submit_rv = frappe.db.sql("""select t1.name submit_rv = frappe.db.sql_list("""select t1.name
from `tabSales Invoice` t1,`tabSales Invoice Item` t2 from `tabSales Invoice` t1,`tabSales Invoice Item` t2
where t1.name = t2.parent and t2.sales_order = %s and t1.docstatus = 1""", where t1.name = t2.parent and t2.sales_order = %s and t1.docstatus = 1""",
self.name) self.name)
if submit_rv: if submit_rv:
msgprint("Sales Invoice : " + cstr(submit_rv[0][0]) + " has already been submitted against " +cstr(self.doctype)+ ". Please cancel Sales Invoice : "+ cstr(submit_rv[0][0]) + " first and then cancel "+ cstr(self.doctype), raise_exception = 1) frappe.throw(_("Sales Invoice {0} must be cancelled before cancelling this Sales Order").format(comma_and(submit_rv)))
#check maintenance schedule #check maintenance schedule
submit_ms = frappe.db.sql("select t1.name from `tabMaintenance Schedule` t1, `tabMaintenance Schedule Item` t2 where t2.parent=t1.name and t2.prevdoc_docname = %s and t1.docstatus = 1",self.name) submit_ms = frappe.db.sql_list("""select t1.name from `tabMaintenance Schedule` t1,
`tabMaintenance Schedule Item` t2
where t2.parent=t1.name and t2.prevdoc_docname = %s and t1.docstatus = 1""", self.name)
if submit_ms: if submit_ms:
msgprint("Maintenance Schedule : " + cstr(submit_ms[0][0]) + " has already been submitted against " +cstr(self.doctype)+ ". Please cancel Maintenance Schedule : "+ cstr(submit_ms[0][0]) + " first and then cancel "+ cstr(self.doctype), raise_exception = 1) frappe.throw(_("Maintenance Schedule {0} must be cancelled before cancelling this Sales Order").format(comma_and(submit_ms)))
# check maintenance visit # check maintenance visit
submit_mv = frappe.db.sql("select t1.name from `tabMaintenance Visit` t1, `tabMaintenance Visit Purpose` t2 where t2.parent=t1.name and t2.prevdoc_docname = %s and t1.docstatus = 1",self.name) submit_mv = frappe.db.sql_list("""select t1.name from `tabMaintenance Visit` t1, `tabMaintenance Visit Purpose` t2
where t2.parent=t1.name and t2.prevdoc_docname = %s and t1.docstatus = 1""",self.name)
if submit_mv: if submit_mv:
msgprint("Maintenance Visit : " + cstr(submit_mv[0][0]) + " has already been submitted against " +cstr(self.doctype)+ ". Please cancel Maintenance Visit : " + cstr(submit_mv[0][0]) + " first and then cancel "+ cstr(self.doctype), raise_exception = 1) frappe.throw(_("Maintenance Visit {0} must be cancelled before cancelling this Sales Order").format(comma_and(submit_mv)))
# check production order # check production order
pro_order = frappe.db.sql("""select name from `tabProduction Order` where sales_order = %s and docstatus = 1""", self.name) pro_order = frappe.db.sql_list("""select name from `tabProduction Order`
where sales_order = %s and docstatus = 1""", self.name)
if pro_order: if pro_order:
msgprint("""Production Order: %s exists against this sales order. frappe.throw(_("Production Order {0} must be cancelled before cancelling this Sales Order").format(comma_and(pro_order)))
Please cancel production order first and then cancel this sales order""" %
pro_order[0][0], raise_exception=1)
def check_modified_date(self): def check_modified_date(self):
mod_db = frappe.db.get_value("Sales Order", self.name, "modified") mod_db = frappe.db.get_value("Sales Order", self.name, "modified")
date_diff = frappe.db.sql("select TIMEDIFF('%s', '%s')" % date_diff = frappe.db.sql("select TIMEDIFF('%s', '%s')" %
( mod_db, cstr(self.modified))) ( mod_db, cstr(self.modified)))
if date_diff and date_diff[0][0]: if date_diff and date_diff[0][0]:
msgprint("%s: %s has been modified after you have opened. Please Refresh" frappe.throw(_("{0} {1} has been modified. Please Refresh").format(self.doctype, self.name))
% (self.doctype, self.name), raise_exception=1)
def stop_sales_order(self): def stop_sales_order(self):
self.check_modified_date() self.check_modified_date()
self.update_stock_ledger(-1) self.update_stock_ledger(-1)
frappe.db.set(self, 'status', 'Stopped') frappe.db.set(self, 'status', 'Stopped')
msgprint("""%s: %s has been Stopped. To make transactions against this Sales Order frappe.msgprint(_("{0} {1} status is Stopped").format(self.doctype, self.name))
you need to Unstop it.""" % (self.doctype, self.name))
def unstop_sales_order(self): def unstop_sales_order(self):
self.check_modified_date() self.check_modified_date()
self.update_stock_ledger(1) self.update_stock_ledger(1)
frappe.db.set(self, 'status', 'Submitted') frappe.db.set(self, 'status', 'Submitted')
msgprint("%s: %s has been Unstopped" % (self.doctype, self.name)) frappe.msgprint(_("{0} {1} status is Unstopped").format(self.doctype, self.name))
def update_stock_ledger(self, update_stock): def update_stock_ledger(self, update_stock):

View File

@@ -4,15 +4,16 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe.utils import cint from frappe.utils import cint
from frappe import _
def execute(filters=None): def execute(filters=None):
if not filters: filters ={} if not filters: filters ={}
days_since_last_order = filters.get("days_since_last_order") days_since_last_order = filters.get("days_since_last_order")
if cint(days_since_last_order) <= 0: if cint(days_since_last_order) <= 0:
frappe.msgprint("Please mention positive value in 'Days Since Last Order' field",raise_exception=1) frappe.throw(_("'Days Since Last Order' must be greater than or equal to zero"))
columns = get_columns() columns = get_columns()
customers = get_so_details() customers = get_so_details()
data = [] data = []
@@ -20,43 +21,43 @@ def execute(filters=None):
if cint(cust[8]) >= cint(days_since_last_order): if cint(cust[8]) >= cint(days_since_last_order):
cust.insert(7,get_last_so_amt(cust[0])) cust.insert(7,get_last_so_amt(cust[0]))
data.append(cust) data.append(cust)
return columns, data return columns, data
def get_so_details(): def get_so_details():
return frappe.db.sql("""select return frappe.db.sql("""select
cust.name, cust.name,
cust.customer_name, cust.customer_name,
cust.territory, cust.territory,
cust.customer_group, cust.customer_group,
count(distinct(so.name)) as 'num_of_order', count(distinct(so.name)) as 'num_of_order',
sum(net_total) as 'total_order_value', sum(net_total) as 'total_order_value',
sum(if(so.status = "Stopped", sum(if(so.status = "Stopped",
so.net_total * so.per_delivered/100, so.net_total * so.per_delivered/100,
so.net_total)) as 'total_order_considered', so.net_total)) as 'total_order_considered',
max(so.transaction_date) as 'last_sales_order_date', max(so.transaction_date) as 'last_sales_order_date',
DATEDIFF(CURDATE(), max(so.transaction_date)) as 'days_since_last_order' DATEDIFF(CURDATE(), max(so.transaction_date)) as 'days_since_last_order'
from `tabCustomer` cust, `tabSales Order` so from `tabCustomer` cust, `tabSales Order` so
where cust.name = so.customer and so.docstatus = 1 where cust.name = so.customer and so.docstatus = 1
group by cust.name group by cust.name
order by 'days_since_last_order' desc """,as_list=1) order by 'days_since_last_order' desc """,as_list=1)
def get_last_so_amt(customer): def get_last_so_amt(customer):
res = frappe.db.sql("""select net_total from `tabSales Order` res = frappe.db.sql("""select net_total from `tabSales Order`
where customer ='%(customer)s' and docstatus = 1 order by transaction_date desc where customer ='%(customer)s' and docstatus = 1 order by transaction_date desc
limit 1""" % {'customer':customer}) limit 1""" % {'customer':customer})
return res and res[0][0] or 0 return res and res[0][0] or 0
def get_columns(): def get_columns():
return [ return [
"Customer:Link/Customer:120", "Customer:Link/Customer:120",
"Customer Name:Data:120", "Customer Name:Data:120",
"Territory::120", "Territory::120",
"Customer Group::120", "Customer Group::120",
"Number of Order::120", "Number of Order::120",
"Total Order Value:Currency:120", "Total Order Value:Currency:120",
"Total Order Considered:Currency:160", "Total Order Considered:Currency:160",
"Last Order Amount:Currency:160", "Last Order Amount:Currency:160",
"Last Sales Order Date:Date:160", "Last Sales Order Date:Date:160",
"Days Since Last Order::160" "Days Since Last Order::160"
] ]

View File

@@ -5,14 +5,12 @@ from __future__ import unicode_literals
import frappe import frappe
from frappe import _, msgprint from frappe import _, msgprint
from frappe.utils import flt from frappe.utils import flt
import time
from erpnext.accounts.utils import get_fiscal_year from erpnext.accounts.utils import get_fiscal_year
from erpnext.controllers.trends import get_period_date_ranges, get_period_month_ranges from erpnext.controllers.trends import get_period_date_ranges, get_period_month_ranges
from frappe.model.meta import get_field_precision
def execute(filters=None): def execute(filters=None):
if not filters: filters = {} if not filters: filters = {}
columns = get_columns(filters) columns = get_columns(filters)
period_month_ranges = get_period_month_ranges(filters["period"], filters["fiscal_year"]) period_month_ranges = get_period_month_ranges(filters["period"], filters["fiscal_year"])
sim_map = get_salesperson_item_month_map(filters) sim_map = get_salesperson_item_month_map(filters)
@@ -37,7 +35,7 @@ def execute(filters=None):
data.append(row) data.append(row)
return columns, sorted(data, key=lambda x: (x[0], x[1])) return columns, sorted(data, key=lambda x: (x[0], x[1]))
def get_columns(filters): def get_columns(filters):
for fieldname in ["fiscal_year", "period", "target_on"]: for fieldname in ["fiscal_year", "period", "target_on"]:
if not filters.get(fieldname): if not filters.get(fieldname):
@@ -55,26 +53,26 @@ def get_columns(filters):
label = label % (from_date.strftime("%b") + " - " + to_date.strftime("%b")) label = label % (from_date.strftime("%b") + " - " + to_date.strftime("%b"))
else: else:
label = label % from_date.strftime("%b") label = label % from_date.strftime("%b")
columns.append(label+":Float:120") columns.append(label+":Float:120")
return columns + ["Total Target:Float:120", "Total Achieved:Float:120", return columns + ["Total Target:Float:120", "Total Achieved:Float:120",
"Total Variance:Float:120"] "Total Variance:Float:120"]
#Get sales person & item group details #Get sales person & item group details
def get_salesperson_details(filters): def get_salesperson_details(filters):
return frappe.db.sql("""select sp.name, td.item_group, td.target_qty, return frappe.db.sql("""select sp.name, td.item_group, td.target_qty,
td.target_amount, sp.distribution_id td.target_amount, sp.distribution_id
from `tabSales Person` sp, `tabTarget Detail` td from `tabSales Person` sp, `tabTarget Detail` td
where td.parent=sp.name and td.fiscal_year=%s order by sp.name""", where td.parent=sp.name and td.fiscal_year=%s order by sp.name""",
(filters["fiscal_year"]), as_dict=1) (filters["fiscal_year"]), as_dict=1)
#Get target distribution details of item group #Get target distribution details of item group
def get_target_distribution_details(filters): def get_target_distribution_details(filters):
target_details = {} target_details = {}
for d in frappe.db.sql("""select bd.name, bdd.month, bdd.percentage_allocation for d in frappe.db.sql("""select bd.name, bdd.month, bdd.percentage_allocation
from `tabBudget Distribution Detail` bdd, `tabBudget Distribution` bd from `tabBudget Distribution Detail` bdd, `tabBudget Distribution` bd
where bdd.parent=bd.name and bd.fiscal_year=%s""", (filters["fiscal_year"]), as_dict=1): where bdd.parent=bd.name and bd.fiscal_year=%s""", (filters["fiscal_year"]), as_dict=1):
target_details.setdefault(d.name, {}).setdefault(d.month, flt(d.percentage_allocation)) target_details.setdefault(d.name, {}).setdefault(d.month, flt(d.percentage_allocation))
@@ -83,13 +81,13 @@ def get_target_distribution_details(filters):
#Get achieved details from sales order #Get achieved details from sales order
def get_achieved_details(filters): def get_achieved_details(filters):
start_date, end_date = get_fiscal_year(fiscal_year = filters["fiscal_year"])[1:] start_date, end_date = get_fiscal_year(fiscal_year = filters["fiscal_year"])[1:]
item_details = frappe.db.sql("""select soi.item_code, soi.qty, soi.base_amount, so.transaction_date, item_details = frappe.db.sql("""select soi.item_code, soi.qty, soi.base_amount, so.transaction_date,
st.sales_person, MONTHNAME(so.transaction_date) as month_name st.sales_person, MONTHNAME(so.transaction_date) as month_name
from `tabSales Order Item` soi, `tabSales Order` so, `tabSales Team` st from `tabSales Order Item` soi, `tabSales Order` so, `tabSales Team` st
where soi.parent=so.name and so.docstatus=1 and where soi.parent=so.name and so.docstatus=1 and
st.parent=so.name and so.transaction_date>=%s and st.parent=so.name and so.transaction_date>=%s and
so.transaction_date<=%s""" % ('%s', '%s'), so.transaction_date<=%s""" % ('%s', '%s'),
(start_date, end_date), as_dict=1) (start_date, end_date), as_dict=1)
item_actual_details = {} item_actual_details = {}
@@ -117,7 +115,7 @@ def get_salesperson_item_month_map(filters):
tav_dict = sim_map[sd.name][sd.item_group][month] tav_dict = sim_map[sd.name][sd.item_group][month]
month_percentage = tdd.get(sd.distribution_id, {}).get(month, 0) \ month_percentage = tdd.get(sd.distribution_id, {}).get(month, 0) \
if sd.distribution_id else 100.0/12 if sd.distribution_id else 100.0/12
for ad in achieved_details.get(sd.name, {}).get(sd.item_group, []): for ad in achieved_details.get(sd.name, {}).get(sd.item_group, []):
if (filters["target_on"] == "Quantity"): if (filters["target_on"] == "Quantity"):
tav_dict.target = flt(sd.target_qty) * month_percentage / 100 tav_dict.target = flt(sd.target_qty) * month_percentage / 100
@@ -132,4 +130,4 @@ def get_salesperson_item_month_map(filters):
return sim_map return sim_map
def get_item_group(item_name): def get_item_group(item_name):
return frappe.db.get_value("Item", item_name, "item_group") return frappe.db.get_value("Item", item_name, "item_group")

View File

@@ -4,12 +4,11 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe.utils import cstr, flt, has_common, make_esc from frappe.utils import cstr, flt, has_common, make_esc, comma_or
from frappe import session, _
from frappe import session, msgprint
from erpnext.setup.utils import get_company_currency
from erpnext.utilities.transaction_base import TransactionBase from erpnext.utilities.transaction_base import TransactionBase
class AuthorizationControl(TransactionBase): class AuthorizationControl(TransactionBase):
@@ -24,27 +23,18 @@ class AuthorizationControl(TransactionBase):
for x in det: for x in det:
amt_list.append(flt(x[0])) amt_list.append(flt(x[0]))
max_amount = max(amt_list) max_amount = max(amt_list)
app_dtl = frappe.db.sql("select approving_user, approving_role from `tabAuthorization Rule` where transaction = %s and (value = %s or value > %s) and docstatus != 2 and based_on = %s and company = %s %s" % ('%s', '%s', '%s', '%s', '%s', condition), (doctype_name, flt(max_amount), total, based_on, company)) app_dtl = frappe.db.sql("select approving_user, approving_role from `tabAuthorization Rule` where transaction = %s and (value = %s or value > %s) and docstatus != 2 and based_on = %s and company = %s %s" % ('%s', '%s', '%s', '%s', '%s', condition), (doctype_name, flt(max_amount), total, based_on, company))
if not app_dtl: if not app_dtl:
app_dtl = frappe.db.sql("select approving_user, approving_role from `tabAuthorization Rule` where transaction = %s and (value = %s or value > %s) and docstatus != 2 and based_on = %s and ifnull(company,'') = '' %s" % ('%s', '%s', '%s', '%s', condition), (doctype_name, flt(max_amount), total, based_on)) app_dtl = frappe.db.sql("select approving_user, approving_role from `tabAuthorization Rule` where transaction = %s and (value = %s or value > %s) and docstatus != 2 and based_on = %s and ifnull(company,'') = '' %s" % ('%s', '%s', '%s', '%s', condition), (doctype_name, flt(max_amount), total, based_on))
for d in app_dtl: for d in app_dtl:
if(d[0]): appr_users.append(d[0]) if(d[0]): appr_users.append(d[0])
if(d[1]): appr_roles.append(d[1]) if(d[1]): appr_roles.append(d[1])
if not has_common(appr_roles, frappe.user.get_roles()) and not has_common(appr_users, [session['user']]): if not has_common(appr_roles, frappe.user.get_roles()) and not has_common(appr_users, [session['user']]):
msg, add_msg = '','' frappe.msgprint(_("Not authroized since {0} exceeds limits").format(_(based_on)))
if max_amount: frappe.throw(_("Can be approved by {0}").format(comma_or(appr_roles + appr_users)))
dcc = get_company_currency(self.company)
if based_on == 'Grand Total': msg = "since Grand Total exceeds %s. %s" % (dcc, flt(max_amount))
elif based_on == 'Itemwise Discount': msg = "since Discount exceeds %s for Item Code : %s" % (cstr(max_amount)+'%', item)
elif based_on == 'Average Discount' or based_on == 'Customerwise Discount': msg = "since Discount exceeds %s" % (cstr(max_amount)+'%')
if appr_users: add_msg = "Users : "+cstr(appr_users)
if appr_roles: add_msg = "Roles : "+cstr(appr_roles)
if appr_users and appr_roles: add_msg = "Users : "+cstr(appr_users)+" or "+"Roles : "+cstr(appr_roles)
msgprint("You are not authorize to submit this %s %s. Please send for approval to %s" % (doctype_name, msg, add_msg))
raise Exception raise Exception
@@ -64,12 +54,12 @@ class AuthorizationControl(TransactionBase):
if chk == 1: if chk == 1:
if based_on == 'Itemwise Discount': add_cond2 += " and ifnull(master_name,'') = ''" if based_on == 'Itemwise Discount': add_cond2 += " and ifnull(master_name,'') = ''"
appr = frappe.db.sql("select value from `tabAuthorization Rule` where transaction = %s and value <= %s and based_on = %s and company = %s and docstatus != 2 %s %s" % ('%s', '%s', '%s', '%s', cond, add_cond2), (doctype_name, total, based_on, company)) appr = frappe.db.sql("select value from `tabAuthorization Rule` where transaction = %s and value <= %s and based_on = %s and company = %s and docstatus != 2 %s %s" % ('%s', '%s', '%s', '%s', cond, add_cond2), (doctype_name, total, based_on, company))
if not appr: if not appr:
appr = frappe.db.sql("select value from `tabAuthorization Rule` where transaction = %s and value <= %s and based_on = %s and ifnull(company,'') = '' and docstatus != 2 %s %s"% ('%s', '%s', '%s', cond, add_cond2), (doctype_name, total, based_on)) appr = frappe.db.sql("select value from `tabAuthorization Rule` where transaction = %s and value <= %s and based_on = %s and ifnull(company,'') = '' and docstatus != 2 %s %s"% ('%s', '%s', '%s', cond, add_cond2), (doctype_name, total, based_on))
self.get_appr_user_role(appr, doctype_name, total, based_on, cond+add_cond2, item, company) self.get_appr_user_role(appr, doctype_name, total, based_on, cond+add_cond2, item, company)
# Bifurcate Authorization based on type # Bifurcate Authorization based on type
# -------------------------------------- # --------------------------------------
def bifurcate_based_on_type(self, doctype_name, total, av_dis, based_on, doc_obj, val, company): def bifurcate_based_on_type(self, doctype_name, total, av_dis, based_on, doc_obj, val, company):
@@ -108,39 +98,39 @@ class AuthorizationControl(TransactionBase):
# Individual User # Individual User
# ================ # ================
# Check for authorization set for individual user # Check for authorization set for individual user
based_on = [x[0] for x in frappe.db.sql("select distinct based_on from `tabAuthorization Rule` where transaction = %s and system_user = %s and (company = %s or ifnull(company,'')='') and docstatus != 2", (doctype_name, session['user'], company))] based_on = [x[0] for x in frappe.db.sql("select distinct based_on from `tabAuthorization Rule` where transaction = %s and system_user = %s and (company = %s or ifnull(company,'')='') and docstatus != 2", (doctype_name, session['user'], company))]
for d in based_on: for d in based_on:
self.bifurcate_based_on_type(doctype_name, total, av_dis, d, doc_obj, 1, company) self.bifurcate_based_on_type(doctype_name, total, av_dis, d, doc_obj, 1, company)
# Remove user specific rules from global authorization rules # Remove user specific rules from global authorization rules
for r in based_on: for r in based_on:
if r in final_based_on and r != 'Itemwise Discount': final_based_on.remove(r) if r in final_based_on and r != 'Itemwise Discount': final_based_on.remove(r)
# Specific Role # Specific Role
# =============== # ===============
# Check for authorization set on particular roles # Check for authorization set on particular roles
based_on = [x[0] for x in frappe.db.sql("""select based_on based_on = [x[0] for x in frappe.db.sql("""select based_on
from `tabAuthorization Rule` from `tabAuthorization Rule`
where transaction = %s and system_role IN (%s) and based_on IN (%s) where transaction = %s and system_role IN (%s) and based_on IN (%s)
and (company = %s or ifnull(company,'')='') and (company = %s or ifnull(company,'')='')
and docstatus != 2 and docstatus != 2
""" % ('%s', "'"+"','".join(frappe.user.get_roles())+"'", "'"+"','".join(final_based_on)+"'", '%s'), (doctype_name, company))] """ % ('%s', "'"+"','".join(frappe.user.get_roles())+"'", "'"+"','".join(final_based_on)+"'", '%s'), (doctype_name, company))]
for d in based_on: for d in based_on:
self.bifurcate_based_on_type(doctype_name, total, av_dis, d, doc_obj, 2, company) self.bifurcate_based_on_type(doctype_name, total, av_dis, d, doc_obj, 2, company)
# Remove role specific rules from global authorization rules # Remove role specific rules from global authorization rules
for r in based_on: for r in based_on:
if r in final_based_on and r != 'Itemwise Discount': final_based_on.remove(r) if r in final_based_on and r != 'Itemwise Discount': final_based_on.remove(r)
# Global Rule # Global Rule
# ============= # =============
# Check for global authorization # Check for global authorization
for g in final_based_on: for g in final_based_on:
self.bifurcate_based_on_type(doctype_name, total, av_dis, g, doc_obj, 0, company) self.bifurcate_based_on_type(doctype_name, total, av_dis, g, doc_obj, 0, company)
#======================================================================================================================== #========================================================================================================================
# payroll related check # payroll related check
def get_value_based_rule(self,doctype_name,employee,total_claimed_amount,company): def get_value_based_rule(self,doctype_name,employee,total_claimed_amount,company):
@@ -153,29 +143,29 @@ class AuthorizationControl(TransactionBase):
val_lst = [y[0] for y in val] val_lst = [y[0] for y in val]
else: else:
val_lst.append(0) val_lst.append(0)
max_val = max(val_lst) max_val = max(val_lst)
rule = frappe.db.sql("select name, to_emp, to_designation, approving_role, approving_user from `tabAuthorization Rule` where transaction=%s and company = %s and (to_emp=%s or to_designation IN (select designation from `tabEmployee` where name=%s)) and ifnull(value,0)= %s and docstatus!=2",(doctype_name,company,employee,employee,flt(max_val)), as_dict=1) rule = frappe.db.sql("select name, to_emp, to_designation, approving_role, approving_user from `tabAuthorization Rule` where transaction=%s and company = %s and (to_emp=%s or to_designation IN (select designation from `tabEmployee` where name=%s)) and ifnull(value,0)= %s and docstatus!=2",(doctype_name,company,employee,employee,flt(max_val)), as_dict=1)
if not rule: if not rule:
rule = frappe.db.sql("select name, to_emp, to_designation, approving_role, approving_user from `tabAuthorization Rule` where transaction=%s and ifnull(company,'') = '' and (to_emp=%s or to_designation IN (select designation from `tabEmployee` where name=%s)) and ifnull(value,0)= %s and docstatus!=2",(doctype_name,employee,employee,flt(max_val)), as_dict=1) rule = frappe.db.sql("select name, to_emp, to_designation, approving_role, approving_user from `tabAuthorization Rule` where transaction=%s and ifnull(company,'') = '' and (to_emp=%s or to_designation IN (select designation from `tabEmployee` where name=%s)) and ifnull(value,0)= %s and docstatus!=2",(doctype_name,employee,employee,flt(max_val)), as_dict=1)
return rule return rule
#--------------------------------------------------------------------------------------------------------------------- #---------------------------------------------------------------------------------------------------------------------
# related to payroll module only # related to payroll module only
def get_approver_name(self, doctype_name, total, doc_obj=''): def get_approver_name(self, doctype_name, total, doc_obj=''):
app_user=[] app_user=[]
app_specific_user =[] app_specific_user =[]
rule ={} rule ={}
if doc_obj: if doc_obj:
if doctype_name == 'Expense Claim': if doctype_name == 'Expense Claim':
rule = self.get_value_based_rule(doctype_name,doc_obj.employee,doc_obj.total_claimed_amount, doc_obj.company) rule = self.get_value_based_rule(doctype_name,doc_obj.employee,doc_obj.total_claimed_amount, doc_obj.company)
elif doctype_name == 'Appraisal': elif doctype_name == 'Appraisal':
rule = frappe.db.sql("select name, to_emp, to_designation, approving_role, approving_user from `tabAuthorization Rule` where transaction=%s and (to_emp=%s or to_designation IN (select designation from `tabEmployee` where name=%s)) and company = %s and docstatus!=2",(doctype_name,doc_obj.employee, doc_obj.employee, doc_obj.company),as_dict=1) rule = frappe.db.sql("select name, to_emp, to_designation, approving_role, approving_user from `tabAuthorization Rule` where transaction=%s and (to_emp=%s or to_designation IN (select designation from `tabEmployee` where name=%s)) and company = %s and docstatus!=2",(doctype_name,doc_obj.employee, doc_obj.employee, doc_obj.company),as_dict=1)
if not rule: if not rule:
rule = frappe.db.sql("select name, to_emp, to_designation, approving_role, approving_user from `tabAuthorization Rule` where transaction=%s and (to_emp=%s or to_designation IN (select designation from `tabEmployee` where name=%s)) and ifnull(company,'') = '' and docstatus!=2",(doctype_name,doc_obj.employee, doc_obj.employee),as_dict=1) rule = frappe.db.sql("select name, to_emp, to_designation, approving_role, approving_user from `tabAuthorization Rule` where transaction=%s and (to_emp=%s or to_designation IN (select designation from `tabEmployee` where name=%s)) and ifnull(company,'') = '' and docstatus!=2",(doctype_name,doc_obj.employee, doc_obj.employee),as_dict=1)
if rule: if rule:
for m in rule: for m in rule:
if m['to_emp'] or m['to_designation']: if m['to_emp'] or m['to_designation']:
@@ -186,7 +176,7 @@ class AuthorizationControl(TransactionBase):
for x in user_lst: for x in user_lst:
if not x in app_user: if not x in app_user:
app_user.append(x) app_user.append(x)
if len(app_specific_user) >0: if len(app_specific_user) >0:
return app_specific_user return app_specific_user
else: else:

View File

@@ -4,8 +4,8 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe.utils import cint, cstr, flt, has_common from frappe.utils import cstr, flt
from frappe import msgprint from frappe import _, msgprint
from frappe.model.document import Document from frappe.model.document import Document
@@ -13,75 +13,41 @@ class AuthorizationRule(Document):
def check_duplicate_entry(self): def check_duplicate_entry(self):
exists = frappe.db.sql("""select name, docstatus from `tabAuthorization Rule` exists = frappe.db.sql("""select name, docstatus from `tabAuthorization Rule`
where transaction = %s and based_on = %s and system_user = %s where transaction = %s and based_on = %s and system_user = %s
and system_role = %s and approving_user = %s and approving_role = %s and system_role = %s and approving_user = %s and approving_role = %s
and to_emp =%s and to_designation=%s and name != %s""", and to_emp =%s and to_designation=%s and name != %s""",
(self.transaction, self.based_on, cstr(self.system_user), (self.transaction, self.based_on, cstr(self.system_user),
cstr(self.system_role), cstr(self.approving_user), cstr(self.system_role), cstr(self.approving_user),
cstr(self.approving_role), cstr(self.to_emp), cstr(self.approving_role), cstr(self.to_emp),
cstr(self.to_designation), self.name)) cstr(self.to_designation), self.name))
auth_exists = exists and exists[0][0] or '' auth_exists = exists and exists[0][0] or ''
if auth_exists: if auth_exists:
if cint(exists[0][1]) == 2: frappe.throw(_("Duplicate Entry. Please check Authorization Rule {0}").format(auth_exists))
msgprint("""Duplicate Entry. Please untrash Authorization Rule : %s \
from Recycle Bin""" % (auth_exists), raise_exception=1)
else:
msgprint("Duplicate Entry. Please check Authorization Rule : %s" %
(auth_exists), raise_exception=1)
def validate_master_name(self):
if self.based_on == 'Customerwise Discount' and \
not frappe.db.sql("""select name from tabCustomer
where name = %s and docstatus != 2""", (self.master_name)):
msgprint("Please select valid Customer Name for Customerwise Discount",
raise_exception=1)
elif self.based_on == 'Itemwise Discount' and \
not frappe.db.sql("select name from tabItem where name = %s and docstatus != 2",
(self.master_name)):
msgprint("Please select valid Item Name for Itemwise Discount", raise_exception=1)
elif (self.based_on == 'Grand Total' or \
self.based_on == 'Average Discount') and self.master_name:
msgprint("Please remove Customer/Item Name for %s." %
self.based_on, raise_exception=1)
def validate_rule(self): def validate_rule(self):
if self.transaction != 'Appraisal': if self.transaction != 'Appraisal':
if not self.approving_role and not self.approving_user: if not self.approving_role and not self.approving_user:
msgprint("Please enter Approving Role or Approving User", raise_exception=1) frappe.throw(_("Please enter Approving Role or Approving User"))
elif self.system_user and self.system_user == self.approving_user: elif self.system_user and self.system_user == self.approving_user:
msgprint("Approving User cannot be same as user the rule is Applicable To (User)", frappe.throw(_("Approving User cannot be same as user the rule is Applicable To"))
raise_exception=1)
elif self.system_role and self.system_role == self.approving_role: elif self.system_role and self.system_role == self.approving_role:
msgprint("Approving Role cannot be same as user the rule is \ frappe.throw(_("Approving Role cannot be same as role the rule is Applicable To"))
Applicable To (Role).", raise_exception=1)
elif self.system_user and self.approving_role and \
has_common([self.approving_role], [x[0] for x in \
frappe.db.sql("select role from `tabUserRole` where parent = %s", \
(self.system_user))]):
msgprint("System User : %s is assigned role : %s. So rule does not make sense" %
(self.system_user,self.approving_role), raise_exception=1)
elif self.transaction in ['Purchase Order', 'Purchase Receipt', \ elif self.transaction in ['Purchase Order', 'Purchase Receipt', \
'Purchase Invoice', 'Stock Entry'] and self.based_on \ 'Purchase Invoice', 'Stock Entry'] and self.based_on \
in ['Average Discount', 'Customerwise Discount', 'Itemwise Discount']: in ['Average Discount', 'Customerwise Discount', 'Itemwise Discount']:
msgprint("You cannot set authorization on basis of Discount for %s" % frappe.throw(_("Cannot set authorization on basis of Discount for {0}").format(self.transaction))
self.transaction, raise_exception=1)
elif self.based_on == 'Average Discount' and flt(self.value) > 100.00: elif self.based_on == 'Average Discount' and flt(self.value) > 100.00:
msgprint("Discount cannot given for more than 100%", raise_exception=1) frappe.throw(_("Discount must be less than 100"))
elif self.based_on == 'Customerwise Discount' and not self.master_name: elif self.based_on == 'Customerwise Discount' and not self.master_name:
msgprint("Please enter Customer Name for 'Customerwise Discount'", frappe.throw(_("Customer required for 'Customerwise Discount'"))
raise_exception=1)
else: else:
if self.transaction == 'Appraisal' and self.based_on != 'Not Applicable': if self.transaction == 'Appraisal':
msgprint("Based on should be 'Not Applicable' while setting authorization rule\ self.based_on = "Not Applicable"
for 'Appraisal'", raise_exception=1)
def validate(self): def validate(self):
self.check_duplicate_entry() self.check_duplicate_entry()
self.validate_rule() self.validate_rule()
self.validate_master_name() self.validate_master_name()
if not self.value: self.value = 0.0 if not self.value: self.value = 0.0

View File

@@ -20,7 +20,7 @@ import mimetypes
import frappe import frappe
import oauth2client.client import oauth2client.client
from frappe.utils import cstr from frappe.utils import cstr
from frappe import _, msgprint from frappe import _
from apiclient.discovery import build from apiclient.discovery import build
from apiclient.http import MediaFileUpload from apiclient.http import MediaFileUpload
@@ -71,17 +71,17 @@ def backup_to_gdrive():
backup = new_backup() backup = new_backup()
path = os.path.join(frappe.local.site_path, "public", "backups") path = os.path.join(frappe.local.site_path, "public", "backups")
filename = os.path.join(path, os.path.basename(backup.backup_path_db)) filename = os.path.join(path, os.path.basename(backup.backup_path_db))
# upload files to database folder # upload files to database folder
upload_files(filename, 'application/x-gzip', drive_service, upload_files(filename, 'application/x-gzip', drive_service,
frappe.db.get_value("Backup Manager", None, "database_folder_id")) frappe.db.get_value("Backup Manager", None, "database_folder_id"))
# upload files to files folder # upload files to files folder
did_not_upload = [] did_not_upload = []
error_log = [] error_log = []
files_folder_id = frappe.db.get_value("Backup Manager", None, "files_folder_id") files_folder_id = frappe.db.get_value("Backup Manager", None, "files_folder_id")
frappe.db.close() frappe.db.close()
path = os.path.join(frappe.local.site_path, "public", "files") path = os.path.join(frappe.local.site_path, "public", "files")
for filename in os.listdir(path): for filename in os.listdir(path):
@@ -94,7 +94,7 @@ def backup_to_gdrive():
mimetype = 'application/x-gzip' mimetype = 'application/x-gzip'
else: else:
mimetype = mimetypes.types_map.get("." + ext) or "application/octet-stream" mimetype = mimetypes.types_map.get("." + ext) or "application/octet-stream"
#Compare Local File with Server File #Compare Local File with Server File
children = drive_service.children().list(folderId=files_folder_id).execute() children = drive_service.children().list(folderId=files_folder_id).execute()
for child in children.get('items', []): for child in children.get('items', []):
@@ -108,29 +108,28 @@ def backup_to_gdrive():
except Exception, e: except Exception, e:
did_not_upload.append(filename) did_not_upload.append(filename)
error_log.append(cstr(e)) error_log.append(cstr(e))
frappe.connect() frappe.connect()
return did_not_upload, list(set(error_log)) return did_not_upload, list(set(error_log))
def get_gdrive_flow(): def get_gdrive_flow():
from oauth2client.client import OAuth2WebServerFlow from oauth2client.client import OAuth2WebServerFlow
from frappe import conf from frappe import conf
if not "gdrive_client_id" in conf:
frappe.msgprint(_("Please set Google Drive access keys in") + " conf.py",
raise_exception=True)
flow = OAuth2WebServerFlow(conf.gdrive_client_id, conf.gdrive_client_secret, if not "gdrive_client_id" in conf:
frappe.throw(_("Please set Google Drive access keys in {0}"),format("site_config.json"))
flow = OAuth2WebServerFlow(conf.gdrive_client_id, conf.gdrive_client_secret,
"https://www.googleapis.com/auth/drive", 'urn:ietf:wg:oauth:2.0:oob') "https://www.googleapis.com/auth/drive", 'urn:ietf:wg:oauth:2.0:oob')
return flow return flow
@frappe.whitelist() @frappe.whitelist()
def gdrive_callback(verification_code = None): def gdrive_callback(verification_code = None):
flow = get_gdrive_flow() flow = get_gdrive_flow()
if verification_code: if verification_code:
credentials = flow.step2_exchange(verification_code) credentials = flow.step2_exchange(verification_code)
allowed = 1 allowed = 1
# make folders to save id # make folders to save id
http = httplib2.Http() http = httplib2.Http()
http = credentials.authorize(http) http = credentials.authorize(http)
@@ -145,7 +144,7 @@ def gdrive_callback(verification_code = None):
final_credentials = credentials.to_json() final_credentials = credentials.to_json()
frappe.db.set_value("Backup Manager", "Backup Manager", "gdrive_credentials", final_credentials) frappe.db.set_value("Backup Manager", "Backup Manager", "gdrive_credentials", final_credentials)
frappe.msgprint("Updated") frappe.msgprint(_("Updated"))
def create_erpnext_folder(service): def create_erpnext_folder(service):
if not frappe.db: if not frappe.db:

View File

@@ -3,9 +3,9 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe import _, msgprint from frappe import _
from frappe.utils import cstr, cint from frappe.utils import cint
import frappe.defaults import frappe.defaults
@@ -13,10 +13,10 @@ from frappe.model.document import Document
class Company(Document): class Company(Document):
def onload(self): def onload(self):
self.set("__transactions_exist", self.check_if_transactions_exist()) self.set("__transactions_exist", self.check_if_transactions_exist())
def check_if_transactions_exist(self): def check_if_transactions_exist(self):
exists = False exists = False
for doctype in ["Sales Invoice", "Delivery Note", "Sales Order", "Quotation", for doctype in ["Sales Invoice", "Delivery Note", "Sales Order", "Quotation",
@@ -25,31 +25,30 @@ class Company(Document):
limit 1""" % (doctype, "%s"), self.name): limit 1""" % (doctype, "%s"), self.name):
exists = True exists = True
break break
return exists return exists
def validate(self): def validate(self):
if self.get('__islocal') and len(self.abbr) > 5: if self.get('__islocal') and len(self.abbr) > 5:
frappe.msgprint("Abbreviation cannot have more than 5 characters", frappe.throw(_("Abbreviation cannot have more than 5 characters"))
raise_exception=1)
self.previous_default_currency = frappe.db.get_value("Company", self.name, "default_currency") self.previous_default_currency = frappe.db.get_value("Company", self.name, "default_currency")
if self.default_currency and self.previous_default_currency and \ if self.default_currency and self.previous_default_currency and \
self.default_currency != self.previous_default_currency and \ self.default_currency != self.previous_default_currency and \
self.check_if_transactions_exist(): self.check_if_transactions_exist():
msgprint(_("Sorry! You cannot change company's default currency, because there are existing transactions against it. You will need to cancel those transactions if you want to change the default currency."), raise_exception=True) frappe.throw(_("Cannot change company's default currency, because there are existing transactions. Transactions must be cancelled to change the default currency."))
def on_update(self): def on_update(self):
if not frappe.db.sql("""select name from tabAccount if not frappe.db.sql("""select name from tabAccount
where company=%s and docstatus<2 limit 1""", self.name): where company=%s and docstatus<2 limit 1""", self.name):
self.create_default_accounts() self.create_default_accounts()
self.create_default_warehouses() self.create_default_warehouses()
self.create_default_web_page() self.create_default_web_page()
if not frappe.db.get_value("Cost Center", {"group_or_ledger": "Ledger", if not frappe.db.get_value("Cost Center", {"group_or_ledger": "Ledger",
"company": self.name}): "company": self.name}):
self.create_default_cost_center() self.create_default_cost_center()
self.set_default_accounts() self.set_default_accounts()
if self.default_currency: if self.default_currency:
@@ -58,7 +57,7 @@ class Company(Document):
def create_default_warehouses(self): def create_default_warehouses(self):
for whname in ("Stores", "Work In Progress", "Finished Goods"): for whname in ("Stores", "Work In Progress", "Finished Goods"):
if not frappe.db.exists("Warehouse", whname + " - " + self.abbr): if not frappe.db.exists("Warehouse", whname + " - " + self.abbr):
stock_group = frappe.db.get_value("Account", {"account_type": "Stock", stock_group = frappe.db.get_value("Account", {"account_type": "Stock",
"group_or_ledger": "Group"}) "group_or_ledger": "Group"})
if stock_group: if stock_group:
frappe.get_doc({ frappe.get_doc({
@@ -67,7 +66,7 @@ class Company(Document):
"company": self.name, "company": self.name,
"create_account_under": stock_group "create_account_under": stock_group
}).insert() }).insert()
def create_default_web_page(self): def create_default_web_page(self):
if not frappe.db.get_value("Website Settings", None, "home_page") and \ if not frappe.db.get_value("Website Settings", None, "home_page") and \
not frappe.db.sql("select name from tabCompany where name!=%s", self.name): not frappe.db.sql("select name from tabCompany where name!=%s", self.name):
@@ -80,7 +79,7 @@ class Company(Document):
"description": "Standard Home Page for " + self.name, "description": "Standard Home Page for " + self.name,
"main_section": webfile.read() % self.as_dict() "main_section": webfile.read() % self.as_dict()
}).insert() }).insert()
# update in home page in settings # update in home page in settings
website_settings = frappe.get_doc("Website Settings", "Website Settings") website_settings = frappe.get_doc("Website Settings", "Website Settings")
website_settings.home_page = webpage.name website_settings.home_page = webpage.name
@@ -109,7 +108,7 @@ class Company(Document):
self.create_standard_accounts() self.create_standard_accounts()
frappe.db.set(self, "receivables_group", "Accounts Receivable - " + self.abbr) frappe.db.set(self, "receivables_group", "Accounts Receivable - " + self.abbr)
frappe.db.set(self, "payables_group", "Accounts Payable - " + self.abbr) frappe.db.set(self, "payables_group", "Accounts Payable - " + self.abbr)
def import_chart_of_account(self): def import_chart_of_account(self):
chart = frappe.get_doc("Chart of Accounts", self.chart_of_accounts) chart = frappe.get_doc("Chart of Accounts", self.chart_of_accounts)
chart.create_accounts(self.name) chart.create_accounts(self.name)
@@ -120,7 +119,7 @@ class Company(Document):
"freeze_account": "No", "freeze_account": "No",
"master_type": "", "master_type": "",
}) })
for d in self.fld_dict.keys(): for d in self.fld_dict.keys():
account.set(d, (d == 'parent_account' and lst[self.fld_dict[d]]) and lst[self.fld_dict[d]] +' - '+ self.abbr or lst[self.fld_dict[d]]) account.set(d, (d == 'parent_account' and lst[self.fld_dict[d]]) and lst[self.fld_dict[d]] +' - '+ self.abbr or lst[self.fld_dict[d]])
account.insert() account.insert()
@@ -128,17 +127,17 @@ class Company(Document):
def set_default_accounts(self): def set_default_accounts(self):
def _set_default_accounts(accounts): def _set_default_accounts(accounts):
for field, account_type in accounts.items(): for field, account_type in accounts.items():
account = frappe.db.get_value("Account", {"account_type": account_type, account = frappe.db.get_value("Account", {"account_type": account_type,
"group_or_ledger": "Ledger", "company": self.name}) "group_or_ledger": "Ledger", "company": self.name})
if account and not self.get(field): if account and not self.get(field):
frappe.db.set(self, field, account) frappe.db.set(self, field, account)
_set_default_accounts({ _set_default_accounts({
"default_cash_account": "Cash", "default_cash_account": "Cash",
"default_bank_account": "Bank" "default_bank_account": "Bank"
}) })
if cint(frappe.db.get_value("Accounts Settings", None, "auto_accounting_for_stock")): if cint(frappe.db.get_value("Accounts Settings", None, "auto_accounting_for_stock")):
_set_default_accounts({ _set_default_accounts({
"stock_received_but_not_billed": "Stock Received But Not Billed", "stock_received_but_not_billed": "Stock Received But Not Billed",
@@ -153,9 +152,9 @@ class Company(Document):
'company':self.name, 'company':self.name,
'group_or_ledger':'Group', 'group_or_ledger':'Group',
'parent_cost_center':'' 'parent_cost_center':''
}, },
{ {
'cost_center_name':'Main', 'cost_center_name':'Main',
'company':self.name, 'company':self.name,
'group_or_ledger':'Ledger', 'group_or_ledger':'Ledger',
'parent_cost_center':self.name + ' - ' + self.abbr 'parent_cost_center':self.name + ' - ' + self.abbr
@@ -165,11 +164,11 @@ class Company(Document):
cc.update({"doctype": "Cost Center"}) cc.update({"doctype": "Cost Center"})
cc_doc = frappe.get_doc(cc) cc_doc = frappe.get_doc(cc)
cc_doc.ignore_permissions = True cc_doc.ignore_permissions = True
if cc.get("cost_center_name") == self.name: if cc.get("cost_center_name") == self.name:
cc_doc.ignore_mandatory = True cc_doc.ignore_mandatory = True
cc_doc.insert() cc_doc.insert()
frappe.db.set(self, "cost_center", "Main - " + self.abbr) frappe.db.set(self, "cost_center", "Main - " + self.abbr)
def on_trash(self): def on_trash(self):
@@ -180,33 +179,33 @@ class Company(Document):
if not rec: if not rec:
#delete tabAccount #delete tabAccount
frappe.db.sql("delete from `tabAccount` where company = %s order by lft desc, rgt desc", self.name) frappe.db.sql("delete from `tabAccount` where company = %s order by lft desc, rgt desc", self.name)
#delete cost center child table - budget detail #delete cost center child table - budget detail
frappe.db.sql("delete bd.* from `tabBudget Detail` bd, `tabCost Center` cc where bd.parent = cc.name and cc.company = %s", self.name) frappe.db.sql("delete bd.* from `tabBudget Detail` bd, `tabCost Center` cc where bd.parent = cc.name and cc.company = %s", self.name)
#delete cost center #delete cost center
frappe.db.sql("delete from `tabCost Center` WHERE company = %s order by lft desc, rgt desc", self.name) frappe.db.sql("delete from `tabCost Center` WHERE company = %s order by lft desc, rgt desc", self.name)
if not frappe.db.get_value("Stock Ledger Entry", {"company": self.name}): if not frappe.db.get_value("Stock Ledger Entry", {"company": self.name}):
frappe.db.sql("""delete from `tabWarehouse` where company=%s""", self.name) frappe.db.sql("""delete from `tabWarehouse` where company=%s""", self.name)
frappe.defaults.clear_default("company", value=self.name) frappe.defaults.clear_default("company", value=self.name)
frappe.db.sql("""update `tabSingles` set value="" frappe.db.sql("""update `tabSingles` set value=""
where doctype='Global Defaults' and field='default_company' where doctype='Global Defaults' and field='default_company'
and value=%s""", self.name) and value=%s""", self.name)
def before_rename(self, olddn, newdn, merge=False): def before_rename(self, olddn, newdn, merge=False):
if merge: if merge:
frappe.throw(_("Sorry, companies cannot be merged")) frappe.throw(_("Sorry, companies cannot be merged"))
def after_rename(self, olddn, newdn, merge=False): def after_rename(self, olddn, newdn, merge=False):
frappe.db.set(self, "company_name", newdn) frappe.db.set(self, "company_name", newdn)
frappe.db.sql("""update `tabDefaultValue` set defvalue=%s frappe.db.sql("""update `tabDefaultValue` set defvalue=%s
where defkey='Company' and defvalue=%s""", (newdn, olddn)) where defkey='Company' and defvalue=%s""", (newdn, olddn))
frappe.defaults.clear_cache() frappe.defaults.clear_cache()
def create_standard_accounts(self): def create_standard_accounts(self):
self.fld_dict = { self.fld_dict = {
'account_name': 0, 'account_name': 0,
@@ -217,7 +216,7 @@ class Company(Document):
'company': 5, 'company': 5,
'tax_rate': 6 'tax_rate': 6
} }
acc_list_common = [ acc_list_common = [
['Application of Funds (Assets)','','Group','','Balance Sheet',self.name,''], ['Application of Funds (Assets)','','Group','','Balance Sheet',self.name,''],
['Current Assets','Application of Funds (Assets)','Group','','Balance Sheet',self.name,''], ['Current Assets','Application of Funds (Assets)','Group','','Balance Sheet',self.name,''],
@@ -282,7 +281,7 @@ class Company(Document):
['Current Liabilities','Source of Funds (Liabilities)','Group','','Balance Sheet',self.name,''], ['Current Liabilities','Source of Funds (Liabilities)','Group','','Balance Sheet',self.name,''],
['Accounts Payable','Current Liabilities','Group','','Balance Sheet',self.name,''], ['Accounts Payable','Current Liabilities','Group','','Balance Sheet',self.name,''],
['Stock Liabilities','Current Liabilities','Group','','Balance Sheet',self.name,''], ['Stock Liabilities','Current Liabilities','Group','','Balance Sheet',self.name,''],
['Stock Received But Not Billed', 'Stock Liabilities', 'Ledger', 'Stock Received But Not Billed', 'Balance Sheet', self.name, ''], ['Stock Received But Not Billed', 'Stock Liabilities', 'Ledger', 'Stock Received But Not Billed', 'Balance Sheet', self.name, ''],
['Duties and Taxes','Current Liabilities','Group','','Balance Sheet',self.name,''], ['Duties and Taxes','Current Liabilities','Group','','Balance Sheet',self.name,''],
['Loans (Liabilities)','Current Liabilities','Group','','Balance Sheet',self.name,''], ['Loans (Liabilities)','Current Liabilities','Group','','Balance Sheet',self.name,''],
['Secured Loans','Loans (Liabilities)','Group','','Balance Sheet',self.name,''], ['Secured Loans','Loans (Liabilities)','Group','','Balance Sheet',self.name,''],
@@ -291,7 +290,7 @@ class Company(Document):
['Temporary Accounts (Liabilities)','Source of Funds (Liabilities)','Group','','Balance Sheet',self.name,''], ['Temporary Accounts (Liabilities)','Source of Funds (Liabilities)','Group','','Balance Sheet',self.name,''],
['Temporary Account (Liabilities)','Temporary Accounts (Liabilities)','Ledger','','Balance Sheet',self.name,''] ['Temporary Account (Liabilities)','Temporary Accounts (Liabilities)','Ledger','','Balance Sheet',self.name,'']
] ]
acc_list_india = [ acc_list_india = [
['CENVAT Capital Goods','Tax Assets','Ledger','Chargeable','Balance Sheet',self.name,''], ['CENVAT Capital Goods','Tax Assets','Ledger','Chargeable','Balance Sheet',self.name,''],
['CENVAT','Tax Assets','Ledger','Chargeable','Balance Sheet',self.name,''], ['CENVAT','Tax Assets','Ledger','Chargeable','Balance Sheet',self.name,''],
@@ -342,23 +341,23 @@ class Company(Document):
@frappe.whitelist() @frappe.whitelist()
def replace_abbr(company, old, new): def replace_abbr(company, old, new):
frappe.db.set_value("Company", company, "abbr", new) frappe.db.set_value("Company", company, "abbr", new)
def _rename_record(dt): def _rename_record(dt):
for d in frappe.db.sql("select name from `tab%s` where company=%s" % (dt, '%s'), company): for d in frappe.db.sql("select name from `tab%s` where company=%s" % (dt, '%s'), company):
parts = d[0].split(" - ") parts = d[0].split(" - ")
if parts[-1].lower() == old.lower(): if parts[-1].lower() == old.lower():
name_without_abbr = " - ".join(parts[:-1]) name_without_abbr = " - ".join(parts[:-1])
frappe.rename_doc(dt, d[0], name_without_abbr + " - " + new) frappe.rename_doc(dt, d[0], name_without_abbr + " - " + new)
for dt in ["Account", "Cost Center", "Warehouse"]: for dt in ["Account", "Cost Center", "Warehouse"]:
_rename_record(dt) _rename_record(dt)
frappe.db.commit() frappe.db.commit()
def get_name_with_abbr(name, company): def get_name_with_abbr(name, company):
company_abbr = frappe.db.get_value("Company", company, "abbr") company_abbr = frappe.db.get_value("Company", company, "abbr")
parts = name.split(" - ") parts = name.split(" - ")
if parts[-1].lower() != company_abbr.lower(): if parts[-1].lower() != company_abbr.lower():
parts.append(company_abbr) parts.append(company_abbr)
return " - ".join(parts) return " - ".join(parts)

View File

@@ -5,16 +5,16 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe import _, msgprint from frappe import _
from frappe.model.controller import DocListController from frappe.model.controller import DocListController
class CurrencyExchange(DocListController): class CurrencyExchange(DocListController):
def autoname(self): def autoname(self):
self.name = self.from_currency + "-" + self.to_currency self.name = self.from_currency + "-" + self.to_currency
def validate(self): def validate(self):
self.validate_value("exchange_rate", ">", 0) self.validate_value("exchange_rate", ">", 0)
if self.from_currency == self.to_currency: if self.from_currency == self.to_currency:
msgprint(_("From Currency and To Currency cannot be same"), raise_exception=True) frappe.throw(_("From Currency and To Currency cannot be same"))

View File

@@ -3,45 +3,20 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe import msgprint from frappe import _
from frappe.utils.nestedset import NestedSet from frappe.utils.nestedset import NestedSet
class CustomerGroup(NestedSet): class CustomerGroup(NestedSet):
nsm_parent_field = 'parent_customer_group'; nsm_parent_field = 'parent_customer_group';
def validate(self):
if frappe.db.sql("select name from `tabCustomer Group` where name = %s and docstatus = 2",
(self.customer_group_name)):
msgprint("""Another %s record is trashed.
To untrash please go to Setup -> Recycle Bin.""" %
(self.customer_group_name), raise_exception = 1)
def on_update(self): def on_update(self):
self.validate_name_with_customer() self.validate_name_with_customer()
super(CustomerGroup, self).on_update() super(CustomerGroup, self).on_update()
self.validate_one_root() self.validate_one_root()
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 (%s), \
please change the Customer Group name or rename the Customer" % please change the Customer Group name or rename the Customer") %
self.name, raise_exception=1) self.name, raise_exception=1)
def on_trash(self):
cust = frappe.db.sql("select name from `tabCustomer` where ifnull(customer_group, '') = %s",
self.name)
cust = [d[0] for d in cust]
if cust:
msgprint("""Customer Group: %s can not be trashed/deleted \
because it is used in customer: %s.
To trash/delete this, remove/change customer group in customer master""" %
(self.name, cust or ''), raise_exception=1)
if frappe.db.sql("select name from `tabCustomer Group` where parent_customer_group = %s \
and docstatus != 2", self.name):
msgprint("Child customer group exists for this customer group. \
You can not trash/cancel/delete this customer group.", raise_exception=1)
# rebuild tree
super(CustomerGroup, self).on_trash()

View File

@@ -4,6 +4,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
"""Global Defaults""" """Global Defaults"""
import frappe import frappe
from frappe import _
import frappe.defaults import frappe.defaults
from frappe.utils import cint from frappe.utils import cint
@@ -25,17 +26,17 @@ keydict = {
from frappe.model.document import Document from frappe.model.document import Document
class GlobalDefaults(Document): class GlobalDefaults(Document):
def on_update(self): def on_update(self):
"""update defaults""" """update defaults"""
self.validate_session_expiry() self.validate_session_expiry()
self.set_country_and_timezone() self.set_country_and_timezone()
for key in keydict: for key in keydict:
frappe.db.set_default(key, self.get(keydict[key], '')) frappe.db.set_default(key, self.get(keydict[key], ''))
# update year start date and year end date from fiscal_year # update year start date and year end date from fiscal_year
year_start_end_date = frappe.db.sql("""select year_start_date, year_end_date year_start_end_date = frappe.db.sql("""select year_start_date, year_end_date
from `tabFiscal Year` where name=%s""", self.current_fiscal_year) from `tabFiscal Year` where name=%s""", self.current_fiscal_year)
ysd = year_start_end_date[0][0] or '' ysd = year_start_end_date[0][0] or ''
@@ -44,20 +45,19 @@ class GlobalDefaults(Document):
if ysd and yed: if ysd and yed:
frappe.db.set_default('year_start_date', ysd.strftime('%Y-%m-%d')) frappe.db.set_default('year_start_date', ysd.strftime('%Y-%m-%d'))
frappe.db.set_default('year_end_date', yed.strftime('%Y-%m-%d')) frappe.db.set_default('year_end_date', yed.strftime('%Y-%m-%d'))
# enable default currency # enable default currency
if self.default_currency: if self.default_currency:
frappe.db.set_value("Currency", self.default_currency, "enabled", 1) frappe.db.set_value("Currency", self.default_currency, "enabled", 1)
# clear cache # clear cache
frappe.clear_cache() frappe.clear_cache()
def validate_session_expiry(self): def validate_session_expiry(self):
if self.session_expiry: if self.session_expiry:
parts = self.session_expiry.split(":") parts = self.session_expiry.split(":")
if len(parts)!=2 or not (cint(parts[0]) or cint(parts[1])): if len(parts)!=2 or not (cint(parts[0]) or cint(parts[1])):
frappe.msgprint("""Session Expiry must be in format hh:mm""", frappe.throw(_("Session Expiry must be in format {0}").format("hh:mm"))
raise_exception=1)
def set_country_and_timezone(self): def set_country_and_timezone(self):
frappe.db.set_default("country", self.country) frappe.db.set_default("country", self.country)

View File

@@ -34,5 +34,4 @@ class ItemGroup(NestedSet, WebsiteGenerator):
def validate_name_with_item(self): def validate_name_with_item(self):
if frappe.db.exists("Item", self.name): if frappe.db.exists("Item", self.name):
frappe.msgprint("An item exists with same name (%s), please change the \ frappe.throw(frappe._("An item exists with same name ({0}), please change the item group name or rename the item").format(self.name))
item group name or rename the item" % self.name, raise_exception=1)

View File

@@ -11,10 +11,9 @@ from frappe.utils import cint
from frappe.model.document import Document from frappe.model.document import Document
class JobsEmailSettings(Document): class JobsEmailSettings(Document):
def validate(self): def validate(self):
if cint(self.extract_emails) and not (self.email_id and self.host and \ if cint(self.extract_emails) and not (self.email_id and self.host and \
self.username and self.password): self.username and self.password):
frappe.msgprint(_("""Host, Email and Password required if emails are to be pulled"""), frappe.throw(_("""Host, Email and Password required if emails are to be pulled"""))
raise_exception=True)

View File

@@ -4,8 +4,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe import msgprint from frappe import _
from frappe.model.document import Document from frappe.model.document import Document
@@ -18,5 +17,5 @@ class NotificationControl(Document):
def set_message(self, arg = ''): def set_message(self, arg = ''):
fn = self.select_transaction.lower().replace(' ', '_') + '_message' fn = self.select_transaction.lower().replace(' ', '_') + '_message'
frappe.db.set(self, fn, self.custom_message) frappe.db.set(self, fn, self.custom_message)
msgprint("Custom Message for %s updated!" % self.select_transaction) frappe.msgprint(_("Message updated"))

View File

@@ -6,17 +6,17 @@ import frappe
from frappe.utils import flt from frappe.utils import flt
from frappe import _
from frappe.utils.nestedset import NestedSet from frappe.utils.nestedset import NestedSet
class Territory(NestedSet): class Territory(NestedSet):
nsm_parent_field = 'parent_territory' nsm_parent_field = 'parent_territory'
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):
msgprint("Either target qty or target amount is mandatory.") frappe.throw(_("Either target qty or target amount is mandatory"))
raise Exception
def on_update(self): def on_update(self):
super(Territory, self).on_update() super(Territory, self).on_update()

View File

@@ -13,33 +13,29 @@ def get_company_currency(company):
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
def get_root_of(doctype): def get_root_of(doctype):
"""Get root element of a DocType with a tree structure""" """Get root element of a DocType with a tree structure"""
result = frappe.db.sql_list("""select name from `tab%s` result = frappe.db.sql_list("""select name from `tab%s`
where lft=1 and rgt=(select max(rgt) from `tab%s` where docstatus < 2)""" % where lft=1 and rgt=(select max(rgt) from `tab%s` where docstatus < 2)""" %
(doctype, doctype)) (doctype, doctype))
return result[0] if result else None return result[0] if result else None
def get_ancestors_of(doctype, name): def get_ancestors_of(doctype, name):
"""Get ancestor elements of a DocType with a tree structure""" """Get ancestor elements of a DocType with a tree structure"""
lft, rgt = frappe.db.get_value(doctype, name, ["lft", "rgt"]) lft, rgt = frappe.db.get_value(doctype, name, ["lft", "rgt"])
result = frappe.db.sql_list("""select name from `tab%s` result = frappe.db.sql_list("""select name from `tab%s`
where lft<%s and rgt>%s order by lft desc""" % (doctype, "%s", "%s"), (lft, rgt)) where lft<%s and rgt>%s order by lft desc""" % (doctype, "%s", "%s"), (lft, rgt))
return result or [] return result or []
@frappe.whitelist() @frappe.whitelist()
def get_price_list_currency(price_list): def get_price_list_currency(price_list):
price_list_currency = frappe.db.get_value("Price List", {"name": price_list, price_list_currency = frappe.db.get_value("Price List", {"name": price_list,
"enabled": 1}, "currency") "enabled": 1}, "currency")
if not price_list_currency: if not price_list_currency:
throw("{message}: {price_list} {disabled}".format(**{ throw(_("Price List {0} is disabled").format(price_list))
"message": _("Price List"),
"price_list": price_list,
"disabled": _("is disabled.")
}))
else: else:
return {"price_list_currency": price_list_currency} return {"price_list_currency": price_list_currency}

View File

@@ -4,7 +4,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe.utils import cstr, flt, cint from frappe.utils import flt, cint
from frappe import msgprint, _ from frappe import msgprint, _
import frappe.defaults import frappe.defaults
@@ -54,9 +54,7 @@ class DeliveryNote(SellingController):
if frappe.db.get_value("Selling Settings", None, 'so_required') == 'Yes': if frappe.db.get_value("Selling Settings", None, 'so_required') == 'Yes':
for d in self.get('delivery_note_details'): for d in self.get('delivery_note_details'):
if not d.against_sales_order: if not d.against_sales_order:
msgprint("Sales Order No. required against item %s"%d.item_code) frappe.throw(_("Sales Order required for Item {0}").format(d.item_code))
raise Exception
def validate(self): def validate(self):
super(DeliveryNote, self).validate() super(DeliveryNote, self).validate()
@@ -108,8 +106,7 @@ class DeliveryNote(SellingController):
where name = %s and (customer = %s or where name = %s and (customer = %s or
ifnull(customer,'')='')""", (self.project_name, self.customer)) ifnull(customer,'')='')""", (self.project_name, self.customer))
if not res: if not res:
msgprint("Customer - %s does not belong to project - %s. \n\nIf you want to use project for multiple customers then please make customer details blank in project - %s."%(self.customer,self.project_name,self.project_name)) frappe.throw(_("Customer {0} does not belong to project {1}").format(self.customer, self.project_name))
raise Exception
def validate_for_items(self): def validate_for_items(self):
check_list, chk_dupl_itm = [], [] check_list, chk_dupl_itm = [], []
@@ -119,14 +116,12 @@ class DeliveryNote(SellingController):
if frappe.db.get_value("Item", d.item_code, "is_stock_item") == 'Yes': if frappe.db.get_value("Item", d.item_code, "is_stock_item") == 'Yes':
if e in check_list: if e in check_list:
msgprint("Please check whether item %s has been entered twice wrongly." msgprint(_("Note: Item {0} entered multiple times").format(d.item_code))
% d.item_code)
else: else:
check_list.append(e) check_list.append(e)
else: else:
if f in chk_dupl_itm: if f in chk_dupl_itm:
msgprint("Please check whether item %s has been entered twice wrongly." msgprint(_("Note: Item {0} entered multiple times").format(d.item_code))
% d.item_code)
else: else:
chk_dupl_itm.append(f) chk_dupl_itm.append(f)
@@ -134,8 +129,7 @@ class DeliveryNote(SellingController):
for d in self.get_item_list(): for d in self.get_item_list():
if frappe.db.get_value("Item", d['item_code'], "is_stock_item") == "Yes": if frappe.db.get_value("Item", d['item_code'], "is_stock_item") == "Yes":
if not d['warehouse']: if not d['warehouse']:
msgprint("Please enter Warehouse for item %s as it is stock item" frappe.throw(_("Warehouse required for stock Item {0}").format(d["item_code"]))
% d['item_code'], raise_exception=1)
def update_current_stock(self): def update_current_stock(self):
@@ -187,18 +181,13 @@ class DeliveryNote(SellingController):
""" """
if not any([flt(d.get('packed_qty')) for d in self.get(self.fname)]): if not any([flt(d.get('packed_qty')) for d in self.get(self.fname)]):
return return
packing_error_list = [] has_error = False
for d in self.get(self.fname): for d in self.get(self.fname):
if flt(d.get('qty')) != flt(d.get('packed_qty')): if flt(d.get('qty')) != flt(d.get('packed_qty')):
packing_error_list.append([ frappe.msgprint(_("Packed quantity must equal quantity for Item {0} in row {1}").format(d.item_code, d.idx))
d.get('item_code', ''), has_error = True
d.get('qty', 0), if has_error:
d.get('packed_qty', 0) raise frappe.ValidationError
])
if packing_error_list:
err_msg = "\n".join([("Item: " + d[0] + ", Qty: " + cstr(d[1]) \
+ ", Packed: " + cstr(d[2])) for d in packing_error_list])
frappe.msgprint("Packing Error:\n" + err_msg, raise_exception=1)
def check_next_docstatus(self): def check_next_docstatus(self):
submit_rv = frappe.db.sql("""select t1.name submit_rv = frappe.db.sql("""select t1.name
@@ -206,16 +195,14 @@ class DeliveryNote(SellingController):
where t1.name = t2.parent and t2.delivery_note = %s and t1.docstatus = 1""", where t1.name = t2.parent and t2.delivery_note = %s and t1.docstatus = 1""",
(self.name)) (self.name))
if submit_rv: if submit_rv:
msgprint("Sales Invoice : " + cstr(submit_rv[0][0]) + " has already been submitted !") frappe.throw(_("Sales Invoice {0} has already been submitted").format(submit_rv[0][0]))
raise Exception , "Validation Error."
submit_in = frappe.db.sql("""select t1.name submit_in = frappe.db.sql("""select t1.name
from `tabInstallation Note` t1, `tabInstallation Note Item` t2 from `tabInstallation Note` t1, `tabInstallation Note Item` t2
where t1.name = t2.parent and t2.prevdoc_docname = %s and t1.docstatus = 1""", where t1.name = t2.parent and t2.prevdoc_docname = %s and t1.docstatus = 1""",
(self.name)) (self.name))
if submit_in: if submit_in:
msgprint("Installation Note : "+cstr(submit_in[0][0]) +" has already been submitted !") frappe.throw(_("Installation Note {0} has already been submitted").format(submit_in[0][0]))
raise Exception , "Validation Error."
def cancel_packing_slips(self): def cancel_packing_slips(self):
""" """
@@ -228,7 +215,7 @@ class DeliveryNote(SellingController):
for r in res: for r in res:
ps = frappe.get_doc('Packing Slip', r[0]) ps = frappe.get_doc('Packing Slip', r[0])
ps.cancel() ps.cancel()
frappe.msgprint(_("Packing Slip(s) Cancelled")) frappe.msgprint(_("Packing Slip(s) cancelled"))
def update_stock_ledger(self): def update_stock_ledger(self):
@@ -293,8 +280,7 @@ def make_sales_invoice(source_name, target_doc=None):
si.run_method("onload_post_render") si.run_method("onload_post_render")
if len(si.get("entries")) == 0: if len(si.get("entries")) == 0:
frappe.msgprint(_("All these items have already been invoiced."), frappe.throw(_("All these items have already been invoiced"))
raise_exception=True)
def update_item(source_doc, target_doc, source_parent): def update_item(source_doc, target_doc, source_parent):
target_doc.qty = source_doc.qty - invoiced_qty_map.get(source_doc.name, 0) target_doc.qty = source_doc.qty - invoiced_qty_map.get(source_doc.name, 0)

View File

@@ -18,13 +18,13 @@ class Item(WebsiteGenerator):
from frappe.model.naming import make_autoname from frappe.model.naming import make_autoname
self.item_code = make_autoname(self.naming_series+'.#####') self.item_code = make_autoname(self.naming_series+'.#####')
elif not self.item_code: elif not self.item_code:
msgprint(_("Item Code (item_code) is mandatory because Item naming is not sequential."), raise_exception=1) msgprint(_("Item Code is mandatory because Item is not automatically numbered"), raise_exception=1)
self.name = self.item_code self.name = self.item_code
def validate(self): def validate(self):
if not self.stock_uom: if not self.stock_uom:
msgprint(_("Please enter Default Unit of Measure"), raise_exception=1) msgprint(_("Please enter default Unit of Measure"), raise_exception=1)
self.check_warehouse_is_set_for_stock_item() self.check_warehouse_is_set_for_stock_item()
self.check_stock_uom_with_bin() self.check_stock_uom_with_bin()
@@ -51,7 +51,7 @@ class Item(WebsiteGenerator):
def check_warehouse_is_set_for_stock_item(self): def check_warehouse_is_set_for_stock_item(self):
if self.is_stock_item=="Yes" and not self.default_warehouse: if self.is_stock_item=="Yes" and not self.default_warehouse:
frappe.msgprint(_("Default Warehouse is mandatory for Stock Item."), frappe.msgprint(_("Default Warehouse is mandatory for stock Item."),
raise_exception=WarehouseNotSet) raise_exception=WarehouseNotSet)
def add_default_uom_in_conversion_factor_table(self): def add_default_uom_in_conversion_factor_table(self):
@@ -97,17 +97,12 @@ class Item(WebsiteGenerator):
check_list = [] check_list = []
for d in self.get('uom_conversion_details'): for d in self.get('uom_conversion_details'):
if cstr(d.uom) in check_list: if cstr(d.uom) in check_list:
msgprint(_("UOM %s has been entered more than once in Conversion Factor Table." % frappe.throw(_("Unit of Measure {0} has been entered more than once in Conversion Factor Table").format(d.uom))
cstr(d.uom)), raise_exception=1)
else: else:
check_list.append(cstr(d.uom)) check_list.append(cstr(d.uom))
if d.uom and cstr(d.uom) == cstr(self.stock_uom) and flt(d.conversion_factor) != 1: if d.uom and cstr(d.uom) == cstr(self.stock_uom) and flt(d.conversion_factor) != 1:
msgprint(_("""Conversion Factor of UOM: %s should be equal to 1. As UOM: %s is Stock UOM of Item: %s.""" % frappe.throw(_("Conversion factor for default Unit of Measure must be 1 in row {0}").format(d.idx))
(d.uom, d.uom, self.name)), raise_exception=1)
elif d.uom and cstr(d.uom)!= self.stock_uom and flt(d.conversion_factor) == 1:
msgprint(_("""Conversion Factor of UOM: %s should not be equal to 1. As UOM: %s is not Stock UOM of Item: %s""" %
(d.uom, d.uom, self.name)), raise_exception=1)
def validate_item_type(self): def validate_item_type(self):
if cstr(self.is_manufactured_item) == "No": if cstr(self.is_manufactured_item) == "No":
@@ -118,7 +113,7 @@ class Item(WebsiteGenerator):
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)
def check_for_active_boms(self): def check_for_active_boms(self):
if self.is_purchase_item != "Yes": if self.is_purchase_item != "Yes":
@@ -153,10 +148,10 @@ class Item(WebsiteGenerator):
account_type = frappe.db.get_value("Account", d.tax_type, "account_type") account_type = frappe.db.get_value("Account", d.tax_type, "account_type")
if account_type not in ['Tax', 'Chargeable', 'Income Account', 'Expense Account']: if account_type not in ['Tax', 'Chargeable', 'Income Account', 'Expense Account']:
msgprint("'%s' is not Tax / Chargeable / Income / Expense Account" % d.tax_type, raise_exception=1) frappe.throw(_("Item Tax Row {0} must have account of type Tax or Income or Expense or Chargeable").format(d.idx))
else: else:
if d.tax_type in check_list: if d.tax_type in check_list:
msgprint("Rate is entered twice for: '%s'" % d.tax_type, raise_exception=1) frappe.throw(_("{0} entered twice in Item Tax").format(d.tax_type))
else: else:
check_list.append(d.tax_type) check_list.append(d.tax_type)
@@ -165,8 +160,7 @@ class Item(WebsiteGenerator):
duplicate = frappe.db.sql("""select name from tabItem where barcode = %s duplicate = frappe.db.sql("""select name from tabItem where barcode = %s
and name != %s""", (self.barcode, self.name)) and name != %s""", (self.barcode, self.name))
if duplicate: if duplicate:
msgprint("Barcode: %s already used in item: %s" % frappe.throw(_("Barcode {0} already used in Item {1}").format(self.barcode, duplicate[0][0]))
(self.barcode, cstr(duplicate[0][0])), raise_exception = 1)
def cant_change(self): def cant_change(self):
if not self.get("__islocal"): if not self.get("__islocal"):
@@ -182,8 +176,7 @@ class Item(WebsiteGenerator):
def validate_item_type_for_reorder(self): def validate_item_type_for_reorder(self):
if self.re_order_level or len(self.get("item_reorder", {"material_request_type": "Purchase"})): if self.re_order_level or len(self.get("item_reorder", {"material_request_type": "Purchase"})):
if not self.is_purchase_item: if not self.is_purchase_item:
frappe.msgprint(_("""To set reorder level, item must be Purchase Item"""), frappe.throw(_("""To set reorder level, item must be Purchase Item"""))
raise_exception=1)
def check_if_sle_exists(self): def check_if_sle_exists(self):
sle = frappe.db.sql("""select name from `tabStock Ledger Entry` sle = frappe.db.sql("""select name from `tabStock Ledger Entry`
@@ -193,9 +186,7 @@ class Item(WebsiteGenerator):
def validate_name_with_item_group(self): def validate_name_with_item_group(self):
# causes problem with tree build # causes problem with tree build
if frappe.db.exists("Item Group", self.name): if frappe.db.exists("Item Group", self.name):
frappe.msgprint("An item group exists with same name (%s), \ frappe.throw(_("An Item Group exists with same name, please change the item name or rename the item group"))
please change the item name or rename the item group" %
self.name, raise_exception=1)
def update_item_price(self): def update_item_price(self):
frappe.db.sql("""update `tabItem Price` set item_name=%s, frappe.db.sql("""update `tabItem Price` set item_name=%s,
@@ -269,14 +260,7 @@ def validate_end_of_life(item_code, end_of_life=None, verbose=1):
end_of_life = frappe.db.get_value("Item", item_code, "end_of_life") end_of_life = frappe.db.get_value("Item", item_code, "end_of_life")
if end_of_life and getdate(end_of_life) <= now_datetime().date(): if end_of_life and getdate(end_of_life) <= now_datetime().date():
msg = (_("Item") + " %(item_code)s: " + _("reached its end of life on") + \ msg = _("Item {0} has reached its end of life on {1}").format(item_code, formatdate(end_of_life))
" %(date)s. " + _("Please check") + ": %(end_of_life_label)s " + \
"in Item master") % {
"item_code": item_code,
"date": formatdate(end_of_life),
"end_of_life_label": frappe.get_meta("Item").get_label("end_of_life")
}
_msgprint(msg, verbose) _msgprint(msg, verbose)
def validate_is_stock_item(item_code, is_stock_item=None, verbose=1): def validate_is_stock_item(item_code, is_stock_item=None, verbose=1):
@@ -284,9 +268,7 @@ def validate_is_stock_item(item_code, is_stock_item=None, verbose=1):
is_stock_item = frappe.db.get_value("Item", item_code, "is_stock_item") is_stock_item = frappe.db.get_value("Item", item_code, "is_stock_item")
if is_stock_item != "Yes": if is_stock_item != "Yes":
msg = (_("Item") + " %(item_code)s: " + _("is not a Stock Item")) % { msg = _("Item {0} is not a stock Item").format(item_code)
"item_code": item_code,
}
_msgprint(msg, verbose) _msgprint(msg, verbose)
@@ -295,10 +277,7 @@ def validate_cancelled_item(item_code, docstatus=None, verbose=1):
docstatus = frappe.db.get_value("Item", item_code, "docstatus") docstatus = frappe.db.get_value("Item", item_code, "docstatus")
if docstatus == 2: if docstatus == 2:
msg = (_("Item") + " %(item_code)s: " + _("is a cancelled Item")) % { msg = _("Item {0} is cancelled").format(item_code)
"item_code": item_code,
}
_msgprint(msg, verbose) _msgprint(msg, verbose)
def _msgprint(msg, verbose): def _msgprint(msg, verbose):

View File

@@ -9,20 +9,20 @@ from frappe import msgprint, _
from frappe.model.document import Document from frappe.model.document import Document
class LandedCostWizard(Document): class LandedCostWizard(Document):
def update_landed_cost(self): def update_landed_cost(self):
""" """
Add extra cost and recalculate all values in pr, Add extra cost and recalculate all values in pr,
Recalculate valuation rate in all sle after pr posting date Recalculate valuation rate in all sle after pr posting date
""" """
purchase_receipts = [row.purchase_receipt for row in purchase_receipts = [row.purchase_receipt for row in
self.get("lc_pr_details")] self.get("lc_pr_details")]
self.validate_purchase_receipts(purchase_receipts) self.validate_purchase_receipts(purchase_receipts)
self.cancel_pr(purchase_receipts) self.cancel_pr(purchase_receipts)
self.add_charges_in_pr(purchase_receipts) self.add_charges_in_pr(purchase_receipts)
self.submit_pr(purchase_receipts) self.submit_pr(purchase_receipts)
msgprint("Landed Cost updated successfully") msgprint(_("Landed Cost updated successfully"))
def validate_purchase_receipts(self, purchase_receipts): def validate_purchase_receipts(self, purchase_receipts):
for pr in purchase_receipts: for pr in purchase_receipts:
@@ -32,21 +32,21 @@ class LandedCostWizard(Document):
def add_charges_in_pr(self, purchase_receipts): def add_charges_in_pr(self, purchase_receipts):
""" Add additional charges in selected pr proportionately""" """ Add additional charges in selected pr proportionately"""
total_amt = self.get_total_pr_amt(purchase_receipts) total_amt = self.get_total_pr_amt(purchase_receipts)
for pr in purchase_receipts: for pr in purchase_receipts:
pr_doc = frappe.get_doc('Purchase Receipt', pr) pr_doc = frappe.get_doc('Purchase Receipt', pr)
pr_items = pr_doc.get("purchase_tax_details") pr_items = pr_doc.get("purchase_tax_details")
for lc in self.get("landed_cost_details"): for lc in self.get("landed_cost_details"):
amt = flt(lc.amount) * flt(pr_doc.net_total)/ flt(total_amt) amt = flt(lc.amount) * flt(pr_doc.net_total)/ flt(total_amt)
matched_row = pr_doc.get("other_charges", { matched_row = pr_doc.get("other_charges", {
"category": "Valuation", "category": "Valuation",
"add_deduct_tax": "Add", "add_deduct_tax": "Add",
"charge_type": "Actual", "charge_type": "Actual",
"account_head": lc.account_head "account_head": lc.account_head
}) })
if not matched_row: # add if not exists if not matched_row: # add if not exists
ch = pr_doc.append("other_charges") ch = pr_doc.append("other_charges")
ch.category = 'Valuation' ch.category = 'Valuation'
@@ -63,30 +63,30 @@ class LandedCostWizard(Document):
matched_row[0].rate = amt matched_row[0].rate = amt
matched_row[0].tax_amount = amt matched_row[0].tax_amount = amt
matched_row[0].cost_center = lc.cost_center matched_row[0].cost_center = lc.cost_center
pr_doc.run_method("validate") pr_doc.run_method("validate")
for d in pr_doc.get_all_children(): for d in pr_doc.get_all_children():
d.db_update() d.db_update()
def get_total_pr_amt(self, purchase_receipts): def get_total_pr_amt(self, purchase_receipts):
return frappe.db.sql("""SELECT SUM(net_total) FROM `tabPurchase Receipt` return frappe.db.sql("""SELECT SUM(net_total) FROM `tabPurchase Receipt`
WHERE name in (%s)""" % ', '.join(['%s']*len(purchase_receipts)), WHERE name in (%s)""" % ', '.join(['%s']*len(purchase_receipts)),
tuple(purchase_receipts))[0][0] tuple(purchase_receipts))[0][0]
def cancel_pr(self, purchase_receipts): def cancel_pr(self, purchase_receipts):
for pr in purchase_receipts: for pr in purchase_receipts:
pr_doc = frappe.get_doc("Purchase Receipt", pr) pr_doc = frappe.get_doc("Purchase Receipt", pr)
pr_doc.run_method("update_ordered_qty") pr_doc.run_method("update_ordered_qty")
frappe.db.sql("""delete from `tabStock Ledger Entry` frappe.db.sql("""delete from `tabStock Ledger Entry`
where voucher_type='Purchase Receipt' and voucher_no=%s""", pr) where voucher_type='Purchase Receipt' and voucher_no=%s""", pr)
frappe.db.sql("""delete from `tabGL Entry` where voucher_type='Purchase Receipt' frappe.db.sql("""delete from `tabGL Entry` where voucher_type='Purchase Receipt'
and voucher_no=%s""", pr) and voucher_no=%s""", pr)
def submit_pr(self, purchase_receipts): def submit_pr(self, purchase_receipts):
for pr in purchase_receipts: for pr in purchase_receipts:
pr_doc = frappe.get_doc("Purchase Receipt", pr) pr_doc = frappe.get_doc("Purchase Receipt", pr)
pr_doc.run_method("update_ordered_qty") pr_doc.run_method("update_ordered_qty")
pr_doc.run_method("update_stock") pr_doc.run_method("update_stock")
pr_doc.run_method("make_gl_entries") pr_doc.run_method("make_gl_entries")

View File

@@ -114,7 +114,7 @@ class MaterialRequest(BuyingController):
self.check_modified_date() self.check_modified_date()
self.update_bin(is_submit = (status == 'Submitted') and 1 or 0, is_stopped = 1) self.update_bin(is_submit = (status == 'Submitted') and 1 or 0, is_stopped = 1)
frappe.db.set(self, 'status', cstr(status)) frappe.db.set(self, 'status', cstr(status))
msgprint(self.doctype + ": " + self.name + " has been %s." % ((status == 'Submitted') and 'Unstopped' or cstr(status))) frappe.msgprint(_("Status updated to {0}").format(_(status)))
def on_cancel(self): def on_cancel(self):
# Step 1:=> Get Purchase Common Obj # Step 1:=> Get Purchase Common Obj

View File

@@ -4,7 +4,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe.utils import flt, cint from frappe.utils import flt, cint
from frappe import msgprint, _ from frappe import _
from frappe.model.document import Document from frappe.model.document import Document
@@ -32,12 +32,12 @@ class PackingSlip(Document):
Validates if delivery note has status as draft Validates if delivery note has status as draft
""" """
if cint(frappe.db.get_value("Delivery Note", self.delivery_note, "docstatus")) != 0: if cint(frappe.db.get_value("Delivery Note", self.delivery_note, "docstatus")) != 0:
msgprint(_("""Invalid Delivery Note. Delivery Note should exist and should be in draft state. Please rectify and try again."""), raise_exception=1) frappe.throw(_("Delivery Note {0} must not be submitted").format(self.delivery_note))
def validate_items_mandatory(self): def validate_items_mandatory(self):
rows = [d.item_code for d in self.get("item_details")] rows = [d.item_code for d in self.get("item_details")]
if not rows: if not rows:
frappe.msgprint(_("No Items to Pack"), raise_exception=1) frappe.msgprint(_("No Items to pack"), raise_exception=1)
def validate_case_nos(self): def validate_case_nos(self):
""" """
@@ -50,8 +50,8 @@ class PackingSlip(Document):
elif self.from_case_no > self.to_case_no: elif self.from_case_no > self.to_case_no:
frappe.msgprint(_("'To Case No.' cannot be less than 'From Case No.'"), frappe.msgprint(_("'To Case No.' cannot be less than 'From Case No.'"),
raise_exception=1) raise_exception=1)
res = frappe.db.sql("""SELECT name FROM `tabPacking Slip` res = frappe.db.sql("""SELECT name FROM `tabPacking Slip`
WHERE delivery_note = %(delivery_note)s AND docstatus = 1 AND WHERE delivery_note = %(delivery_note)s AND docstatus = 1 AND
(from_case_no BETWEEN %(from_case_no)s AND %(to_case_no)s (from_case_no BETWEEN %(from_case_no)s AND %(to_case_no)s
@@ -60,9 +60,7 @@ class PackingSlip(Document):
""", self.as_dict()) """, self.as_dict())
if res: if res:
frappe.msgprint(_("""Case No(s) already in use. Please rectify and try again. frappe.throw(_("""Case No(s) already in use. Try from Case No {0}""").format(self.get_recommended_case_no()))
Recommended <b>From Case No. = %s</b>""") % self.get_recommended_case_no(),
raise_exception=1)
def validate_qty(self): def validate_qty(self):
""" """
@@ -85,13 +83,13 @@ class PackingSlip(Document):
* Item Quantity dict of current packing slip doc * Item Quantity dict of current packing slip doc
* No. of Cases of this packing slip * No. of Cases of this packing slip
""" """
rows = [d.item_code for d in self.get("item_details")] rows = [d.item_code for d in self.get("item_details")]
condition = "" condition = ""
if rows: if rows:
condition = " and item_code in (%s)" % (", ".join(["%s"]*len(rows))) condition = " and item_code in (%s)" % (", ".join(["%s"]*len(rows)))
# gets item code, qty per item code, latest packed qty per item code and stock uom # gets item code, qty per item code, latest packed qty per item code and stock uom
res = frappe.db.sql("""select item_code, ifnull(sum(qty), 0) as qty, res = frappe.db.sql("""select item_code, ifnull(sum(qty), 0) as qty,
(select sum(ifnull(psi.qty, 0) * (abs(ps.to_case_no - ps.from_case_no) + 1)) (select sum(ifnull(psi.qty, 0) * (abs(ps.to_case_no - ps.from_case_no) + 1))
@@ -100,7 +98,7 @@ class PackingSlip(Document):
and ps.delivery_note = dni.parent and psi.item_code=dni.item_code) as packed_qty, and ps.delivery_note = dni.parent and psi.item_code=dni.item_code) as packed_qty,
stock_uom, item_name stock_uom, item_name
from `tabDelivery Note Item` dni from `tabDelivery Note Item` dni
where parent=%s %s where parent=%s %s
group by item_code""" % ("%s", condition), group by item_code""" % ("%s", condition),
tuple([self.delivery_note] + rows), as_dict=1) tuple([self.delivery_note] + rows), as_dict=1)
@@ -117,12 +115,8 @@ class PackingSlip(Document):
item['recommended_qty'] = (flt(item['qty']) - flt(item['packed_qty'])) / no_of_cases item['recommended_qty'] = (flt(item['qty']) - flt(item['packed_qty'])) / no_of_cases
item['specified_qty'] = flt(ps_item_qty[item['item_code']]) item['specified_qty'] = flt(ps_item_qty[item['item_code']])
if not item['packed_qty']: item['packed_qty'] = 0 if not item['packed_qty']: item['packed_qty'] = 0
frappe.msgprint(""" frappe.throw(_("Quantity for Item {0} must be less than {1}").format(item.get("item_code"), item.get("recommended_qty")))
Invalid Quantity specified (%(specified_qty)s %(stock_uom)s).
%(packed_qty)s out of %(qty)s %(stock_uom)s already packed for %(item_code)s.
<b>Recommended quantity for %(item_code)s = %(recommended_qty)s
%(stock_uom)s</b>""" % item, raise_exception=1)
def update_item_details(self): def update_item_details(self):
""" """
@@ -132,9 +126,9 @@ class PackingSlip(Document):
self.from_case_no = self.get_recommended_case_no() self.from_case_no = self.get_recommended_case_no()
for d in self.get("item_details"): for d in self.get("item_details"):
res = frappe.db.get_value("Item", d.item_code, res = frappe.db.get_value("Item", d.item_code,
["net_weight", "weight_uom"], as_dict=True) ["net_weight", "weight_uom"], as_dict=True)
if res and len(res)>0: if res and len(res)>0:
d.net_weight = res["net_weight"] d.net_weight = res["net_weight"]
d.weight_uom = res["weight_uom"] d.weight_uom = res["weight_uom"]
@@ -146,12 +140,12 @@ class PackingSlip(Document):
""" """
recommended_case_no = frappe.db.sql("""SELECT MAX(to_case_no) FROM `tabPacking Slip` recommended_case_no = frappe.db.sql("""SELECT MAX(to_case_no) FROM `tabPacking Slip`
WHERE delivery_note = %(delivery_note)s AND docstatus=1""", self.as_dict()) WHERE delivery_note = %(delivery_note)s AND docstatus=1""", self.as_dict())
return cint(recommended_case_no[0][0]) + 1 return cint(recommended_case_no[0][0]) + 1
def get_items(self): def get_items(self):
self.set("item_details", []) self.set("item_details", [])
dn_details = self.get_details_for_packing()[0] dn_details = self.get_details_for_packing()[0]
for item in dn_details: for item in dn_details:
if flt(item.qty) > flt(item.packed_qty): if flt(item.qty) > flt(item.packed_qty):
@@ -164,11 +158,11 @@ class PackingSlip(Document):
def item_details(doctype, txt, searchfield, start, page_len, filters): def item_details(doctype, txt, searchfield, start, page_len, filters):
from erpnext.controllers.queries import get_match_cond from erpnext.controllers.queries import get_match_cond
return frappe.db.sql("""select name, item_name, description from `tabItem` return frappe.db.sql("""select name, item_name, description from `tabItem`
where name in ( select item_code FROM `tabDelivery Note Item` where name in ( select item_code FROM `tabDelivery Note Item`
where parent= %s where parent= %s
and ifnull(qty, 0) > ifnull(packed_qty, 0)) and ifnull(qty, 0) > ifnull(packed_qty, 0))
and %s like "%s" %s and %s like "%s" %s
limit %s, %s """ % ("%s", searchfield, "%s", limit %s, %s """ % ("%s", searchfield, "%s",
get_match_cond(doctype), "%s", "%s"), get_match_cond(doctype), "%s", "%s"),
(filters["delivery_note"], "%%%s%%" % txt, start, page_len)) (filters["delivery_note"], "%%%s%%" % txt, start, page_len))

View File

@@ -6,7 +6,7 @@ import frappe
from frappe.utils import cstr, flt, cint from frappe.utils import cstr, flt, cint
from frappe import msgprint, _ from frappe import _
import frappe.defaults import frappe.defaults
from erpnext.stock.utils import update_bin from erpnext.stock.utils import update_bin
@@ -86,21 +86,18 @@ class PurchaseReceipt(BuyingController):
# Check Received Qty = Accepted Qty + Rejected Qty # Check Received Qty = Accepted Qty + Rejected Qty
if ((flt(d.qty) + flt(d.rejected_qty)) != flt(d.received_qty)): if ((flt(d.qty) + flt(d.rejected_qty)) != flt(d.received_qty)):
frappe.throw(_("Accepted + Rejected Qty must be equal to Received quantity for Item {0}").format(d.item_code))
msgprint("Sum of Accepted Qty and Rejected Qty must be equal to Received quantity. Error for Item: " + cstr(d.item_code))
raise Exception
def validate_challan_no(self): def validate_challan_no(self):
"Validate if same challan no exists for same supplier in a submitted purchase receipt" "Validate if same challan no exists for same supplier in a submitted purchase receipt"
if self.challan_no: if self.challan_no:
exists = frappe.db.sql(""" exists = frappe.db.sql_list("""
SELECT name FROM `tabPurchase Receipt` SELECT name FROM `tabPurchase Receipt`
WHERE name!=%s AND supplier=%s AND challan_no=%s WHERE name!=%s AND supplier=%s AND challan_no=%s
AND docstatus=1""", (self.name, self.supplier, self.challan_no)) AND docstatus=1""", (self.name, self.supplier, self.challan_no))
if exists: if exists:
frappe.msgprint("Another Purchase Receipt using the same Challan No. already exists.\ frappe.throw(_("Supplier delivery number duplicate in {0}").format(exists))
Please enter a valid Challan No.", raise_exception=1)
def validate_with_previous_doc(self): def validate_with_previous_doc(self):
super(PurchaseReceipt, self).validate_with_previous_doc(self.tname, { super(PurchaseReceipt, self).validate_with_previous_doc(self.tname, {
@@ -129,8 +126,7 @@ class PurchaseReceipt(BuyingController):
if frappe.db.get_value("Buying Settings", None, "po_required") == 'Yes': if frappe.db.get_value("Buying Settings", None, "po_required") == 'Yes':
for d in self.get('purchase_receipt_details'): for d in self.get('purchase_receipt_details'):
if not d.prevdoc_docname: if not d.prevdoc_docname:
msgprint("Purchse Order No. required against item %s"%d.item_code) frappe.throw(_("Purchase Order number required for Item {0}").format(d.item_code))
raise Exception
def update_stock(self): def update_stock(self):
sl_entries = [] sl_entries = []
@@ -212,7 +208,7 @@ class PurchaseReceipt(BuyingController):
(d.item_code,), as_dict = 1) (d.item_code,), as_dict = 1)
ins_reqd = ins_reqd and ins_reqd[0]['inspection_required'] or 'No' ins_reqd = ins_reqd and ins_reqd[0]['inspection_required'] or 'No'
if ins_reqd == 'Yes' and not d.qa_no: if ins_reqd == 'Yes' and not d.qa_no:
msgprint("Item: " + d.item_code + " requires QA Inspection. Please enter QA No or report to authorized person to create Quality Inspection") frappe.throw(_("Quality Inspection required for Item {0}").format(d.item_code))
# Check for Stopped status # Check for Stopped status
def check_for_stopped_status(self, pc_obj): def check_for_stopped_status(self, pc_obj):
@@ -251,9 +247,7 @@ class PurchaseReceipt(BuyingController):
where t1.name = t2.parent and t2.purchase_receipt = %s and t1.docstatus = 1""", where t1.name = t2.parent and t2.purchase_receipt = %s and t1.docstatus = 1""",
(self.name)) (self.name))
if submit_rv: if submit_rv:
msgprint("Purchase Invoice : " + cstr(self.submit_rv[0][0]) + " has already been submitted !") frappe.throw(_("Purchase Invoice {0} is already submitted").format(self.submit_rv[0][0]))
raise Exception , "Validation Error."
def on_cancel(self): def on_cancel(self):
pc_obj = frappe.get_doc('Purchase Common') pc_obj = frappe.get_doc('Purchase Common')

View File

@@ -29,7 +29,7 @@ class SerialNo(StockController):
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()
self.validate_item() self.validate_item()
@@ -38,28 +38,28 @@ class SerialNo(StockController):
def set_maintenance_status(self): def set_maintenance_status(self):
if not self.warranty_expiry_date and not self.amc_expiry_date: if not self.warranty_expiry_date and not self.amc_expiry_date:
self.maintenance_status = None self.maintenance_status = None
if self.warranty_expiry_date and self.warranty_expiry_date < nowdate(): if self.warranty_expiry_date and self.warranty_expiry_date < nowdate():
self.maintenance_status = "Out of Warranty" self.maintenance_status = "Out of Warranty"
if self.amc_expiry_date and self.amc_expiry_date < nowdate(): if self.amc_expiry_date and self.amc_expiry_date < nowdate():
self.maintenance_status = "Out of AMC" self.maintenance_status = "Out of AMC"
if self.amc_expiry_date and self.amc_expiry_date >= nowdate(): if self.amc_expiry_date and self.amc_expiry_date >= nowdate():
self.maintenance_status = "Under AMC" self.maintenance_status = "Under AMC"
if self.warranty_expiry_date and self.warranty_expiry_date >= nowdate(): if self.warranty_expiry_date and self.warranty_expiry_date >= nowdate():
self.maintenance_status = "Under Warranty" self.maintenance_status = "Under Warranty"
def validate_warehouse(self): def validate_warehouse(self):
if not self.get("__islocal"): if not self.get("__islocal"):
item_code, warehouse = frappe.db.get_value("Serial No", item_code, warehouse = frappe.db.get_value("Serial No",
self.name, ["item_code", "warehouse"]) self.name, ["item_code", "warehouse"])
if item_code != self.item_code: if item_code != self.item_code:
frappe.throw(_("Item Code cannot be changed for Serial No."), frappe.throw(_("Item Code cannot be changed for Serial No."),
SerialNoCannotCannotChangeError) SerialNoCannotCannotChangeError)
if not self.via_stock_ledger and warehouse != self.warehouse: if not self.via_stock_ledger and warehouse != self.warehouse:
frappe.throw(_("Warehouse cannot be changed for Serial No."), frappe.throw(_("Warehouse cannot be changed for Serial No."),
SerialNoCannotCannotChangeError) SerialNoCannotCannotChangeError)
def validate_item(self): def validate_item(self):
@@ -69,17 +69,17 @@ class SerialNo(StockController):
item = frappe.get_doc("Item", self.item_code) item = frappe.get_doc("Item", self.item_code)
if item.has_serial_no!="Yes": if item.has_serial_no!="Yes":
frappe.throw(_("Item must have 'Has Serial No' as 'Yes'") + ": " + self.item_code) frappe.throw(_("Item must have 'Has Serial No' as 'Yes'") + ": " + self.item_code)
self.item_group = item.item_group self.item_group = item.item_group
self.description = item.description self.description = item.description
self.item_name = item.item_name self.item_name = item.item_name
self.brand = item.brand self.brand = item.brand
self.warranty_period = item.warranty_period self.warranty_period = item.warranty_period
def set_status(self, last_sle): def set_status(self, last_sle):
if last_sle: if last_sle:
if last_sle.voucher_type == "Stock Entry": if last_sle.voucher_type == "Stock Entry":
document_type = frappe.db.get_value("Stock Entry", last_sle.voucher_no, document_type = frappe.db.get_value("Stock Entry", last_sle.voucher_no,
"purpose") "purpose")
else: else:
document_type = last_sle.voucher_type document_type = last_sle.voucher_type
@@ -98,7 +98,7 @@ class SerialNo(StockController):
self.status = "Not Available" self.status = "Not Available"
else: else:
self.status = "Not Available" self.status = "Not Available"
def set_purchase_details(self, purchase_sle): def set_purchase_details(self, purchase_sle):
if purchase_sle: if purchase_sle:
self.purchase_document_type = purchase_sle.voucher_type self.purchase_document_type = purchase_sle.voucher_type
@@ -108,13 +108,13 @@ class SerialNo(StockController):
self.purchase_rate = purchase_sle.incoming_rate self.purchase_rate = purchase_sle.incoming_rate
if purchase_sle.voucher_type == "Purchase Receipt": if purchase_sle.voucher_type == "Purchase Receipt":
self.supplier, self.supplier_name = \ self.supplier, self.supplier_name = \
frappe.db.get_value("Purchase Receipt", purchase_sle.voucher_no, frappe.db.get_value("Purchase Receipt", purchase_sle.voucher_no,
["supplier", "supplier_name"]) ["supplier", "supplier_name"])
else: else:
for fieldname in ("purchase_document_type", "purchase_document_no", for fieldname in ("purchase_document_type", "purchase_document_no",
"purchase_date", "purchase_time", "purchase_rate", "supplier", "supplier_name"): "purchase_date", "purchase_time", "purchase_rate", "supplier", "supplier_name"):
self.set(fieldname, None) self.set(fieldname, None)
def set_sales_details(self, delivery_sle): def set_sales_details(self, delivery_sle):
if delivery_sle: if delivery_sle:
self.delivery_document_type = delivery_sle.voucher_type self.delivery_document_type = delivery_sle.voucher_type
@@ -122,70 +122,70 @@ class SerialNo(StockController):
self.delivery_date = delivery_sle.posting_date self.delivery_date = delivery_sle.posting_date
self.delivery_time = delivery_sle.posting_time self.delivery_time = delivery_sle.posting_time
self.customer, self.customer_name = \ self.customer, self.customer_name = \
frappe.db.get_value(delivery_sle.voucher_type, delivery_sle.voucher_no, frappe.db.get_value(delivery_sle.voucher_type, delivery_sle.voucher_no,
["customer", "customer_name"]) ["customer", "customer_name"])
if self.warranty_period: if self.warranty_period:
self.warranty_expiry_date = add_days(cstr(delivery_sle.posting_date), self.warranty_expiry_date = add_days(cstr(delivery_sle.posting_date),
cint(self.warranty_period)) cint(self.warranty_period))
else: else:
for fieldname in ("delivery_document_type", "delivery_document_no", for fieldname in ("delivery_document_type", "delivery_document_no",
"delivery_date", "delivery_time", "customer", "customer_name", "delivery_date", "delivery_time", "customer", "customer_name",
"warranty_expiry_date"): "warranty_expiry_date"):
self.set(fieldname, None) self.set(fieldname, None)
def get_last_sle(self): def get_last_sle(self):
entries = {} entries = {}
sle_dict = self.get_stock_ledger_entries() sle_dict = self.get_stock_ledger_entries()
if sle_dict: if sle_dict:
if sle_dict.get("incoming", []): if sle_dict.get("incoming", []):
entries["purchase_sle"] = sle_dict["incoming"][0] entries["purchase_sle"] = sle_dict["incoming"][0]
if len(sle_dict.get("incoming", [])) - len(sle_dict.get("outgoing", [])) > 0: if len(sle_dict.get("incoming", [])) - len(sle_dict.get("outgoing", [])) > 0:
entries["last_sle"] = sle_dict["incoming"][0] entries["last_sle"] = sle_dict["incoming"][0]
else: else:
entries["last_sle"] = sle_dict["outgoing"][0] entries["last_sle"] = sle_dict["outgoing"][0]
entries["delivery_sle"] = sle_dict["outgoing"][0] entries["delivery_sle"] = sle_dict["outgoing"][0]
return entries return entries
def get_stock_ledger_entries(self): def get_stock_ledger_entries(self):
sle_dict = {} sle_dict = {}
for sle in frappe.db.sql("""select * from `tabStock Ledger Entry` for sle in frappe.db.sql("""select * from `tabStock Ledger Entry`
where serial_no like %s and item_code=%s and ifnull(is_cancelled, 'No')='No' where serial_no like %s and item_code=%s and ifnull(is_cancelled, 'No')='No'
order by posting_date desc, posting_time desc, name desc""", order by posting_date desc, posting_time desc, name desc""",
("%%%s%%" % self.name, self.item_code), as_dict=1): ("%%%s%%" % self.name, self.item_code), as_dict=1):
if self.name.upper() in get_serial_nos(sle.serial_no): if self.name.upper() in get_serial_nos(sle.serial_no):
if sle.actual_qty > 0: if sle.actual_qty > 0:
sle_dict.setdefault("incoming", []).append(sle) sle_dict.setdefault("incoming", []).append(sle)
else: else:
sle_dict.setdefault("outgoing", []).append(sle) sle_dict.setdefault("outgoing", []).append(sle)
return sle_dict return sle_dict
def on_trash(self): def on_trash(self):
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:
frappe.throw(_("Sorry, Serial Nos cannot be merged")) frappe.throw(_("Sorry, Serial Nos cannot be merged"))
def after_rename(self, old, new, merge=False): def after_rename(self, old, new, merge=False):
"""rename serial_no text fields""" """rename serial_no text fields"""
for dt in frappe.db.sql("""select parent from tabDocField for dt in frappe.db.sql("""select parent from tabDocField
where fieldname='serial_no' and fieldtype='Text'"""): where fieldname='serial_no' and fieldtype='Text'"""):
for item in frappe.db.sql("""select name, serial_no from `tab%s` for item in frappe.db.sql("""select name, serial_no from `tab%s`
where serial_no like '%%%s%%'""" % (dt[0], old)): where serial_no like '%%%s%%'""" % (dt[0], old)):
serial_nos = map(lambda i: i==old and new or i, item[1].split('\n')) serial_nos = map(lambda i: i==old and new or i, item[1].split('\n'))
frappe.db.sql("""update `tab%s` set serial_no = %s frappe.db.sql("""update `tab%s` set serial_no = %s
where name=%s""" % (dt[0], '%s', '%s'), where name=%s""" % (dt[0], '%s', '%s'),
('\n'.join(serial_nos), item[0])) ('\n'.join(serial_nos), item[0]))
def on_stock_ledger_entry(self): def on_stock_ledger_entry(self):
if self.via_stock_ledger and not self.get("__islocal"): if self.via_stock_ledger and not self.get("__islocal"):
last_sle = self.get_last_sle() last_sle = self.get_last_sle()
@@ -193,7 +193,7 @@ class SerialNo(StockController):
self.set_purchase_details(last_sle.get("purchase_sle")) self.set_purchase_details(last_sle.get("purchase_sle"))
self.set_sales_details(last_sle.get("delivery_sle")) self.set_sales_details(last_sle.get("delivery_sle"))
self.set_maintenance_status() self.set_maintenance_status()
def on_communication(self): def on_communication(self):
return return
@@ -201,11 +201,11 @@ def process_serial_no(sle):
item_det = get_item_details(sle.item_code) item_det = get_item_details(sle.item_code)
validate_serial_no(sle, item_det) validate_serial_no(sle, item_det)
update_serial_nos(sle, item_det) update_serial_nos(sle, item_det)
def validate_serial_no(sle, item_det): def validate_serial_no(sle, item_det):
if item_det.has_serial_no=="No": if item_det.has_serial_no=="No":
if sle.serial_no: if sle.serial_no:
frappe.throw(_("Serial Number should be blank for Non Serialized Item" + ": " frappe.throw(_("Serial Number should be blank for Non Serialized Item" + ": "
+ sle.item_code), SerialNoNotRequiredError) + sle.item_code), SerialNoNotRequiredError)
else: else:
if sle.serial_no: if sle.serial_no:
@@ -216,41 +216,41 @@ def validate_serial_no(sle, item_det):
if len(serial_nos) and len(serial_nos) != abs(cint(sle.actual_qty)): if len(serial_nos) and len(serial_nos) != abs(cint(sle.actual_qty)):
frappe.throw(_("Serial Nos do not match with qty") + \ frappe.throw(_("Serial Nos do not match with qty") + \
(": %s (%s)" % (sle.item_code, sle.actual_qty)), SerialNoQtyError) (": %s (%s)" % (sle.item_code, sle.actual_qty)), SerialNoQtyError)
if len(serial_nos) != len(set(serial_nos)): if len(serial_nos) != len(set(serial_nos)):
frappe.throw(_("Duplicate Serial No entered against item") + frappe.throw(_("Duplicate Serial No entered against item") +
(": %s" % sle.item_code), SerialNoDuplicateError) (": %s" % sle.item_code), SerialNoDuplicateError)
for serial_no in serial_nos: for serial_no in serial_nos:
if frappe.db.exists("Serial No", serial_no): if frappe.db.exists("Serial No", serial_no):
sr = frappe.get_doc("Serial No", serial_no) sr = frappe.get_doc("Serial No", serial_no)
if sr.item_code!=sle.item_code: if sr.item_code!=sle.item_code:
frappe.throw(_("Serial No does not belong to Item") + frappe.throw(_("Serial No does not belong to Item") +
(": %s (%s)" % (sle.item_code, serial_no)), SerialNoItemError) (": %s (%s)" % (sle.item_code, serial_no)), SerialNoItemError)
if sr.warehouse and sle.actual_qty > 0: if sr.warehouse and sle.actual_qty > 0:
frappe.throw(_("Same Serial No") + ": " + sr.name + frappe.throw(_("Same Serial No") + ": " + sr.name +
_(" can not be received twice"), SerialNoDuplicateError) _(" can not be received twice"), SerialNoDuplicateError)
if sle.actual_qty < 0: if sle.actual_qty < 0:
if sr.warehouse!=sle.warehouse: if sr.warehouse!=sle.warehouse:
frappe.throw(_("Serial No") + ": " + serial_no + frappe.throw(_("Serial No") + ": " + serial_no +
_(" does not belong to Warehouse") + ": " + sle.warehouse, _(" does not belong to Warehouse") + ": " + sle.warehouse,
SerialNoWarehouseError) SerialNoWarehouseError)
if sle.voucher_type in ("Delivery Note", "Sales Invoice") \ if sle.voucher_type in ("Delivery Note", "Sales Invoice") \
and sr.status != "Available": and sr.status != "Available":
frappe.throw(_("Serial No status must be 'Available' to Deliver") frappe.throw(_("Serial No status must be 'Available' to Deliver")
+ ": " + serial_no, SerialNoStatusError) + ": " + serial_no, SerialNoStatusError)
elif sle.actual_qty < 0: elif sle.actual_qty < 0:
# transfer out # transfer out
frappe.throw(_("Serial No must exist to transfer out.") + \ frappe.throw(_("Serial No must exist to transfer out.") + \
": " + serial_no, SerialNoNotExistsError) ": " + serial_no, SerialNoNotExistsError)
elif sle.actual_qty < 0 or not item_det.serial_no_series: elif sle.actual_qty < 0 or not item_det.serial_no_series:
frappe.throw(_("Serial Number Required for Serialized Item" + ": " frappe.throw(_("Serial Number Required for Serialized Item" + ": "
+ sle.item_code), SerialNoRequiredError) + sle.item_code), SerialNoRequiredError)
def update_serial_nos(sle, item_det): def update_serial_nos(sle, item_det):
if sle.is_cancelled == "No" and not sle.serial_no and sle.actual_qty > 0 and item_det.serial_no_series: if sle.is_cancelled == "No" and not sle.serial_no and sle.actual_qty > 0 and item_det.serial_no_series:
from frappe.model.naming import make_autoname from frappe.model.naming import make_autoname
@@ -258,7 +258,7 @@ def update_serial_nos(sle, item_det):
for i in xrange(cint(sle.actual_qty)): for i in xrange(cint(sle.actual_qty)):
serial_nos.append(make_autoname(item_det.serial_no_series)) serial_nos.append(make_autoname(item_det.serial_no_series))
frappe.db.set(sle, "serial_no", "\n".join(serial_nos)) frappe.db.set(sle, "serial_no", "\n".join(serial_nos))
if sle.serial_no: if sle.serial_no:
serial_nos = get_serial_nos(sle.serial_no) serial_nos = get_serial_nos(sle.serial_no)
for serial_no in serial_nos: for serial_no in serial_nos:
@@ -271,12 +271,12 @@ def update_serial_nos(sle, item_det):
make_serial_no(serial_no, sle) make_serial_no(serial_no, sle)
def get_item_details(item_code): def get_item_details(item_code):
return frappe.db.sql("""select name, has_batch_no, docstatus, return frappe.db.sql("""select name, has_batch_no, docstatus,
is_stock_item, has_serial_no, serial_no_series is_stock_item, has_serial_no, serial_no_series
from tabItem where name=%s""", item_code, as_dict=True)[0] from tabItem where name=%s""", item_code, as_dict=True)[0]
def get_serial_nos(serial_no): def get_serial_nos(serial_no):
return [s.strip() for s in cstr(serial_no).strip().upper().replace(',', '\n').split('\n') return [s.strip() for s in cstr(serial_no).strip().upper().replace(',', '\n').split('\n')
if s.strip()] if s.strip()]
def make_serial_no(serial_no, sle): def make_serial_no(serial_no, sle):
@@ -290,14 +290,14 @@ def make_serial_no(serial_no, sle):
sr.warehouse = sle.warehouse sr.warehouse = sle.warehouse
sr.status = "Available" sr.status = "Available"
sr.save() sr.save()
frappe.msgprint(_("Serial No created") + ": " + sr.name) frappe.msgprint(_("Serial No {0} created").format(sr.name))
return sr.name return sr.name
def update_serial_nos_after_submit(controller, parentfield): def update_serial_nos_after_submit(controller, parentfield):
stock_ledger_entries = frappe.db.sql("""select voucher_detail_no, serial_no stock_ledger_entries = frappe.db.sql("""select voucher_detail_no, serial_no
from `tabStock Ledger Entry` where voucher_type=%s and voucher_no=%s""", from `tabStock Ledger Entry` where voucher_type=%s and voucher_no=%s""",
(controller.doctype, controller.name), as_dict=True) (controller.doctype, controller.name), as_dict=True)
if not stock_ledger_entries: return if not stock_ledger_entries: return
for d in controller.get(parentfield): for d in controller.get(parentfield):

View File

@@ -7,7 +7,7 @@ import frappe.defaults
from frappe.utils import cstr, cint, flt, comma_or, nowdate from frappe.utils import cstr, cint, flt, comma_or, nowdate
from frappe import msgprint, _ from frappe import _
from erpnext.stock.utils import get_incoming_rate from erpnext.stock.utils import get_incoming_rate
from erpnext.stock.stock_ledger import get_previous_sle from erpnext.stock.stock_ledger import get_previous_sle
from erpnext.controllers.queries import get_match_cond from erpnext.controllers.queries import get_match_cond
@@ -67,15 +67,13 @@ class StockEntry(StockController):
valid_purposes = ["Material Issue", "Material Receipt", "Material Transfer", valid_purposes = ["Material Issue", "Material Receipt", "Material Transfer",
"Manufacture/Repack", "Subcontract", "Sales Return", "Purchase Return"] "Manufacture/Repack", "Subcontract", "Sales Return", "Purchase Return"]
if self.purpose not in valid_purposes: if self.purpose not in valid_purposes:
msgprint(_("Purpose must be one of ") + comma_or(valid_purposes), frappe.throw(_("Purpose must be one of {0}").format(comma_or(valid_purposes)))
raise_exception=True)
def validate_item(self): def validate_item(self):
stock_items = self.get_stock_items() stock_items = self.get_stock_items()
for item in self.get("mtn_details"): for item in self.get("mtn_details"):
if item.item_code not in stock_items: if item.item_code not in stock_items:
msgprint(_("""Only Stock Items are allowed for Stock Entry"""), frappe.throw(_("""Only Stock Items are allowed for Stock Entry"""))
raise_exception=True)
def validate_warehouse(self, pro_obj): def validate_warehouse(self, pro_obj):
"""perform various (sometimes conditional) validations on warehouse""" """perform various (sometimes conditional) validations on warehouse"""
@@ -100,15 +98,13 @@ class StockEntry(StockController):
d.t_warehouse = self.to_warehouse d.t_warehouse = self.to_warehouse
if not (d.s_warehouse or d.t_warehouse): if not (d.s_warehouse or d.t_warehouse):
msgprint(_("Atleast one warehouse is mandatory"), raise_exception=1) frappe.throw(_("Atleast one warehouse is mandatory"))
if self.purpose in source_mandatory and not d.s_warehouse: if self.purpose in source_mandatory and not d.s_warehouse:
msgprint(_("Row # ") + "%s: " % cint(d.idx) frappe.throw(_("Source warehouse is mandatory for row {0}").format(d.idx))
+ _("Source Warehouse") + _(" is mandatory"), raise_exception=1)
if self.purpose in target_mandatory and not d.t_warehouse: if self.purpose in target_mandatory and not d.t_warehouse:
msgprint(_("Row # ") + "%s: " % cint(d.idx) frappe.throw(_("Target warehouse is mandatory for row {0}").format(d.idx))
+ _("Target Warehouse") + _(" is mandatory"), raise_exception=1)
if self.purpose == "Manufacture/Repack": if self.purpose == "Manufacture/Repack":
if validate_for_manufacture_repack: if validate_for_manufacture_repack:
@@ -116,23 +112,18 @@ class StockEntry(StockController):
d.s_warehouse = None d.s_warehouse = None
if not d.t_warehouse: if not d.t_warehouse:
msgprint(_("Row # ") + "%s: " % cint(d.idx) frappe.throw(_("Target warehouse is mandatory for row {0}").format(d.idx))
+ _("Target Warehouse") + _(" is mandatory"), raise_exception=1)
elif pro_obj and cstr(d.t_warehouse) != pro_obj.fg_warehouse: elif pro_obj and cstr(d.t_warehouse) != pro_obj.fg_warehouse:
msgprint(_("Row # ") + "%s: " % cint(d.idx) frappe.throw(_("Target warehouse in row {0} must be same as Production Order").format(d.idx))
+ _("Target Warehouse") + _(" should be same as that in ")
+ _("Production Order"), raise_exception=1)
else: else:
d.t_warehouse = None d.t_warehouse = None
if not d.s_warehouse: if not d.s_warehouse:
msgprint(_("Row # ") + "%s: " % cint(d.idx) frappe.throw(_("Source warehouse is mandatory for row {0}").format(d.idx))
+ _("Source Warehouse") + _(" is mandatory"), raise_exception=1)
if cstr(d.s_warehouse) == cstr(d.t_warehouse): if cstr(d.s_warehouse) == cstr(d.t_warehouse):
msgprint(_("Source and Target Warehouse cannot be same"), frappe.throw(_("Source and target warehouse cannot be same for row {0}").format(d.idx))
raise_exception=1)
def validate_production_order(self, pro_obj=None): def validate_production_order(self, pro_obj=None):
if not pro_obj: if not pro_obj:
@@ -228,17 +219,13 @@ class StockEntry(StockController):
if d.bom_no and not frappe.db.sql("""select name from `tabBOM` if d.bom_no and not frappe.db.sql("""select name from `tabBOM`
where item = %s and name = %s and docstatus = 1 and is_active = 1""", where item = %s and name = %s and docstatus = 1 and is_active = 1""",
(d.item_code, d.bom_no)): (d.item_code, d.bom_no)):
msgprint(_("Item") + " %s: " % cstr(d.item_code) frappe.throw(_("BOM {0} is not submitted or inactive BOM for Item {1}").format(d.bom_no, d.item_code))
+ _("does not belong to BOM: ") + cstr(d.bom_no)
+ _(" or the BOM is cancelled or inactive"), raise_exception=1)
def validate_finished_goods(self): def validate_finished_goods(self):
"""validation: finished good quantity should be same as manufacturing quantity""" """validation: finished good quantity should be same as manufacturing quantity"""
import json
for d in self.get('mtn_details'): for d in self.get('mtn_details'):
if d.bom_no and flt(d.transfer_qty) != flt(self.fg_completed_qty): if d.bom_no and flt(d.transfer_qty) != flt(self.fg_completed_qty):
msgprint(_("Row #") + " %s: " % d.idx frappe.throw(_("Quantity in row {0} must be same as manufactured quantity").format(d.idx))
+ _("Quantity should be equal to Manufacturing Quantity. To fetch items again, click on 'Get Items' button or update the Quantity manually."), raise_exception=1)
def validate_return_reference_doc(self): def validate_return_reference_doc(self):
"""validate item with reference doc""" """validate item with reference doc"""
@@ -247,14 +234,12 @@ class StockEntry(StockController):
if ref.doc: if ref.doc:
# validate docstatus # validate docstatus
if ref.doc.docstatus != 1: if ref.doc.docstatus != 1:
frappe.msgprint(_(ref.doc.doctype) + ' "' + ref.doc.name + '": ' frappe.throw(_("{0} {1} must be submitted").format(ref.doc.doctype, ref.doc.name),
+ _("Status should be Submitted"), raise_exception=frappe.InvalidStatusError) frappe.InvalidStatusError)
# update stock check # update stock check
if ref.doc.doctype == "Sales Invoice" and cint(ref.doc.update_stock) != 1: if ref.doc.doctype == "Sales Invoice" and cint(ref.doc.update_stock) != 1:
frappe.msgprint(_(ref.doc.doctype) + ' "' + ref.doc.name + '": ' frappe.throw(_("'Update Stock' for Sales Invoice {0} must be set").format(ref.doc.name), NotUpdateStockError)
+ _("Update Stock should be checked."),
raise_exception=NotUpdateStockError)
# posting date check # posting date check
ref_posting_datetime = "%s %s" % (cstr(ref.doc.posting_date), ref_posting_datetime = "%s %s" % (cstr(ref.doc.posting_date),
@@ -263,9 +248,7 @@ class StockEntry(StockController):
cstr(self.posting_time)) cstr(self.posting_time))
if this_posting_datetime < ref_posting_datetime: if this_posting_datetime < ref_posting_datetime:
from frappe.utils.dateutils import datetime_in_user_format from frappe.utils.dateutils import datetime_in_user_format
frappe.msgprint(_("Posting Date Time cannot be before") frappe.throw(_("Posting timestamp must be after {0}").format(datetime_in_user_format(ref_posting_datetime)))
+ ": " + datetime_in_user_format(ref_posting_datetime),
raise_exception=True)
stock_items = get_stock_items_for_return(ref.doc, ref.parentfields) stock_items = get_stock_items_for_return(ref.doc, ref.parentfields)
already_returned_item_qty = self.get_already_returned_item_qty(ref.fieldname) already_returned_item_qty = self.get_already_returned_item_qty(ref.fieldname)
@@ -273,9 +256,8 @@ class StockEntry(StockController):
for item in self.get("mtn_details"): for item in self.get("mtn_details"):
# validate if item exists in the ref doc and that it is a stock item # validate if item exists in the ref doc and that it is a stock item
if item.item_code not in stock_items: if item.item_code not in stock_items:
msgprint(_("Item") + ': "' + item.item_code + _("\" does not exist in ") + frappe.throw(_("Item {0} does not exist in {1} {2}").format(item.item_code, ref.doc.doctype, ref.doc.name),
ref.doc.doctype + ": " + ref.doc.name, frappe.DoesNotExistError)
raise_exception=frappe.DoesNotExistError)
# validate quantity <= ref item's qty - qty already returned # validate quantity <= ref item's qty - qty already returned
ref_item = ref.doc.getone({"item_code": item.item_code}) ref_item = ref.doc.getone({"item_code": item.item_code})
@@ -328,12 +310,10 @@ class StockEntry(StockController):
def update_production_order(self): def update_production_order(self):
def _validate_production_order(pro_doc): def _validate_production_order(pro_doc):
if flt(pro_doc.docstatus) != 1: if flt(pro_doc.docstatus) != 1:
frappe.throw(_("Production Order must be submitted") + ": " + frappe.throw(_("Production Order {0} must be submitted").format(self.production_order))
self.production_order)
if pro_doc.status == 'Stopped': if pro_doc.status == 'Stopped':
msgprint(_("Transaction not allowed against stopped Production Order") + ": " + frappe.throw(_("Transaction not allowed against stopped Production Order {0}").format(self.production_order))
self.production_order)
if self.production_order: if self.production_order:
pro_doc = frappe.get_doc("Production Order", self.production_order) pro_doc = frappe.get_doc("Production Order", self.production_order)
@@ -372,7 +352,7 @@ class StockEntry(StockController):
where name = %s and (ifnull(end_of_life,'')='' or end_of_life > now())""", where name = %s and (ifnull(end_of_life,'')='' or end_of_life > now())""",
(arg.get('item_code')), as_dict = 1) (arg.get('item_code')), as_dict = 1)
if not item: if not item:
msgprint("Item is not active", raise_exception=1) frappe.throw(_("Item {0} is not active or end of life has been reached").format(arg.get("item_code")))
ret = { ret = {
'uom' : item and item[0]['stock_uom'] or '', 'uom' : item and item[0]['stock_uom'] or '',
@@ -398,8 +378,7 @@ class StockEntry(StockController):
uom = frappe.db.sql("""select conversion_factor from `tabUOM Conversion Detail` uom = frappe.db.sql("""select conversion_factor from `tabUOM Conversion Detail`
where parent = %s and uom = %s""", (arg['item_code'], arg['uom']), as_dict = 1) where parent = %s and uom = %s""", (arg['item_code'], arg['uom']), as_dict = 1)
if not uom or not flt(uom[0].conversion_factor): if not uom or not flt(uom[0].conversion_factor):
msgprint("There is no Conversion Factor for UOM '%s' in Item '%s'" % (arg['uom'], frappe.msgprint(_("UOM coversion factor required for UOM {0} in Item {1}").format(arg["uom"], arg["item_code"]))
arg['item_code']))
ret = {'uom' : ''} ret = {'uom' : ''}
else: else:
ret = { ret = {
@@ -531,12 +510,10 @@ class StockEntry(StockController):
# show some message # show some message
if not len(item_dict): if not len(item_dict):
frappe.msgprint(_("""All items have already been transferred \ frappe.msgprint(_("""All items have already been transferred for this Production Order."""))
for this Production Order."""))
elif only_pending_fetched: elif only_pending_fetched:
frappe.msgprint(_("""Only quantities pending to be transferred \ frappe.msgprint(_("Pending Items {0} updated").format(only_pending_fetched))
were fetched for the following items:\n""" + "\n".join(only_pending_fetched)))
return item_dict return item_dict
@@ -589,10 +566,8 @@ class StockEntry(StockController):
{"name": item.material_request_item, "parent": item.material_request}, {"name": item.material_request_item, "parent": item.material_request},
["item_code", "warehouse", "idx"], as_dict=True) ["item_code", "warehouse", "idx"], as_dict=True)
if mreq_item.item_code != item.item_code or mreq_item.warehouse != item.t_warehouse: if mreq_item.item_code != item.item_code or mreq_item.warehouse != item.t_warehouse:
msgprint(_("Row #") + (" %d: " % item.idx) + _("does not match") frappe.throw(_("Item or Warehouse for row {0} does not match Material Request").format(item.idx),
+ " " + _("Row #") + (" %d %s " % (mreq_item.idx, _("of"))) frappe.MappingMismatchError)
+ _("Material Request") + (" - %s" % item.material_request),
raise_exception=frappe.MappingMismatchError)
@frappe.whitelist() @frappe.whitelist()
def get_party_details(ref_dt, ref_dn): def get_party_details(ref_dt, ref_dn):

View File

@@ -3,7 +3,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe import msgprint from frappe import _
from frappe.utils import cint, flt, cstr, now from frappe.utils import cint, flt, cstr, now
from erpnext.stock.utils import get_valuation_method from erpnext.stock.utils import get_valuation_method
import json import json
@@ -309,17 +309,11 @@ def get_fifo_values(qty_after_transaction, sle, stock_queue):
def _raise_exceptions(args, verbose=1): def _raise_exceptions(args, verbose=1):
deficiency = min(e["diff"] for e in _exceptions) deficiency = min(e["diff"] for e in _exceptions)
msg = """Negative stock error: msg = _("Negative Stock Error ({6}) for Item {0} in Warehouse {1} on {2} {3} in {4} {5}").format(args["item_code"],
Cannot complete this transaction because stock will start args.get("warehouse"), _exceptions[0]["posting_date"], _exceptions[0]["posting_time"],
becoming negative (%s) for Item <b>%s</b> in Warehouse _(_exceptions[0]["voucher_type"]), _exceptions[0]["voucher_no"], deficiency)
<b>%s</b> on <b>%s %s</b> in Transaction %s %s.
Total Quantity Deficiency: <b>%s</b>""" % \
(_exceptions[0]["diff"], args.get("item_code"), args.get("warehouse"),
_exceptions[0]["posting_date"], _exceptions[0]["posting_time"],
_exceptions[0]["voucher_type"], _exceptions[0]["voucher_no"],
abs(deficiency))
if verbose: if verbose:
msgprint(msg, raise_exception=NegativeStockError) frappe.throw(msg, NegativeStockError)
else: else:
raise NegativeStockError, msg raise NegativeStockError, msg

View File

@@ -2,7 +2,7 @@
# License: GNU General Public License v3. See license.txt # License: GNU General Public License v3. See license.txt
import frappe import frappe
from frappe import msgprint, _ from frappe import _
import json import json
from frappe.utils import flt, cstr, nowdate, add_days, cint from frappe.utils import flt, cstr, nowdate, add_days, cint
from frappe.defaults import get_global_default from frappe.defaults import get_global_default
@@ -58,8 +58,7 @@ def update_bin(args):
bin.update_stock(args) bin.update_stock(args)
return bin return bin
else: else:
msgprint("[Stock Update] Ignored %s since it is not a stock item" frappe.msgprint(_("Item {0} ignored since it is not a stock item").format(args.get("item_code")))
% args.get("item_code"))
def get_incoming_rate(args): def get_incoming_rate(args):
"""Get Incoming Rate based on valuation method""" """Get Incoming Rate based on valuation method"""
@@ -134,22 +133,20 @@ def get_valid_serial_nos(sr_nos, qty=0, item_code=''):
if val: if val:
val = val.strip() val = val.strip()
if val in valid_serial_nos: if val in valid_serial_nos:
msgprint("You have entered duplicate serial no: '%s'" % val, raise_exception=1) frappe.throw(_("Serial number {0} entered more than once").format(val))
else: else:
valid_serial_nos.append(val) valid_serial_nos.append(val)
if qty and len(valid_serial_nos) != abs(qty): if qty and len(valid_serial_nos) != abs(qty):
msgprint("Please enter serial nos for " frappe.throw(_("{0} valid serial nos for Item {1}").format(abs(qty), item_code))
+ cstr(abs(qty)) + " quantity against item code: " + item_code,
raise_exception=1)
return valid_serial_nos return valid_serial_nos
def validate_warehouse_company(warehouse, company): def validate_warehouse_company(warehouse, company):
warehouse_company = frappe.db.get_value("Warehouse", warehouse, "company") warehouse_company = frappe.db.get_value("Warehouse", warehouse, "company")
if warehouse_company and warehouse_company != company: if warehouse_company and warehouse_company != company:
frappe.msgprint(_("Warehouse does not belong to company.") + " (" + \ frappe.throw(_("Warehouse {0} does not belong to company {1}").format(warehouse, company),
warehouse + ", " + company +")", raise_exception=InvalidWarehouseCompany) InvalidWarehouseCompany)
def get_sales_bom_buying_amount(item_code, warehouse, voucher_type, voucher_no, voucher_detail_no, def get_sales_bom_buying_amount(item_code, warehouse, voucher_type, voucher_no, voucher_detail_no,
stock_ledger_entries, item_sales_bom): stock_ledger_entries, item_sales_bom):