Merge branch 'develop'

This commit is contained in:
Nabin Hait 2015-11-16 16:42:56 +05:30
commit a975fea3ac
38 changed files with 1043 additions and 920 deletions

View File

@ -1,2 +1,2 @@
from __future__ import unicode_literals from __future__ import unicode_literals
__version__ = '6.8.4' __version__ = '6.9.0'

View File

@ -21,11 +21,13 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
if(doc.docstatus == 1 && !in_list(["Stopped", "Closed", "Delivered"], doc.status)) { if(doc.docstatus == 1 && !in_list(["Stopped", "Closed", "Delivered"], doc.status)) {
if(flt(doc.per_billed, 2) < 100 || doc.per_received < 100) { if (this.frm.has_perm("submit")) {
cur_frm.add_custom_button(__('Stop'), this.stop_purchase_order); if(flt(doc.per_billed, 2) < 100 || doc.per_received < 100) {
} cur_frm.add_custom_button(__('Stop'), this.stop_purchase_order);
}
cur_frm.add_custom_button(__('Close'), this.close_purchase_order); cur_frm.add_custom_button(__('Close'), this.close_purchase_order);
}
if(doc.delivered_by_supplier && doc.status!="Delivered"){ if(doc.delivered_by_supplier && doc.status!="Delivered"){
cur_frm.add_custom_button(__('Mark as Delivered'), this.delivered_by_supplier); cur_frm.add_custom_button(__('Mark as Delivered'), this.delivered_by_supplier);
@ -35,7 +37,7 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
cur_frm.add_custom_button(__('Payment'), cur_frm.cscript.make_bank_entry); cur_frm.add_custom_button(__('Payment'), cur_frm.cscript.make_bank_entry);
} }
if(flt(doc.per_received, 2) < 100) { if(flt(doc.per_received, 2) < 100 && this.frm.doc.__onload.has_stock_item) {
cur_frm.add_custom_button(__('Receive'), this.make_purchase_receipt).addClass("btn-primary"); cur_frm.add_custom_button(__('Receive'), this.make_purchase_receipt).addClass("btn-primary");
if(doc.is_subcontracted==="Yes") { if(doc.is_subcontracted==="Yes") {
@ -53,7 +55,9 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
} }
if(doc.docstatus == 1 && in_list(["Stopped", "Closed", "Delivered"], doc.status)) { if(doc.docstatus == 1 && in_list(["Stopped", "Closed", "Delivered"], doc.status)) {
cur_frm.add_custom_button(__('Re-open'), this.unstop_purchase_order); if (this.frm.has_perm("submit")) {
cur_frm.add_custom_button(__('Re-open'), this.unstop_purchase_order);
}
} }
}, },

View File

@ -33,6 +33,9 @@ class PurchaseOrder(BuyingController):
'overflow_type': 'order' 'overflow_type': 'order'
}] }]
def onload(self):
self.set_onload("has_stock_item", len(self.get_stock_items()) > 0)
def validate(self): def validate(self):
super(PurchaseOrder, self).validate() super(PurchaseOrder, self).validate()

View File

@ -398,17 +398,18 @@ class calculate_taxes_and_totals(object):
return return
self.doc.round_floats_in(self.doc, ["grand_total", "total_advance", "write_off_amount"]) self.doc.round_floats_in(self.doc, ["grand_total", "total_advance", "write_off_amount"])
total_amount_to_pay = flt(self.doc.grand_total - self.doc.total_advance - self.doc.write_off_amount, if self.doc.party_account_currency == self.doc.currency:
self.doc.precision("grand_total")) total_amount_to_pay = flt(self.doc.grand_total - self.doc.total_advance
- flt(self.doc.write_off_amount), self.doc.precision("grand_total"))
else:
total_amount_to_pay = flt(self.doc.base_grand_total - self.doc.total_advance
- flt(self.doc.base_write_off_amount), self.doc.precision("grand_total"))
if self.doc.doctype == "Sales Invoice": if self.doc.doctype == "Sales Invoice":
self.doc.round_floats_in(self.doc, ["paid_amount"]) self.doc.round_floats_in(self.doc, ["paid_amount"])
outstanding_amount = flt(total_amount_to_pay - self.doc.paid_amount, self.doc.precision("outstanding_amount")) paid_amount = self.doc.paid_amount \
if self.doc.party_account_currency == self.doc.currency else self.doc.base_paid_amount
self.doc.outstanding_amount = flt(total_amount_to_pay - flt(paid_amount),
self.doc.precision("outstanding_amount"))
elif self.doc.doctype == "Purchase Invoice": elif self.doc.doctype == "Purchase Invoice":
outstanding_amount = flt(total_amount_to_pay, self.doc.precision("outstanding_amount")) self.doc.outstanding_amount = flt(total_amount_to_pay, self.doc.precision("outstanding_amount"))
if self.doc.party_account_currency == self.doc.currency:
self.doc.outstanding_amount = outstanding_amount
else:
self.doc.outstanding_amount = flt(outstanding_amount * self.doc.conversion_rate,
self.doc.precision("outstanding_amount"))

View File

@ -29,7 +29,7 @@ blogs.
""" """
app_icon = "icon-th" app_icon = "icon-th"
app_color = "#e74c3c" app_color = "#e74c3c"
app_version = "6.8.4" app_version = "6.9.0"
app_email = "info@erpnext.com" app_email = "info@erpnext.com"
app_license = "GNU General Public License (v3)" app_license = "GNU General Public License (v3)"
source_link = "https://github.com/frappe/erpnext" source_link = "https://github.com/frappe/erpnext"
@ -96,7 +96,8 @@ has_website_permission = {
"Sales Order": "erpnext.controllers.website_list_for_contact.has_website_permission", "Sales Order": "erpnext.controllers.website_list_for_contact.has_website_permission",
"Sales Invoice": "erpnext.controllers.website_list_for_contact.has_website_permission", "Sales Invoice": "erpnext.controllers.website_list_for_contact.has_website_permission",
"Delivery Note": "erpnext.controllers.website_list_for_contact.has_website_permission", "Delivery Note": "erpnext.controllers.website_list_for_contact.has_website_permission",
"Issue": "erpnext.support.doctype.issue.issue.has_website_permission" "Issue": "erpnext.support.doctype.issue.issue.has_website_permission",
"Address": "erpnext.utilities.doctype.address.address.has_website_permission"
} }
permission_query_conditions = { permission_query_conditions = {

View File

@ -19,7 +19,7 @@
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Series", "label": "Series",
"no_copy": 1, "no_copy": 1,
"options": "EXP", "options": "EXP",
"permlevel": 0, "permlevel": 0,
@ -44,7 +44,7 @@
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 1, "in_filter": 1,
"in_list_view": 0, "in_list_view": 0,
"label": "Approval Status", "label": "Approval Status",
"no_copy": 1, "no_copy": 1,
"oldfieldname": "approval_status", "oldfieldname": "approval_status",
"oldfieldtype": "Select", "oldfieldtype": "Select",
@ -536,7 +536,7 @@
"is_submittable": 1, "is_submittable": 1,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-10-02 07:38:50.191920", "modified": "2015-11-14 12:11:13.213073",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "HR", "module": "HR",
"name": "Expense Claim", "name": "Expense Claim",
@ -564,7 +564,7 @@
}, },
{ {
"amend": 0, "amend": 0,
"apply_user_permissions": 0, "apply_user_permissions": 1,
"cancel": 0, "cancel": 0,
"create": 1, "create": 1,
"delete": 0, "delete": 0,
@ -580,6 +580,7 @@
"set_user_permissions": 0, "set_user_permissions": 0,
"share": 1, "share": 1,
"submit": 0, "submit": 0,
"user_permission_doctypes": "[\"Employee\"]",
"write": 1 "write": 1
}, },
{ {

View File

@ -1,48 +1,62 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt // License: GNU General Public License v3. See license.txt
cur_frm.cscript.onload = function(doc, dt, dn) {
if(!doc.posting_date) set_multiple(dt,dn,{posting_date:get_today()});
}
cur_frm.add_fetch('employee','employee_name','employee_name'); cur_frm.add_fetch('employee','employee_name','employee_name');
cur_frm.cscript.employee = function(doc, dt, dn) { frappe.ui.form.on("Leave Allocation", {
calculate_total_leaves_allocated(doc, dt, dn); onload: function(frm) {
} if(!frm.doc.from_date) frm.set_value("from_date", get_today());
cur_frm.cscript.leave_type = function(doc, dt, dn) { frm.set_query("employee", function() {
calculate_total_leaves_allocated(doc, dt, dn); return {
} query: "erpnext.controllers.queries.employee_query"
}
cur_frm.cscript.fiscal_year = function(doc, dt, dn) { })
calculate_total_leaves_allocated(doc, dt, dn); },
}
employee: function(frm) {
cur_frm.cscript.carry_forward = function(doc, dt, dn) { frm.trigger("calculate_total_leaves_allocated");
calculate_total_leaves_allocated(doc, dt, dn); },
}
leave_type: function(frm) {
cur_frm.cscript.carry_forwarded_leaves = function(doc, dt, dn) { frm.trigger("calculate_total_leaves_allocated");
set_multiple(dt,dn,{total_leaves_allocated : flt(doc.carry_forwarded_leaves)+flt(doc.new_leaves_allocated)}); },
}
carry_forward: function(frm) {
cur_frm.cscript.new_leaves_allocated = function(doc, dt, dn) { frm.trigger("calculate_total_leaves_allocated");
set_multiple(dt,dn,{total_leaves_allocated : flt(doc.carry_forwarded_leaves)+flt(doc.new_leaves_allocated)}); },
}
carry_forwarded_leaves: function(frm) {
calculate_total_leaves_allocated = function(doc, dt, dn) { frm.set_value("total_leaves_allocated",
if(cint(doc.carry_forward) == 1 && doc.leave_type && doc.fiscal_year && doc.employee){ flt(frm.doc.carry_forwarded_leaves) + flt(frm.doc.new_leaves_allocated));
return get_server_fields('get_carry_forwarded_leaves','','', doc, dt, dn, 1); },
new_leaves_allocated: function(frm) {
frm.set_value("total_leaves_allocated",
flt(frm.doc.carry_forwarded_leaves) + flt(frm.doc.new_leaves_allocated));
},
calculate_total_leaves_allocated: function(frm) {
if (cint(frm.doc.carry_forward) == 1 && frm.doc.leave_type && frm.doc.employee) {
return frappe.call({
method: "erpnext.hr.doctype.leave_allocation.leave_allocation.get_carry_forwarded_leaves",
args: {
"employee": frm.doc.employee,
"date": frm.doc.from_date,
"leave_type": frm.doc.leave_type,
"carry_forward": frm.doc.carry_forward
},
callback: function(r) {
if (!r.exc && r.message) {
frm.set_value('carry_forwarded_leaves', r.message);
frm.set_value("total_leaves_allocated",
flt(r.message) + flt(frm.doc.new_leaves_allocated));
}
}
})
} else if (cint(frm.doc.carry_forward) == 0) {
frm.set_value("carry_forwarded_leaves", 0);
frm.set_value("total_leaves_allocated", flt(frm.doc.new_leaves_allocated));
}
} }
else if(cint(doc.carry_forward) == 0){ })
set_multiple(dt,dn,{carry_forwarded_leaves : 0,total_leaves_allocated : flt(doc.new_leaves_allocated)});
}
}
cur_frm.fields_dict.employee.get_query = function(doc,cdt,cdn) {
return{
query: "erpnext.controllers.queries.employee_query"
}
}

View File

@ -3,93 +3,112 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe.utils import cint, flt, date_diff from frappe.utils import flt, date_diff, formatdate
from frappe import _ from frappe import _
from frappe.model.document import Document from frappe.model.document import Document
from erpnext.hr.utils import set_employee_name from erpnext.hr.utils import set_employee_name
from erpnext.hr.doctype.leave_application.leave_application import get_approved_leaves_for_period
class OverlapError(frappe.ValidationError): pass
class BackDatedAllocationError(frappe.ValidationError): pass
class OverAllocationError(frappe.ValidationError): pass
class LessAllocationError(frappe.ValidationError): pass
class ValueMultiplierError(frappe.ValidationError): pass
class LeaveAllocation(Document): class LeaveAllocation(Document):
def validate(self): def validate(self):
self.validate_period() self.validate_period()
self.validate_new_leaves_allocated_value() self.validate_new_leaves_allocated_value()
self.check_existing_leave_allocation() self.validate_allocation_overlap()
if not self.total_leaves_allocated: self.validate_back_dated_allocation()
self.total_leaves_allocated = self.new_leaves_allocated self.set_total_leaves_allocated()
self.validate_total_leaves_allocated()
set_employee_name(self) set_employee_name(self)
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.set_total_leaves_allocated()
def on_update(self):
self.get_total_allocated_leaves() frappe.db.set(self,'carry_forwarded_leaves', flt(self.carry_forwarded_leaves))
frappe.db.set(self,'total_leaves_allocated',flt(self.total_leaves_allocated))
self.validate_against_leave_applications()
def validate_period(self): def validate_period(self):
if date_diff(self.to_date, self.from_date) <= 0: if date_diff(self.to_date, self.from_date) <= 0:
frappe.throw(_("Invalid period")) frappe.throw(_("To date cannot be before from date"))
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:
frappe.throw(_("Leaves must be allocated in multiples of 0.5")) frappe.throw(_("Leaves must be allocated in multiples of 0.5"), ValueMultiplierError)
def check_existing_leave_allocation(self): def validate_allocation_overlap(self):
"""check whether leave for same type is already allocated or not""" leave_allocation = frappe.db.sql("""
leave_allocation = frappe.db.sql("""select name from `tabLeave Allocation` select name from `tabLeave Allocation`
where employee='%s' and leave_type='%s' and to_date >= '%s' and from_date <= '%s' and docstatus=1 where employee=%s and leave_type=%s and docstatus=1
"""%(self.employee, self.leave_type, self.from_date, self.to_date)) and to_date >= %s and from_date <= %s""",
(self.employee, self.leave_type, self.from_date, self.to_date))
if leave_allocation: if leave_allocation:
frappe.msgprint(_("Leaves for type {0} already allocated for Employee {1} for period {2} - {3}").format(self.leave_type, frappe.msgprint(_("{0} already allocated for Employee {1} for period {2} - {3}")
self.employee, self.from_date, self.to_date)) .format(self.leave_type, self.employee, formatdate(self.from_date), formatdate(self.to_date)))
frappe.throw(_('Reference') + ': <a href="#Form/Leave Allocation/{0}">{0}</a>'.format(leave_allocation[0][0]))
frappe.throw(_('Reference') + ': <a href="#Form/Leave Allocation/{0}">{0}</a>'
.format(leave_allocation[0][0]), OverlapError)
def validate_back_dated_allocation(self):
future_allocation = frappe.db.sql("""select name, from_date from `tabLeave Allocation`
where employee=%s and leave_type=%s and docstatus=1 and from_date > %s
and carry_forward=1""", (self.employee, self.leave_type, self.to_date), as_dict=1)
if future_allocation:
frappe.throw(_("Leave cannot be allocated before {0}, as leave balance has already been carry-forwarded in the future leave allocation record {1}")
.format(formatdate(future_allocation[0].from_date), future_allocation[0].name),
BackDatedAllocationError)
def get_leave_bal(self): def set_total_leaves_allocated(self):
return self.get_leaves_allocated() - self.get_leaves_applied() self.carry_forwarded_leaves = get_carry_forwarded_leaves(self.employee,
self.leave_type, self.from_date, self.carry_forward)
self.total_leaves_allocated = flt(self.carry_forwarded_leaves) + flt(self.new_leaves_allocated)
if not self.total_leaves_allocated:
frappe.throw(_("Total leaves allocated is mandatory"))
def get_leaves_applied(self): def validate_total_leaves_allocated(self):
leaves_applied = frappe.db.sql("""select SUM(ifnull(total_leave_days, 0)) if date_diff(self.to_date, self.from_date) <= flt(self.total_leaves_allocated):
from `tabLeave Application` where employee=%s and leave_type=%s frappe.throw(_("Total allocated leaves are more than days in the period"), OverAllocationError)
and to_date<=%s and docstatus=1""",
(self.employee, self.leave_type, self.from_date)) def validate_against_leave_applications(self):
return leaves_applied and flt(leaves_applied[0][0]) or 0 leaves_taken = get_approved_leaves_for_period(self.employee, self.leave_type,
self.from_date, self.to_date)
if flt(leaves_taken) > flt(self.total_leaves_allocated):
frappe.throw(_("Total allocated leaves {0} cannot be less than already approved leaves {1} for the period").format(self.total_leaves_allocated, leaves_taken), LessAllocationError)
def get_leaves_allocated(self): @frappe.whitelist()
leaves_allocated = frappe.db.sql("""select SUM(ifnull(total_leaves_allocated, 0)) def get_carry_forwarded_leaves(employee, leave_type, date, carry_forward=None):
from `tabLeave Allocation` where employee=%s and leave_type=%s carry_forwarded_leaves = 0
and to_date<=%s and docstatus=1 and name!=%s""",
(self.employee, self.leave_type, self.from_date, self.name)) if carry_forward:
return leaves_allocated and flt(leaves_allocated[0][0]) or 0 validate_carry_forward(leave_type)
def allow_carry_forward(self): previous_allocation = frappe.db.sql("""
"""check whether carry forward is allowed or not for this leave type""" select name, from_date, to_date, total_leaves_allocated
cf = frappe.db.sql("""select is_carry_forward from `tabLeave Type` where name = %s""", from `tabLeave Allocation`
self.leave_type) where employee=%s and leave_type=%s and docstatus=1 and to_date < %s
cf = cf and cint(cf[0][0]) or 0 order by to_date desc limit 1
if not cf: """, (employee, leave_type, date), as_dict=1)
frappe.db.set(self,'carry_forward',0) if previous_allocation:
frappe.throw(_("Cannot carry forward {0}").format(self.leave_type)) leaves_taken = get_approved_leaves_for_period(employee, leave_type,
previous_allocation[0].from_date, previous_allocation[0].to_date)
def get_carry_forwarded_leaves(self):
if self.carry_forward: carry_forwarded_leaves = flt(previous_allocation[0].total_leaves_allocated) - flt(leaves_taken)
self.allow_carry_forward()
return carry_forwarded_leaves
prev_bal = 0
if cint(self.carry_forward) == 1: def validate_carry_forward(leave_type):
prev_bal = self.get_leave_bal() if not frappe.db.get_value("Leave Type", leave_type, "is_carry_forward"):
frappe.throw(_("Leave Type {0} cannot be carry-forwarded").format(leave_type))
ret = {
'carry_forwarded_leaves': prev_bal,
'total_leaves_allocated': flt(prev_bal) + flt(self.new_leaves_allocated)
}
return ret
def get_total_allocated_leaves(self):
leave_det = self.get_carry_forwarded_leaves()
self.validate_total_leaves_allocated(leave_det)
frappe.db.set(self,'carry_forwarded_leaves',flt(leave_det['carry_forwarded_leaves']))
frappe.db.set(self,'total_leaves_allocated',flt(leave_det['total_leaves_allocated']))
def validate_total_leaves_allocated(self, leave_det):
if date_diff(self.to_date, self.from_date) <= leave_det['total_leaves_allocated']:
frappe.throw(_("Total allocated leaves are more than period"))

View File

@ -13,7 +13,7 @@ class TestLeaveAllocation(unittest.TestCase):
"employee": employee.name, "employee": employee.name,
"employee_name": employee.employee_name, "employee_name": employee.employee_name,
"leave_type": "_Test Leave Type", "leave_type": "_Test Leave Type",
"from_date": getdate("2015-10-1"), "from_date": getdate("2015-10-01"),
"to_date": getdate("2015-10-31"), "to_date": getdate("2015-10-31"),
"new_leaves_allocated": 5, "new_leaves_allocated": 5,
"docstatus": 1 "docstatus": 1
@ -24,7 +24,7 @@ class TestLeaveAllocation(unittest.TestCase):
"employee": employee.name, "employee": employee.name,
"employee_name": employee.employee_name, "employee_name": employee.employee_name,
"leave_type": "_Test Leave Type", "leave_type": "_Test Leave Type",
"from_date": getdate("2015-09-1"), "from_date": getdate("2015-09-01"),
"to_date": getdate("2015-11-30"), "to_date": getdate("2015-11-30"),
"new_leaves_allocated": 5 "new_leaves_allocated": 5
} }

View File

@ -66,14 +66,18 @@ frappe.ui.form.on("Leave Application", {
}, },
get_leave_balance: function(frm) { get_leave_balance: function(frm) {
if(frm.doc.docstatus==0 && frm.doc.employee && frm.doc.leave_type && frm.doc.from_date && frm.doc.to_date) { if(frm.doc.docstatus==0 && frm.doc.employee && frm.doc.leave_type && frm.doc.from_date) {
return frm.call({ return frappe.call({
method: "get_leave_balance", method: "erpnext.hr.doctype.leave_application.leave_application.get_leave_balance_on",
args: { args: {
employee: frm.doc.employee, employee: frm.doc.employee,
from_date: frm.doc.from_date, date: frm.doc.from_date,
to_date: frm.doc.to_date,
leave_type: frm.doc.leave_type leave_type: frm.doc.leave_type
},
callback: function(r) {
if (!r.exc && r.message) {
frm.set_value('leave_balance', r.message);
}
} }
}); });
} }
@ -83,14 +87,20 @@ frappe.ui.form.on("Leave Application", {
if(frm.doc.from_date && frm.doc.to_date) { if(frm.doc.from_date && frm.doc.to_date) {
if (cint(frm.doc.half_day)==1) { if (cint(frm.doc.half_day)==1) {
frm.set_value("total_leave_days", 0.5); frm.set_value("total_leave_days", 0.5);
} else { } else if (frm.doc.employee && frm.doc.leave_type){
// server call is done to include holidays in leave days calculations // server call is done to include holidays in leave days calculations
return frappe.call({ return frappe.call({
method: 'erpnext.hr.doctype.leave_application.leave_application.get_total_leave_days', method: 'erpnext.hr.doctype.leave_application.leave_application.get_number_of_leave_days',
args: { leave_app: frm.doc }, args: {
callback: function(response) { "employee": frm.doc.employee,
if (response && response.message) { "leave_type": frm.doc.leave_type,
frm.set_value('total_leave_days', response.message.total_leave_days); "from_date": frm.doc.from_date,
"to_date": frm.doc.to_date,
"half_day": frm.doc.half_day
},
callback: function(r) {
if (r && r.message) {
frm.set_value('total_leave_days', r.message);
frm.trigger("get_leave_balance"); frm.trigger("get_leave_balance");
} }
} }

View File

@ -418,28 +418,6 @@
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "fiscal_year",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 1,
"in_list_view": 0,
"label": "Fiscal Year",
"no_copy": 0,
"options": "Fiscal Year",
"permlevel": 0,
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{ {
"allow_on_submit": 1, "allow_on_submit": 1,
"bold": 0, "bold": 0,
@ -559,7 +537,7 @@
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"max_attachments": 3, "max_attachments": 3,
"modified": "2015-10-28 16:14:25.640730", "modified": "2015-11-15 19:32:32.258397",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "HR", "module": "HR",
"name": "Leave Application", "name": "Leave Application",
@ -711,7 +689,7 @@
], ],
"read_only": 0, "read_only": 0,
"read_only_onload": 0, "read_only_onload": 0,
"search_fields": "employee,employee_name,leave_type,from_date,to_date,total_leave_days,fiscal_year", "search_fields": "employee,employee_name,leave_type,from_date,to_date,total_leave_days",
"sort_field": "modified", "sort_field": "modified",
"sort_order": "DESC", "sort_order": "DESC",
"title_field": "employee_name" "title_field": "employee_name"

View File

@ -2,13 +2,13 @@
# License: GNU General Public License v3. See license.txt # License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe, json import frappe
from frappe import _ from frappe import _
from frappe.utils import cint, cstr, date_diff, flt, formatdate, getdate, get_link_to_form, \ from frappe.utils import cint, cstr, date_diff, flt, formatdate, getdate, get_link_to_form, \
comma_or, get_fullname comma_or, get_fullname
from frappe import msgprint
from erpnext.hr.utils import set_employee_name from erpnext.hr.utils import set_employee_name
from erpnext.hr.doctype.leave_block_list.leave_block_list import get_applicable_block_dates
class LeaveDayBlockedError(frappe.ValidationError): pass class LeaveDayBlockedError(frappe.ValidationError): pass
class OverlapError(frappe.ValidationError): pass class OverlapError(frappe.ValidationError): pass
@ -18,8 +18,7 @@ class LeaveApproverIdentityError(frappe.ValidationError): pass
from frappe.model.document import Document from frappe.model.document import Document
class LeaveApplication(Document): class LeaveApplication(Document):
def get_feed(self): def get_feed(self):
return _("{0}: From {0} of type {1}").format(self.status, return _("{0}: From {0} of type {1}").format(self.status, self.employee_name, self.leave_type)
self.employee_name, self.leave_type)
def validate(self): def validate(self):
if not getattr(self, "__islocal", None) and frappe.db.exists(self.doctype, self.name): if not getattr(self, "__islocal", None) and frappe.db.exists(self.doctype, self.name):
@ -29,7 +28,7 @@ class LeaveApplication(Document):
set_employee_name(self) set_employee_name(self)
self.validate_to_date() self.validate_dates()
self.validate_balance_leaves() self.validate_balance_leaves()
self.validate_leave_overlap() self.validate_leave_overlap()
self.validate_max_days() self.validate_max_days()
@ -50,6 +49,8 @@ class LeaveApplication(Document):
def on_submit(self): def on_submit(self):
if self.status != "Approved": if self.status != "Approved":
frappe.throw(_("Only Leave Applications with status 'Approved' can be submitted")) frappe.throw(_("Only Leave Applications with status 'Approved' can be submitted"))
self.validate_back_dated_application()
# notify leave applier about approval # notify leave applier about approval
self.notify_employee(self.status) self.notify_employee(self.status)
@ -57,10 +58,41 @@ class LeaveApplication(Document):
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 validate_dates(self):
if self.from_date and self.to_date and (getdate(self.to_date) < getdate(self.from_date)):
frappe.throw(_("To date cannot be before from date"))
self.validate_dates_acorss_allocation()
self.validate_back_dated_application()
def validate_dates_acorss_allocation(self):
def _get_leave_alloction_record(date):
allocation = frappe.db.sql("""select name from `tabLeave Allocation`
where employee=%s and leave_type=%s and docstatus=1
and %s between from_date and to_date""", (self.employee, self.leave_type, date))
return allocation and allocation[0][0]
allocation_based_on_from_date = _get_leave_alloction_record(self.from_date)
allocation_based_on_to_date = _get_leave_alloction_record(self.to_date)
if not (allocation_based_on_from_date or allocation_based_on_to_date):
frappe.throw(_("Application period cannot be outside leave allocation period"))
elif allocation_based_on_from_date != allocation_based_on_to_date:
frappe.throw(_("Application period cannot be across two alocation records"))
def validate_back_dated_application(self):
future_allocation = frappe.db.sql("""select name, from_date from `tabLeave Allocation`
where employee=%s and leave_type=%s and docstatus=1 and from_date > %s
and carry_forward=1""", (self.employee, self.leave_type, self.to_date), as_dict=1)
if future_allocation:
frappe.throw(_("Leave cannot be applied/cancelled before {0}, as leave balance has already been carry-forwarded in the future leave allocation record {1}")
.format(formatdate(future_allocation[0].from_date), future_allocation[0].name))
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
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)
@ -70,60 +102,39 @@ class LeaveApplication(Document):
frappe.msgprint(formatdate(d.block_date) + ": " + d.reason) frappe.msgprint(formatdate(d.block_date) + ": " + d.reason)
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
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 and self.status == "Approved":
if self.status == "Approved": frappe.throw(_("You are not authorized to approve leaves on Block Dates"), LeaveDayBlockedError)
frappe.throw(_("Cannot approve leave as you are not authorized to approve leaves on Block Dates"),
LeaveDayBlockedError)
def get_holidays(self):
return get_holidays(self)
def get_total_leave_days(self):
return get_total_leave_days(self)
def validate_to_date(self):
if self.from_date and self.to_date and \
(getdate(self.to_date) < getdate(self.from_date)):
frappe.throw(_("To date cannot be before from date"))
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 = get_number_of_leave_days(self.employee, self.leave_type,
self.from_date, self.to_date, self.half_day)
if self.total_leave_days == 0: if self.total_leave_days == 0:
frappe.throw(_("The day(s) on which you are applying for leave are holiday. You need not apply for leave.")) frappe.throw(_("The day(s) on which you are applying for leave are holidays. You need not apply for leave."))
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_on(self.employee, self.leave_type, self.from_date)
self.leave_type, self.from_date, self.to_date)["leave_balance"]
if self.status != "Rejected" \ if self.status != "Rejected" and self.leave_balance < self.total_leave_days:
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.
if frappe.db.get_value("Leave Type", self.leave_type, "allow_negative"): if frappe.db.get_value("Leave Type", self.leave_type, "allow_negative"):
frappe.msgprint(_("Note: There is not enough leave balance for Leave Type {0}").format(self.leave_type)) frappe.msgprint(_("Note: There is not enough leave balance for Leave Type {0}")
.format(self.leave_type))
else: else:
frappe.throw(_("There is not enough leave balance for Leave Type {0}").format(self.leave_type)) 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 and docstatus < 2 and status in ("Open", "Approved")
employee = %(employee)s and to_date >= %(from_date)s and from_date <= %(to_date)s
and docstatus < 2
and status in ("Open", "Approved")
and to_date >= %(from_date)s
and from_date <= %(to_date)s
and name != %(name)s""", { and name != %(name)s""", {
"employee": self.employee, "employee": self.employee,
"from_date": self.from_date, "from_date": self.from_date,
@ -131,9 +142,12 @@ class LeaveApplication(Document):
"name": self.name "name": self.name
}, as_dict = 1): }, as_dict = 1):
frappe.msgprint(_("Employee {0} has already applied for {1} between {2} and {3}").format(self.employee, frappe.msgprint(_("Employee {0} has already applied for {1} between {2} and {3}")
cstr(d['leave_type']), formatdate(d['from_date']), formatdate(d['to_date']))) .format(self.employee, cstr(d['leave_type']),
frappe.throw('<a href="#Form/Leave Application/{0}">{0}</a>'.format(d["name"]), OverlapError) formatdate(d['from_date']), formatdate(d['to_date'])))
frappe.throw("""Exising Application: <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")
@ -145,7 +159,8 @@ class LeaveApplication(Document):
leave_approvers = [l.leave_approver for l in employee.get("leave_approvers")] leave_approvers = [l.leave_approver for l in employee.get("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:
frappe.throw(_("Leave approver must be one of {0}").format(comma_or(leave_approvers)), InvalidLeaveApproverError) frappe.throw(_("Leave approver must be one of {0}")
.format(comma_or(leave_approvers)), 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):
@ -153,8 +168,8 @@ class LeaveApplication(Document):
.format(get_fullname(self.leave_approver), self.leave_approver), InvalidLeaveApproverError) .format(get_fullname(self.leave_approver), self.leave_approver), 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"), frappe.throw(_("Only the selected Leave Approver can submit this Leave Application"),
raise_exception=LeaveApproverIdentityError) 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)
@ -213,60 +228,89 @@ def get_approvers(doctype, txt, searchfield, start, page_len, filters):
approver.parent = %s approver.parent = %s
and user.name like %s and user.name like %s
and approver.leave_approver=user.name""", (filters.get("employee"), "%" + txt + "%")) and approver.leave_approver=user.name""", (filters.get("employee"), "%" + txt + "%"))
@frappe.whitelist()
def get_number_of_leave_days(employee, leave_type, from_date, to_date, half_day=None):
if half_day:
return 0.5
number_of_days = date_diff(to_date, from_date) + 1
if not frappe.db.get_value("Leave Type", leave_type, "include_holiday"):
number_of_days = flt(number_of_days) - flt(get_holidays(employee, from_date, to_date))
return number_of_days
def get_holidays(leave_app): @frappe.whitelist()
def get_leave_balance_on(employee, leave_type, date, allocation_records=None):
if allocation_records == None:
allocation_records = get_leave_allocation_records(date, employee).get(employee, frappe._dict())
allocation = allocation_records.get(leave_type, frappe._dict())
leaves_taken = get_approved_leaves_for_period(employee, leave_type, allocation.from_date, date)
return flt(allocation.total_leaves_allocated) - flt(leaves_taken)
def get_approved_leaves_for_period(employee, leave_type, from_date, to_date):
leave_applications = frappe.db.sql("""
select employee, leave_type, from_date, to_date, total_leave_days
from `tabLeave Application`
where employee=%(employee)s and leave_type=%(leave_type)s
and status="Approved" and docstatus=1
and (from_date between %(from_date)s and %(to_date)s
or to_date between %(from_date)s and %(to_date)s
or (from_date < %(from_date)s and to_date > %(to_date)s))
""", {
"from_date": from_date,
"to_date": to_date,
"employee": employee,
"leave_type": leave_type
}, as_dict=1)
leave_days = 0
for leave_app in leave_applications:
if leave_app.from_date >= getdate(from_date) and leave_app.to_date <= getdate(to_date):
leave_days += leave_app.total_leave_days
else:
if leave_app.from_date < getdate(from_date):
leave_app.from_date = from_date
if leave_app.to_date > getdate(to_date):
leave_app.to_date = to_date
leave_days += get_number_of_leave_days(employee, leave_type,
leave_app.from_date, leave_app.to_date)
return leave_days
def get_leave_allocation_records(date, employee=None):
conditions = (" and employee='%s'" % employee) if employee else ""
leave_allocation_records = frappe.db.sql("""
select employee, leave_type, total_leaves_allocated, from_date
from `tabLeave Allocation`
where %s between from_date and to_date and docstatus=1 {0}""".format(conditions), (date), as_dict=1)
allocated_leaves = frappe._dict()
for d in leave_allocation_records:
allocated_leaves.setdefault(d.employee, frappe._dict()).setdefault(d.leave_type, frappe._dict({
"from_date": d.from_date,
"total_leaves_allocated": d.total_leaves_allocated
}))
return allocated_leaves
def get_holidays(employee, from_date, to_date):
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""", (leave_app.employee, leave_app.from_date, and h1.holiday_date between %s and %s""", (employee, from_date, to_date))[0][0]
leave_app.to_date))[0][0]
# below line is needed. If an employee hasn't been assigned with any holiday list then above will return 0 rows.
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""", (from_date, to_date))[0][0]
(leave_app.from_date, leave_app.to_date, leave_app.fiscal_year))[0][0]
return tot_hol return tot_hol
@frappe.whitelist()
def get_total_leave_days(leave_app):
# Parse Leave Application if neccessary
if isinstance(leave_app, str) or isinstance(leave_app, unicode):
leave_app = frappe.get_doc(json.loads(leave_app))
"""Calculates total leave days based on input and holidays"""
ret = {'total_leave_days' : 0.5}
if not leave_app.half_day:
tot_days = date_diff(leave_app.to_date, leave_app.from_date) + 1
if frappe.db.get_value("Leave Type", leave_app.leave_type, "include_holiday"):
ret = {
'total_leave_days' : flt(tot_days)
}
else:
holidays = leave_app.get_holidays()
ret = {
'total_leave_days' : flt(tot_days)-flt(holidays)
}
return ret
@frappe.whitelist()
def get_leave_balance(employee, leave_type, from_date, to_date):
leave_all = frappe.db.sql("""select total_leaves_allocated
from `tabLeave Allocation` where employee = %s and leave_type = %s
and from_date<=%s and to_date>=%s and docstatus = 1""", (employee,
leave_type, from_date, to_date))
leave_all = leave_all and flt(leave_all[0][0]) or 0
leave_app = frappe.db.sql("""select SUM(total_leave_days)
from `tabLeave Application`
where employee = %s and leave_type = %s and to_date>=%s and from_date<=%s
and status="Approved" and docstatus = 1""", (employee, leave_type, from_date, to_date))
leave_app = leave_app and flt(leave_app[0][0]) or 0
ret = {'leave_balance': leave_all - leave_app}
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

View File

@ -41,7 +41,7 @@ class ProcessPayroll(Document):
def get_joining_releiving_condition(self): def get_joining_releiving_condition(self):
m = self.get_month_details(self.fiscal_year, self.month) m = 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'
@ -54,24 +54,6 @@ class ProcessPayroll(Document):
if not self.get(f): if not self.get(f):
frappe.throw(_("Please set {0}").format(f)) frappe.throw(_("Please set {0}").format(f))
def get_month_details(self, year, month):
ysd = frappe.db.get_value("Fiscal Year", year, "year_start_date")
if ysd:
from dateutil.relativedelta import relativedelta
import calendar, datetime
diff_mnt = cint(month)-cint(ysd.month)
if diff_mnt<0:
diff_mnt = 12-int(ysd.month)+cint(month)
msd = ysd + relativedelta(months=diff_mnt) # month start date
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
return {
'year': msd.year,
'month_start_date': msd,
'month_end_date': med,
'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
@ -205,3 +187,22 @@ class ProcessPayroll(Document):
]) ])
return journal_entry.as_dict() return journal_entry.as_dict()
def get_month_details(year, month):
ysd = frappe.db.get_value("Fiscal Year", year, "year_start_date")
if ysd:
from dateutil.relativedelta import relativedelta
import calendar, datetime
diff_mnt = cint(month)-cint(ysd.month)
if diff_mnt<0:
diff_mnt = 12-int(ysd.month)+cint(month)
msd = ysd + relativedelta(months=diff_mnt) # month start date
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
return frappe._dict({
'year': msd.year,
'month_start_date': msd,
'month_end_date': med,
'month_days': month_days
})

View File

@ -10,6 +10,7 @@ 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.hr.utils import set_employee_name from erpnext.hr.utils import set_employee_name
from erpnext.hr.doctype.process_payroll.process_payroll import get_month_details
from erpnext.utilities.transaction_base import TransactionBase from erpnext.utilities.transaction_base import TransactionBase
@ -25,11 +26,17 @@ class SalarySlip(TransactionBase):
self.pull_sal_struct(struct) self.pull_sal_struct(struct)
def check_sal_struct(self): def check_sal_struct(self):
m = get_month_details(self.fiscal_year, self.month)
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'
and from_date <= %s and (to_date is null or to_date >= %s)""",
(self.employee, m.month_start_date, m.month_end_date))
if not struct: if not struct:
msgprint(_("Please create Salary Structure for employee {0}").format(self.employee)) msgprint(_("No active Salary Structure found for employee {0} and the month")
.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):
@ -49,7 +56,7 @@ class SalarySlip(TransactionBase):
if not self.month: if not self.month:
self.month = "%02d" % getdate(nowdate()).month self.month = "%02d" % getdate(nowdate()).month
m = frappe.get_doc('Process Payroll').get_month_details(self.fiscal_year, self.month) m = 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",

View File

@ -10,8 +10,20 @@ from erpnext.hr.doctype.salary_structure.salary_structure import make_salary_sli
class TestSalarySlip(unittest.TestCase): class TestSalarySlip(unittest.TestCase):
def setUp(self): def setUp(self):
frappe.db.sql("""delete from `tabLeave Application`""") for dt in ["Leave Application", "Leave Allocation", "Salary Slip"]:
frappe.db.sql("""delete from `tabSalary Slip`""") frappe.db.sql("delete from `tab%s`" % dt)
allocation = frappe.get_doc({
"doctype": "Leave Allocation",
"employee": "_T-Employee-0001",
"leave_type": "_Test Leave Type LWP",
"from_date": "2013-01-01",
"to_date": "2015-12-31",
"new_leaves_allocated": 5
})
allocation.insert()
allocation.submit()
frappe.db.set_value("Holiday List", "_Test Holiday List", "is_default", 1) frappe.db.set_value("Holiday List", "_Test Holiday List", "is_default", 1)

View File

@ -7,12 +7,14 @@ frappe.query_reports["Employee Leave Balance"] = {
"fieldname":"from_date", "fieldname":"from_date",
"label": __("From Date"), "label": __("From Date"),
"fieldtype": "Date", "fieldtype": "Date",
"reqd": 1,
"default": frappe.datetime.year_start() "default": frappe.datetime.year_start()
}, },
{ {
"fieldname":"to_date", "fieldname":"to_date",
"label": __("To Date"), "label": __("To Date"),
"fieldtype": "Date", "fieldtype": "Date",
"reqd": 1,
"default": frappe.datetime.year_end() "default": frappe.datetime.year_end()
}, },
{ {
@ -20,6 +22,7 @@ frappe.query_reports["Employee Leave Balance"] = {
"label": __("Company"), "label": __("Company"),
"fieldtype": "Link", "fieldtype": "Link",
"options": "Company", "options": "Company",
"reqd": 1,
"default": frappe.defaults.get_user_default("company") "default": frappe.defaults.get_user_default("company")
} }
] ]

View File

@ -4,67 +4,54 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe import _ from frappe import _
from frappe.desk.reportview import execute as runreport from erpnext.hr.doctype.leave_application.leave_application \
import get_leave_allocation_records, get_leave_balance_on, get_approved_leaves_for_period
def execute(filters=None): def execute(filters=None):
if not filters: filters = {} leave_types = frappe.db.sql_list("select name from `tabLeave Type` order by name asc")
employee_filters = {
"status": "Active"
}
if filters.get("company"): columns = get_columns(leave_types)
filters["company"] = filters.company data = get_data(filters, leave_types)
employees = runreport(doctype="Employee", fields=["name", "employee_name", "department"], return columns, data
filters=employee_filters, limit_page_length=None)
def get_columns(leave_types):
if not employees:
frappe.throw(_("No employee found!"))
leave_types = frappe.db.sql_list("select name from `tabLeave Type`")
employee_names = [d.name for d in employees]
allocations = frappe.db.sql("""select employee, leave_type, sum(new_leaves_allocated) as leaves_allocated
from `tabLeave Allocation`
where docstatus=1 and employee in (%s) and from_date >= '%s' and to_date <= '%s'""" %
(','.join(['%s']*len(employee_names)), filters.get("from_date"),
filters.get("to_date")), employee_names, as_dict=True)
applications = frappe.db.sql("""select employee, leave_type,
SUM(total_leave_days) as leaves
from `tabLeave Application`
where status="Approved" and docstatus = 1 and employee in (%s)
and from_date >= '%s' and to_date <= '%s'
group by employee, leave_type""" %
(','.join(['%s']*len(employee_names)), filters.get("from_date"),
filters.get("to_date")), employee_names, as_dict=True)
columns = [ columns = [
_("Employee") + ":Link/Employee:150", _("Employee Name") + "::200", _("Department") +"::150" _("Employee") + ":Link/Employee:150",
_("Employee Name") + "::200",
_("Department") +"::150"
] ]
for leave_type in leave_types: for leave_type in leave_types:
columns.append(_(leave_type) + " " + _("Opening") + ":Float") columns.append(_(leave_type) + " " + _("Taken") + ":Float:160")
columns.append(_(leave_type) + " " + _("Taken") + ":Float") columns.append(_(leave_type) + " " + _("Balance") + ":Float:160")
columns.append(_(leave_type) + " " + _("Balance") + ":Float")
return columns
def get_data(filters, leave_types):
data = {} allocation_records_based_on_to_date = get_leave_allocation_records(filters.to_date)
for d in allocations:
data.setdefault((d.employee,d.leave_type), frappe._dict()).allocation = d.leaves_allocated
for d in applications: active_employees = frappe.get_all("Employee",
data.setdefault((d.employee, d.leave_type), frappe._dict()).leaves = d.leaves filters = { "status": "Active", "company": filters.company},
fields = ["name", "employee_name", "department"])
result = []
for employee in employees: data = []
for employee in active_employees:
row = [employee.name, employee.employee_name, employee.department] row = [employee.name, employee.employee_name, employee.department]
result.append(row)
for leave_type in leave_types:
tmp = data.get((employee.name, leave_type), frappe._dict())
row.append(tmp.allocation or 0)
row.append(tmp.leaves or 0)
row.append((tmp.allocation or 0) - (tmp.leaves or 0))
return columns, result for leave_type in leave_types:
# leaves taken
leaves_taken = get_approved_leaves_for_period(employee.name, leave_type,
filters.from_date, filters.to_date)
# closing balance
closing = get_leave_balance_on(employee.name, leave_type, filters.to_date,
allocation_records_based_on_to_date.get(employee.name, frappe._dict()))
row += [leaves_taken, closing]
data.append(row)
return data

View File

@ -231,4 +231,5 @@ erpnext.patches.v6_4.fix_expense_included_in_valuation
execute:frappe.delete_doc_if_exists("Report", "Item-wise Last Purchase Rate") execute:frappe.delete_doc_if_exists("Report", "Item-wise Last Purchase Rate")
erpnext.patches.v6_6.fix_website_image erpnext.patches.v6_6.fix_website_image
erpnext.patches.v6_6.remove_fiscal_year_from_leave_allocation erpnext.patches.v6_6.remove_fiscal_year_from_leave_allocation
execute:frappe.delete_doc_if_exists("DocType", "Stock UOM Replace Utility") execute:frappe.delete_doc_if_exists("DocType", "Stock UOM Replace Utility")
erpnext.patches.v6_8.make_webform_standard

View File

View File

@ -0,0 +1,9 @@
import frappe
def execute():
frappe.reload_doctype("Web Form")
frappe.delete_doc("Web Form", "Issues")
frappe.delete_doc("Web Form", "Addresses")
from erpnext.setup.install import add_web_forms
add_web_forms()

View File

@ -4,11 +4,13 @@
frappe.provide("erpnext.projects"); frappe.provide("erpnext.projects");
frappe.ui.form.on("Time Log", "onload", function(frm) { frappe.ui.form.on("Time Log", "onload", function(frm) {
if (frm.doc.for_manufacturing) { if (frm.doc.__islocal) {
frappe.ui.form.trigger("Time Log", "production_order"); if (frm.doc.for_manufacturing) {
} frappe.ui.form.trigger("Time Log", "production_order");
if (frm.doc.from_time && frm.doc.to_time) { }
frappe.ui.form.trigger("Time Log", "to_time"); if (frm.doc.from_time && frm.doc.to_time) {
frappe.ui.form.trigger("Time Log", "to_time");
}
} }
}); });

View File

@ -500,9 +500,13 @@ erpnext.taxes_and_totals = erpnext.stock.StockController.extend({
if(this.frm.doc.is_return || this.frm.doc.docstatus > 0) return; if(this.frm.doc.is_return || this.frm.doc.docstatus > 0) return;
frappe.model.round_floats_in(this.frm.doc, ["grand_total", "total_advance", "write_off_amount"]); frappe.model.round_floats_in(this.frm.doc, ["grand_total", "total_advance", "write_off_amount"]);
if(this.frm.doc.party_account_currency == this.frm.doc.currency) {
var total_amount_to_pay = flt((this.frm.doc.grand_total - this.frm.doc.total_advance var total_amount_to_pay = flt((this.frm.doc.grand_total - this.frm.doc.total_advance
- this.frm.doc.write_off_amount), precision("grand_total")); - this.frm.doc.write_off_amount), precision("grand_total"));
else {
var total_amount_to_pay = flt((this.frm.doc.base_grand_total - this.frm.doc.total_advance
- this.frm.doc.base_write_off_amount), precision("base_grand_total"));
}
if(this.frm.doc.doctype == "Sales Invoice") { if(this.frm.doc.doctype == "Sales Invoice") {
frappe.model.round_floats_in(this.frm.doc, ["paid_amount"]); frappe.model.round_floats_in(this.frm.doc, ["paid_amount"]);
@ -518,18 +522,15 @@ erpnext.taxes_and_totals = erpnext.stock.StockController.extend({
this.frm.refresh_field("paid_amount"); this.frm.refresh_field("paid_amount");
this.frm.refresh_field("base_paid_amount"); this.frm.refresh_field("base_paid_amount");
var outstanding_amount = flt(total_amount_to_pay - this.frm.doc.paid_amount, var paid_amount = (this.frm.doc.party_account_currency == this.frm.doc.currency) ?
this.frm.doc.paid_amount : this.frm.doc.base_paid_amount;
var outstanding_amount = flt(total_amount_to_pay - flt(paid_amount),
precision("outstanding_amount")); precision("outstanding_amount"));
} else if(this.frm.doc.doctype == "Purchase Invoice") { } else if(this.frm.doc.doctype == "Purchase Invoice") {
var outstanding_amount = flt(total_amount_to_pay, precision("outstanding_amount")); var outstanding_amount = flt(total_amount_to_pay, precision("outstanding_amount"));
} }
this.frm.set_value("outstanding_amount", outstanding_amount);
if(this.frm.doc.party_account_currency == this.frm.doc.currency) {
this.frm.set_value("outstanding_amount", outstanding_amount);
} else {
this.frm.set_value("outstanding_amount",
flt(outstanding_amount * this.frm.doc.conversion_rate, precision("outstanding_amount")));
}
} }
}) })

View File

@ -5,404 +5,407 @@ frappe.pages['setup-wizard'].on_page_load = function(wrapper) {
frappe.set_route("desk"); frappe.set_route("desk");
return; return;
} }
} };
$.extend(erpnext.wiz, { function load_erpnext_slides() {
region: { $.extend(erpnext.wiz, {
title: __("Region"), region: {
icon: "icon-flag", title: __("Region"),
help: __("Select your Country, Time Zone and Currency"), icon: "icon-flag",
fields: [ help: __("Select your Country, Time Zone and Currency"),
{ fieldname: "country", label: __("Country"), reqd:1, fields: [
fieldtype: "Select" }, { fieldname: "country", label: __("Country"), reqd:1,
{ fieldname: "timezone", label: __("Time Zone"), reqd:1, fieldtype: "Select" },
fieldtype: "Select" }, { fieldname: "timezone", label: __("Time Zone"), reqd:1,
{ fieldname: "currency", label: __("Currency"), reqd:1, fieldtype: "Select" },
fieldtype: "Select" }, { fieldname: "currency", label: __("Currency"), reqd:1,
], fieldtype: "Select" },
],
onload: function(slide) { onload: function(slide) {
frappe.call({ frappe.call({
method:"frappe.geo.country_info.get_country_timezone_info", method:"frappe.geo.country_info.get_country_timezone_info",
callback: function(data) { callback: function(data) {
erpnext.wiz.region.data = data.message; erpnext.wiz.region.data = data.message;
erpnext.wiz.region.setup_fields(slide); erpnext.wiz.region.setup_fields(slide);
erpnext.wiz.region.bind_events(slide); erpnext.wiz.region.bind_events(slide);
} }
}); });
}, },
css_class: "single-column", css_class: "single-column",
setup_fields: function(slide) { setup_fields: function(slide) {
var data = erpnext.wiz.region.data;
slide.get_input("country").empty()
.add_options([""].concat(keys(data.country_info).sort()));
slide.get_input("currency").empty()
.add_options(frappe.utils.unique([""].concat($.map(data.country_info,
function(opts, country) { return opts.currency; }))).sort());
slide.get_input("timezone").empty()
.add_options([""].concat(data.all_timezones));
if (data.default_country) {
slide.set_input("country", data.default_country);
}
},
bind_events: function(slide) {
slide.get_input("country").on("change", function() {
var country = slide.get_input("country").val();
var $timezone = slide.get_input("timezone");
var data = erpnext.wiz.region.data; var data = erpnext.wiz.region.data;
$timezone.empty(); slide.get_input("country").empty()
.add_options([""].concat(keys(data.country_info).sort()));
// add country specific timezones first slide.get_input("currency").empty()
if(country) { .add_options(frappe.utils.unique([""].concat($.map(data.country_info,
var timezone_list = data.country_info[country].timezones || []; function(opts, country) { return opts.currency; }))).sort());
$timezone.add_options(timezone_list.sort());
slide.get_field("currency").set_input(data.country_info[country].currency); slide.get_input("timezone").empty()
slide.get_field("currency").$input.trigger("change"); .add_options([""].concat(data.all_timezones));
if (data.default_country) {
slide.set_input("country", data.default_country);
} }
},
// add all timezones at the end, so that user has the option to change it to any timezone bind_events: function(slide) {
$timezone.add_options([""].concat(data.all_timezones)); slide.get_input("country").on("change", function() {
var country = slide.get_input("country").val();
var $timezone = slide.get_input("timezone");
var data = erpnext.wiz.region.data;
slide.get_field("timezone").set_input($timezone.val()); $timezone.empty();
// temporarily set date format // add country specific timezones first
frappe.boot.sysdefaults.date_format = (data.country_info[country].date_format if(country) {
|| "dd-mm-yyyy"); var timezone_list = data.country_info[country].timezones || [];
}); $timezone.add_options(timezone_list.sort());
slide.get_field("currency").set_input(data.country_info[country].currency);
slide.get_input("currency").on("change", function() { slide.get_field("currency").$input.trigger("change");
var currency = slide.get_input("currency").val();
if (!currency) return;
frappe.model.with_doc("Currency", currency, function() {
frappe.provide("locals.:Currency." + currency);
var currency_doc = frappe.model.get_doc("Currency", currency);
var number_format = currency_doc.number_format;
if (number_format==="#.###") {
number_format = "#.###,##";
} else if (number_format==="#,###") {
number_format = "#,###.##"
} }
frappe.boot.sysdefaults.number_format = number_format; // add all timezones at the end, so that user has the option to change it to any timezone
locals[":Currency"][currency] = $.extend({}, currency_doc); $timezone.add_options([""].concat(data.all_timezones));
slide.get_field("timezone").set_input($timezone.val());
// temporarily set date format
frappe.boot.sysdefaults.date_format = (data.country_info[country].date_format
|| "dd-mm-yyyy");
}); });
});
}
},
user: { slide.get_input("currency").on("change", function() {
title: __("The First User: You"), var currency = slide.get_input("currency").val();
icon: "icon-user", if (!currency) return;
fields: [ frappe.model.with_doc("Currency", currency, function() {
{"fieldname": "first_name", "label": __("First Name"), "fieldtype": "Data", frappe.provide("locals.:Currency." + currency);
reqd:1}, var currency_doc = frappe.model.get_doc("Currency", currency);
{"fieldname": "last_name", "label": __("Last Name"), "fieldtype": "Data"}, var number_format = currency_doc.number_format;
{"fieldname": "email", "label": __("Email Address"), "fieldtype": "Data", if (number_format==="#.###") {
reqd:1, "description": __("You will use it to Login"), "options":"Email"}, number_format = "#.###,##";
{"fieldname": "password", "label": __("Password"), "fieldtype": "Password", } else if (number_format==="#,###") {
reqd:1}, number_format = "#,###.##"
{fieldtype:"Attach Image", fieldname:"attach_user", }
label: __("Attach Your Picture")},
],
help: __('The first user will become the System Manager (you can change this later).'),
onload: function(slide) {
if(user!=="Administrator") {
slide.form.fields_dict.password.$wrapper.toggle(false);
slide.form.fields_dict.email.$wrapper.toggle(false);
slide.form.fields_dict.first_name.set_input(frappe.boot.user.first_name);
slide.form.fields_dict.last_name.set_input(frappe.boot.user.last_name);
var user_image = frappe.get_cookie("user_image"); frappe.boot.sysdefaults.number_format = number_format;
if(user_image) { locals[":Currency"][currency] = $.extend({}, currency_doc);
var $attach_user = slide.form.fields_dict.attach_user.$wrapper; });
$attach_user.find(".missing-image").toggle(false); });
$attach_user.find("img").attr("src", decodeURIComponent(user_image)).toggle(true);
}
delete slide.form.fields_dict.email;
delete slide.form.fields_dict.password;
} }
}, },
css_class: "single-column"
},
org: { user: {
title: __("The Organization"), title: __("The First User: You"),
icon: "icon-building", icon: "icon-user",
fields: [ fields: [
{fieldname:'company_name', label: __('Company Name'), fieldtype:'Data', reqd:1, {"fieldname": "first_name", "label": __("First Name"), "fieldtype": "Data",
placeholder: __('e.g. "My Company LLC"')}, reqd:1},
{fieldname:'company_abbr', label: __('Company Abbreviation'), fieldtype:'Data', {"fieldname": "last_name", "label": __("Last Name"), "fieldtype": "Data"},
description: __('Max 5 characters'), placeholder: __('e.g. "MC"'), reqd:1}, {"fieldname": "email", "label": __("Email Address"), "fieldtype": "Data",
{fieldname:'company_tagline', label: __('What does it do?'), fieldtype:'Data', reqd:1, "description": __("You will use it to Login"), "options":"Email"},
placeholder:__('e.g. "Build tools for builders"'), reqd:1}, {"fieldname": "password", "label": __("Password"), "fieldtype": "Password",
{fieldname:'bank_account', label: __('Bank Account'), fieldtype:'Data', reqd:1},
placeholder: __('e.g. "XYZ National Bank"'), reqd:1 }, {fieldtype:"Attach Image", fieldname:"attach_user",
{fieldname:'chart_of_accounts', label: __('Chart of Accounts'), label: __("Attach Your Picture")},
options: "", fieldtype: 'Select'}, ],
help: __('The first user will become the System Manager (you can change this later).'),
onload: function(slide) {
if(user!=="Administrator") {
slide.form.fields_dict.password.$wrapper.toggle(false);
slide.form.fields_dict.email.$wrapper.toggle(false);
slide.form.fields_dict.first_name.set_input(frappe.boot.user.first_name);
slide.form.fields_dict.last_name.set_input(frappe.boot.user.last_name);
// TODO remove this var user_image = frappe.get_cookie("user_image");
{fieldtype: "Section Break"}, if(user_image) {
{fieldname:'fy_start_date', label:__('Financial Year Start Date'), fieldtype:'Date', var $attach_user = slide.form.fields_dict.attach_user.$wrapper;
description: __('Your financial year begins on'), reqd:1}, $attach_user.find(".missing-image").toggle(false);
{fieldname:'fy_end_date', label:__('Financial Year End Date'), fieldtype:'Date', $attach_user.find("img").attr("src", decodeURIComponent(user_image)).toggle(true);
description: __('Your financial year ends on'), reqd:1}, }
],
help: __('The name of your company for which you are setting up this system.'),
onload: function(slide) { delete slide.form.fields_dict.email;
erpnext.wiz.org.load_chart_of_accounts(slide); delete slide.form.fields_dict.password;
erpnext.wiz.org.bind_events(slide); }
erpnext.wiz.org.set_fy_dates(slide); },
css_class: "single-column"
}, },
css_class: "single-column", org: {
title: __("The Organization"),
icon: "icon-building",
fields: [
{fieldname:'company_name', label: __('Company Name'), fieldtype:'Data', reqd:1,
placeholder: __('e.g. "My Company LLC"')},
{fieldname:'company_abbr', label: __('Company Abbreviation'), fieldtype:'Data',
description: __('Max 5 characters'), placeholder: __('e.g. "MC"'), reqd:1},
{fieldname:'company_tagline', label: __('What does it do?'), fieldtype:'Data',
placeholder:__('e.g. "Build tools for builders"'), reqd:1},
{fieldname:'bank_account', label: __('Bank Account'), fieldtype:'Data',
placeholder: __('e.g. "XYZ National Bank"'), reqd:1 },
{fieldname:'chart_of_accounts', label: __('Chart of Accounts'),
options: "", fieldtype: 'Select'},
set_fy_dates: function(slide) { // TODO remove this
var country = slide.wiz.get_values().country; {fieldtype: "Section Break"},
{fieldname:'fy_start_date', label:__('Financial Year Start Date'), fieldtype:'Date',
description: __('Your financial year begins on'), reqd:1},
{fieldname:'fy_end_date', label:__('Financial Year End Date'), fieldtype:'Date',
description: __('Your financial year ends on'), reqd:1},
],
help: __('The name of your company for which you are setting up this system.'),
if(country) { onload: function(slide) {
var fy = erpnext.wiz.fiscal_years[country]; erpnext.wiz.org.load_chart_of_accounts(slide);
var current_year = moment(new Date()).year(); erpnext.wiz.org.bind_events(slide);
var next_year = current_year + 1; erpnext.wiz.org.set_fy_dates(slide);
if(!fy) { },
fy = ["01-01", "12-31"];
next_year = current_year; css_class: "single-column",
set_fy_dates: function(slide) {
var country = slide.wiz.get_values().country;
if(country) {
var fy = erpnext.wiz.fiscal_years[country];
var current_year = moment(new Date()).year();
var next_year = current_year + 1;
if(!fy) {
fy = ["01-01", "12-31"];
next_year = current_year;
}
slide.get_field("fy_start_date").set_input(current_year + "-" + fy[0]);
slide.get_field("fy_end_date").set_input(next_year + "-" + fy[1]);
} }
slide.get_field("fy_start_date").set_input(current_year + "-" + fy[0]); },
slide.get_field("fy_end_date").set_input(next_year + "-" + fy[1]);
}
}, load_chart_of_accounts: function(slide) {
var country = slide.wiz.get_values().country;
load_chart_of_accounts: function(slide) { if(country) {
var country = slide.wiz.get_values().country; frappe.call({
method: "erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts.get_charts_for_country",
args: {"country": country},
callback: function(r) {
if(r.message) {
slide.get_input("chart_of_accounts").empty()
.add_options(r.message);
if(country) { if (r.message.length===1) {
frappe.call({ var field = slide.get_field("chart_of_accounts");
method: "erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts.get_charts_for_country", field.set_value(r.message[0]);
args: {"country": country}, field.df.hidden = 1;
callback: function(r) { field.refresh();
if(r.message) { }
slide.get_input("chart_of_accounts").empty()
.add_options(r.message);
if (r.message.length===1) {
var field = slide.get_field("chart_of_accounts");
field.set_value(r.message[0]);
field.df.hidden = 1;
field.refresh();
} }
} }
} })
})
}
},
bind_events: function(slide) {
slide.get_input("company_name").on("change", function() {
var parts = slide.get_input("company_name").val().split(" ");
var abbr = $.map(parts, function(p) { return p ? p.substr(0,1) : null }).join("");
slide.get_field("company_abbr").set_input(abbr.slice(0, 5).toUpperCase());
}).val(frappe.boot.sysdefaults.company_name || "").trigger("change");
slide.get_input("company_abbr").on("change", function() {
if(slide.get_input("company_abbr").val().length > 5) {
msgprint("Company Abbreviation cannot have more than 5 characters");
slide.get_field("company_abbr").set_input("");
} }
});
// TODO remove this
slide.get_input("fy_start_date").on("change", function() {
var year_end_date =
frappe.datetime.add_days(frappe.datetime.add_months(
frappe.datetime.user_to_obj(slide.get_input("fy_start_date").val()), 12), -1);
slide.get_input("fy_end_date").val(frappe.datetime.obj_to_user(year_end_date));
});
}
},
branding: {
icon: "icon-bookmark",
title: __("The Brand"),
help: __('Upload your letter head and logo. (you can edit them later).'),
fields: [
{fieldtype:"Attach Image", fieldname:"attach_letterhead",
label: __("Attach Letterhead"),
description: __("Keep it web friendly 900px (w) by 100px (h)")
}, },
{fieldtype: "Column Break"},
{fieldtype:"Attach Image", fieldname:"attach_logo",
label:__("Attach Logo"),
description: __("100px by 100px")},
],
css_class: "two-column" bind_events: function(slide) {
}, slide.get_input("company_name").on("change", function() {
var parts = slide.get_input("company_name").val().split(" ");
var abbr = $.map(parts, function(p) { return p ? p.substr(0,1) : null }).join("");
slide.get_field("company_abbr").set_input(abbr.slice(0, 5).toUpperCase());
}).val(frappe.boot.sysdefaults.company_name || "").trigger("change");
users: { slide.get_input("company_abbr").on("change", function() {
icon: "icon-money", if(slide.get_input("company_abbr").val().length > 5) {
"title": __("Add Users"), msgprint("Company Abbreviation cannot have more than 5 characters");
"help": __("Add users to your organization, other than yourself"), slide.get_field("company_abbr").set_input("");
"fields": [], }
before_load: function(slide) { });
slide.fields = [];
for(var i=1; i<5; i++) { // TODO remove this
slide.fields = slide.fields.concat([ slide.get_input("fy_start_date").on("change", function() {
{fieldtype:"Section Break"}, var year_end_date =
{fieldtype:"Data", fieldname:"user_fullname_"+ i, frappe.datetime.add_days(frappe.datetime.add_months(
label:__("Full Name")}, frappe.datetime.user_to_obj(slide.get_input("fy_start_date").val()), 12), -1);
{fieldtype:"Data", fieldname:"user_email_" + i, slide.get_input("fy_end_date").val(frappe.datetime.obj_to_user(year_end_date));
label:__("Email ID"), placeholder:__("user@example.com"),
options: "Email"}, });
{fieldtype:"Column Break"},
{fieldtype: "Check", fieldname: "user_sales_" + i,
label:__("Sales"), default: 1},
{fieldtype: "Check", fieldname: "user_purchaser_" + i,
label:__("Purchaser"), default: 1},
{fieldtype: "Check", fieldname: "user_accountant_" + i,
label:__("Accountant"), default: 1},
]);
} }
}, },
css_class: "two-column"
},
taxes: { branding: {
icon: "icon-money", icon: "icon-bookmark",
"title": __("Add Taxes"), title: __("The Brand"),
"help": __("List your tax heads (e.g. VAT, Customs etc; they should have unique names) and their standard rates. This will create a standard template, which you can edit and add more later."), help: __('Upload your letter head and logo. (you can edit them later).'),
"fields": [], fields: [
before_load: function(slide) { {fieldtype:"Attach Image", fieldname:"attach_letterhead",
slide.fields = []; label: __("Attach Letterhead"),
for(var i=1; i<4; i++) { description: __("Keep it web friendly 900px (w) by 100px (h)")
slide.fields = slide.fields.concat([ },
{fieldtype:"Section Break"}, {fieldtype: "Column Break"},
{fieldtype:"Data", fieldname:"tax_"+ i, label:__("Tax") + " " + i, {fieldtype:"Attach Image", fieldname:"attach_logo",
placeholder:__("e.g. VAT") + " " + i}, label:__("Attach Logo"),
{fieldtype:"Column Break"}, description: __("100px by 100px")},
{fieldtype:"Float", fieldname:"tax_rate_" + i, label:__("Rate (%)"), placeholder:__("e.g. 5")}, ],
]);
} css_class: "two-column"
}, },
css_class: "two-column"
},
customers: { users: {
icon: "icon-group", icon: "icon-money",
"title": __("Your Customers"), "title": __("Add Users"),
"help": __("List a few of your customers. They could be organizations or individuals."), "help": __("Add users to your organization, other than yourself"),
"fields": [], "fields": [],
before_load: function(slide) { before_load: function(slide) {
slide.fields = []; slide.fields = [];
for(var i=1; i<6; i++) { for(var i=1; i<5; i++) {
slide.fields = slide.fields.concat([ slide.fields = slide.fields.concat([
{fieldtype:"Section Break"}, {fieldtype:"Section Break"},
{fieldtype:"Data", fieldname:"customer_" + i, label:__("Customer") + " " + i, {fieldtype:"Data", fieldname:"user_fullname_"+ i,
placeholder:__("Customer Name")}, label:__("Full Name")},
{fieldtype:"Column Break"}, {fieldtype:"Data", fieldname:"user_email_" + i,
{fieldtype:"Data", fieldname:"customer_contact_" + i, label:__("Email ID"), placeholder:__("user@example.com"),
label:__("Contact Name") + " " + i, placeholder:__("Contact Name")} options: "Email"},
]) {fieldtype:"Column Break"},
} {fieldtype: "Check", fieldname: "user_sales_" + i,
slide.fields[1].reqd = 1; label:__("Sales"), default: 1},
{fieldtype: "Check", fieldname: "user_purchaser_" + i,
label:__("Purchaser"), default: 1},
{fieldtype: "Check", fieldname: "user_accountant_" + i,
label:__("Accountant"), default: 1},
]);
}
},
css_class: "two-column"
}, },
css_class: "two-column"
},
suppliers: { taxes: {
icon: "icon-group", icon: "icon-money",
"title": __("Your Suppliers"), "title": __("Add Taxes"),
"help": __("List a few of your suppliers. They could be organizations or individuals."), "help": __("List your tax heads (e.g. VAT, Customs etc; they should have unique names) and their standard rates. This will create a standard template, which you can edit and add more later."),
"fields": [], "fields": [],
before_load: function(slide) { before_load: function(slide) {
slide.fields = []; slide.fields = [];
for(var i=1; i<6; i++) { for(var i=1; i<4; i++) {
slide.fields = slide.fields.concat([ slide.fields = slide.fields.concat([
{fieldtype:"Section Break"}, {fieldtype:"Section Break"},
{fieldtype:"Data", fieldname:"supplier_" + i, label:__("Supplier")+" " + i, {fieldtype:"Data", fieldname:"tax_"+ i, label:__("Tax") + " " + i,
placeholder:__("Supplier Name")}, placeholder:__("e.g. VAT") + " " + i},
{fieldtype:"Column Break"}, {fieldtype:"Column Break"},
{fieldtype:"Data", fieldname:"supplier_contact_" + i, {fieldtype:"Float", fieldname:"tax_rate_" + i, label:__("Rate (%)"), placeholder:__("e.g. 5")},
label:__("Contact Name") + " " + i, placeholder:__("Contact Name")}, ]);
]) }
} },
slide.fields[1].reqd = 1; css_class: "two-column"
}, },
css_class: "two-column"
},
items: { customers: {
icon: "icon-barcode", icon: "icon-group",
"title": __("Your Products or Services"), "title": __("Your Customers"),
"help": __("List your products or services that you buy or sell. Make sure to check the Item Group, Unit of Measure and other properties when you start."), "help": __("List a few of your customers. They could be organizations or individuals."),
"fields": [], "fields": [],
before_load: function(slide) { before_load: function(slide) {
slide.fields = []; slide.fields = [];
for(var i=1; i<6; i++) { for(var i=1; i<6; i++) {
slide.fields = slide.fields.concat([ slide.fields = slide.fields.concat([
{fieldtype:"Section Break", show_section_border: true}, {fieldtype:"Section Break"},
{fieldtype:"Data", fieldname:"item_" + i, label:__("Item") + " " + i, {fieldtype:"Data", fieldname:"customer_" + i, label:__("Customer") + " " + i,
placeholder:__("A Product or Service")}, placeholder:__("Customer Name")},
{fieldtype:"Select", label:__("Group"), fieldname:"item_group_" + i, {fieldtype:"Column Break"},
options:[__("Products"), __("Services"), {fieldtype:"Data", fieldname:"customer_contact_" + i,
__("Raw Material"), __("Consumable"), __("Sub Assemblies")], label:__("Contact Name") + " " + i, placeholder:__("Contact Name")}
"default": __("Products")}, ])
{fieldtype:"Select", fieldname:"item_uom_" + i, label:__("UOM"), }
options:[__("Unit"), __("Nos"), __("Box"), __("Pair"), __("Kg"), __("Set"), slide.fields[1].reqd = 1;
__("Hour"), __("Minute")], },
"default": __("Unit")}, css_class: "two-column"
{fieldtype: "Check", fieldname: "is_sales_item_" + i, label:__("We sell this Item"), default: 1},
{fieldtype: "Check", fieldname: "is_purchase_item_" + i, label:__("We buy this Item")},
{fieldtype:"Column Break"},
{fieldtype:"Currency", fieldname:"item_price_" + i, label:__("Rate")},
{fieldtype:"Attach Image", fieldname:"item_img_" + i, label:__("Attach Image")},
])
}
slide.fields[1].reqd = 1;
// dummy data
slide.fields.push({fieldtype: "Section Break"});
slide.fields.push({fieldtype: "Check", fieldname: "add_sample_data",
label: __("Add a few sample records"), "default": 1});
}, },
css_class: "two-column"
},
});
// Source: https://en.wikipedia.org/wiki/Fiscal_year suppliers: {
// default 1st Jan - 31st Dec icon: "icon-group",
"title": __("Your Suppliers"),
"help": __("List a few of your suppliers. They could be organizations or individuals."),
"fields": [],
before_load: function(slide) {
slide.fields = [];
for(var i=1; i<6; i++) {
slide.fields = slide.fields.concat([
{fieldtype:"Section Break"},
{fieldtype:"Data", fieldname:"supplier_" + i, label:__("Supplier")+" " + i,
placeholder:__("Supplier Name")},
{fieldtype:"Column Break"},
{fieldtype:"Data", fieldname:"supplier_contact_" + i,
label:__("Contact Name") + " " + i, placeholder:__("Contact Name")},
])
}
slide.fields[1].reqd = 1;
},
css_class: "two-column"
},
erpnext.wiz.fiscal_years = { items: {
"Afghanistan": ["12-20", "12-21"], icon: "icon-barcode",
"Australia": ["07-01", "06-30"], "title": __("Your Products or Services"),
"Bangladesh": ["07-01", "06-30"], "help": __("List your products or services that you buy or sell. Make sure to check the Item Group, Unit of Measure and other properties when you start."),
"Canada": ["04-01", "03-31"], "fields": [],
"Costa Rica": ["10-01", "09-30"], before_load: function(slide) {
"Egypt": ["07-01", "06-30"], slide.fields = [];
"Hong Kong": ["04-01", "03-31"], for(var i=1; i<6; i++) {
"India": ["04-01", "03-31"], slide.fields = slide.fields.concat([
"Iran": ["06-23", "06-22"], {fieldtype:"Section Break", show_section_border: true},
"Italy": ["07-01", "06-30"], {fieldtype:"Data", fieldname:"item_" + i, label:__("Item") + " " + i,
"Myanmar": ["04-01", "03-31"], placeholder:__("A Product or Service")},
"New Zealand": ["04-01", "03-31"], {fieldtype:"Select", label:__("Group"), fieldname:"item_group_" + i,
"Pakistan": ["07-01", "06-30"], options:[__("Products"), __("Services"),
"Singapore": ["04-01", "03-31"], __("Raw Material"), __("Consumable"), __("Sub Assemblies")],
"South Africa": ["03-01", "02-28"], "default": __("Products")},
"Thailand": ["10-01", "09-30"], {fieldtype:"Select", fieldname:"item_uom_" + i, label:__("UOM"),
"United Kingdom": ["04-01", "03-31"], options:[__("Unit"), __("Nos"), __("Box"), __("Pair"), __("Kg"), __("Set"),
} __("Hour"), __("Minute")],
"default": __("Unit")},
{fieldtype: "Check", fieldname: "is_sales_item_" + i, label:__("We sell this Item"), default: 1},
{fieldtype: "Check", fieldname: "is_purchase_item_" + i, label:__("We buy this Item")},
{fieldtype:"Column Break"},
{fieldtype:"Currency", fieldname:"item_price_" + i, label:__("Rate")},
{fieldtype:"Attach Image", fieldname:"item_img_" + i, label:__("Attach Image")},
])
}
slide.fields[1].reqd = 1;
// dummy data
slide.fields.push({fieldtype: "Section Break"});
slide.fields.push({fieldtype: "Check", fieldname: "add_sample_data",
label: __("Add a few sample records"), "default": 1});
},
css_class: "two-column"
},
});
// Source: https://en.wikipedia.org/wiki/Fiscal_year
// default 1st Jan - 31st Dec
erpnext.wiz.fiscal_years = {
"Afghanistan": ["12-20", "12-21"],
"Australia": ["07-01", "06-30"],
"Bangladesh": ["07-01", "06-30"],
"Canada": ["04-01", "03-31"],
"Costa Rica": ["10-01", "09-30"],
"Egypt": ["07-01", "06-30"],
"Hong Kong": ["04-01", "03-31"],
"India": ["04-01", "03-31"],
"Iran": ["06-23", "06-22"],
"Italy": ["07-01", "06-30"],
"Myanmar": ["04-01", "03-31"],
"New Zealand": ["04-01", "03-31"],
"Pakistan": ["07-01", "06-30"],
"Singapore": ["04-01", "03-31"],
"South Africa": ["03-01", "02-28"],
"Thailand": ["10-01", "09-30"],
"United Kingdom": ["04-01", "03-31"],
};
};
frappe.wiz.on("before_load", function() { frappe.wiz.on("before_load", function() {
load_erpnext_slides();
frappe.wiz.add_slide(erpnext.wiz.user); frappe.wiz.add_slide(erpnext.wiz.user);
frappe.wiz.add_slide(erpnext.wiz.org); frappe.wiz.add_slide(erpnext.wiz.org);
frappe.wiz.add_slide(erpnext.wiz.branding); frappe.wiz.add_slide(erpnext.wiz.branding);

View File

@ -56,13 +56,15 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
cur_frm.add_custom_button(__('Payment'), cur_frm.cscript.make_bank_entry); cur_frm.add_custom_button(__('Payment'), cur_frm.cscript.make_bank_entry);
} }
// stop if (this.frm.has_perm("submit")) {
if(flt(doc.per_delivered, 2) < 100 || flt(doc.per_billed) < 100) { // stop
cur_frm.add_custom_button(__('Stop'), this.stop_sales_order) if(flt(doc.per_delivered, 2) < 100 || flt(doc.per_billed) < 100) {
} cur_frm.add_custom_button(__('Stop'), this.stop_sales_order)
}
cur_frm.add_custom_button(__('Close'), this.close_sales_order) cur_frm.add_custom_button(__('Close'), this.close_sales_order)
}
// maintenance // maintenance
if(flt(doc.per_delivered, 2) < 100 && ["Sales", "Shopping Cart"].indexOf(doc.order_type)===-1) { if(flt(doc.per_delivered, 2) < 100 && ["Sales", "Shopping Cart"].indexOf(doc.order_type)===-1) {
@ -82,8 +84,10 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
} else { } else {
// un-stop if (this.frm.has_perm("submit")) {
cur_frm.add_custom_button(__('Re-open'), cur_frm.cscript['Unstop Sales Order']); // un-stop
cur_frm.add_custom_button(__('Re-open'), cur_frm.cscript['Unstop Sales Order']);
}
} }
} }

View File

@ -27,6 +27,7 @@ def get_random_quote():
("There is more to life than increasing its speed.", "Mahatma Gandhi"), ("There is more to life than increasing its speed.", "Mahatma Gandhi"),
("A small body of determined spirits fired by an unquenchable faith in their mission can alter the course of history.", "Mahatma Gandhi"), ("A small body of determined spirits fired by an unquenchable faith in their mission can alter the course of history.", "Mahatma Gandhi"),
("If two wrongs don't make a right, try three.", "Laurence J. Peter"), ("If two wrongs don't make a right, try three.", "Laurence J. Peter"),
("Inspiration exists, but it has to find you working.", "Pablo Picasso"),
] ]
return random.choice(quotes) return random.choice(quotes)

View File

@ -1,167 +1,168 @@
[ [
{ {
"allow_comments": 0, "allow_comments": 0,
"allow_delete": 0, "allow_delete": 0,
"allow_edit": 1, "allow_edit": 1,
"allow_multiple": 1, "allow_multiple": 1,
"breadcrumbs": null, "breadcrumbs": null,
"doc_type": "Address", "doc_type": "Address",
"docstatus": 0, "docstatus": 0,
"doctype": "Web Form", "doctype": "Web Form",
"introduction_text": null, "introduction_text": null,
"login_required": 1, "is_standard": 1,
"modified": "2015-06-01 06:53:43.699336", "login_required": 1,
"name": "addresses", "modified": "2015-06-01 06:53:43.699336",
"page_name": "addresses", "name": "addresses",
"published": 1, "page_name": "addresses",
"success_message": null, "published": 1,
"success_url": "/addresses", "success_message": null,
"title": "Addresses", "success_url": "/addresses",
"title": "Addresses",
"web_form_fields": [ "web_form_fields": [
{ {
"default": null, "default": null,
"description": "", "description": "",
"fieldname": "address_title", "fieldname": "address_title",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 0, "hidden": 0,
"label": "Address Title", "label": "Address Title",
"options": null, "options": null,
"read_only": 0, "read_only": 0,
"reqd": 0 "reqd": 0
}, },
{ {
"default": null, "default": null,
"description": null, "description": null,
"fieldname": "address_type", "fieldname": "address_type",
"fieldtype": "Select", "fieldtype": "Select",
"hidden": 0, "hidden": 0,
"label": "Address Type", "label": "Address Type",
"options": "Billing\nShipping\nOffice\nPersonal\nPlant\nPostal\nShop\nSubsidiary\nWarehouse\nOther", "options": "Billing\nShipping\nOffice\nPersonal\nPlant\nPostal\nShop\nSubsidiary\nWarehouse\nOther",
"read_only": 0, "read_only": 0,
"reqd": 1 "reqd": 1
}, },
{ {
"default": null, "default": null,
"description": null, "description": null,
"fieldname": "address_line1", "fieldname": "address_line1",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 0, "hidden": 0,
"label": "Address Line 1", "label": "Address Line 1",
"options": null, "options": null,
"read_only": 0, "read_only": 0,
"reqd": 1 "reqd": 1
}, },
{ {
"default": null, "default": null,
"description": null, "description": null,
"fieldname": "address_line2", "fieldname": "address_line2",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 0, "hidden": 0,
"label": "Address Line 2", "label": "Address Line 2",
"options": null, "options": null,
"read_only": 0, "read_only": 0,
"reqd": 0 "reqd": 0
}, },
{ {
"default": null, "default": null,
"description": null, "description": null,
"fieldname": "city", "fieldname": "city",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 0, "hidden": 0,
"label": "City/Town", "label": "City/Town",
"options": null, "options": null,
"read_only": 0, "read_only": 0,
"reqd": 1 "reqd": 1
}, },
{ {
"default": null, "default": null,
"description": null, "description": null,
"fieldname": "state", "fieldname": "state",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 0, "hidden": 0,
"label": "State", "label": "State",
"options": null, "options": null,
"read_only": 0, "read_only": 0,
"reqd": 0 "reqd": 0
}, },
{ {
"default": null, "default": null,
"description": null, "description": null,
"fieldname": "pincode", "fieldname": "pincode",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 0, "hidden": 0,
"label": "Pincode", "label": "Pincode",
"options": null, "options": null,
"read_only": 0, "read_only": 0,
"reqd": 0 "reqd": 0
}, },
{ {
"default": null, "default": null,
"description": null, "description": null,
"fieldname": "country", "fieldname": "country",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 0, "hidden": 0,
"label": "Country", "label": "Country",
"options": "Country", "options": "Country",
"read_only": 0, "read_only": 0,
"reqd": 1 "reqd": 1
}, },
{ {
"default": null, "default": null,
"description": null, "description": null,
"fieldname": null, "fieldname": null,
"fieldtype": "Column Break", "fieldtype": "Column Break",
"hidden": null, "hidden": null,
"label": null, "label": null,
"options": null, "options": null,
"read_only": null, "read_only": null,
"reqd": null "reqd": null
}, },
{ {
"default": null, "default": null,
"description": null, "description": null,
"fieldname": "email_id", "fieldname": "email_id",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 0, "hidden": 0,
"label": "Email Id", "label": "Email Id",
"options": null, "options": null,
"read_only": 0, "read_only": 0,
"reqd": 0 "reqd": 0
}, },
{ {
"default": null, "default": null,
"description": null, "description": null,
"fieldname": "phone", "fieldname": "phone",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 0, "hidden": 0,
"label": "Phone", "label": "Phone",
"options": null, "options": null,
"read_only": 0, "read_only": 0,
"reqd": 1 "reqd": 1
}, },
{ {
"default": "0", "default": "0",
"description": "", "description": "",
"fieldname": "is_primary_address", "fieldname": "is_primary_address",
"fieldtype": "Check", "fieldtype": "Check",
"hidden": 0, "hidden": 0,
"label": "Preferred Billing Address", "label": "Preferred Billing Address",
"options": null, "options": null,
"read_only": 0, "read_only": 0,
"reqd": 0 "reqd": 0
}, },
{ {
"default": "0", "default": "0",
"description": "", "description": "",
"fieldname": "is_shipping_address", "fieldname": "is_shipping_address",
"fieldtype": "Check", "fieldtype": "Check",
"hidden": 0, "hidden": 0,
"label": "Preferred Shipping Address", "label": "Preferred Shipping Address",
"options": null, "options": null,
"read_only": 0, "read_only": 0,
"reqd": 0 "reqd": 0
} }
], ],
"web_page_link_text": null "web_page_link_text": null
} }
] ]

View File

@ -1,68 +1,69 @@
[ [
{ {
"allow_comments": 1, "allow_comments": 1,
"allow_delete": 1, "allow_delete": 1,
"allow_edit": 1, "allow_edit": 1,
"allow_multiple": 1, "allow_multiple": 1,
"breadcrumbs": "[{\"title\":\"Issues\", \"name\":\"issues\"}]", "breadcrumbs": "[{\"title\":\"Issues\", \"name\":\"issues\"}]",
"doc_type": "Issue", "doc_type": "Issue",
"docstatus": 0, "docstatus": 0,
"doctype": "Web Form", "doctype": "Web Form",
"introduction_text": null, "is_standard": 1,
"login_required": 1, "introduction_text": null,
"modified": "2015-06-01 08:14:26.350792", "login_required": 1,
"name": "issues", "modified": "2015-06-01 08:14:26.350792",
"page_name": "issues", "name": "issues",
"published": 1, "page_name": "issues",
"success_message": "", "published": 1,
"success_url": "/issues", "success_message": "",
"title": "Issues", "success_url": "/issues",
"title": "Issues",
"web_form_fields": [ "web_form_fields": [
{ {
"default": null, "default": null,
"description": null, "description": null,
"fieldname": "subject", "fieldname": "subject",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 0, "hidden": 0,
"label": "Subject", "label": "Subject",
"options": null, "options": null,
"read_only": 0, "read_only": 0,
"reqd": 1 "reqd": 1
}, },
{ {
"default": "Open", "default": "Open",
"description": null, "description": null,
"fieldname": "status", "fieldname": "status",
"fieldtype": "Select", "fieldtype": "Select",
"hidden": null, "hidden": null,
"label": "Status", "label": "Status",
"options": "Open\nReplied\nHold\nClosed", "options": "Open\nReplied\nHold\nClosed",
"read_only": 1, "read_only": 1,
"reqd": 0 "reqd": 0
}, },
{ {
"default": null, "default": null,
"description": null, "description": null,
"fieldname": "description", "fieldname": "description",
"fieldtype": "Text", "fieldtype": "Text",
"hidden": 0, "hidden": 0,
"label": "Description", "label": "Description",
"options": null, "options": null,
"read_only": 0, "read_only": 0,
"reqd": 0 "reqd": 0
}, },
{ {
"default": null, "default": null,
"description": null, "description": null,
"fieldname": "attachment", "fieldname": "attachment",
"fieldtype": "Attach", "fieldtype": "Attach",
"hidden": null, "hidden": null,
"label": "Attachment", "label": "Attachment",
"options": null, "options": null,
"read_only": null, "read_only": null,
"reqd": null "reqd": null
} }
], ],
"web_page_link_text": null "web_page_link_text": null
} }
] ]

View File

@ -18,7 +18,7 @@
<br> <br>
<hr> <hr>
<h3>Next Steps</h3> <h3>{%= __("Next Steps") %}</h3>
<ul class="list-unstyled"> <ul class="list-unstyled">
<li><a class="text-muted" href="#">{%= __("Go to the Desktop and start using ERPNext") %}</a></li> <li><a class="text-muted" href="#">{%= __("Go to the Desktop and start using ERPNext") %}</a></li>
<li><a class="text-muted" href="#Module/Learn">{%= __("View a list of all the help videos") %}</a></li> <li><a class="text-muted" href="#Module/Learn">{%= __("View a list of all the help videos") %}</a></li>

View File

@ -46,13 +46,14 @@ erpnext.stock.DeliveryNoteController = erpnext.selling.SellingController.extend(
if (cint(frappe.defaults.get_default("auto_accounting_for_stock"))) { if (cint(frappe.defaults.get_default("auto_accounting_for_stock"))) {
this.show_general_ledger(); this.show_general_ledger();
} }
if (this.frm.has_perm("submit") && (doc.status !== "Closed")
if(doc.status !== "Closed") { && this.frm.doc.__onload && this.frm.doc.__onload.has_return_entry) {
cur_frm.add_custom_button(__("Close"), this.close_delivery_note) cur_frm.add_custom_button(__("Close"), this.close_delivery_note)
} }
} }
if(doc.__onload && !doc.__onload.billing_complete && doc.docstatus==1 && !doc.is_return && doc.status!="Closed") { if(doc.__onload && !doc.__onload.billing_complete && doc.docstatus==1
&& !doc.is_return && doc.status!="Closed") {
// show Make Invoice button only if Delivery Note is not created from Sales Invoice // show Make Invoice button only if Delivery Note is not created from Sales Invoice
var from_sales_invoice = false; var from_sales_invoice = false;
from_sales_invoice = cur_frm.doc.items.some(function(item) { from_sales_invoice = cur_frm.doc.items.some(function(item) {
@ -63,7 +64,7 @@ erpnext.stock.DeliveryNoteController = erpnext.selling.SellingController.extend(
cur_frm.add_custom_button(__('Invoice'), this.make_sales_invoice).addClass("btn-primary"); cur_frm.add_custom_button(__('Invoice'), this.make_sales_invoice).addClass("btn-primary");
} }
if(doc.docstatus==1 && doc.status === "Closed") { if(doc.docstatus==1 && doc.status === "Closed" && this.frm.has_perm("submit")) {
cur_frm.add_custom_button(__('Re-open'), this.reopen_delivery_note) cur_frm.add_custom_button(__('Re-open'), this.reopen_delivery_note)
} }
erpnext.stock.delivery_note.set_print_hide(doc, dt, dn); erpnext.stock.delivery_note.set_print_hide(doc, dt, dn);

View File

@ -69,7 +69,10 @@ class DeliveryNote(SellingController):
where docstatus=1 and delivery_note=%s""", self.name) where docstatus=1 and delivery_note=%s""", self.name)
if billed_qty: if billed_qty:
total_qty = sum((item.qty for item in self.get("items"))) total_qty = sum((item.qty for item in self.get("items")))
self.get("__onload").billing_complete = (billed_qty[0][0] == total_qty) self.set_onload("billing_complete", (billed_qty[0][0] == total_qty))
self.set_onload("has_return_entry", len(frappe.db.exists({"doctype": "Delivery Note",
"is_return": 1, "return_against": self.name, "docstatus": 1})))
def before_print(self): def before_print(self):
def toggle_print_hide(meta, fieldname): def toggle_print_hide(meta, fieldname):

View File

@ -105,7 +105,11 @@ class Item(WebsiteGenerator):
frappe.local.message_log.pop() frappe.local.message_log.pop()
except requests.exceptions.HTTPError: except requests.exceptions.HTTPError:
frappe.msgprint(_("Warning: Invalid Attachment {0}").format(self.website_image)) frappe.msgprint(_("Warning: Invalid attachment {0}").format(self.website_image))
self.website_image = None
except requests.exceptions.SSLError:
frappe.msgprint(_("Warning: Invalid SSL certificate on attachment {0}").format(self.website_image))
self.website_image = None self.website_image = None
# for CSV import # for CSV import

View File

@ -57,14 +57,18 @@ erpnext.stock.PurchaseReceiptController = erpnext.buying.BuyingController.extend
if(this.frm.doc.docstatus == 1 && this.frm.doc.status!="Closed") { if(this.frm.doc.docstatus == 1 && this.frm.doc.status!="Closed") {
cur_frm.add_custom_button(__('Return'), this.make_purchase_return); cur_frm.add_custom_button(__('Return'), this.make_purchase_return);
if(this.frm.doc.__onload && !this.frm.doc.__onload.billing_complete) { if(this.frm.doc.__onload && !this.frm.doc.__onload.billing_complete) {
cur_frm.add_custom_button(__('Invoice'), this.make_purchase_invoice).addClass("btn-primary"); cur_frm.add_custom_button(__('Invoice'),
this.make_purchase_invoice).addClass("btn-primary");
}
if (this.frm.has_perm("submit") &&
this.frm.doc.__onload && this.frm.doc.__onload.has_return_entry) {
cur_frm.add_custom_button(__("Close"), this.close_purchase_receipt)
} }
cur_frm.add_custom_button(__("Close"), this.close_purchase_receipt)
} }
} }
if(this.frm.doc.docstatus==1 && this.frm.doc.status === "Closed") { if(this.frm.doc.docstatus==1 && this.frm.doc.status === "Closed" && this.frm.has_perm("submit")) {
cur_frm.add_custom_button(__('Re-open'), this.reopen_purchase_receipt) cur_frm.add_custom_button(__('Re-open'), this.reopen_purchase_receipt)
} }

View File

@ -51,7 +51,10 @@ class PurchaseReceipt(BuyingController):
where purchase_receipt=%s and docstatus=1""", self.name) where purchase_receipt=%s and docstatus=1""", self.name)
if billed_qty: if billed_qty:
total_qty = sum((item.qty for item in self.get("items"))) total_qty = sum((item.qty for item in self.get("items")))
self.get("__onload").billing_complete = (billed_qty[0][0] == total_qty) self.set_onload("billing_complete", (billed_qty[0][0] == total_qty))
self.set_onload("has_return_entry", len(frappe.db.exists({"doctype": "Purchase Receipt",
"is_return": 1, "return_against": self.name, "docstatus": 1})))
def validate(self): def validate(self):
super(PurchaseReceipt, self).validate() super(PurchaseReceipt, self).validate()

View File

@ -1,5 +1,5 @@
<div class="web-list-item"> <div class="web-list-item">
<a href="/addresses?name={{ doc.name }}" no-pjax class="no-decoration"> <a href="/addresses?name={{ doc.name | urlencode }}" no-pjax class="no-decoration">
<h4 class="strong">{{ doc.address_title }}</h4> <h4 class="strong">{{ doc.address_title }}</h4>
<p class="text-muted small"> <p class="text-muted small">
{{ frappe.get_doc(doc).get_display() }} {{ frappe.get_doc(doc).get_display() }}

View File

@ -1,6 +1,6 @@
from setuptools import setup, find_packages from setuptools import setup, find_packages
version = "6.8.4" version = "6.9.0"
with open("requirements.txt", "r") as f: with open("requirements.txt", "r") as f:
install_requires = f.readlines() install_requires = f.readlines()

View File

@ -1 +1 @@
erpnext erpnext

View File

@ -1 +1 @@
en english en english

View File

@ -9,4 +9,4 @@
"run_selenium_tests": 1, "run_selenium_tests": 1,
"host_name": "http://localhost:8000", "host_name": "http://localhost:8000",
"install_apps": ["erpnext"] "install_apps": ["erpnext"]
} }