Merge branch 'develop'
This commit is contained in:
commit
a975fea3ac
@ -1,2 +1,2 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
__version__ = '6.8.4'
|
__version__ = '6.9.0'
|
||||||
|
@ -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 (this.frm.has_perm("submit")) {
|
||||||
if(flt(doc.per_billed, 2) < 100 || doc.per_received < 100) {
|
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(__('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,8 +55,10 @@ 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 (this.frm.has_perm("submit")) {
|
||||||
cur_frm.add_custom_button(__('Re-open'), this.unstop_purchase_order);
|
cur_frm.add_custom_button(__('Re-open'), this.unstop_purchase_order);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
make_stock_entry: function() {
|
make_stock_entry: function() {
|
||||||
|
@ -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()
|
||||||
|
|
||||||
|
@ -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 \
|
||||||
elif self.doc.doctype == "Purchase Invoice":
|
if self.doc.party_account_currency == self.doc.currency else self.doc.base_paid_amount
|
||||||
outstanding_amount = flt(total_amount_to_pay, self.doc.precision("outstanding_amount"))
|
self.doc.outstanding_amount = flt(total_amount_to_pay - flt(paid_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"))
|
self.doc.precision("outstanding_amount"))
|
||||||
|
elif self.doc.doctype == "Purchase Invoice":
|
||||||
|
self.doc.outstanding_amount = flt(total_amount_to_pay, self.doc.precision("outstanding_amount"))
|
@ -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 = {
|
||||||
|
@ -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
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
cur_frm.cscript.fiscal_year = function(doc, dt, dn) {
|
|
||||||
calculate_total_leaves_allocated(doc, dt, dn);
|
|
||||||
}
|
|
||||||
|
|
||||||
cur_frm.cscript.carry_forward = function(doc, dt, dn) {
|
|
||||||
calculate_total_leaves_allocated(doc, dt, dn);
|
|
||||||
}
|
|
||||||
|
|
||||||
cur_frm.cscript.carry_forwarded_leaves = function(doc, dt, dn) {
|
|
||||||
set_multiple(dt,dn,{total_leaves_allocated : flt(doc.carry_forwarded_leaves)+flt(doc.new_leaves_allocated)});
|
|
||||||
}
|
|
||||||
|
|
||||||
cur_frm.cscript.new_leaves_allocated = function(doc, dt, dn) {
|
|
||||||
set_multiple(dt,dn,{total_leaves_allocated : flt(doc.carry_forwarded_leaves)+flt(doc.new_leaves_allocated)});
|
|
||||||
}
|
|
||||||
|
|
||||||
calculate_total_leaves_allocated = function(doc, dt, dn) {
|
|
||||||
if(cint(doc.carry_forward) == 1 && doc.leave_type && doc.fiscal_year && doc.employee){
|
|
||||||
return get_server_fields('get_carry_forwarded_leaves','','', doc, dt, dn, 1);
|
|
||||||
}
|
|
||||||
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 {
|
return {
|
||||||
query: "erpnext.controllers.queries.employee_query"
|
query: "erpnext.controllers.queries.employee_query"
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
employee: function(frm) {
|
||||||
|
frm.trigger("calculate_total_leaves_allocated");
|
||||||
|
},
|
||||||
|
|
||||||
|
leave_type: function(frm) {
|
||||||
|
frm.trigger("calculate_total_leaves_allocated");
|
||||||
|
},
|
||||||
|
|
||||||
|
carry_forward: function(frm) {
|
||||||
|
frm.trigger("calculate_total_leaves_allocated");
|
||||||
|
},
|
||||||
|
|
||||||
|
carry_forwarded_leaves: function(frm) {
|
||||||
|
frm.set_value("total_leaves_allocated",
|
||||||
|
flt(frm.doc.carry_forwarded_leaves) + flt(frm.doc.new_leaves_allocated));
|
||||||
|
},
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
@ -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):
|
frappe.db.set(self,'carry_forwarded_leaves', flt(self.carry_forwarded_leaves))
|
||||||
self.get_total_allocated_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]))
|
|
||||||
|
|
||||||
def get_leave_bal(self):
|
frappe.throw(_('Reference') + ': <a href="#Form/Leave Allocation/{0}">{0}</a>'
|
||||||
return self.get_leaves_allocated() - self.get_leaves_applied()
|
.format(leave_allocation[0][0]), OverlapError)
|
||||||
|
|
||||||
def get_leaves_applied(self):
|
def validate_back_dated_allocation(self):
|
||||||
leaves_applied = frappe.db.sql("""select SUM(ifnull(total_leave_days, 0))
|
future_allocation = frappe.db.sql("""select name, from_date from `tabLeave Allocation`
|
||||||
from `tabLeave Application` where employee=%s and leave_type=%s
|
where employee=%s and leave_type=%s and docstatus=1 and from_date > %s
|
||||||
and to_date<=%s and docstatus=1""",
|
and carry_forward=1""", (self.employee, self.leave_type, self.to_date), as_dict=1)
|
||||||
(self.employee, self.leave_type, self.from_date))
|
|
||||||
return leaves_applied and flt(leaves_applied[0][0]) or 0
|
|
||||||
|
|
||||||
def get_leaves_allocated(self):
|
if future_allocation:
|
||||||
leaves_allocated = frappe.db.sql("""select SUM(ifnull(total_leaves_allocated, 0))
|
frappe.throw(_("Leave cannot be allocated before {0}, as leave balance has already been carry-forwarded in the future leave allocation record {1}")
|
||||||
from `tabLeave Allocation` where employee=%s and leave_type=%s
|
.format(formatdate(future_allocation[0].from_date), future_allocation[0].name),
|
||||||
and to_date<=%s and docstatus=1 and name!=%s""",
|
BackDatedAllocationError)
|
||||||
(self.employee, self.leave_type, self.from_date, self.name))
|
|
||||||
return leaves_allocated and flt(leaves_allocated[0][0]) or 0
|
|
||||||
|
|
||||||
def allow_carry_forward(self):
|
def set_total_leaves_allocated(self):
|
||||||
"""check whether carry forward is allowed or not for this leave type"""
|
self.carry_forwarded_leaves = get_carry_forwarded_leaves(self.employee,
|
||||||
cf = frappe.db.sql("""select is_carry_forward from `tabLeave Type` where name = %s""",
|
self.leave_type, self.from_date, self.carry_forward)
|
||||||
self.leave_type)
|
|
||||||
cf = cf and cint(cf[0][0]) or 0
|
|
||||||
if not cf:
|
|
||||||
frappe.db.set(self,'carry_forward',0)
|
|
||||||
frappe.throw(_("Cannot carry forward {0}").format(self.leave_type))
|
|
||||||
|
|
||||||
def get_carry_forwarded_leaves(self):
|
self.total_leaves_allocated = flt(self.carry_forwarded_leaves) + flt(self.new_leaves_allocated)
|
||||||
if self.carry_forward:
|
|
||||||
self.allow_carry_forward()
|
|
||||||
|
|
||||||
prev_bal = 0
|
if not self.total_leaves_allocated:
|
||||||
if cint(self.carry_forward) == 1:
|
frappe.throw(_("Total leaves allocated is mandatory"))
|
||||||
prev_bal = self.get_leave_bal()
|
|
||||||
|
|
||||||
ret = {
|
def validate_total_leaves_allocated(self):
|
||||||
'carry_forwarded_leaves': prev_bal,
|
if date_diff(self.to_date, self.from_date) <= flt(self.total_leaves_allocated):
|
||||||
'total_leaves_allocated': flt(prev_bal) + flt(self.new_leaves_allocated)
|
frappe.throw(_("Total allocated leaves are more than days in the period"), OverAllocationError)
|
||||||
}
|
|
||||||
return ret
|
|
||||||
|
|
||||||
def get_total_allocated_leaves(self):
|
def validate_against_leave_applications(self):
|
||||||
leave_det = self.get_carry_forwarded_leaves()
|
leaves_taken = get_approved_leaves_for_period(self.employee, self.leave_type,
|
||||||
self.validate_total_leaves_allocated(leave_det)
|
self.from_date, self.to_date)
|
||||||
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']))
|
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)
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def get_carry_forwarded_leaves(employee, leave_type, date, carry_forward=None):
|
||||||
|
carry_forwarded_leaves = 0
|
||||||
|
|
||||||
|
if carry_forward:
|
||||||
|
validate_carry_forward(leave_type)
|
||||||
|
|
||||||
|
previous_allocation = frappe.db.sql("""
|
||||||
|
select name, from_date, to_date, total_leaves_allocated
|
||||||
|
from `tabLeave Allocation`
|
||||||
|
where employee=%s and leave_type=%s and docstatus=1 and to_date < %s
|
||||||
|
order by to_date desc limit 1
|
||||||
|
""", (employee, leave_type, date), as_dict=1)
|
||||||
|
if previous_allocation:
|
||||||
|
leaves_taken = get_approved_leaves_for_period(employee, leave_type,
|
||||||
|
previous_allocation[0].from_date, previous_allocation[0].to_date)
|
||||||
|
|
||||||
|
carry_forwarded_leaves = flt(previous_allocation[0].total_leaves_allocated) - flt(leaves_taken)
|
||||||
|
|
||||||
|
return carry_forwarded_leaves
|
||||||
|
|
||||||
|
def validate_carry_forward(leave_type):
|
||||||
|
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))
|
||||||
|
|
||||||
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"))
|
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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"
|
||||||
|
@ -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()
|
||||||
@ -51,6 +50,8 @@ class LeaveApplication(Document):
|
|||||||
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)
|
||||||
|
|
||||||
@ -58,9 +59,40 @@ class LeaveApplication(Document):
|
|||||||
# notify leave applier about cancellation
|
# notify leave applier about cancellation
|
||||||
self.notify_employee("cancelled")
|
self.notify_employee("cancelled")
|
||||||
|
|
||||||
def show_block_day_warning(self):
|
def validate_dates(self):
|
||||||
from erpnext.hr.doctype.leave_block_list.leave_block_list import get_applicable_block_dates
|
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):
|
||||||
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)
|
||||||
@ -214,59 +229,88 @@ def get_approvers(doctype, txt, searchfield, start, page_len, filters):
|
|||||||
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 + "%"))
|
||||||
|
|
||||||
def get_holidays(leave_app):
|
@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
|
||||||
|
|
||||||
|
@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
|
||||||
|
@ -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
|
||||||
|
})
|
@ -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",
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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")
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -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 = {
|
columns = get_columns(leave_types)
|
||||||
"status": "Active"
|
data = get_data(filters, leave_types)
|
||||||
}
|
|
||||||
|
|
||||||
if filters.get("company"):
|
return columns, data
|
||||||
filters["company"] = filters.company
|
|
||||||
|
|
||||||
employees = runreport(doctype="Employee", fields=["name", "employee_name", "department"],
|
|
||||||
filters=employee_filters, limit_page_length=None)
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
|
def get_columns(leave_types):
|
||||||
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")
|
|
||||||
|
|
||||||
data = {}
|
return columns
|
||||||
for d in allocations:
|
|
||||||
data.setdefault((d.employee,d.leave_type), frappe._dict()).allocation = d.leaves_allocated
|
|
||||||
|
|
||||||
for d in applications:
|
def get_data(filters, leave_types):
|
||||||
data.setdefault((d.employee, d.leave_type), frappe._dict()).leaves = d.leaves
|
|
||||||
|
|
||||||
result = []
|
allocation_records_based_on_to_date = get_leave_allocation_records(filters.to_date)
|
||||||
for employee in employees:
|
|
||||||
|
active_employees = frappe.get_all("Employee",
|
||||||
|
filters = { "status": "Active", "company": filters.company},
|
||||||
|
fields = ["name", "employee_name", "department"])
|
||||||
|
|
||||||
|
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
|
@ -232,3 +232,4 @@ 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
|
||||||
|
0
erpnext/patches/v6_8/__init__.py
Normal file
0
erpnext/patches/v6_8/__init__.py
Normal file
9
erpnext/patches/v6_8/make_webform_standard.py
Normal file
9
erpnext/patches/v6_8/make_webform_standard.py
Normal 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()
|
@ -4,12 +4,14 @@
|
|||||||
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.__islocal) {
|
||||||
if (frm.doc.for_manufacturing) {
|
if (frm.doc.for_manufacturing) {
|
||||||
frappe.ui.form.trigger("Time Log", "production_order");
|
frappe.ui.form.trigger("Time Log", "production_order");
|
||||||
}
|
}
|
||||||
if (frm.doc.from_time && frm.doc.to_time) {
|
if (frm.doc.from_time && frm.doc.to_time) {
|
||||||
frappe.ui.form.trigger("Time Log", "to_time");
|
frappe.ui.form.trigger("Time Log", "to_time");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
frappe.ui.form.on("Time Log", "refresh", function(frm) {
|
frappe.ui.form.on("Time Log", "refresh", function(frm) {
|
||||||
|
@ -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"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(this.frm.doc.party_account_currency == this.frm.doc.currency) {
|
|
||||||
this.frm.set_value("outstanding_amount", outstanding_amount);
|
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")));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -5,8 +5,9 @@ frappe.pages['setup-wizard'].on_page_load = function(wrapper) {
|
|||||||
frappe.set_route("desk");
|
frappe.set_route("desk");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
function load_erpnext_slides() {
|
||||||
$.extend(erpnext.wiz, {
|
$.extend(erpnext.wiz, {
|
||||||
region: {
|
region: {
|
||||||
title: __("Region"),
|
title: __("Region"),
|
||||||
@ -400,9 +401,11 @@ erpnext.wiz.fiscal_years = {
|
|||||||
"South Africa": ["03-01", "02-28"],
|
"South Africa": ["03-01", "02-28"],
|
||||||
"Thailand": ["10-01", "09-30"],
|
"Thailand": ["10-01", "09-30"],
|
||||||
"United Kingdom": ["04-01", "03-31"],
|
"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);
|
||||||
|
@ -56,6 +56,7 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.frm.has_perm("submit")) {
|
||||||
// stop
|
// stop
|
||||||
if(flt(doc.per_delivered, 2) < 100 || flt(doc.per_billed) < 100) {
|
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(__('Stop'), this.stop_sales_order)
|
||||||
@ -63,6 +64,7 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
|
|||||||
|
|
||||||
|
|
||||||
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,10 +84,12 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
|
|||||||
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
if (this.frm.has_perm("submit")) {
|
||||||
// un-stop
|
// un-stop
|
||||||
cur_frm.add_custom_button(__('Re-open'), cur_frm.cscript['Unstop Sales Order']);
|
cur_frm.add_custom_button(__('Re-open'), cur_frm.cscript['Unstop Sales Order']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (this.frm.doc.docstatus===0) {
|
if (this.frm.doc.docstatus===0) {
|
||||||
cur_frm.add_custom_button(__('From Quotation'),
|
cur_frm.add_custom_button(__('From Quotation'),
|
||||||
|
@ -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)
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "Web Form",
|
"doctype": "Web Form",
|
||||||
"introduction_text": null,
|
"introduction_text": null,
|
||||||
|
"is_standard": 1,
|
||||||
"login_required": 1,
|
"login_required": 1,
|
||||||
"modified": "2015-06-01 06:53:43.699336",
|
"modified": "2015-06-01 06:53:43.699336",
|
||||||
"name": "addresses",
|
"name": "addresses",
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
"doc_type": "Issue",
|
"doc_type": "Issue",
|
||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "Web Form",
|
"doctype": "Web Form",
|
||||||
|
"is_standard": 1,
|
||||||
"introduction_text": null,
|
"introduction_text": null,
|
||||||
"login_required": 1,
|
"login_required": 1,
|
||||||
"modified": "2015-06-01 08:14:26.350792",
|
"modified": "2015-06-01 08:14:26.350792",
|
||||||
|
@ -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>
|
||||||
|
@ -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);
|
||||||
|
@ -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):
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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()
|
||||||
|
@ -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() }}
|
||||||
|
2
setup.py
2
setup.py
@ -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()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user