mirror of
https://github.com/frappe/erpnext.git
synced 2026-09-17 18:45:20 +00:00
fix: conflicts
This commit is contained in:
@@ -8,7 +8,8 @@ frappe.ui.form.on('Additional Salary', {
|
||||
frm.set_query("employee", function() {
|
||||
return {
|
||||
filters: {
|
||||
company: frm.doc.company
|
||||
company: frm.doc.company,
|
||||
status: "Active"
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
30
erpnext/hr/doctype/appointment_letter/appointment_letter.js
Normal file
30
erpnext/hr/doctype/appointment_letter/appointment_letter.js
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Appointment Letter', {
|
||||
appointment_letter_template: function(frm){
|
||||
if (frm.doc.appointment_letter_template){
|
||||
frappe.call({
|
||||
method: 'erpnext.hr.doctype.appointment_letter.appointment_letter.get_appointment_letter_details',
|
||||
args : {
|
||||
template : frm.doc.appointment_letter_template
|
||||
},
|
||||
callback: function(r){
|
||||
if(r.message){
|
||||
let message_body = r.message;
|
||||
frm.set_value("introduction", message_body[0].introduction);
|
||||
frm.set_value("closing_notes", message_body[0].closing_notes);
|
||||
frm.doc.terms = []
|
||||
for (var i in message_body[1].description){
|
||||
frm.add_child("terms");
|
||||
frm.fields_dict.terms.get_value()[i].title = message_body[1].description[i].title;
|
||||
frm.fields_dict.terms.get_value()[i].description = message_body[1].description[i].description;
|
||||
}
|
||||
frm.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
124
erpnext/hr/doctype/appointment_letter/appointment_letter.json
Normal file
124
erpnext/hr/doctype/appointment_letter/appointment_letter.json
Normal file
@@ -0,0 +1,124 @@
|
||||
{
|
||||
"actions": [],
|
||||
"autoname": "HR-APP-LETTER-.#####",
|
||||
"creation": "2019-12-26 12:35:49.574828",
|
||||
"default_print_format": "Standard Appointment Letter",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"job_applicant",
|
||||
"applicant_name",
|
||||
"column_break_3",
|
||||
"company",
|
||||
"appointment_date",
|
||||
"appointment_letter_template",
|
||||
"body_section",
|
||||
"introduction",
|
||||
"terms",
|
||||
"closing_notes"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fetch_from": "job_applicant.applicant_name",
|
||||
"fieldname": "applicant_name",
|
||||
"fieldtype": "Data",
|
||||
"in_global_search": 1,
|
||||
"in_list_view": 1,
|
||||
"label": "Applicant Name",
|
||||
"read_only": 1,
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "appointment_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Appointment Date",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "appointment_letter_template",
|
||||
"fieldtype": "Link",
|
||||
"label": "Appointment Letter Template",
|
||||
"options": "Appointment Letter Template",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fetch_from": "appointment_letter_template.introduction",
|
||||
"fieldname": "introduction",
|
||||
"fieldtype": "Long Text",
|
||||
"label": "Introduction",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "body_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Body"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_3",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "job_applicant",
|
||||
"fieldtype": "Link",
|
||||
"label": "Job Applicant",
|
||||
"options": "Job Applicant",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "company",
|
||||
"fieldtype": "Link",
|
||||
"label": "Company",
|
||||
"options": "Company",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "closing_notes",
|
||||
"fieldtype": "Text",
|
||||
"label": "Closing Notes"
|
||||
},
|
||||
{
|
||||
"fieldname": "terms",
|
||||
"fieldtype": "Table",
|
||||
"label": "Terms",
|
||||
"options": "Appointment Letter content",
|
||||
"reqd": 1
|
||||
}
|
||||
],
|
||||
"links": [],
|
||||
"modified": "2020-01-21 17:30:36.334395",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Appointment Letter",
|
||||
"name_case": "Title Case",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "HR Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
||||
25
erpnext/hr/doctype/appointment_letter/appointment_letter.py
Normal file
25
erpnext/hr/doctype/appointment_letter/appointment_letter.py
Normal file
@@ -0,0 +1,25 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
class AppointmentLetter(Document):
|
||||
pass
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_appointment_letter_details(template):
|
||||
body = []
|
||||
intro= frappe.get_list("Appointment Letter Template",
|
||||
fields = ['introduction', 'closing_notes'],
|
||||
filters={'name': template
|
||||
})[0]
|
||||
content = frappe.get_list("Appointment Letter content",
|
||||
fields = ['title', 'description'],
|
||||
filters={'parent': template
|
||||
})
|
||||
body.append(intro)
|
||||
body.append({'description': content})
|
||||
return body
|
||||
@@ -1,10 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
class LoanType(Document):
|
||||
# import frappe
|
||||
import unittest
|
||||
|
||||
class TestAppointmentLetter(unittest.TestCase):
|
||||
pass
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"actions": [],
|
||||
"creation": "2019-12-26 12:22:16.575767",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"title",
|
||||
"description"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "title",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
"label": "Title",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "description",
|
||||
"fieldtype": "Long Text",
|
||||
"in_list_view": 1,
|
||||
"label": "Description",
|
||||
"reqd": 1
|
||||
}
|
||||
],
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2019-12-26 12:24:09.824084",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Appointment Letter content",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
# import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
class RepaymentSchedule(Document):
|
||||
class AppointmentLettercontent(Document):
|
||||
pass
|
||||
@@ -0,0 +1,8 @@
|
||||
// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Appointment Letter Template', {
|
||||
// refresh: function(frm) {
|
||||
|
||||
// }
|
||||
});
|
||||
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"actions": [],
|
||||
"autoname": "HR-APP-LETTER-TEMP-.#####",
|
||||
"creation": "2019-12-26 12:20:14.219578",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"introduction",
|
||||
"terms",
|
||||
"closing_notes"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "introduction",
|
||||
"fieldtype": "Long Text",
|
||||
"in_list_view": 1,
|
||||
"label": "Introduction",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "closing_notes",
|
||||
"fieldtype": "Text",
|
||||
"label": "Closing Notes"
|
||||
},
|
||||
{
|
||||
"fieldname": "terms",
|
||||
"fieldtype": "Table",
|
||||
"label": "Terms",
|
||||
"options": "Appointment Letter content",
|
||||
"reqd": 1
|
||||
}
|
||||
],
|
||||
"links": [],
|
||||
"modified": "2020-01-21 17:00:46.779420",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Appointment Letter Template",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "HR Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
# import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
class SalarySlipLoan(Document):
|
||||
class AppointmentLetterTemplate(Document):
|
||||
pass
|
||||
@@ -0,0 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
# import frappe
|
||||
import unittest
|
||||
|
||||
class TestAppointmentLetterTemplate(unittest.TestCase):
|
||||
pass
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"actions": [],
|
||||
"allow_import": 1,
|
||||
"autoname": "naming_series:",
|
||||
"creation": "2013-01-10 16:34:13",
|
||||
@@ -57,11 +58,20 @@
|
||||
{
|
||||
"fetch_from": "employee.employee_name",
|
||||
"fieldname": "employee_name",
|
||||
"fieldtype": "Read Only",
|
||||
"fieldtype": "Data",
|
||||
"in_global_search": 1,
|
||||
"label": "Employee Name",
|
||||
"oldfieldname": "employee_name",
|
||||
"oldfieldtype": "Data"
|
||||
"oldfieldtype": "Data",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "working_hours",
|
||||
"fieldname": "working_hours",
|
||||
"fieldtype": "Float",
|
||||
"label": "Working Hours",
|
||||
"precision": "1",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"default": "Present",
|
||||
@@ -72,7 +82,7 @@
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "status",
|
||||
"oldfieldtype": "Select",
|
||||
"options": "\nPresent\nAbsent\nOn Leave\nHalf Day",
|
||||
"options": "\nPresent\nAbsent\nOn Leave\nHalf Day\nWork From Home",
|
||||
"reqd": 1,
|
||||
"search_index": 1
|
||||
},
|
||||
@@ -126,6 +136,12 @@
|
||||
"options": "Department",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "shift",
|
||||
"fieldtype": "Link",
|
||||
"label": "Shift",
|
||||
"options": "Shift Type"
|
||||
},
|
||||
{
|
||||
"fieldname": "attendance_request",
|
||||
"fieldtype": "Link",
|
||||
@@ -143,20 +159,6 @@
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "working_hours",
|
||||
"fieldname": "working_hours",
|
||||
"fieldtype": "Float",
|
||||
"label": "Working Hours",
|
||||
"precision": "1",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "shift",
|
||||
"fieldtype": "Link",
|
||||
"label": "Shift",
|
||||
"options": "Shift Type"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "late_entry",
|
||||
@@ -173,7 +175,7 @@
|
||||
"icon": "fa fa-ok",
|
||||
"idx": 1,
|
||||
"is_submittable": 1,
|
||||
"modified": "2019-07-29 20:35:40.845422",
|
||||
"modified": "2020-02-19 14:25:32.945842",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Attendance",
|
||||
|
||||
@@ -52,7 +52,7 @@ class Attendance(Document):
|
||||
|
||||
def validate(self):
|
||||
from erpnext.controllers.status_updater import validate_status
|
||||
validate_status(self.status, ["Present", "Absent", "On Leave", "Half Day"])
|
||||
validate_status(self.status, ["Present", "Absent", "On Leave", "Half Day", "Work From Home"])
|
||||
self.validate_attendance_date()
|
||||
self.validate_duplicate_record()
|
||||
self.check_leave_record()
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
frappe.listview_settings['Attendance'] = {
|
||||
add_fields: ["status", "attendance_date"],
|
||||
get_indicator: function(doc) {
|
||||
return [__(doc.status), doc.status=="Present" ? "green" : "darkgrey", "status,=," + doc.status];
|
||||
get_indicator: function (doc) {
|
||||
if (["Present", "Work From Home"].includes(doc.status)) {
|
||||
return [__(doc.status), "green", "status,=," + doc.status];
|
||||
} else if (["Absent", "On Leave"].includes(doc.status)) {
|
||||
return [__(doc.status), "red", "status,=," + doc.status];
|
||||
} else if (doc.status == "Half Day") {
|
||||
return [__(doc.status), "orange", "status,=," + doc.status];
|
||||
}
|
||||
},
|
||||
onload: function(list_view) {
|
||||
let me = this;
|
||||
@@ -44,7 +50,7 @@ frappe.listview_settings['Attendance'] = {
|
||||
label: __("Status"),
|
||||
fieldtype: "Select",
|
||||
fieldname: "status",
|
||||
options: ["Present", "Absent", "Half Day"],
|
||||
options: ["Present", "Absent", "Half Day", "Work From Home"],
|
||||
hidden:1,
|
||||
reqd: 1,
|
||||
|
||||
@@ -102,5 +108,4 @@ frappe.listview_settings['Attendance'] = {
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@@ -38,6 +38,8 @@ class AttendanceRequest(Document):
|
||||
attendance.employee_name = self.employee_name
|
||||
if self.half_day and date_diff(getdate(self.half_day_date), getdate(attendance_date)) == 0:
|
||||
attendance.status = "Half Day"
|
||||
elif self.reason == "Work From Home":
|
||||
attendance.status = "Work From Home"
|
||||
else:
|
||||
attendance.status = "Present"
|
||||
attendance.attendance_date = attendance_date
|
||||
|
||||
@@ -13,7 +13,28 @@ class TestAttendanceRequest(unittest.TestCase):
|
||||
for doctype in ["Attendance Request", "Attendance"]:
|
||||
frappe.db.sql("delete from `tab{doctype}`".format(doctype=doctype))
|
||||
|
||||
def test_attendance_request(self):
|
||||
def test_on_duty_attendance_request(self):
|
||||
today = nowdate()
|
||||
employee = get_employee()
|
||||
attendance_request = frappe.new_doc("Attendance Request")
|
||||
attendance_request.employee = employee.name
|
||||
attendance_request.from_date = date(date.today().year, 1, 1)
|
||||
attendance_request.to_date = date(date.today().year, 1, 2)
|
||||
attendance_request.reason = "On Duty"
|
||||
attendance_request.company = "_Test Company"
|
||||
attendance_request.insert()
|
||||
attendance_request.submit()
|
||||
attendance = frappe.get_doc('Attendance', {
|
||||
'employee': employee.name,
|
||||
'attendance_date': date(date.today().year, 1, 1),
|
||||
'docstatus': 1
|
||||
})
|
||||
self.assertEqual(attendance.status, 'Present')
|
||||
attendance_request.cancel()
|
||||
attendance.reload()
|
||||
self.assertEqual(attendance.docstatus, 2)
|
||||
|
||||
def test_work_from_home_attendance_request(self):
|
||||
today = nowdate()
|
||||
employee = get_employee()
|
||||
attendance_request = frappe.new_doc("Attendance Request")
|
||||
@@ -29,7 +50,7 @@ class TestAttendanceRequest(unittest.TestCase):
|
||||
'attendance_date': date(date.today().year, 1, 1),
|
||||
'docstatus': 1
|
||||
})
|
||||
self.assertEqual(attendance.status, 'Present')
|
||||
self.assertEqual(attendance.status, 'Work From Home')
|
||||
attendance_request.cancel()
|
||||
attendance.reload()
|
||||
self.assertEqual(attendance.docstatus, 2)
|
||||
|
||||
@@ -97,9 +97,9 @@ class DailyWorkSummary(Document):
|
||||
|
||||
return dict(replies=replies,
|
||||
original_message=dws_group.message,
|
||||
title=_('Work Summary for {0}'.format(
|
||||
title=_('Work Summary for {0}').format(
|
||||
global_date_format(self.creation)
|
||||
)),
|
||||
),
|
||||
did_not_reply=', '.join(did_not_reply) or '',
|
||||
did_not_reply_title=_('No replies from'))
|
||||
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Department', {
|
||||
onload: function(frm) {
|
||||
frm.set_query("parent_department", function(){
|
||||
return {"filters": [["Department", "is_group", "=", 1]]};
|
||||
});
|
||||
},
|
||||
refresh: function(frm) {
|
||||
// read-only for root department
|
||||
if(!frm.doc.parent_department && !frm.is_new()) {
|
||||
|
||||
@@ -124,8 +124,10 @@ erpnext.EmployeeSelector = Class.extend({
|
||||
|
||||
var mark_employee_toolbar = $('<div class="col-sm-12 bottom-toolbar">\
|
||||
<button class="btn btn-primary btn-mark-present btn-xs"></button>\
|
||||
<button class="btn btn-default btn-mark-absent btn-xs"></button>\
|
||||
<button class="btn btn-default btn-mark-half-day btn-xs"></button></div>')
|
||||
<button class="btn btn-primary btn-mark-work-from-home btn-xs"></button>\
|
||||
<button class="btn btn-warning btn-mark-half-day btn-xs"></button>\
|
||||
<button class="btn btn-danger btn-mark-absent btn-xs"></button>\
|
||||
</div>');
|
||||
|
||||
employee_toolbar.find(".btn-add")
|
||||
.html(__('Check all'))
|
||||
@@ -224,6 +226,31 @@ erpnext.EmployeeSelector = Class.extend({
|
||||
});
|
||||
|
||||
|
||||
mark_employee_toolbar.find(".btn-mark-work-from-home")
|
||||
.html(__('Mark Work From Home'))
|
||||
.on("click", function() {
|
||||
var employee_work_from_home = [];
|
||||
$(me.wrapper).find('input[type="checkbox"]').each(function(i, check) {
|
||||
if($(check).is(":checked")) {
|
||||
employee_work_from_home.push(employee[i]);
|
||||
}
|
||||
});
|
||||
frappe.call({
|
||||
method: "erpnext.hr.doctype.employee_attendance_tool.employee_attendance_tool.mark_employee_attendance",
|
||||
args:{
|
||||
"employee_list":employee_work_from_home,
|
||||
"status":"Work From Home",
|
||||
"date":frm.doc.date,
|
||||
"company":frm.doc.company
|
||||
},
|
||||
|
||||
callback: function(r) {
|
||||
erpnext.employee_attendance_tool.load_employees(frm);
|
||||
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
var row;
|
||||
$.each(employee, function(i, m) {
|
||||
if (i===0 || (i % 4) === 0) {
|
||||
|
||||
@@ -84,7 +84,7 @@ def get_benefit_pro_rata_ratio_amount(employee, on_date, sal_struct):
|
||||
pay_against_benefit_claim, max_benefit_amount = frappe.db.get_value("Salary Component", sal_struct_row.salary_component, ["pay_against_benefit_claim", "max_benefit_amount"])
|
||||
except TypeError:
|
||||
# show the error in tests?
|
||||
frappe.throw(_("Unable to find Salary Component {0}".format(sal_struct_row.salary_component)))
|
||||
frappe.throw(_("Unable to find Salary Component {0}").format(sal_struct_row.salary_component))
|
||||
if sal_struct_row.is_flexible_benefit == 1 and pay_against_benefit_claim != 1:
|
||||
total_pro_rata_max += max_benefit_amount
|
||||
if total_pro_rata_max > 0:
|
||||
|
||||
@@ -104,11 +104,16 @@ frappe.ui.form.on("Leave Application", {
|
||||
},
|
||||
|
||||
half_day: function(frm) {
|
||||
if (frm.doc.from_date == frm.doc.to_date) {
|
||||
frm.set_value("half_day_date", frm.doc.from_date);
|
||||
if (frm.doc.half_day) {
|
||||
if (frm.doc.from_date == frm.doc.to_date) {
|
||||
frm.set_value("half_day_date", frm.doc.from_date);
|
||||
}
|
||||
else {
|
||||
frm.trigger("half_day_datepicker");
|
||||
}
|
||||
}
|
||||
else {
|
||||
frm.trigger("half_day_datepicker");
|
||||
frm.set_value("half_day_date", "");
|
||||
}
|
||||
frm.trigger("calculate_total_days");
|
||||
},
|
||||
|
||||
@@ -409,7 +409,7 @@ class TestLeaveApplication(unittest.TestCase):
|
||||
|
||||
self.assertEqual(get_leave_balance_on(employee.name, leave_type.name, nowdate(), add_days(nowdate(), 8)), 21)
|
||||
|
||||
def test_earned_leave(self):
|
||||
def test_earned_leaves_creation(self):
|
||||
leave_period = get_leave_period()
|
||||
employee = get_employee()
|
||||
leave_type = 'Test Earned Leave Type'
|
||||
@@ -437,6 +437,14 @@ class TestLeaveApplication(unittest.TestCase):
|
||||
i += 1
|
||||
self.assertEqual(get_leave_balance_on(employee.name, leave_type, nowdate()), 6)
|
||||
|
||||
# validate earned leaves creation without maximum leaves
|
||||
frappe.db.set_value('Leave Type', leave_type, 'max_leaves_allowed', 0)
|
||||
i = 0
|
||||
while(i<6):
|
||||
allocate_earned_leaves()
|
||||
i += 1
|
||||
self.assertEqual(get_leave_balance_on(employee.name, leave_type, nowdate()), 9)
|
||||
|
||||
# test to not consider current leave in leave balance while submitting
|
||||
def test_current_leave_on_submit(self):
|
||||
employee = get_employee()
|
||||
|
||||
@@ -64,6 +64,9 @@ class LeaveEncashment(Document):
|
||||
|
||||
allocation = self.get_leave_allocation()
|
||||
|
||||
if not allocation:
|
||||
frappe.throw(_("No Leaves Allocated to Employee: {0} for Leave Type: {1}").format(self.employee, self.leave_type))
|
||||
|
||||
self.leave_balance = allocation.total_leaves_allocated - allocation.carry_forwarded_leaves_count\
|
||||
- get_unused_leaves(self.employee, self.leave_type, allocation.from_date, self.encashment_date)
|
||||
|
||||
@@ -116,4 +119,4 @@ def create_leave_encashment(leave_allocation):
|
||||
leave_type=allocation.leave_type,
|
||||
encashment_date=allocation.to_date
|
||||
))
|
||||
leave_encashment.insert(ignore_permissions=True)
|
||||
leave_encashment.insert(ignore_permissions=True)
|
||||
|
||||
@@ -1,229 +0,0 @@
|
||||
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
{% include 'erpnext/hr/loan_common.js' %};
|
||||
|
||||
frappe.ui.form.on('Loan', {
|
||||
onload: function (frm) {
|
||||
frm.set_query("loan_application", function () {
|
||||
return {
|
||||
"filters": {
|
||||
"applicant": frm.doc.applicant,
|
||||
"docstatus": 1,
|
||||
"status": "Approved"
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
frm.set_query("interest_income_account", function () {
|
||||
return {
|
||||
"filters": {
|
||||
"company": frm.doc.company,
|
||||
"root_type": "Income",
|
||||
"is_group": 0
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
$.each(["payment_account", "loan_account"], function (i, field) {
|
||||
frm.set_query(field, function () {
|
||||
return {
|
||||
"filters": {
|
||||
"company": frm.doc.company,
|
||||
"root_type": "Asset",
|
||||
"is_group": 0
|
||||
}
|
||||
};
|
||||
});
|
||||
})
|
||||
},
|
||||
|
||||
refresh: function (frm) {
|
||||
if (frm.doc.docstatus == 1) {
|
||||
if (frm.doc.status == "Sanctioned") {
|
||||
frm.add_custom_button(__('Create Disbursement Entry'), function() {
|
||||
frm.trigger("make_jv");
|
||||
}).addClass("btn-primary");
|
||||
} else if (frm.doc.status == "Disbursed" && frm.doc.repayment_start_date && (frm.doc.applicant_type == 'Member' || frm.doc.repay_from_salary == 0)) {
|
||||
frm.add_custom_button(__('Create Repayment Entry'), function() {
|
||||
frm.trigger("make_repayment_entry");
|
||||
}).addClass("btn-primary");
|
||||
}
|
||||
}
|
||||
frm.trigger("toggle_fields");
|
||||
},
|
||||
|
||||
make_jv: function (frm) {
|
||||
frappe.call({
|
||||
args: {
|
||||
"loan": frm.doc.name,
|
||||
"company": frm.doc.company,
|
||||
"loan_account": frm.doc.loan_account,
|
||||
"applicant_type": frm.doc.applicant_type,
|
||||
"applicant": frm.doc.applicant,
|
||||
"loan_amount": frm.doc.loan_amount,
|
||||
"payment_account": frm.doc.payment_account
|
||||
},
|
||||
method: "erpnext.hr.doctype.loan.loan.make_jv_entry",
|
||||
callback: function (r) {
|
||||
if (r.message)
|
||||
var doc = frappe.model.sync(r.message)[0];
|
||||
frappe.set_route("Form", doc.doctype, doc.name);
|
||||
}
|
||||
})
|
||||
},
|
||||
make_repayment_entry: function(frm) {
|
||||
var repayment_schedule = $.map(frm.doc.repayment_schedule, function(d) { return d.paid ? d.payment_date : false; });
|
||||
if(repayment_schedule.length >= 1){
|
||||
frm.repayment_data = [];
|
||||
frm.show_dialog = 1;
|
||||
let title = "";
|
||||
let fields = [
|
||||
{fieldtype:'Section Break', label: __('Repayment Schedule')},
|
||||
{fieldname: 'payments', fieldtype: 'Table',
|
||||
fields: [
|
||||
{
|
||||
fieldtype:'Data',
|
||||
fieldname:'payment_date',
|
||||
label: __('Date'),
|
||||
read_only:1,
|
||||
in_list_view: 1,
|
||||
columns: 2
|
||||
},
|
||||
{
|
||||
fieldtype:'Currency',
|
||||
fieldname:'principal_amount',
|
||||
label: __('Principal Amount'),
|
||||
read_only:1,
|
||||
in_list_view: 1,
|
||||
columns: 3
|
||||
},
|
||||
{
|
||||
fieldtype:'Currency',
|
||||
fieldname:'interest_amount',
|
||||
label: __('Interest'),
|
||||
read_only:1,
|
||||
in_list_view: 1,
|
||||
columns: 2
|
||||
},
|
||||
{
|
||||
fieldtype:'Currency',
|
||||
read_only:1,
|
||||
fieldname:'total_payment',
|
||||
label: __('Total Payment'),
|
||||
in_list_view: 1,
|
||||
columns: 3
|
||||
},
|
||||
],
|
||||
data: frm.repayment_data,
|
||||
get_data: function() {
|
||||
return frm.repayment_data;
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
var dialog = new frappe.ui.Dialog({
|
||||
title: title, fields: fields,
|
||||
});
|
||||
if (frm.doc['repayment_schedule']) {
|
||||
frm.doc['repayment_schedule'].forEach((payment, index) => {
|
||||
if (payment.paid == 0 && payment.payment_date <= frappe.datetime.now_date()) {
|
||||
frm.repayment_data.push ({
|
||||
'id': index,
|
||||
'name': payment.name,
|
||||
'payment_date': payment.payment_date,
|
||||
'principal_amount': payment.principal_amount,
|
||||
'interest_amount': payment.interest_amount,
|
||||
'total_payment': payment.total_payment
|
||||
});
|
||||
dialog.fields_dict.payments.grid.refresh();
|
||||
$(dialog.wrapper.find(".grid-buttons")).hide();
|
||||
$(`.octicon.octicon-triangle-down`).hide();
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
dialog.show()
|
||||
dialog.set_primary_action(__('Create Repayment Entry'), function() {
|
||||
frm.values = dialog.get_values();
|
||||
if(frm.values) {
|
||||
_make_repayment_entry(frm, dialog.fields_dict.payments.grid.get_selected_children());
|
||||
dialog.hide()
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
dialog.get_close_btn().on('click', () => {
|
||||
dialog.hide();
|
||||
});
|
||||
},
|
||||
|
||||
mode_of_payment: function (frm) {
|
||||
if (frm.doc.mode_of_payment && frm.doc.company) {
|
||||
frappe.call({
|
||||
method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.get_bank_cash_account",
|
||||
args: {
|
||||
"mode_of_payment": frm.doc.mode_of_payment,
|
||||
"company": frm.doc.company
|
||||
},
|
||||
callback: function (r, rt) {
|
||||
if (r.message) {
|
||||
frm.set_value("payment_account", r.message.account);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
loan_application: function (frm) {
|
||||
if(frm.doc.loan_application){
|
||||
return frappe.call({
|
||||
method: "erpnext.hr.doctype.loan.loan.get_loan_application",
|
||||
args: {
|
||||
"loan_application": frm.doc.loan_application
|
||||
},
|
||||
callback: function (r) {
|
||||
if (!r.exc && r.message) {
|
||||
frm.set_value("loan_type", r.message.loan_type);
|
||||
frm.set_value("loan_amount", r.message.loan_amount);
|
||||
frm.set_value("repayment_method", r.message.repayment_method);
|
||||
frm.set_value("monthly_repayment_amount", r.message.repayment_amount);
|
||||
frm.set_value("repayment_periods", r.message.repayment_periods);
|
||||
frm.set_value("rate_of_interest", r.message.rate_of_interest);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
repayment_method: function (frm) {
|
||||
frm.trigger("toggle_fields")
|
||||
},
|
||||
|
||||
toggle_fields: function (frm) {
|
||||
frm.toggle_enable("monthly_repayment_amount", frm.doc.repayment_method == "Repay Fixed Amount per Period")
|
||||
frm.toggle_enable("repayment_periods", frm.doc.repayment_method == "Repay Over Number of Periods")
|
||||
}
|
||||
});
|
||||
|
||||
var _make_repayment_entry = function(frm, payment_rows) {
|
||||
frappe.call({
|
||||
method:"erpnext.hr.doctype.loan.loan.make_repayment_entry",
|
||||
args: {
|
||||
payment_rows: payment_rows,
|
||||
"loan": frm.doc.name,
|
||||
"company": frm.doc.company,
|
||||
"loan_account": frm.doc.loan_account,
|
||||
"applicant_type": frm.doc.applicant_type,
|
||||
"applicant": frm.doc.applicant,
|
||||
"payment_account": frm.doc.payment_account,
|
||||
"interest_income_account": frm.doc.interest_income_account
|
||||
},
|
||||
callback: function(r) {
|
||||
if (r.message)
|
||||
var doc = frappe.model.sync(r.message)[0];
|
||||
frappe.set_route("Form", doc.doctype, doc.name, {'payment_rows': payment_rows});
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1,298 +0,0 @@
|
||||
{
|
||||
"actions": [],
|
||||
"allow_import": 1,
|
||||
"autoname": "ACC-LOAN-.YYYY.-.#####",
|
||||
"creation": "2016-12-02 10:11:49.673604",
|
||||
"doctype": "DocType",
|
||||
"document_type": "Document",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"applicant_type",
|
||||
"applicant",
|
||||
"applicant_name",
|
||||
"loan_type",
|
||||
"loan_application",
|
||||
"column_break_3",
|
||||
"posting_date",
|
||||
"company",
|
||||
"status",
|
||||
"repay_from_salary",
|
||||
"section_break_8",
|
||||
"loan_amount",
|
||||
"rate_of_interest",
|
||||
"disbursement_date",
|
||||
"repayment_start_date",
|
||||
"column_break_11",
|
||||
"repayment_method",
|
||||
"repayment_periods",
|
||||
"monthly_repayment_amount",
|
||||
"account_info",
|
||||
"mode_of_payment",
|
||||
"payment_account",
|
||||
"column_break_9",
|
||||
"loan_account",
|
||||
"interest_income_account",
|
||||
"section_break_15",
|
||||
"repayment_schedule",
|
||||
"section_break_17",
|
||||
"total_payment",
|
||||
"column_break_19",
|
||||
"total_interest_payable",
|
||||
"total_amount_paid",
|
||||
"amended_from"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "applicant_type",
|
||||
"fieldtype": "Select",
|
||||
"label": "Applicant Type",
|
||||
"options": "Employee\nMember",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "applicant",
|
||||
"fieldtype": "Dynamic Link",
|
||||
"in_standard_filter": 1,
|
||||
"label": "Applicant",
|
||||
"options": "applicant_type",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "applicant_name",
|
||||
"fieldtype": "Data",
|
||||
"in_global_search": 1,
|
||||
"label": "Applicant Name",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "loan_application",
|
||||
"fieldtype": "Link",
|
||||
"label": "Loan Application",
|
||||
"options": "Loan Application"
|
||||
},
|
||||
{
|
||||
"fieldname": "loan_type",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Loan Type",
|
||||
"options": "Loan Type",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_3",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"default": "Today",
|
||||
"fieldname": "posting_date",
|
||||
"fieldtype": "Date",
|
||||
"in_list_view": 1,
|
||||
"label": "Posting Date",
|
||||
"no_copy": 1,
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "company",
|
||||
"fieldtype": "Link",
|
||||
"label": "Company",
|
||||
"options": "Company",
|
||||
"remember_last_selected_value": 1,
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"default": "Sanctioned",
|
||||
"fieldname": "status",
|
||||
"fieldtype": "Select",
|
||||
"label": "Status",
|
||||
"no_copy": 1,
|
||||
"options": "Sanctioned\nDisbursed\nRepaid/Closed",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"depends_on": "eval:doc.applicant_type==\"Employee\"",
|
||||
"fieldname": "repay_from_salary",
|
||||
"fieldtype": "Check",
|
||||
"label": "Repay from Salary"
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_8",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Loan Details"
|
||||
},
|
||||
{
|
||||
"fieldname": "loan_amount",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Loan Amount",
|
||||
"options": "Company:company:default_currency",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fetch_from": "loan_type.rate_of_interest",
|
||||
"fieldname": "rate_of_interest",
|
||||
"fieldtype": "Percent",
|
||||
"label": "Rate of Interest (%) / Year",
|
||||
"read_only": 1,
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.status==\"Disbursed\"",
|
||||
"fieldname": "disbursement_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Disbursement Date"
|
||||
},
|
||||
{
|
||||
"fieldname": "repayment_start_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Repayment Start Date",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_11",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"default": "Repay Over Number of Periods",
|
||||
"fieldname": "repayment_method",
|
||||
"fieldtype": "Select",
|
||||
"label": "Repayment Method",
|
||||
"options": "\nRepay Fixed Amount per Period\nRepay Over Number of Periods",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "repayment_periods",
|
||||
"fieldtype": "Int",
|
||||
"label": "Repayment Period in Months"
|
||||
},
|
||||
{
|
||||
"fieldname": "monthly_repayment_amount",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Monthly Repayment Amount",
|
||||
"options": "Company:company:default_currency"
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"fieldname": "account_info",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Account Info"
|
||||
},
|
||||
{
|
||||
"fieldname": "mode_of_payment",
|
||||
"fieldtype": "Link",
|
||||
"label": "Mode of Payment",
|
||||
"options": "Mode of Payment",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "payment_account",
|
||||
"fieldtype": "Link",
|
||||
"label": "Payment Account",
|
||||
"options": "Account",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_9",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "loan_account",
|
||||
"fieldtype": "Link",
|
||||
"label": "Loan Account",
|
||||
"options": "Account",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "interest_income_account",
|
||||
"fieldtype": "Link",
|
||||
"label": "Interest Income Account",
|
||||
"options": "Account",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_15",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Repayment Schedule"
|
||||
},
|
||||
{
|
||||
"fieldname": "repayment_schedule",
|
||||
"fieldtype": "Table",
|
||||
"label": "Repayment Schedule",
|
||||
"no_copy": 1,
|
||||
"options": "Repayment Schedule"
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_17",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Totals"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "total_payment",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Total Payment",
|
||||
"options": "Company:company:default_currency",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_19",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "total_interest_payable",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Total Interest Payable",
|
||||
"options": "Company:company:default_currency",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"fieldname": "total_amount_paid",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Total Amount Paid",
|
||||
"options": "Company:company:default_currency",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "amended_from",
|
||||
"fieldtype": "Link",
|
||||
"label": "Amended From",
|
||||
"no_copy": 1,
|
||||
"options": "Loan",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2019-12-12 14:45:38.823072",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Loan",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"cancel": 1,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "HR Manager",
|
||||
"share": 1,
|
||||
"submit": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"read": 1,
|
||||
"role": "Employee"
|
||||
}
|
||||
],
|
||||
"search_fields": "posting_date",
|
||||
"sort_field": "creation",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
||||
@@ -1,240 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe, math, json
|
||||
import erpnext
|
||||
from frappe import _
|
||||
from frappe.utils import flt, rounded, add_months, nowdate, getdate
|
||||
from erpnext.controllers.accounts_controller import AccountsController
|
||||
|
||||
class Loan(AccountsController):
|
||||
def validate(self):
|
||||
validate_repayment_method(self.repayment_method, self.loan_amount, self.monthly_repayment_amount, self.repayment_periods)
|
||||
self.set_missing_fields()
|
||||
self.make_repayment_schedule()
|
||||
self.set_repayment_period()
|
||||
self.calculate_totals()
|
||||
|
||||
def set_missing_fields(self):
|
||||
if not self.company:
|
||||
self.company = erpnext.get_default_company()
|
||||
|
||||
if not self.posting_date:
|
||||
self.posting_date = nowdate()
|
||||
|
||||
if self.loan_type and not self.rate_of_interest:
|
||||
self.rate_of_interest = frappe.db.get_value("Loan Type", self.loan_type, "rate_of_interest")
|
||||
|
||||
if self.repayment_method == "Repay Over Number of Periods":
|
||||
self.monthly_repayment_amount = get_monthly_repayment_amount(self.repayment_method, self.loan_amount, self.rate_of_interest, self.repayment_periods)
|
||||
|
||||
if self.status == "Repaid/Closed":
|
||||
self.total_amount_paid = self.total_payment
|
||||
|
||||
|
||||
def make_jv_entry(self):
|
||||
self.check_permission('write')
|
||||
journal_entry = frappe.new_doc('Journal Entry')
|
||||
journal_entry.voucher_type = 'Bank Entry'
|
||||
journal_entry.user_remark = _('Against Loan: {0}').format(self.name)
|
||||
journal_entry.company = self.company
|
||||
journal_entry.posting_date = nowdate()
|
||||
|
||||
account_amt_list = []
|
||||
|
||||
account_amt_list.append({
|
||||
"account": self.loan_account,
|
||||
"party_type": self.applicant_type,
|
||||
"party": self.applicant,
|
||||
"debit_in_account_currency": self.loan_amount,
|
||||
"reference_type": "Loan",
|
||||
"reference_name": self.name,
|
||||
})
|
||||
account_amt_list.append({
|
||||
"account": self.payment_account,
|
||||
"credit_in_account_currency": self.loan_amount,
|
||||
"reference_type": "Loan",
|
||||
"reference_name": self.name,
|
||||
})
|
||||
journal_entry.set("accounts", account_amt_list)
|
||||
return journal_entry.as_dict()
|
||||
|
||||
def make_repayment_schedule(self):
|
||||
self.repayment_schedule = []
|
||||
payment_date = self.repayment_start_date
|
||||
balance_amount = self.loan_amount
|
||||
while(balance_amount > 0):
|
||||
interest_amount = rounded(balance_amount * flt(self.rate_of_interest) / (12*100))
|
||||
principal_amount = self.monthly_repayment_amount - interest_amount
|
||||
balance_amount = rounded(balance_amount + interest_amount - self.monthly_repayment_amount)
|
||||
|
||||
if balance_amount < 0:
|
||||
principal_amount += balance_amount
|
||||
balance_amount = 0.0
|
||||
|
||||
total_payment = principal_amount + interest_amount
|
||||
self.append("repayment_schedule", {
|
||||
"payment_date": payment_date,
|
||||
"principal_amount": principal_amount,
|
||||
"interest_amount": interest_amount,
|
||||
"total_payment": total_payment,
|
||||
"balance_loan_amount": balance_amount
|
||||
})
|
||||
next_payment_date = add_months(payment_date, 1)
|
||||
payment_date = next_payment_date
|
||||
|
||||
def set_repayment_period(self):
|
||||
if self.repayment_method == "Repay Fixed Amount per Period":
|
||||
repayment_periods = len(self.repayment_schedule)
|
||||
|
||||
self.repayment_periods = repayment_periods
|
||||
|
||||
def calculate_totals(self):
|
||||
self.total_payment = 0
|
||||
self.total_interest_payable = 0
|
||||
self.total_amount_paid = 0
|
||||
for data in self.repayment_schedule:
|
||||
self.total_payment += data.total_payment
|
||||
self.total_interest_payable +=data.interest_amount
|
||||
if data.paid:
|
||||
self.total_amount_paid += data.total_payment
|
||||
|
||||
def update_total_amount_paid(doc):
|
||||
total_amount_paid = 0
|
||||
for data in doc.repayment_schedule:
|
||||
if data.paid:
|
||||
total_amount_paid += data.total_payment
|
||||
frappe.db.set_value("Loan", doc.name, "total_amount_paid", total_amount_paid)
|
||||
|
||||
def update_disbursement_status(doc):
|
||||
disbursement = frappe.db.sql("""
|
||||
select posting_date, ifnull(sum(credit_in_account_currency), 0) as disbursed_amount
|
||||
from `tabGL Entry`
|
||||
where account = %s and against_voucher_type = 'Loan' and against_voucher = %s
|
||||
""", (doc.payment_account, doc.name), as_dict=1)[0]
|
||||
|
||||
disbursement_date = None
|
||||
if not disbursement or disbursement.disbursed_amount == 0:
|
||||
status = "Sanctioned"
|
||||
elif disbursement.disbursed_amount == doc.loan_amount:
|
||||
disbursement_date = disbursement.posting_date
|
||||
status = "Disbursed"
|
||||
elif disbursement.disbursed_amount > doc.loan_amount:
|
||||
frappe.throw(_("Disbursed Amount cannot be greater than Loan Amount {0}").format(doc.loan_amount))
|
||||
|
||||
if status == 'Disbursed' and getdate(disbursement_date) > getdate(frappe.db.get_value("Loan", doc.name, "repayment_start_date")):
|
||||
frappe.throw(_("Disbursement Date cannot be after Loan Repayment Start Date"))
|
||||
|
||||
frappe.db.sql("""
|
||||
update `tabLoan`
|
||||
set status = %s, disbursement_date = %s
|
||||
where name = %s
|
||||
""", (status, disbursement_date, doc.name))
|
||||
|
||||
def validate_repayment_method(repayment_method, loan_amount, monthly_repayment_amount, repayment_periods):
|
||||
if repayment_method == "Repay Over Number of Periods" and not repayment_periods:
|
||||
frappe.throw(_("Please enter Repayment Periods"))
|
||||
|
||||
if repayment_method == "Repay Fixed Amount per Period":
|
||||
if not monthly_repayment_amount:
|
||||
frappe.throw(_("Please enter repayment Amount"))
|
||||
if monthly_repayment_amount > loan_amount:
|
||||
frappe.throw(_("Monthly Repayment Amount cannot be greater than Loan Amount"))
|
||||
|
||||
def get_monthly_repayment_amount(repayment_method, loan_amount, rate_of_interest, repayment_periods):
|
||||
if rate_of_interest:
|
||||
monthly_interest_rate = flt(rate_of_interest) / (12 *100)
|
||||
monthly_repayment_amount = math.ceil((loan_amount * monthly_interest_rate *
|
||||
(1 + monthly_interest_rate)**repayment_periods) \
|
||||
/ ((1 + monthly_interest_rate)**repayment_periods - 1))
|
||||
else:
|
||||
monthly_repayment_amount = math.ceil(flt(loan_amount) / repayment_periods)
|
||||
return monthly_repayment_amount
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_loan_application(loan_application):
|
||||
loan = frappe.get_doc("Loan Application", loan_application)
|
||||
if loan:
|
||||
return loan.as_dict()
|
||||
|
||||
@frappe.whitelist()
|
||||
def make_repayment_entry(payment_rows, loan, company, loan_account, applicant_type, applicant, \
|
||||
payment_account=None, interest_income_account=None):
|
||||
|
||||
if isinstance(payment_rows, frappe.string_types):
|
||||
payment_rows_list = json.loads(payment_rows)
|
||||
else:
|
||||
frappe.throw(_("No repayments available for Journal Entry"))
|
||||
|
||||
if payment_rows_list:
|
||||
row_name = list(set(d["name"] for d in payment_rows_list))
|
||||
else:
|
||||
frappe.throw(_("No repayments selected for Journal Entry"))
|
||||
total_payment = 0
|
||||
principal_amount = 0
|
||||
interest_amount = 0
|
||||
for d in payment_rows_list:
|
||||
total_payment += d["total_payment"]
|
||||
principal_amount += d["principal_amount"]
|
||||
interest_amount += d["interest_amount"]
|
||||
|
||||
journal_entry = frappe.new_doc('Journal Entry')
|
||||
journal_entry.voucher_type = 'Bank Entry'
|
||||
journal_entry.user_remark = _('Against Loan: {0}').format(loan)
|
||||
journal_entry.company = company
|
||||
journal_entry.posting_date = nowdate()
|
||||
journal_entry.paid_loan = json.dumps(row_name)
|
||||
account_amt_list = []
|
||||
|
||||
account_amt_list.append({
|
||||
"account": payment_account,
|
||||
"debit_in_account_currency": total_payment,
|
||||
"reference_type": "Loan",
|
||||
"reference_name": loan,
|
||||
})
|
||||
account_amt_list.append({
|
||||
"account": loan_account,
|
||||
"credit_in_account_currency": principal_amount,
|
||||
"party_type": applicant_type,
|
||||
"party": applicant,
|
||||
"reference_type": "Loan",
|
||||
"reference_name": loan,
|
||||
})
|
||||
account_amt_list.append({
|
||||
"account": interest_income_account,
|
||||
"credit_in_account_currency": interest_amount,
|
||||
"reference_type": "Loan",
|
||||
"reference_name": loan,
|
||||
})
|
||||
journal_entry.set("accounts", account_amt_list)
|
||||
|
||||
return journal_entry.as_dict()
|
||||
|
||||
@frappe.whitelist()
|
||||
def make_jv_entry(loan, company, loan_account, applicant_type, applicant, loan_amount,payment_account=None):
|
||||
|
||||
journal_entry = frappe.new_doc('Journal Entry')
|
||||
journal_entry.voucher_type = 'Bank Entry'
|
||||
journal_entry.user_remark = _('Against Loan: {0}').format(loan)
|
||||
journal_entry.company = company
|
||||
journal_entry.posting_date = nowdate()
|
||||
account_amt_list = []
|
||||
|
||||
account_amt_list.append({
|
||||
"account": loan_account,
|
||||
"debit_in_account_currency": loan_amount,
|
||||
"party_type": applicant_type,
|
||||
"party": applicant,
|
||||
"reference_type": "Loan",
|
||||
"reference_name": loan,
|
||||
})
|
||||
account_amt_list.append({
|
||||
"account": payment_account,
|
||||
"credit_in_account_currency": loan_amount,
|
||||
"reference_type": "Loan",
|
||||
"reference_name": loan,
|
||||
})
|
||||
journal_entry.set("accounts", account_amt_list)
|
||||
return journal_entry.as_dict()
|
||||
@@ -1,26 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
from frappe import _
|
||||
|
||||
def get_data():
|
||||
return {
|
||||
'fieldname': 'applicant',
|
||||
'non_standard_fieldnames': {
|
||||
'Journal Entry': 'reference_name',
|
||||
'Salary Slip': 'employee'
|
||||
},
|
||||
'transactions': [
|
||||
{
|
||||
'label': _('Applicant'),
|
||||
'items': ['Loan Application']
|
||||
},
|
||||
|
||||
{
|
||||
'label': _('Account'),
|
||||
'items': ['Journal Entry']
|
||||
},
|
||||
{
|
||||
'label': _('Employee'),
|
||||
'items': ['Salary Slip']
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
|
||||
QUnit.test("Test Loan [HR]", function(assert) {
|
||||
assert.expect(8);
|
||||
let done = assert.async();
|
||||
let employee_name;
|
||||
|
||||
// To create a loan and check principal,interest and balance amount
|
||||
let loan_creation = (ename,lname) => {
|
||||
return frappe.run_serially([
|
||||
() => frappe.db.get_value('Employee', {'employee_name': ename}, 'name'),
|
||||
(r) => {
|
||||
employee_name = r.message.name;
|
||||
},
|
||||
() => frappe.db.get_value('Loan Application', {'loan_type': lname}, 'name'),
|
||||
(r) => {
|
||||
// Creating loan for an employee
|
||||
return frappe.tests.make('Loan', [
|
||||
{ company: 'For Testing'},
|
||||
{ posting_date: '2017-08-26'},
|
||||
{ applicant: employee_name},
|
||||
{ loan_application: r.message.name},
|
||||
{ disbursement_date: '2018-08-26'},
|
||||
{ mode_of_payment: 'Cash'},
|
||||
{ loan_account: 'Temporary Opening - FT'},
|
||||
{ interest_income_account: 'Service - FT'}
|
||||
]);
|
||||
},
|
||||
() => frappe.timeout(3),
|
||||
() => frappe.click_button('Submit'),
|
||||
() => frappe.timeout(1),
|
||||
() => frappe.click_button('Yes'),
|
||||
() => frappe.timeout(3),
|
||||
|
||||
// Checking if all the amounts are correctly calculated
|
||||
() => {
|
||||
assert.ok(cur_frm.get_field('applicant_name').value=='Test Employee 1'&&
|
||||
(cur_frm.get_field('status').value=='Sanctioned'),
|
||||
'Loan Sanctioned for correct employee');
|
||||
|
||||
assert.equal(7270,
|
||||
cur_frm.get_doc('repayment_schedule').repayment_schedule[0].principal_amount,
|
||||
'Principal amount for first instalment is correctly calculated');
|
||||
|
||||
assert.equal(2333,
|
||||
cur_frm.get_doc('repayment_schedule').repayment_schedule[0].interest_amount,
|
||||
'Interest amount for first instalment is correctly calculated');
|
||||
|
||||
assert.equal(192730,
|
||||
cur_frm.get_doc('repayment_schedule').repayment_schedule[0].balance_loan_amount,
|
||||
'Balance amount after first instalment is correctly calculated');
|
||||
|
||||
assert.equal(9479,
|
||||
cur_frm.get_doc('repayment_schedule').repayment_schedule[23].principal_amount,
|
||||
'Principal amount for last instalment is correctly calculated');
|
||||
|
||||
assert.equal(111,
|
||||
cur_frm.get_doc('repayment_schedule').repayment_schedule[23].interest_amount,
|
||||
'Interest amount for last instalment is correctly calculated');
|
||||
|
||||
assert.equal(0,
|
||||
cur_frm.get_doc('repayment_schedule').repayment_schedule[23].balance_loan_amount,
|
||||
'Balance amount after last instalment is correctly calculated');
|
||||
|
||||
},
|
||||
() => frappe.set_route('List','Loan','List'),
|
||||
() => frappe.timeout(2),
|
||||
|
||||
// Checking the submission of Loan
|
||||
() => {
|
||||
assert.ok(cur_list.data[0].docstatus==1,'Loan sanctioned and submitted successfully');
|
||||
},
|
||||
]);
|
||||
};
|
||||
frappe.run_serially([
|
||||
// Creating loan
|
||||
() => loan_creation('Test Employee 1','Test Loan'),
|
||||
() => done()
|
||||
]);
|
||||
});
|
||||
@@ -1,71 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
import erpnext
|
||||
import unittest
|
||||
from frappe.utils import nowdate, add_days
|
||||
from erpnext.hr.doctype.salary_structure.test_salary_structure import make_employee
|
||||
|
||||
class TestLoan(unittest.TestCase):
|
||||
def setUp(self):
|
||||
create_loan_type("Personal Loan", 500000, 8.4)
|
||||
self.applicant = make_employee("robert_loan@loan.com")
|
||||
create_loan(self.applicant, "Personal Loan", 280000, "Repay Over Number of Periods", 20)
|
||||
|
||||
def test_loan(self):
|
||||
loan = frappe.get_doc("Loan", {"applicant":self.applicant})
|
||||
self.assertEquals(loan.monthly_repayment_amount, 15052)
|
||||
self.assertEquals(loan.total_interest_payable, 21034)
|
||||
self.assertEquals(loan.total_payment, 301034)
|
||||
|
||||
schedule = loan.repayment_schedule
|
||||
|
||||
self.assertEqual(len(schedule), 20)
|
||||
|
||||
for idx, principal_amount, interest_amount, balance_loan_amount in [[3, 13369, 1683, 227079], [19, 14941, 105, 0], [17, 14740, 312, 29785]]:
|
||||
self.assertEqual(schedule[idx].principal_amount, principal_amount)
|
||||
self.assertEqual(schedule[idx].interest_amount, interest_amount)
|
||||
self.assertEqual(schedule[idx].balance_loan_amount, balance_loan_amount)
|
||||
|
||||
loan.repayment_method = "Repay Fixed Amount per Period"
|
||||
loan.monthly_repayment_amount = 14000
|
||||
loan.save()
|
||||
|
||||
self.assertEquals(len(loan.repayment_schedule), 22)
|
||||
self.assertEquals(loan.total_interest_payable, 22712)
|
||||
self.assertEquals(loan.total_payment, 302712)
|
||||
|
||||
def create_loan_type(loan_name, maximum_loan_amount, rate_of_interest):
|
||||
if not frappe.db.exists("Loan Type", loan_name):
|
||||
frappe.get_doc({
|
||||
"doctype": "Loan Type",
|
||||
"loan_name": loan_name,
|
||||
"maximum_loan_amount": maximum_loan_amount,
|
||||
"rate_of_interest": rate_of_interest
|
||||
}).insert()
|
||||
|
||||
def create_loan(applicant, loan_type, loan_amount, repayment_method, repayment_periods):
|
||||
create_loan_type(loan_type, 500000, 8.4)
|
||||
if not frappe.db.get_value("Loan", {"applicant":applicant}):
|
||||
loan = frappe.new_doc("Loan")
|
||||
loan.update({
|
||||
"applicant": applicant,
|
||||
"loan_type": loan_type,
|
||||
"loan_amount": loan_amount,
|
||||
"repayment_method": repayment_method,
|
||||
"repayment_periods": repayment_periods,
|
||||
"disbursement_date": nowdate(),
|
||||
"repayment_start_date": nowdate(),
|
||||
"status": "Disbursed",
|
||||
"mode_of_payment": frappe.db.get_value('Mode of Payment', {'type': 'Cash'}, 'name'),
|
||||
"payment_account": frappe.db.get_value('Account', {'account_type': 'Cash', 'company': erpnext.get_default_company(),'is_group':0}, "name"),
|
||||
"loan_account": frappe.db.get_value('Account', {'account_type': 'Cash', 'company': erpnext.get_default_company(),'is_group':0}, "name"),
|
||||
"interest_income_account": frappe.db.get_value('Account', {'account_type': 'Cash', 'company': erpnext.get_default_company(),'is_group':0}, "name")
|
||||
})
|
||||
loan.insert()
|
||||
return loan
|
||||
else:
|
||||
return frappe.get_doc("Loan", {"applicant":applicant})
|
||||
@@ -1,42 +0,0 @@
|
||||
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
{% include 'erpnext/hr/loan_common.js' %};
|
||||
|
||||
frappe.ui.form.on('Loan Application', {
|
||||
refresh: function(frm) {
|
||||
frm.trigger("toggle_fields")
|
||||
frm.trigger("add_toolbar_buttons")
|
||||
},
|
||||
repayment_method: function(frm) {
|
||||
frm.doc.repayment_amount = frm.doc.repayment_periods = ""
|
||||
frm.trigger("toggle_fields")
|
||||
frm.trigger("toggle_required")
|
||||
},
|
||||
toggle_fields: function(frm) {
|
||||
frm.toggle_enable("repayment_amount", frm.doc.repayment_method=="Repay Fixed Amount per Period")
|
||||
frm.toggle_enable("repayment_periods", frm.doc.repayment_method=="Repay Over Number of Periods")
|
||||
},
|
||||
toggle_required: function(frm){
|
||||
frm.toggle_reqd("repayment_amount", cint(frm.doc.repayment_method=='Repay Fixed Amount per Period'))
|
||||
frm.toggle_reqd("repayment_periods", cint(frm.doc.repayment_method=='Repay Over Number of Periods'))
|
||||
},
|
||||
add_toolbar_buttons: function(frm) {
|
||||
if (frm.doc.status == "Approved") {
|
||||
frm.add_custom_button(__('Create Loan'), function() {
|
||||
frappe.call({
|
||||
method: "erpnext.hr.doctype.loan_application.loan_application.make_loan",
|
||||
args: {
|
||||
"source_name": frm.doc.name
|
||||
},
|
||||
callback: function(r) {
|
||||
if(!r.exc) {
|
||||
var doc = frappe.model.sync(r.message);
|
||||
frappe.set_route("Form", r.message.doctype, r.message.name);
|
||||
}
|
||||
}
|
||||
});
|
||||
}).addClass("btn-primary");
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -1,840 +0,0 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"autoname": "ACC-LOAP-.YYYY.-.#####",
|
||||
"beta": 0,
|
||||
"creation": "2016-12-02 12:35:56.046811",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "applicant_type",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Applicant Type",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Employee\nMember",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "applicant",
|
||||
"fieldtype": "Dynamic Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 1,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 1,
|
||||
"label": "Applicant",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "applicant_type",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "applicant",
|
||||
"fieldname": "applicant_name",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 1,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Applicant Name",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "column_break_2",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "Today",
|
||||
"fieldname": "posting_date",
|
||||
"fieldtype": "Date",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Posting Date",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "status",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Status",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"options": "Open\nApproved\nRejected",
|
||||
"permlevel": 1,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "company",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Company",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Company",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "section_break_4",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Loan Info",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "loan_type",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Loan Type",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Loan Type",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "loan_amount",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Loan Amount",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Company:company:default_currency",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "required_by_date",
|
||||
"fieldtype": "Date",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Required by Date",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "column_break_7",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "description",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Reason",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "repayment_info",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Repayment Info",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "repayment_method",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Repayment Method",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "\nRepay Fixed Amount per Period\nRepay Over Number of Periods",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_from": "loan_type.rate_of_interest",
|
||||
"fieldname": "rate_of_interest",
|
||||
"fieldtype": "Percent",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Rate of Interest",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "total_payable_interest",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Total Payable Interest",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Company:company:default_currency",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "column_break_11",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "",
|
||||
"fieldname": "repayment_amount",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Monthly Repayment Amount",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Company:company:default_currency",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "",
|
||||
"fieldname": "repayment_periods",
|
||||
"fieldtype": "Int",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Repayment Period in Months",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "total_payable_amount",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Total Payable Amount",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Company:company:default_currency",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "amended_from",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Amended From",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"options": "Loan Application",
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"is_submittable": 1,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-08-21 16:15:53.688596",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Loan Application",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 1,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "HR Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Employee",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 0,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "HR Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 0,
|
||||
"delete": 0,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Employee",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 0
|
||||
}
|
||||
],
|
||||
"quick_entry": 0,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"search_fields": "applicant_type, applicant, loan_type, loan_amount",
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"timeline_field": "applicant",
|
||||
"title_field": "applicant",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0,
|
||||
"track_views": 0
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe, math
|
||||
from frappe import _
|
||||
from frappe.utils import flt, rounded
|
||||
from frappe.model.mapper import get_mapped_doc
|
||||
from frappe.model.document import Document
|
||||
|
||||
from erpnext.hr.doctype.loan.loan import get_monthly_repayment_amount, validate_repayment_method
|
||||
|
||||
class LoanApplication(Document):
|
||||
def validate(self):
|
||||
validate_repayment_method(self.repayment_method, self.loan_amount, self.repayment_amount, self.repayment_periods)
|
||||
self.validate_loan_amount()
|
||||
self.get_repayment_details()
|
||||
|
||||
def validate_loan_amount(self):
|
||||
maximum_loan_limit = frappe.db.get_value('Loan Type', self.loan_type, 'maximum_loan_amount')
|
||||
if maximum_loan_limit and self.loan_amount > maximum_loan_limit:
|
||||
frappe.throw(_("Loan Amount cannot exceed Maximum Loan Amount of {0}").format(maximum_loan_limit))
|
||||
|
||||
def get_repayment_details(self):
|
||||
if self.repayment_method == "Repay Over Number of Periods":
|
||||
self.repayment_amount = get_monthly_repayment_amount(self.repayment_method, self.loan_amount, self.rate_of_interest, self.repayment_periods)
|
||||
|
||||
if self.repayment_method == "Repay Fixed Amount per Period":
|
||||
monthly_interest_rate = flt(self.rate_of_interest) / (12 *100)
|
||||
if monthly_interest_rate:
|
||||
min_repayment_amount = self.loan_amount*monthly_interest_rate
|
||||
if (self.repayment_amount - min_repayment_amount) <= 0:
|
||||
frappe.throw(_("Repayment Amount must be greater than " \
|
||||
+ str(flt(min_repayment_amount, 2))))
|
||||
self.repayment_periods = math.ceil((math.log(self.repayment_amount) -
|
||||
math.log(self.repayment_amount - min_repayment_amount)) /(math.log(1 + monthly_interest_rate)))
|
||||
else:
|
||||
self.repayment_periods = self.loan_amount / self.repayment_amount
|
||||
|
||||
self.calculate_payable_amount()
|
||||
|
||||
def calculate_payable_amount(self):
|
||||
balance_amount = self.loan_amount
|
||||
self.total_payable_amount = 0
|
||||
self.total_payable_interest = 0
|
||||
|
||||
while(balance_amount > 0):
|
||||
interest_amount = rounded(balance_amount * flt(self.rate_of_interest) / (12*100))
|
||||
balance_amount = rounded(balance_amount + interest_amount - self.repayment_amount)
|
||||
|
||||
self.total_payable_interest += interest_amount
|
||||
|
||||
self.total_payable_amount = self.loan_amount + self.total_payable_interest
|
||||
|
||||
@frappe.whitelist()
|
||||
def make_loan(source_name, target_doc = None):
|
||||
doclist = get_mapped_doc("Loan Application", source_name, {
|
||||
"Loan Application": {
|
||||
"doctype": "Loan",
|
||||
"field_map": {
|
||||
"repayment_amount": "monthly_repayment_amount"
|
||||
},
|
||||
"validation": {
|
||||
"docstatus": ["=", 1]
|
||||
}
|
||||
}
|
||||
}, target_doc)
|
||||
|
||||
return doclist
|
||||
@@ -1,12 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
from frappe import _
|
||||
|
||||
def get_data():
|
||||
return {
|
||||
'fieldname': 'loan_application',
|
||||
'transactions': [
|
||||
{
|
||||
'items': ['Loan']
|
||||
},
|
||||
],
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
QUnit.module('hr');
|
||||
|
||||
QUnit.test("Test: Loan Application [HR]", function (assert) {
|
||||
assert.expect(8);
|
||||
let done = assert.async();
|
||||
let employee_name;
|
||||
|
||||
frappe.run_serially([
|
||||
// Creation of Loan Application
|
||||
() => frappe.db.get_value('Employee', {'employee_name': 'Test Employee 1'}, 'name'),
|
||||
(r) => {
|
||||
employee_name = r.message.name;
|
||||
},
|
||||
() => {
|
||||
return frappe.tests.make('Loan Application', [
|
||||
{ company: 'For Testing'},
|
||||
{ applicant: employee_name},
|
||||
{ applicant_name: 'Test Employee 1'},
|
||||
{ status: 'Approved'},
|
||||
{ loan_type: 'Test Loan '},
|
||||
{ loan_amount: 200000},
|
||||
{ description: 'This is just a test'},
|
||||
{ repayment_method: 'Repay Over Number of Periods'},
|
||||
{ repayment_periods: 24},
|
||||
{ rate_of_interest: 14}
|
||||
]);
|
||||
},
|
||||
() => frappe.timeout(6),
|
||||
() => frappe.click_button('Submit'),
|
||||
() => frappe.timeout(1),
|
||||
() => frappe.click_button('Yes'),
|
||||
() => frappe.timeout(2),
|
||||
() => {
|
||||
// To check if all the amounts are correctly calculated
|
||||
|
||||
assert.ok(cur_frm.get_field('applicant_name').value == 'Test Employee 1',
|
||||
'Application created successfully');
|
||||
|
||||
assert.ok(cur_frm.get_field('status').value=='Approved',
|
||||
'Status of application is correctly set');
|
||||
|
||||
assert.ok(cur_frm.get_field('loan_type').value=='Test Loan',
|
||||
'Application is created for correct Loan Type');
|
||||
|
||||
assert.ok(cur_frm.get_field('status').value=='Approved',
|
||||
'Status of application is correctly set');
|
||||
|
||||
assert.ok(cur_frm.get_field('repayment_amount').value==9603,
|
||||
'Repayment amount is correctly calculated');
|
||||
|
||||
assert.ok(cur_frm.get_field('total_payable_interest').value==30459,
|
||||
'Interest amount is correctly calculated');
|
||||
|
||||
assert.ok(cur_frm.get_field('total_payable_amount').value==230459,
|
||||
'Total payable amount is correctly calculated');
|
||||
},
|
||||
|
||||
() => frappe.set_route('List','Loan Application','List'),
|
||||
() => frappe.timeout(2),
|
||||
|
||||
// Checking the submission of Loan Application
|
||||
() => {
|
||||
assert.ok(cur_list.data[0].docstatus==1,'Loan Application submitted successfully');
|
||||
},
|
||||
() => frappe.timeout(1),
|
||||
() => done()
|
||||
]);
|
||||
});
|
||||
@@ -1,52 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
import unittest
|
||||
from erpnext.hr.doctype.salary_structure.test_salary_structure import make_employee
|
||||
|
||||
class TestLoanApplication(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.create_loan_type()
|
||||
self.applicant = make_employee("kate_loan@loan.com")
|
||||
self.create_loan_application()
|
||||
|
||||
def create_loan_type(self):
|
||||
if not frappe.db.get_value("Loan Type", "Home Loan"):
|
||||
frappe.get_doc({
|
||||
"doctype": "Loan Type",
|
||||
"loan_name": "Home Loan",
|
||||
"maximum_loan_amount": 500000,
|
||||
"rate_of_interest": 9.2
|
||||
}).insert()
|
||||
|
||||
def create_loan_application(self):
|
||||
if not frappe.db.get_value("Loan Application", {"applicant":self.applicant}, "name"):
|
||||
loan_application = frappe.new_doc("Loan Application")
|
||||
loan_application.update({
|
||||
"applicant": self.applicant,
|
||||
"loan_type": "Home Loan",
|
||||
"rate_of_interest": 9.2,
|
||||
"loan_amount": 250000,
|
||||
"repayment_method": "Repay Over Number of Periods",
|
||||
"repayment_periods": 18
|
||||
})
|
||||
loan_application.insert()
|
||||
|
||||
|
||||
def test_loan_totals(self):
|
||||
loan_application = frappe.get_doc("Loan Application", {"applicant":self.applicant})
|
||||
|
||||
self.assertEqual(loan_application.total_payable_interest, 18599)
|
||||
self.assertEqual(loan_application.total_payable_amount, 268599)
|
||||
self.assertEqual(loan_application.repayment_amount, 14923)
|
||||
|
||||
loan_application.repayment_periods = 24
|
||||
loan_application.save()
|
||||
loan_application.reload()
|
||||
|
||||
self.assertEqual(loan_application.total_payable_interest, 24657)
|
||||
self.assertEqual(loan_application.total_payable_amount, 274657)
|
||||
self.assertEqual(loan_application.repayment_amount, 11445)
|
||||
@@ -1,7 +0,0 @@
|
||||
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Loan Type', {
|
||||
refresh: function(frm) {
|
||||
}
|
||||
});
|
||||
@@ -1,259 +0,0 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"autoname": "field:loan_name",
|
||||
"beta": 0,
|
||||
"creation": "2016-12-02 10:41:40.732843",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"fields": [
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "loan_name",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Loan Name",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "maximum_loan_amount",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Maximum Loan Amount",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Company:company:default_currency",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "rate_of_interest",
|
||||
"fieldtype": "Percent",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Rate of Interest (%) Yearly",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "column_break_2",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "",
|
||||
"fieldname": "disabled",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Disabled",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "description",
|
||||
"fieldtype": "Text",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Description",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-03-29 21:23:08.665245",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Loan Type",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "HR Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 0,
|
||||
"delete": 0,
|
||||
"email": 0,
|
||||
"export": 0,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 0,
|
||||
"read": 1,
|
||||
"report": 0,
|
||||
"role": "Employee",
|
||||
"set_user_permissions": 0,
|
||||
"share": 0,
|
||||
"submit": 0,
|
||||
"write": 0
|
||||
}
|
||||
],
|
||||
"quick_entry": 0,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 0,
|
||||
"track_seen": 0
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
from frappe import _
|
||||
|
||||
def get_data():
|
||||
return {
|
||||
'fieldname': 'loan_type',
|
||||
'transactions': [
|
||||
{
|
||||
'items': ['Loan Application']
|
||||
},
|
||||
],
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
QUnit.module('hr');
|
||||
|
||||
QUnit.test("Test: Loan Type [HR]", function (assert) {
|
||||
assert.expect(3);
|
||||
let done = assert.async();
|
||||
|
||||
frappe.run_serially([
|
||||
// Loan Type creation
|
||||
() => {
|
||||
frappe.tests.make('Loan Type', [
|
||||
{ loan_name: 'Test Loan'},
|
||||
{ maximum_loan_amount: 400000},
|
||||
{ rate_of_interest: 14},
|
||||
{ description:
|
||||
'This is just a test.'}
|
||||
]);
|
||||
},
|
||||
() => frappe.timeout(7),
|
||||
() => frappe.set_route('List','Loan Type','List'),
|
||||
() => frappe.timeout(4),
|
||||
|
||||
// Checking if the fields are correctly set
|
||||
() => {
|
||||
assert.ok(cur_list.data.length==1, 'Loan Type created successfully');
|
||||
assert.ok(cur_list.data[0].name=='Test Loan', 'Loan title Correctly set');
|
||||
assert.ok(cur_list.data[0].disabled==0, 'Loan enabled');
|
||||
},
|
||||
() => done()
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
import unittest
|
||||
|
||||
# test_records = frappe.get_test_records('Loan Type')
|
||||
|
||||
class TestLoanType(unittest.TestCase):
|
||||
pass
|
||||
@@ -157,19 +157,6 @@ class PayrollEntry(Document):
|
||||
for ss in submitted_ss:
|
||||
ss.email_salary_slip()
|
||||
|
||||
def get_loan_details(self):
|
||||
"""
|
||||
Get loan details from submitted salary slip based on selected criteria
|
||||
"""
|
||||
cond = self.get_filter_condition()
|
||||
return frappe.db.sql(""" select eld.loan_account, eld.loan,
|
||||
eld.interest_income_account, eld.principal_amount, eld.interest_amount, eld.total_payment,t1.employee
|
||||
from
|
||||
`tabSalary Slip` t1, `tabSalary Slip Loan` eld
|
||||
where
|
||||
t1.docstatus = 1 and t1.name = eld.parent and start_date >= %s and end_date <= %s %s
|
||||
""" % ('%s', '%s', cond), (self.start_date, self.end_date), as_dict=True) or []
|
||||
|
||||
def get_salary_component_account(self, salary_component):
|
||||
account = frappe.db.get_value("Salary Component Account",
|
||||
{"parent": salary_component, "company": self.company}, "default_account")
|
||||
@@ -225,7 +212,6 @@ class PayrollEntry(Document):
|
||||
earnings = self.get_salary_component_total(component_type = "earnings") or {}
|
||||
deductions = self.get_salary_component_total(component_type = "deductions") or {}
|
||||
default_payroll_payable_account = self.get_default_payroll_payable_account()
|
||||
loan_details = self.get_loan_details()
|
||||
jv_name = ""
|
||||
precision = frappe.get_precision("Journal Entry Account", "debit_in_account_currency")
|
||||
|
||||
@@ -262,29 +248,6 @@ class PayrollEntry(Document):
|
||||
"project": self.project
|
||||
})
|
||||
|
||||
# Loan
|
||||
for data in loan_details:
|
||||
accounts.append({
|
||||
"account": data.loan_account,
|
||||
"credit_in_account_currency": data.principal_amount,
|
||||
"party_type": "Employee",
|
||||
"party": data.employee
|
||||
})
|
||||
|
||||
if data.interest_amount and not data.interest_income_account:
|
||||
frappe.throw(_("Select interest income account in loan {0}").format(data.loan))
|
||||
|
||||
if data.interest_income_account and data.interest_amount:
|
||||
accounts.append({
|
||||
"account": data.interest_income_account,
|
||||
"credit_in_account_currency": data.interest_amount,
|
||||
"cost_center": self.cost_center,
|
||||
"project": self.project,
|
||||
"party_type": "Employee",
|
||||
"party": data.employee
|
||||
})
|
||||
payable_amount -= flt(data.total_payment, precision)
|
||||
|
||||
# Payable amount
|
||||
accounts.append({
|
||||
"account": default_payroll_payable_account,
|
||||
|
||||
@@ -6,20 +6,22 @@ import erpnext
|
||||
import frappe
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from erpnext.accounts.utils import get_fiscal_year, getdate, nowdate
|
||||
from frappe.utils import add_months
|
||||
from erpnext.hr.doctype.payroll_entry.payroll_entry import get_start_end_dates, get_end_date
|
||||
from erpnext.hr.doctype.employee.test_employee import make_employee
|
||||
from erpnext.hr.doctype.salary_slip.test_salary_slip import get_salary_component_account, \
|
||||
make_earning_salary_component, make_deduction_salary_component
|
||||
from erpnext.hr.doctype.salary_structure.test_salary_structure import make_salary_structure
|
||||
from erpnext.hr.doctype.loan.test_loan import create_loan
|
||||
from erpnext.loan_management.doctype.loan.test_loan import create_loan, make_loan_disbursement_entry
|
||||
from erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual import make_accrual_interest_entry_for_term_loans
|
||||
|
||||
class TestPayrollEntry(unittest.TestCase):
|
||||
def setUp(self):
|
||||
for dt in ["Salary Slip", "Salary Component", "Salary Component Account", "Payroll Entry", "Loan"]:
|
||||
for dt in ["Salary Slip", "Salary Component", "Salary Component Account", "Payroll Entry"]:
|
||||
frappe.db.sql("delete from `tab%s`" % dt)
|
||||
|
||||
make_earning_salary_component(setup=True)
|
||||
make_deduction_salary_component(setup=True)
|
||||
make_earning_salary_component(setup=True, company_list=["_Test Company"])
|
||||
make_deduction_salary_component(setup=True, company_list=["_Test Company"])
|
||||
|
||||
frappe.db.set_value("HR Settings", None, "email_salary_slip_to_employee", 0)
|
||||
|
||||
@@ -49,8 +51,8 @@ class TestPayrollEntry(unittest.TestCase):
|
||||
def test_loan(self):
|
||||
|
||||
branch = "Test Employee Branch"
|
||||
applicant = make_employee("test_employee@loan.com")
|
||||
company = erpnext.get_default_company()
|
||||
applicant = make_employee("test_employee@loan.com", company="_Test Company")
|
||||
company = "_Test Company"
|
||||
holiday_list = make_holiday("test holiday for loan")
|
||||
|
||||
company_doc = frappe.get_doc('Company', company)
|
||||
@@ -70,16 +72,21 @@ class TestPayrollEntry(unittest.TestCase):
|
||||
employee_doc.holiday_list = holiday_list
|
||||
employee_doc.save()
|
||||
|
||||
loan = create_loan(applicant,
|
||||
"Personal Loan", 280000, "Repay Over Number of Periods", 20)
|
||||
salary_structure = "Test Salary Structure for Loan"
|
||||
make_salary_structure(salary_structure, "Monthly", employee=employee_doc.name, company="_Test Company")
|
||||
|
||||
loan = create_loan(applicant, "Car Loan", 280000, "Repay Over Number of Periods", 20, posting_date=add_months(nowdate(), -1))
|
||||
loan.repay_from_salary = 1
|
||||
loan.submit()
|
||||
salary_structure = "Test Salary Structure for Loan"
|
||||
make_salary_structure(salary_structure, "Monthly", employee_doc.name)
|
||||
|
||||
make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=add_months(nowdate(), -1))
|
||||
|
||||
make_accrual_interest_entry_for_term_loans(posting_date=nowdate())
|
||||
|
||||
|
||||
dates = get_start_end_dates('Monthly', nowdate())
|
||||
make_payroll_entry(start_date=dates.start_date,
|
||||
end_date=dates.end_date, branch=branch)
|
||||
make_payroll_entry(company="_Test Company", start_date=dates.start_date,
|
||||
end_date=dates.end_date, branch=branch, cost_center="Main - _TC", payment_account="Cash - _TC")
|
||||
|
||||
name = frappe.db.get_value('Salary Slip',
|
||||
{'posting_date': nowdate(), 'employee': applicant}, 'name')
|
||||
@@ -109,6 +116,13 @@ def make_payroll_entry(**args):
|
||||
payroll_entry.posting_date = nowdate()
|
||||
payroll_entry.payroll_frequency = "Monthly"
|
||||
payroll_entry.branch = args.branch or None
|
||||
|
||||
if args.cost_center:
|
||||
payroll_entry.cost_center = args.cost_center
|
||||
|
||||
if args.payment_account:
|
||||
payroll_entry.payment_account = args.payment_account
|
||||
|
||||
payroll_entry.save()
|
||||
payroll_entry.create_salary_slips()
|
||||
payroll_entry.submit_salary_slips()
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
{
|
||||
"creation": "2016-12-20 15:32:25.078334",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"payment_date",
|
||||
"principal_amount",
|
||||
"interest_amount",
|
||||
"total_payment",
|
||||
"balance_loan_amount",
|
||||
"paid"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"columns": 2,
|
||||
"fieldname": "payment_date",
|
||||
"fieldtype": "Date",
|
||||
"in_list_view": 1,
|
||||
"label": "Payment Date"
|
||||
},
|
||||
{
|
||||
"columns": 2,
|
||||
"fieldname": "principal_amount",
|
||||
"fieldtype": "Currency",
|
||||
"in_list_view": 1,
|
||||
"label": "Principal Amount",
|
||||
"no_copy": 1,
|
||||
"options": "Company:company:default_currency",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"columns": 2,
|
||||
"fieldname": "interest_amount",
|
||||
"fieldtype": "Currency",
|
||||
"in_list_view": 1,
|
||||
"label": "Interest Amount",
|
||||
"no_copy": 1,
|
||||
"options": "Company:company:default_currency",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"columns": 2,
|
||||
"fieldname": "total_payment",
|
||||
"fieldtype": "Currency",
|
||||
"in_list_view": 1,
|
||||
"label": "Total Payment",
|
||||
"options": "Company:company:default_currency",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"columns": 2,
|
||||
"fieldname": "balance_loan_amount",
|
||||
"fieldtype": "Currency",
|
||||
"in_list_view": 1,
|
||||
"label": "Balance Loan Amount",
|
||||
"no_copy": 1,
|
||||
"options": "Company:company:default_currency",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "paid",
|
||||
"fieldtype": "Check",
|
||||
"in_list_view": 1,
|
||||
"label": "Paid",
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"istable": 1,
|
||||
"modified": "2019-10-29 11:45:10.694557",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Repayment Schedule",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
||||
@@ -17,6 +17,7 @@ from erpnext.hr.doctype.additional_salary.additional_salary import get_additiona
|
||||
from erpnext.hr.doctype.payroll_period.payroll_period import get_period_factor, get_payroll_period
|
||||
from erpnext.hr.doctype.employee_benefit_application.employee_benefit_application import get_benefit_component_amount
|
||||
from erpnext.hr.doctype.employee_benefit_claim.employee_benefit_claim import get_benefit_claim_amount, get_last_payroll_period_benefits
|
||||
from erpnext.loan_management.doctype.loan_repayment.loan_repayment import calculate_amounts, create_repayment_entry
|
||||
|
||||
class SalarySlip(TransactionBase):
|
||||
def __init__(self, *args, **kwargs):
|
||||
@@ -66,6 +67,7 @@ class SalarySlip(TransactionBase):
|
||||
self.set_status()
|
||||
self.update_status(self.name)
|
||||
self.update_salary_slip_in_additional_salary()
|
||||
self.make_loan_repayment_entry()
|
||||
if (frappe.db.get_single_value("HR Settings", "email_salary_slip_to_employee")) and not frappe.flags.via_payroll_entry:
|
||||
self.email_salary_slip()
|
||||
|
||||
@@ -73,6 +75,7 @@ class SalarySlip(TransactionBase):
|
||||
self.set_status()
|
||||
self.update_status()
|
||||
self.update_salary_slip_in_additional_salary()
|
||||
self.cancel_loan_repayment_entry()
|
||||
|
||||
def on_trash(self):
|
||||
from frappe.model.naming import revert_series_if_last
|
||||
@@ -364,11 +367,11 @@ class SalarySlip(TransactionBase):
|
||||
return amount
|
||||
|
||||
except NameError as err:
|
||||
frappe.throw(_("Name error: {0}".format(err)))
|
||||
frappe.throw(_("Name error: {0}").format(err))
|
||||
except SyntaxError as err:
|
||||
frappe.throw(_("Syntax error in formula or condition: {0}".format(err)))
|
||||
frappe.throw(_("Syntax error in formula or condition: {0}").format(err))
|
||||
except Exception as e:
|
||||
frappe.throw(_("Error in formula or condition: {0}".format(e)))
|
||||
frappe.throw(_("Error in formula or condition: {0}").format(e))
|
||||
raise
|
||||
|
||||
def add_employee_benefits(self, payroll_period):
|
||||
@@ -705,11 +708,11 @@ class SalarySlip(TransactionBase):
|
||||
if condition:
|
||||
return frappe.safe_eval(condition, self.whitelisted_globals, data)
|
||||
except NameError as err:
|
||||
frappe.throw(_("Name error: {0}".format(err)))
|
||||
frappe.throw(_("Name error: {0}").format(err))
|
||||
except SyntaxError as err:
|
||||
frappe.throw(_("Syntax error in condition: {0}".format(err)))
|
||||
frappe.throw(_("Syntax error in condition: {0}").format(err))
|
||||
except Exception as e:
|
||||
frappe.throw(_("Error in formula or condition: {0}".format(e)))
|
||||
frappe.throw(_("Error in formula or condition: {0}").format(e))
|
||||
raise
|
||||
|
||||
def get_salary_slip_row(self, salary_component):
|
||||
@@ -754,28 +757,35 @@ class SalarySlip(TransactionBase):
|
||||
self.total_principal_amount = 0
|
||||
|
||||
for loan in self.get_loan_details():
|
||||
self.append('loans', {
|
||||
'loan': loan.name,
|
||||
'total_payment': loan.total_payment,
|
||||
'interest_amount': loan.interest_amount,
|
||||
'principal_amount': loan.principal_amount,
|
||||
'loan_account': loan.loan_account,
|
||||
'interest_income_account': loan.interest_income_account
|
||||
})
|
||||
|
||||
self.total_loan_repayment += loan.total_payment
|
||||
self.total_interest_amount += loan.interest_amount
|
||||
self.total_principal_amount += loan.principal_amount
|
||||
amounts = calculate_amounts(loan.name, self.posting_date, "Regular Payment")
|
||||
|
||||
total_payment = amounts['interest_amount'] + amounts['payable_principal_amount']
|
||||
|
||||
if total_payment:
|
||||
self.append('loans', {
|
||||
'loan': loan.name,
|
||||
'total_payment': total_payment,
|
||||
'interest_amount': amounts['interest_amount'],
|
||||
'principal_amount': amounts['payable_principal_amount'],
|
||||
'loan_account': loan.loan_account,
|
||||
'interest_income_account': loan.interest_income_account
|
||||
})
|
||||
|
||||
self.total_loan_repayment += total_payment
|
||||
self.total_interest_amount += amounts['interest_amount']
|
||||
self.total_principal_amount += amounts['payable_principal_amount']
|
||||
|
||||
def get_loan_details(self):
|
||||
return frappe.db.sql("""select rps.principal_amount, rps.interest_amount, l.name,
|
||||
rps.total_payment, l.loan_account, l.interest_income_account
|
||||
from
|
||||
`tabRepayment Schedule` as rps, `tabLoan` as l
|
||||
where
|
||||
l.name = rps.parent and rps.payment_date between %s and %s and
|
||||
l.repay_from_salary = 1 and l.docstatus = 1 and l.applicant = %s""",
|
||||
(self.start_date, self.end_date, self.employee), as_dict=True) or []
|
||||
|
||||
return frappe.get_all("Loan",
|
||||
fields=["name", "interest_income_account", "loan_account", "loan_type"],
|
||||
filters = {
|
||||
"applicant": self.employee,
|
||||
"docstatus": 1,
|
||||
"repay_from_salary": 1,
|
||||
})
|
||||
|
||||
|
||||
def update_salary_slip_in_additional_salary(self):
|
||||
salary_slip = self.name if self.docstatus==1 else None
|
||||
@@ -784,6 +794,23 @@ class SalarySlip(TransactionBase):
|
||||
where employee=%s and payroll_date between %s and %s and docstatus=1
|
||||
""", (salary_slip, self.employee, self.start_date, self.end_date))
|
||||
|
||||
def make_loan_repayment_entry(self):
|
||||
for loan in self.loans:
|
||||
repayment_entry = create_repayment_entry(loan.loan, self.employee,
|
||||
self.company, self.posting_date, loan.loan_type, "Regular Payment", loan.interest_amount,
|
||||
loan.principal_amount, loan.total_payment)
|
||||
|
||||
repayment_entry.save()
|
||||
repayment_entry.submit()
|
||||
|
||||
loan.loan_repayment_entry = repayment_entry.name
|
||||
|
||||
def cancel_loan_repayment_entry(self):
|
||||
for loan in self.loans:
|
||||
if loan.loan_repayment_entry:
|
||||
repayment_entry = frappe.get_doc("Loan Repayment", loan.loan_repayment_entry)
|
||||
repayment_entry.cancel()
|
||||
|
||||
def email_salary_slip(self):
|
||||
receiver = frappe.db.get_value("Employee", self.employee, "prefered_email")
|
||||
hr_settings = frappe.get_single("HR Settings")
|
||||
|
||||
@@ -18,8 +18,8 @@ from erpnext.hr.doctype.employee_tax_exemption_declaration.test_employee_tax_exe
|
||||
|
||||
class TestSalarySlip(unittest.TestCase):
|
||||
def setUp(self):
|
||||
make_earning_salary_component(setup=True)
|
||||
make_deduction_salary_component(setup=True)
|
||||
make_earning_salary_component(setup=True, company_list=["_Test Company"])
|
||||
make_deduction_salary_component(setup=True, company_list=["_Test Company"])
|
||||
|
||||
for dt in ["Leave Application", "Leave Allocation", "Salary Slip"]:
|
||||
frappe.db.sql("delete from `tab%s`" % dt)
|
||||
@@ -50,7 +50,7 @@ class TestSalarySlip(unittest.TestCase):
|
||||
self.assertEqual(ss.deductions[0].amount, 5000)
|
||||
self.assertEqual(ss.deductions[1].amount, 5000)
|
||||
self.assertEqual(ss.gross_pay, 78000)
|
||||
self.assertEqual(ss.net_pay, 67418.0)
|
||||
self.assertEqual(ss.net_pay, 68000.0)
|
||||
|
||||
def test_salary_slip_with_holidays_excluded(self):
|
||||
no_of_days = self.get_no_of_days()
|
||||
@@ -70,7 +70,7 @@ class TestSalarySlip(unittest.TestCase):
|
||||
self.assertEqual(ss.deductions[0].amount, 5000)
|
||||
self.assertEqual(ss.deductions[1].amount, 5000)
|
||||
self.assertEqual(ss.gross_pay, 78000)
|
||||
self.assertEqual(ss.net_pay, 67418.0)
|
||||
self.assertEqual(ss.net_pay, 68000.0)
|
||||
|
||||
def test_payment_days(self):
|
||||
no_of_days = self.get_no_of_days()
|
||||
@@ -137,21 +137,41 @@ class TestSalarySlip(unittest.TestCase):
|
||||
|
||||
make_employee("test_employee@salary.com")
|
||||
ss = make_employee_salary_slip("test_employee@salary.com", "Monthly")
|
||||
ss.company = "_Test Company"
|
||||
ss.save()
|
||||
ss.submit()
|
||||
|
||||
email_queue = frappe.db.sql("""select name from `tabEmail Queue`""")
|
||||
self.assertTrue(email_queue)
|
||||
|
||||
def test_loan_repayment_salary_slip(self):
|
||||
from erpnext.hr.doctype.loan.test_loan import create_loan_type, create_loan
|
||||
applicant = make_employee("test_employee@salary.com")
|
||||
create_loan_type("Car Loan", 500000, 6.4)
|
||||
loan = create_loan(applicant, "Car Loan", 11000, "Repay Over Number of Periods", 20)
|
||||
from erpnext.loan_management.doctype.loan.test_loan import create_loan_type, create_loan, make_loan_disbursement_entry, create_loan_accounts
|
||||
from erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual import make_accrual_interest_entry_for_term_loans
|
||||
|
||||
applicant = make_employee("test_loanemployee@salary.com", company="_Test Company")
|
||||
|
||||
create_loan_accounts()
|
||||
|
||||
create_loan_type("Car Loan", 500000, 8.4,
|
||||
is_term_loan=1,
|
||||
mode_of_payment='Cash',
|
||||
payment_account='Payment Account - _TC',
|
||||
loan_account='Loan Account - _TC',
|
||||
interest_income_account='Interest Income Account - _TC',
|
||||
penalty_income_account='Penalty Income Account - _TC')
|
||||
|
||||
loan = create_loan(applicant, "Car Loan", 11000, "Repay Over Number of Periods", 20, posting_date=add_months(nowdate(), -1))
|
||||
loan.repay_from_salary = 1
|
||||
loan.submit()
|
||||
ss = make_employee_salary_slip("test_employee@salary.com", "Monthly")
|
||||
|
||||
make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=add_months(nowdate(), -1))
|
||||
|
||||
make_accrual_interest_entry_for_term_loans(posting_date=nowdate())
|
||||
|
||||
ss = make_employee_salary_slip("test_loanemployee@salary.com", "Monthly")
|
||||
ss.submit()
|
||||
self.assertEqual(ss.total_loan_repayment, 582)
|
||||
|
||||
self.assertEqual(ss.total_loan_repayment, 592)
|
||||
self.assertEqual(ss.net_pay, (flt(ss.gross_pay) - (flt(ss.total_deduction) + flt(ss.total_loan_repayment))))
|
||||
|
||||
def test_payroll_frequency(self):
|
||||
@@ -321,7 +341,7 @@ def make_employee_salary_slip(user, payroll_frequency, salary_structure=None):
|
||||
|
||||
return salary_slip
|
||||
|
||||
def make_salary_component(salary_components, test_tax):
|
||||
def make_salary_component(salary_components, test_tax, company_list=None):
|
||||
for salary_component in salary_components:
|
||||
if not frappe.db.exists('Salary Component', salary_component["salary_component"]):
|
||||
if test_tax:
|
||||
@@ -336,17 +356,22 @@ def make_salary_component(salary_components, test_tax):
|
||||
salary_component["doctype"] = "Salary Component"
|
||||
salary_component["salary_component_abbr"] = salary_component["abbr"]
|
||||
frappe.get_doc(salary_component).insert()
|
||||
get_salary_component_account(salary_component["salary_component"])
|
||||
get_salary_component_account(salary_component["salary_component"], company_list)
|
||||
|
||||
def get_salary_component_account(sal_comp):
|
||||
def get_salary_component_account(sal_comp, company_list=None):
|
||||
company = erpnext.get_default_company()
|
||||
|
||||
if company_list and company not in company_list:
|
||||
company_list.append(company)
|
||||
|
||||
sal_comp = frappe.get_doc("Salary Component", sal_comp)
|
||||
if not sal_comp.get("accounts"):
|
||||
sal_comp.append("accounts", {
|
||||
"company": company,
|
||||
"default_account": create_account(company)
|
||||
})
|
||||
sal_comp.save()
|
||||
for d in company_list:
|
||||
sal_comp.append("accounts", {
|
||||
"company": d,
|
||||
"default_account": create_account(d)
|
||||
})
|
||||
sal_comp.save()
|
||||
|
||||
def create_account(company):
|
||||
salary_account = frappe.db.get_value("Account", "Salary - " + frappe.get_cached_value('Company', company, 'abbr'))
|
||||
@@ -359,7 +384,7 @@ def create_account(company):
|
||||
}).insert()
|
||||
return salary_account
|
||||
|
||||
def make_earning_salary_component(setup=False, test_tax=False):
|
||||
def make_earning_salary_component(setup=False, test_tax=False, company_list=None):
|
||||
data = [
|
||||
{
|
||||
"salary_component": 'Basic Salary',
|
||||
@@ -415,7 +440,7 @@ def make_earning_salary_component(setup=False, test_tax=False):
|
||||
}
|
||||
])
|
||||
if setup or test_tax:
|
||||
make_salary_component(data, test_tax)
|
||||
make_salary_component(data, test_tax, company_list)
|
||||
data.append({
|
||||
"salary_component": 'Basic Salary',
|
||||
"abbr":'BS',
|
||||
@@ -426,7 +451,7 @@ def make_earning_salary_component(setup=False, test_tax=False):
|
||||
})
|
||||
return data
|
||||
|
||||
def make_deduction_salary_component(setup=False, test_tax=False):
|
||||
def make_deduction_salary_component(setup=False, test_tax=False, company_list=None):
|
||||
data = [
|
||||
{
|
||||
"salary_component": 'Professional Tax',
|
||||
@@ -458,7 +483,7 @@ def make_deduction_salary_component(setup=False, test_tax=False):
|
||||
"round_to_the_nearest_integer": 1
|
||||
})
|
||||
if setup or test_tax:
|
||||
make_salary_component(data, test_tax)
|
||||
make_salary_component(data, test_tax, company_list)
|
||||
|
||||
return data
|
||||
|
||||
|
||||
@@ -1,263 +0,0 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"beta": 0,
|
||||
"creation": "2017-11-08 12:51:12.834479",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "loan",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Loan",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Loan",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "loan_account",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Loan Account",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Account",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "interest_income_account",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Interest Income Account",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Account",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "column_break_4",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "principal_amount",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Principal Amount",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "interest_amount",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Interest Amount",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "total_payment",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Total Payment",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-02-26 05:24:31.369630",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Salary Slip Loan",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 1,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0
|
||||
}
|
||||
@@ -86,16 +86,17 @@ class TestSalaryStructure(unittest.TestCase):
|
||||
self.assertEqual(salary_structure_assignment.base, 5000)
|
||||
self.assertEqual(salary_structure_assignment.variable, 200)
|
||||
|
||||
def make_salary_structure(salary_structure, payroll_frequency, employee=None, dont_submit=False, other_details=None, test_tax=False):
|
||||
def make_salary_structure(salary_structure, payroll_frequency, employee=None, dont_submit=False, other_details=None,
|
||||
test_tax=False, company=None):
|
||||
if test_tax:
|
||||
frappe.db.sql("""delete from `tabSalary Structure` where name=%s""",(salary_structure))
|
||||
if not frappe.db.exists('Salary Structure', salary_structure):
|
||||
details = {
|
||||
"doctype": "Salary Structure",
|
||||
"name": salary_structure,
|
||||
"company": erpnext.get_default_company(),
|
||||
"earnings": make_earning_salary_component(test_tax=test_tax),
|
||||
"deductions": make_deduction_salary_component(test_tax=test_tax),
|
||||
"company": company or erpnext.get_default_company(),
|
||||
"earnings": make_earning_salary_component(test_tax=test_tax, company_list=["_Test Company"]),
|
||||
"deductions": make_deduction_salary_component(test_tax=test_tax, company_list=["_Test Company"]),
|
||||
"payroll_frequency": payroll_frequency,
|
||||
"payment_account": get_random("Account")
|
||||
}
|
||||
@@ -109,11 +110,11 @@ def make_salary_structure(salary_structure, payroll_frequency, employee=None, do
|
||||
|
||||
if employee and not frappe.db.get_value("Salary Structure Assignment",
|
||||
{'employee':employee, 'docstatus': 1}) and salary_structure_doc.docstatus==1:
|
||||
create_salary_structure_assignment(employee, salary_structure)
|
||||
create_salary_structure_assignment(employee, salary_structure, company=company)
|
||||
|
||||
return salary_structure_doc
|
||||
|
||||
def create_salary_structure_assignment(employee, salary_structure, from_date=None):
|
||||
def create_salary_structure_assignment(employee, salary_structure, from_date=None, company=None):
|
||||
if frappe.db.exists("Salary Structure Assignment", {"employee": employee}):
|
||||
frappe.db.sql("""delete from `tabSalary Structure Assignment` where employee=%s""",(employee))
|
||||
salary_structure_assignment = frappe.new_doc("Salary Structure Assignment")
|
||||
@@ -122,7 +123,7 @@ def create_salary_structure_assignment(employee, salary_structure, from_date=Non
|
||||
salary_structure_assignment.variable = 5000
|
||||
salary_structure_assignment.from_date = from_date or add_months(nowdate(), -1)
|
||||
salary_structure_assignment.salary_structure = salary_structure
|
||||
salary_structure_assignment.company = erpnext.get_default_company()
|
||||
salary_structure_assignment.company = company or erpnext.get_default_company()
|
||||
salary_structure_assignment.save(ignore_permissions=True)
|
||||
salary_structure_assignment.submit()
|
||||
return salary_structure_assignment
|
||||
|
||||
@@ -57,8 +57,8 @@ class StaffingPlan(Document):
|
||||
and sp.to_date >= %s and sp.from_date <= %s and sp.company = %s
|
||||
""", (staffing_plan_detail.designation, self.from_date, self.to_date, self.company))
|
||||
if overlap and overlap [0][0]:
|
||||
frappe.throw(_("Staffing Plan {0} already exist for designation {1}"
|
||||
.format(overlap[0][0], staffing_plan_detail.designation)))
|
||||
frappe.throw(_("Staffing Plan {0} already exist for designation {1}")
|
||||
.format(overlap[0][0], staffing_plan_detail.designation))
|
||||
|
||||
def validate_with_parent_plan(self, staffing_plan_detail):
|
||||
if not frappe.get_cached_value('Company', self.company, "parent_company"):
|
||||
|
||||
@@ -14,7 +14,7 @@ test_dependencies = ["Designation"]
|
||||
class TestStaffingPlan(unittest.TestCase):
|
||||
def test_staffing_plan(self):
|
||||
_set_up()
|
||||
frappe.db.set_value("Company", "_Test Company", "is_group", 1)
|
||||
frappe.db.set_value("Company", "_Test Company 3", "is_group", 1)
|
||||
if frappe.db.exists("Staffing Plan", "Test"):
|
||||
return
|
||||
staffing_plan = frappe.new_doc("Staffing Plan")
|
||||
@@ -36,7 +36,7 @@ class TestStaffingPlan(unittest.TestCase):
|
||||
if frappe.db.exists("Staffing Plan", "Test 1"):
|
||||
return
|
||||
staffing_plan = frappe.new_doc("Staffing Plan")
|
||||
staffing_plan.company = "_Test Company"
|
||||
staffing_plan.company = "_Test Company 3"
|
||||
staffing_plan.name = "Test 1"
|
||||
staffing_plan.from_date = nowdate()
|
||||
staffing_plan.to_date = add_days(nowdate(), 10)
|
||||
@@ -52,7 +52,7 @@ class TestStaffingPlan(unittest.TestCase):
|
||||
if frappe.db.exists("Staffing Plan", "Test"):
|
||||
return
|
||||
staffing_plan = frappe.new_doc("Staffing Plan")
|
||||
staffing_plan.company = "_Test Company"
|
||||
staffing_plan.company = "_Test Company 3"
|
||||
staffing_plan.name = "Test"
|
||||
staffing_plan.from_date = nowdate()
|
||||
staffing_plan.to_date = add_days(nowdate(), 10)
|
||||
@@ -87,10 +87,11 @@ def _set_up():
|
||||
def make_company():
|
||||
if frappe.db.exists("Company", "_Test Company 10"):
|
||||
return
|
||||
|
||||
company = frappe.new_doc("Company")
|
||||
company.company_name = "_Test Company 10"
|
||||
company.abbr = "_TC10"
|
||||
company.parent_company = "_Test Company"
|
||||
company.parent_company = "_Test Company 3"
|
||||
company.default_currency = "INR"
|
||||
company.country = "Pakistan"
|
||||
company.insert()
|
||||
@@ -8,25 +8,9 @@ import unittest
|
||||
from frappe.utils import nowdate,flt, cstr,random_string
|
||||
# test_records = frappe.get_test_records('Vehicle Log')
|
||||
class TestVehicleLog(unittest.TestCase):
|
||||
def test_make_vehicle_log(self):
|
||||
license_plate=random_string(10).upper()
|
||||
def test_make_vehicle_log_and_syncing_of_odometer_value(self):
|
||||
employee_id=frappe.db.sql("""select name from `tabEmployee` order by modified desc limit 1""")[0][0]
|
||||
vehicle = frappe.get_doc({
|
||||
"doctype": "Vehicle",
|
||||
"license_plate": cstr(license_plate),
|
||||
"make": "Maruti",
|
||||
"model": "PCM",
|
||||
"last_odometer":5000,
|
||||
"acquisition_date":frappe.utils.nowdate(),
|
||||
"location": "Mumbai",
|
||||
"chassis_no": "1234ABCD",
|
||||
"uom": "Litre",
|
||||
"vehicle_value":frappe.utils.flt(500000)
|
||||
})
|
||||
try:
|
||||
vehicle.insert()
|
||||
except frappe.DuplicateEntryError:
|
||||
pass
|
||||
license_plate = get_vehicle(employee_id)
|
||||
vehicle_log = frappe.get_doc({
|
||||
"doctype": "Vehicle Log",
|
||||
"license_plate": cstr(license_plate),
|
||||
@@ -36,5 +20,41 @@ class TestVehicleLog(unittest.TestCase):
|
||||
"fuel_qty":frappe.utils.flt(50),
|
||||
"price": frappe.utils.flt(500)
|
||||
})
|
||||
vehicle_log.insert()
|
||||
vehicle_log.submit()
|
||||
vehicle_log.save()
|
||||
vehicle_log.submit()
|
||||
|
||||
#checking value of vehicle odometer value on submit.
|
||||
vehicle = frappe.get_doc("Vehicle", license_plate)
|
||||
self.assertEqual(vehicle.last_odometer, vehicle_log.odometer)
|
||||
|
||||
#checking value vehicle odometer on vehicle log cancellation.
|
||||
last_odometer = vehicle_log.last_odometer
|
||||
current_odometer = vehicle_log.odometer
|
||||
distance_travelled = current_odometer - last_odometer
|
||||
|
||||
vehicle_log.cancel()
|
||||
vehicle.reload()
|
||||
|
||||
self.assertEqual(vehicle.last_odometer, current_odometer - distance_travelled)
|
||||
|
||||
|
||||
def get_vehicle(employee_id):
|
||||
license_plate=random_string(10).upper()
|
||||
vehicle = frappe.get_doc({
|
||||
"doctype": "Vehicle",
|
||||
"license_plate": cstr(license_plate),
|
||||
"make": "Maruti",
|
||||
"model": "PCM",
|
||||
"employee": employee_id,
|
||||
"last_odometer":5000,
|
||||
"acquisition_date":frappe.utils.nowdate(),
|
||||
"location": "Mumbai",
|
||||
"chassis_no": "1234ABCD",
|
||||
"uom": "Litre",
|
||||
"vehicle_value":frappe.utils.flt(500000)
|
||||
})
|
||||
try:
|
||||
vehicle.insert()
|
||||
except frappe.DuplicateEntryError:
|
||||
pass
|
||||
return license_plate
|
||||
@@ -2,29 +2,41 @@
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on("Vehicle Log", {
|
||||
refresh: function(frm,cdt,cdn) {
|
||||
var vehicle_log=frappe.model.get_doc(cdt,cdn);
|
||||
if (vehicle_log.license_plate) {
|
||||
frappe.call({
|
||||
method: "erpnext.hr.doctype.vehicle_log.vehicle_log.get_make_model",
|
||||
args: {
|
||||
license_plate: vehicle_log.license_plate
|
||||
},
|
||||
callback: function(r) {
|
||||
frappe.model.set_value(cdt, cdn, ("model"), r.message[0]);
|
||||
frappe.model.set_value(cdt, cdn, ("make"), r.message[1]);
|
||||
}
|
||||
})
|
||||
refresh: function(frm) {
|
||||
|
||||
if(frm.doc.license_plate && frm.doc.__islocal){
|
||||
frm.events.set_vehicle_details(frm);
|
||||
}
|
||||
|
||||
if(frm.doc.docstatus == 1) {
|
||||
frm.add_custom_button(__('Expense Claim'), function() {
|
||||
frm.events.expense_claim(frm)
|
||||
frm.events.expense_claim(frm);
|
||||
}, __('Create'));
|
||||
frm.page.set_inner_btn_group_as_primary(__('Create'));
|
||||
}
|
||||
},
|
||||
|
||||
license_plate: function(frm) {
|
||||
if(frm.doc.license_plate){
|
||||
frm.events.set_vehicle_details(frm);
|
||||
}
|
||||
},
|
||||
|
||||
set_vehicle_details: function(frm) {
|
||||
frappe.call({
|
||||
method: "erpnext.hr.doctype.vehicle_log.vehicle_log.get_make_model",
|
||||
args: {
|
||||
license_plate: frm.doc.license_plate
|
||||
},
|
||||
callback: function(r) {
|
||||
frappe.model.set_value(cur_frm.doctype, cur_frm.docname, "make", r.message[0]);
|
||||
frappe.model.set_value(cur_frm.doctype, cur_frm.docname, "model", r.message[1]);
|
||||
frappe.model.set_value(cur_frm.doctype, cur_frm.docname, "last_odometer", r.message[2]);
|
||||
frappe.model.set_value(cur_frm.doctype, cur_frm.docname, "employee", r.message[3]);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
expense_claim: function(frm){
|
||||
frappe.call({
|
||||
method: "erpnext.hr.doctype.vehicle_log.vehicle_log.make_expense_claim",
|
||||
|
||||
@@ -1,706 +1,192 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"autoname": "naming_series:",
|
||||
"beta": 0,
|
||||
"creation": "2016-09-03 14:14:51.788550",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "Document",
|
||||
"editable_grid": 1,
|
||||
"actions": [],
|
||||
"autoname": "naming_series:",
|
||||
"creation": "2016-09-03 14:14:51.788550",
|
||||
"doctype": "DocType",
|
||||
"document_type": "Document",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"vehicle_section",
|
||||
"naming_series",
|
||||
"license_plate",
|
||||
"employee",
|
||||
"column_break_4",
|
||||
"column_break_7",
|
||||
"model",
|
||||
"make",
|
||||
"odometer_reading",
|
||||
"date",
|
||||
"odometer",
|
||||
"column_break_12",
|
||||
"last_odometer",
|
||||
"refuelling_details",
|
||||
"fuel_qty",
|
||||
"price",
|
||||
"column_break_15",
|
||||
"supplier",
|
||||
"invoice",
|
||||
"service_details",
|
||||
"service_detail",
|
||||
"amended_from"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "vehicle_section",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "fa fa-user",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "vehicle_section",
|
||||
"fieldtype": "Section Break",
|
||||
"options": "fa fa-user"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "",
|
||||
"fieldname": "naming_series",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Series",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"options": "HR-VLOG-.YYYY.-",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 1,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "naming_series",
|
||||
"fieldtype": "Select",
|
||||
"label": "Series",
|
||||
"no_copy": 1,
|
||||
"options": "HR-VLOG-.YYYY.-",
|
||||
"print_hide": 1,
|
||||
"reqd": 1,
|
||||
"set_only_once": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "license_plate",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 1,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "License Plate",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Vehicle",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "license_plate",
|
||||
"fieldtype": "Link",
|
||||
"in_global_search": 1,
|
||||
"in_list_view": 1,
|
||||
"label": "License Plate",
|
||||
"options": "Vehicle",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "employee",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 1,
|
||||
"label": "Employee",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Employee",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "employee",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 1,
|
||||
"label": "Employee",
|
||||
"options": "Employee",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "column_break_4",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "column_break_4",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "column_break_7",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "column_break_7",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "model",
|
||||
"fieldtype": "Read Only",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Model",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "model",
|
||||
"fieldtype": "Read Only",
|
||||
"label": "Model"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "make",
|
||||
"fieldtype": "Read Only",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Make",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "make",
|
||||
"fieldtype": "Read Only",
|
||||
"label": "Make"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "odometer_reading",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Odometer Reading",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "odometer_reading",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Odometer Reading"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "date",
|
||||
"fieldtype": "Date",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Date",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Date",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "odometer",
|
||||
"fieldtype": "Int",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Odometer",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "odometer",
|
||||
"fieldtype": "Int",
|
||||
"label": "Current Odometer value ",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 1,
|
||||
"columns": 0,
|
||||
"fieldname": "refuelling_details",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Refuelling Details",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"collapsible": 1,
|
||||
"fieldname": "refuelling_details",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Refuelling Details"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "fuel_qty",
|
||||
"fieldtype": "Float",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Fuel Qty",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "fuel_qty",
|
||||
"fieldtype": "Float",
|
||||
"label": "Fuel Qty"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "price",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Fuel Price",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "price",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Fuel Price"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "column_break_15",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "column_break_15",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "supplier",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Supplier",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Supplier",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "supplier",
|
||||
"fieldtype": "Link",
|
||||
"label": "Supplier",
|
||||
"options": "Supplier"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "invoice",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Invoice Ref",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "invoice",
|
||||
"fieldtype": "Data",
|
||||
"label": "Invoice Ref"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 1,
|
||||
"columns": 0,
|
||||
"fieldname": "service_details",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Service Details",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"collapsible": 1,
|
||||
"fieldname": "service_details",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Service Details"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "service_detail",
|
||||
"fieldtype": "Table",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Service Detail",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Vehicle Service",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "service_detail",
|
||||
"fieldtype": "Table",
|
||||
"label": "Service Detail",
|
||||
"options": "Vehicle Service"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "amended_from",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Amended From",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"options": "Vehicle Log",
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"fieldname": "amended_from",
|
||||
"fieldtype": "Link",
|
||||
"label": "Amended From",
|
||||
"no_copy": 1,
|
||||
"options": "Vehicle Log",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "last_odometer",
|
||||
"fieldtype": "Int",
|
||||
"label": "last Odometer Value ",
|
||||
"read_only": 1,
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_12",
|
||||
"fieldtype": "Column Break"
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"is_submittable": 1,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-08-21 14:44:51.131186",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Vehicle Log",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
],
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-01-28 12:43:34.419647",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Vehicle Log",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 1,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Fleet Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 1,
|
||||
"cancel": 1,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Fleet Manager",
|
||||
"share": 1,
|
||||
"submit": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"quick_entry": 1,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"title_field": "",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0,
|
||||
"track_views": 0
|
||||
],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
||||
@@ -11,22 +11,33 @@ from frappe.model.document import Document
|
||||
|
||||
class VehicleLog(Document):
|
||||
def validate(self):
|
||||
last_odometer=frappe.db.get_value("Vehicle", self.license_plate, "last_odometer")
|
||||
if flt(self.odometer) < flt(last_odometer):
|
||||
frappe.throw(_("Current Odometer reading entered should be greater than initial Vehicle Odometer {0}").format(last_odometer))
|
||||
if flt(self.odometer) < flt(self.last_odometer):
|
||||
frappe.throw(_("Current Odometer reading entered should be greater than initial Vehicle Odometer {0}").format(self.last_odometer))
|
||||
for service_detail in self.service_detail:
|
||||
if (service_detail.service_item or service_detail.type or service_detail.frequency or service_detail.expense_amount):
|
||||
if not (service_detail.service_item and service_detail.type and service_detail.frequency and service_detail.expense_amount):
|
||||
frappe.throw(_("Service Item,Type,frequency and expense amount are required"))
|
||||
|
||||
def before_insert(self):
|
||||
model_details = get_make_model(self.license_plate)
|
||||
self.make = model_details[0]
|
||||
self.model = model_details[1]
|
||||
self.last_odometer = model_details[2]
|
||||
self.employee = model_details[3]
|
||||
|
||||
def on_submit(self):
|
||||
frappe.db.sql("update `tabVehicle` set last_odometer=%s where license_plate=%s",
|
||||
(self.odometer, self.license_plate))
|
||||
frappe.db.set_value("Vehicle", self.license_plate, "last_odometer", self.odometer)
|
||||
|
||||
def on_cancel(self):
|
||||
distance_travelled = self.odometer - self.last_odometer
|
||||
if(distance_travelled > 0):
|
||||
updated_odometer_value = int(frappe.db.get_value("Vehicle", self.license_plate, "last_odometer")) - distance_travelled
|
||||
frappe.db.set_value("Vehicle", self.license_plate, "last_odometer", updated_odometer_value)
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_make_model(license_plate):
|
||||
vehicle=frappe.get_doc("Vehicle",license_plate)
|
||||
return (vehicle.make,vehicle.model)
|
||||
return (vehicle.make, vehicle.model, vehicle.last_odometer, vehicle.employee)
|
||||
|
||||
@frappe.whitelist()
|
||||
def make_expense_claim(docname):
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on(cur_frm.doctype, {
|
||||
refresh: function(frm) {
|
||||
if (!frappe.boot.active_domains.includes("Non Profit")) {
|
||||
frm.set_df_property('applicant_type', 'options', ['Employee']);
|
||||
frm.refresh_field('applicant_type');
|
||||
}
|
||||
},
|
||||
applicant_type: function(frm) {
|
||||
frm.set_value("applicant", null);
|
||||
frm.set_value("applicant_name", null);
|
||||
},
|
||||
applicant: function(frm) {
|
||||
if (frm.doc.applicant) {
|
||||
frappe.model.with_doc(frm.doc.applicant_type, frm.doc.applicant, function() {
|
||||
var applicant = frappe.model.get_doc(frm.doc.applicant_type, frm.doc.applicant);
|
||||
frm.set_value("applicant_name",
|
||||
applicant.employee_name || applicant.member_name);
|
||||
});
|
||||
}
|
||||
else {
|
||||
frm.set_value("applicant_name", null);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,38 @@
|
||||
{%- from "templates/print_formats/standard_macros.html" import add_header -%}
|
||||
|
||||
<div class="text-center" style="margin-bottom: 10%;"><h3>Appointment Letter</h3></div>
|
||||
<div class ='row' style="margin-bottom: 5%;">
|
||||
<div class = "col-sm-6">
|
||||
<b>{{ doc.applicant_name }},</b>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<span style = "float: right;"><b> Date: </b>{{ doc.appointment_date }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin-bottom: 5%;">
|
||||
{{ doc.introduction }}
|
||||
</div>
|
||||
<div style="margin-bottom: 5%;">
|
||||
<ul>
|
||||
{% for content in doc.terms %}
|
||||
<li style="padding-bottom: 3%;">
|
||||
<span>
|
||||
<span><b>{{ content.title }}: </b></span> {{ content.description }}
|
||||
</span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
<div style="margin-bottom: 5%;">
|
||||
<span>Your sincerely,</span><br>
|
||||
<span><b>For {{ doc.company }}</b></span>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 5%;">
|
||||
<span>{{ doc.closing_notes }}</span>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<span><b>________________</b></span><br>
|
||||
<span><b>{{ doc.applicant_name }}</b></span>
|
||||
</div>
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"align_labels_right": 0,
|
||||
"creation": "2019-12-26 15:22:44.200332",
|
||||
"custom_format": 0,
|
||||
"default_print_language": "en",
|
||||
"disabled": 0,
|
||||
"doc_type": "Appointment Letter",
|
||||
"docstatus": 0,
|
||||
"doctype": "Print Format",
|
||||
"font": "Default",
|
||||
"idx": 0,
|
||||
"line_breaks": 0,
|
||||
"modified": "2020-01-21 17:24:16.705082",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Standard Appointment Letter",
|
||||
"owner": "Administrator",
|
||||
"print_format_builder": 0,
|
||||
"print_format_type": "Jinja",
|
||||
"raw_printing": 0,
|
||||
"show_section_headings": 0,
|
||||
"standard": "Yes"
|
||||
}
|
||||
@@ -76,7 +76,7 @@ def get_data(filters, leave_types):
|
||||
opening = get_leave_balance_on(employee.name, leave_type, filters.from_date)
|
||||
|
||||
# closing balance
|
||||
closing = get_leave_balance_on(employee.name, leave_type, filters.to_date)
|
||||
closing = max(opening - leaves_taken, 0)
|
||||
|
||||
row += [opening, leaves_taken, closing]
|
||||
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
/* eslint-disable */
|
||||
|
||||
frappe.query_reports["Loan Repayment"] = {
|
||||
"filters": [
|
||||
|
||||
]
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
{
|
||||
"add_total_row": 0,
|
||||
"creation": "2019-03-29 18:58:00.166032",
|
||||
"disable_prepared_report": 0,
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"idx": 0,
|
||||
"is_standard": "Yes",
|
||||
"letter_head": "",
|
||||
"modified": "2019-03-29 18:58:00.166032",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Loan Repayment",
|
||||
"owner": "Administrator",
|
||||
"prepared_report": 0,
|
||||
"ref_doctype": "Loan",
|
||||
"report_name": "Loan Repayment",
|
||||
"report_type": "Script Report",
|
||||
"roles": [
|
||||
{
|
||||
"role": "HR Manager"
|
||||
},
|
||||
{
|
||||
"role": "Employee"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
|
||||
def execute(filters=None):
|
||||
|
||||
columns = create_columns()
|
||||
data = get_record()
|
||||
return columns, data
|
||||
|
||||
def create_columns():
|
||||
return [
|
||||
{
|
||||
"label": _("Employee"),
|
||||
"fieldtype": "Data",
|
||||
"fieldname": "employee",
|
||||
"options": "Employee",
|
||||
"width": 200
|
||||
},
|
||||
{
|
||||
"label": _("Loan"),
|
||||
"fieldtype": "Link",
|
||||
"fieldname": "loan_name",
|
||||
"options": "Loan",
|
||||
"width": 200
|
||||
},
|
||||
{
|
||||
"label": _("Loan Amount"),
|
||||
"fieldtype": "Currency",
|
||||
"fieldname": "loan_amount",
|
||||
"options": "currency",
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"label": _("Interest"),
|
||||
"fieldtype": "Data",
|
||||
"fieldname": "interest",
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"label": _("Payable Amount"),
|
||||
"fieldtype": "Currency",
|
||||
"fieldname": "payable_amount",
|
||||
"options": "currency",
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"label": _("EMI"),
|
||||
"fieldtype": "Currency",
|
||||
"fieldname": "emi",
|
||||
"options": "currency",
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"label": _("Paid Amount"),
|
||||
"fieldtype": "Currency",
|
||||
"fieldname": "paid_amount",
|
||||
"options": "currency",
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"label": _("Outstanding Amount"),
|
||||
"fieldtype": "Currency",
|
||||
"fieldname": "out_amt",
|
||||
"options": "currency",
|
||||
"width": 100
|
||||
},
|
||||
]
|
||||
|
||||
def get_record():
|
||||
data = []
|
||||
loans = frappe.get_all("Loan",
|
||||
filters=[("status", "=", "Disbursed")],
|
||||
fields=["applicant", "applicant_name", "name", "loan_amount", "rate_of_interest",
|
||||
"total_payment", "monthly_repayment_amount", "total_amount_paid"]
|
||||
)
|
||||
|
||||
for loan in loans:
|
||||
row = {
|
||||
"employee": loan.applicant + ": " + loan.applicant_name,
|
||||
"loan_name": loan.name,
|
||||
"loan_amount": loan.loan_amount,
|
||||
"interest": str(loan.rate_of_interest) + "%",
|
||||
"payable_amount": loan.total_payment,
|
||||
"emi": loan.monthly_repayment_amount,
|
||||
"paid_amount": loan.total_amount_paid,
|
||||
"out_amt": loan.total_payment - loan.total_amount_paid
|
||||
}
|
||||
|
||||
data.append(row)
|
||||
|
||||
return data
|
||||
@@ -37,13 +37,22 @@ def execute(filters=None):
|
||||
|
||||
total_p = total_a = total_l = 0.0
|
||||
for day in range(filters["total_days_in_month"]):
|
||||
status = att_map.get(emp).get(day + 1, "None")
|
||||
status_map = {"Present": "P", "Absent": "A", "Half Day": "HD", "On Leave": "L", "None": "", "Holiday":"<b>H</b>"}
|
||||
if status == "None" and holiday_map:
|
||||
status = att_map.get(emp).get(day + 1)
|
||||
status_map = {
|
||||
"Absent": "A",
|
||||
"Half Day": "HD",
|
||||
"Holiday":"<b>H</b>",
|
||||
"On Leave": "L",
|
||||
"Present": "P",
|
||||
"Work From Home": "WFH"
|
||||
}
|
||||
|
||||
if status is None and holiday_map:
|
||||
emp_holiday_list = emp_det.holiday_list if emp_det.holiday_list else default_holiday_list
|
||||
if emp_holiday_list in holiday_map and (day+1) in holiday_map[emp_holiday_list]:
|
||||
status = "Holiday"
|
||||
row.append(status_map[status])
|
||||
|
||||
row.append(status_map.get(status, ""))
|
||||
|
||||
if status == "Present":
|
||||
total_p += 1
|
||||
@@ -66,7 +75,7 @@ def execute(filters=None):
|
||||
|
||||
leave_details = frappe.db.sql("""select leave_type, status, count(*) as count from `tabAttendance`\
|
||||
where leave_type is not NULL %s group by leave_type, status""" % conditions, filters, as_dict=1)
|
||||
|
||||
|
||||
time_default_counts = frappe.db.sql("""select (select count(*) from `tabAttendance` where \
|
||||
late_entry = 1 %s) as late_entry_count, (select count(*) from tabAttendance where \
|
||||
early_exit = 1 %s) as early_exit_count""" % (conditions, conditions), filters)
|
||||
@@ -85,7 +94,7 @@ def execute(filters=None):
|
||||
row.append(leaves[d])
|
||||
else:
|
||||
row.append("0.0")
|
||||
|
||||
|
||||
row.extend([time_default_counts[0][0],time_default_counts[0][1]])
|
||||
data.append(row)
|
||||
return columns, data
|
||||
|
||||
@@ -316,7 +316,9 @@ def allocate_earned_leaves():
|
||||
|
||||
allocation = frappe.get_doc('Leave Allocation', allocation.name)
|
||||
new_allocation = flt(allocation.total_leaves_allocated) + flt(earned_leaves)
|
||||
new_allocation = new_allocation if new_allocation <= e_leave_type.max_leaves_allowed else e_leave_type.max_leaves_allowed
|
||||
|
||||
if new_allocation > e_leave_type.max_leaves_allowed and e_leave_type.max_leaves_allowed > 0:
|
||||
new_allocation = e_leave_type.max_leaves_allowed
|
||||
|
||||
if new_allocation == allocation.total_leaves_allocated:
|
||||
continue
|
||||
|
||||
Reference in New Issue
Block a user