Merge branch 'develop' of github.com:frappe/erpnext into develop

This commit is contained in:
Rohit Waghchaure 2017-01-31 11:34:07 +05:30
commit d9a265fab1
82 changed files with 2536 additions and 1195 deletions

View File

@ -337,14 +337,19 @@ def set_taxes(party, party_type, posting_date, company, customer_group=None, sup
def validate_party_frozen_disabled(party_type, party_name):
if party_type and party_name:
if party_type in ("Customer", "Supplier"):
party = frappe.db.get_value(party_type, party_name, ["is_frozen", "disabled"], as_dict=True)
if party.disabled:
frappe.throw(_("{0} {1} is disabled").format(party_type, party_name), PartyDisabled)
elif party.is_frozen:
elif party.get("is_frozen"):
frozen_accounts_modifier = frappe.db.get_value( 'Accounts Settings', None,'frozen_accounts_modifier')
if not frozen_accounts_modifier in frappe.get_roles():
frappe.throw(_("{0} {1} is frozen").format(party_type, party_name), PartyFrozen)
elif party_type == "Employee":
if frappe.db.get_value("Employee", party_name, "status") == "Left":
frappe.msgprint(_("{0} {1} is not active").format(party_type, party_name), PartyDisabled, alert=True)
def get_timeline_data(doctype, name):
'''returns timeline data for the past one year'''
from frappe.desk.form.load import get_communication_data

View File

@ -47,6 +47,12 @@ frappe.query_reports["General Ledger"] = {
"label": __("Voucher No"),
"fieldtype": "Data",
},
{
"fieldname":"project",
"label": __("Project"),
"fieldtype": "Link",
"options": "Project"
},
{
"fieldtype": "Break",
},

View File

@ -149,6 +149,9 @@ def get_conditions(filters):
if not (filters.get("account") or filters.get("party") or filters.get("group_by_account")):
conditions.append("posting_date >=%(from_date)s")
if filters.get("project"):
conditions.append("project=%(project)s")
from frappe.desk.reportview import build_match_conditions
match_conditions = build_match_conditions("GL Entry")
if match_conditions: conditions.append(match_conditions)

View File

@ -21,20 +21,12 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
if(this.frm.get_field('shipping_address')) {
this.frm.set_query("shipping_address", function(){
if(me.frm.doc.customer){
return{
filters:{
"customer": me.frm.doc.customer
}
}
}
else{
return{
filters:{
"is_your_company_address": 1,
"company": me.frm.doc.company
}
}
}
return {
query: 'frappe.geo.doctype.address.address.address_query',
filters: { link_doctype: 'Customer', link_name: me.frm.doc.customer }
};
} else
return erpnext.queries.company_address_query(me.frm.doc)
});
}
},

View File

@ -26,11 +26,11 @@ frappe.ui.form.on("Supplier", {
if(frm.doc.__islocal){
hide_field(['address_html','contact_html']);
erpnext.utils.clear_address_and_contact(frm);
frappe.geo.clear_address_and_contact(frm);
}
else {
unhide_field(['address_html','contact_html']);
erpnext.utils.render_address_and_contact(frm);
frappe.geo.render_address_and_contact(frm);
// custom buttons
frm.add_custom_button(__('Accounting Ledger'), function() {

View File

@ -6,7 +6,7 @@ import frappe
import frappe.defaults
from frappe import msgprint, _
from frappe.model.naming import make_autoname
from erpnext.utilities.address_and_contact import (load_address_and_contact,
from frappe.geo.address_and_contact import (load_address_and_contact,
delete_contact_and_address)
from erpnext.utilities.transaction_base import TransactionBase
@ -61,14 +61,6 @@ class Supplier(TransactionBase):
validate_party_accounts(self)
self.status = get_party_status(self)
def get_contacts(self,nm):
if nm:
contact_details =frappe.db.convert_to_lists(frappe.db.sql("select name, CONCAT(IFNULL(first_name,''),' ',IFNULL(last_name,'')),contact_no,email_id from `tabContact` where supplier = %s", nm))
return contact_details
else:
return ''
def on_trash(self):
delete_contact_and_address('Supplier', self.name)

View File

@ -1,17 +0,0 @@
{
"apply_user_permissions": 1,
"creation": "2013-10-09 10:38:40",
"docstatus": 0,
"doctype": "Report",
"idx": 1,
"is_standard": "Yes",
"modified": "2014-09-11 08:53:17.358554",
"modified_by": "Administrator",
"module": "Buying",
"name": "Supplier Addresses and Contacts",
"owner": "Administrator",
"query": "SELECT\n `tabSupplier`.name as \"Supplier:Link/Supplier:120\",\n\t`tabSupplier`.supplier_name as \"Supplier Name::120\",\n\t`tabSupplier`.supplier_type as \"Supplier Type:Link/Supplier Type:120\",\n\tconcat_ws(', ', \n\t\ttrim(',' from `tabAddress`.address_line1), \n\t\ttrim(',' from tabAddress.address_line2), \n\t\ttabAddress.state, tabAddress.pincode, tabAddress.country\n\t) as 'Address::180',\n concat_ws(', ', `tabContact`.first_name, `tabContact`.last_name) as \"Contact Name::180\",\n\t`tabContact`.phone as \"Phone\",\n\t`tabContact`.mobile_no as \"Mobile No\",\n\t`tabContact`.email_id as \"Email Address::120\",\n\t`tabContact`.is_primary_contact as \"Is Primary Contact::120\"\nFROM\n\t`tabSupplier`\n\tleft join `tabAddress` on (\n\t\t`tabAddress`.supplier=`tabSupplier`.name\n\t)\n\tleft join `tabContact` on (\n\t\t`tabContact`.supplier=`tabSupplier`.name\n\t)\nWHERE\n\t`tabSupplier`.docstatus<2\nORDER BY\n\t`tabSupplier`.name asc",
"ref_doctype": "Supplier",
"report_name": "Supplier Addresses and Contacts",
"report_type": "Query Report"
}

View File

@ -172,8 +172,12 @@ def get_data():
{
"type": "report",
"is_query_report": True,
"name": "Supplier Addresses and Contacts",
"doctype": "Supplier"
"name": "Addresses And Contacts",
"label": "Supplier Addresses And Contacts",
"doctype": "Address",
"route_options": {
"party_type": "Supplier"
}
},
]
},

View File

@ -265,6 +265,26 @@ def get_data():
]
},
{
"label": _("Employee Loan Management"),
"icon": "icon-list",
"items": [
{
"type": "doctype",
"name": "Loan Type",
"description": _("Define various loan types")
},
{
"type": "doctype",
"name": "Employee Loan Application",
"description": _("Employee Loan Application")
},
{
"type": "doctype",
"name": "Employee Loan"
},
]
},
{
"label": _("Help"),
"icon": "fa fa-facetime-video",

View File

@ -159,23 +159,6 @@ def get_data():
}
]
},
{
"label": _("LMS"),
"items": [
{
"type": "doctype",
"name": "Announcement"
},
{
"type": "doctype",
"name": "Topic"
},
{
"type": "doctype",
"name": "Discussion"
}
]
},
{
"label": _("Setup"),
"items": [

View File

@ -117,6 +117,16 @@ def get_data():
"link": "Tree/Sales Person",
"description": _("Manage Sales Person Tree."),
},
{
"type": "report",
"is_query_report": True,
"name": "Addresses And Contacts",
"label": "Sales Partner Addresses And Contacts",
"doctype": "Address",
"route_options": {
"party_type": "Sales Partner"
}
},
{
"type": "report",
"is_query_report": True,
@ -215,8 +225,12 @@ def get_data():
{
"type": "report",
"is_query_report": True,
"name": "Customer Addresses And Contacts",
"doctype": "Contact"
"name": "Addresses And Contacts",
"label": "Customer Addresses And Contacts",
"doctype": "Address",
"route_options": {
"party_type": "Customer"
}
},
{
"type": "report",

View File

@ -25,6 +25,7 @@ erpnext.LeadController = frappe.ui.form.Controller.extend({
refresh: function() {
var doc = this.frm.doc;
erpnext.toggle_naming_series();
frappe.dynamic_link = {doc: this.frm.doc, fieldname: 'name', doctype: 'Lead'}
if(!this.frm.doc.__islocal && this.frm.doc.__onload && !this.frm.doc.__onload.is_customer) {
this.frm.add_custom_button(__("Customer"), this.create_customer, __("Make"));
@ -34,9 +35,9 @@ erpnext.LeadController = frappe.ui.form.Controller.extend({
}
if(!this.frm.doc.__islocal) {
erpnext.utils.render_address_and_contact(cur_frm);
frappe.geo.render_address_and_contact(cur_frm);
} else {
erpnext.utils.clear_address_and_contact(cur_frm);
frappe.geo.clear_address_and_contact(cur_frm);
}
},

View File

@ -8,7 +8,7 @@ from frappe.utils import cstr, validate_email_add, cint, comma_and, has_gravatar
from frappe.model.mapper import get_mapped_doc
from erpnext.controllers.selling_controller import SellingController
from erpnext.utilities.address_and_contact import load_address_and_contact
from frappe.geo.address_and_contact import load_address_and_contact
from erpnext.accounts.party import set_taxes
sender_field = "email_id"

View File

@ -106,30 +106,6 @@ class Opportunity(TransactionBase):
lead_name, company_name = frappe.db.get_value("Lead", self.lead, ["lead_name", "company_name"])
self.customer_name = company_name or lead_name
def get_cust_address(self,name):
details = frappe.db.sql("""select customer_name, address, territory, customer_group
from `tabCustomer` where name = %s and docstatus != 2""", (name), as_dict = 1)
if details:
ret = {
'customer_name': details and details[0]['customer_name'] or '',
'address' : details and details[0]['address'] or '',
'territory' : details and details[0]['territory'] or '',
'customer_group' : details and details[0]['customer_group'] or ''
}
# ********** get primary contact details (this is done separately coz. , in case there is no primary contact thn it would not be able to fetch customer details in case of join query)
contact_det = frappe.db.sql("""select contact_name, contact_no, email_id
from `tabContact` where customer = %s and is_customer = 1
and is_primary_contact = 'Yes' and docstatus != 2""", name, as_dict = 1)
ret['contact_person'] = contact_det and contact_det[0]['contact_name'] or ''
ret['contact_no'] = contact_det and contact_det[0]['contact_no'] or ''
ret['email_id'] = contact_det and contact_det[0]['email_id'] or ''
return ret
else:
frappe.throw(_("Customer {0} does not exist").format(name), frappe.DoesNotExistError)
def on_update(self):
self.add_calendar_event()

Binary file not shown.

After

Width:  |  Height:  |  Size: 209 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 380 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 424 KiB

View File

@ -0,0 +1,56 @@
# Employee Loan Management
This module enables companies which provides employee loans to define and manage employee loans.
Employees can request loans, which are then reviewed and approved. For the approved loans,
repayment schedule for the entire loan cycle can be generated and automatic deduction from salary can also be set up.
### Loan Type
To create a new Loan Type go to:
> Human Resources > Employee Loan Management > Loan Type > New Loan Type
Configure Loan limit and Rate of interest.
<img class="screenshot" alt="Loan Type" src="{{docs_base_url}}/assets/img/human-resources/loan-type.png">
### Employee Loan Application
Employee can apply for loan by going to:
> Human Resources > Employee Loan Management > Employee Loan Application > New Employee Loan Application
<img class="screenshot" alt="Employee Loan Application" src="{{docs_base_url}}/assets/img/human-resources/employee-loan-application.png">
#### In the Employee Loan Application,
* Enter Employee details and Loan details
* Select the repayment method, and based on your selection enter Repayment Period in Months or repayment Amount
On save, Employee can see Repayment Information and make changes if required before submitting.
<img class="screenshot" alt="Employee Loan Application" src="{{docs_base_url}}/assets/img/human-resources/repayment-info.png">
### Employee Loan
Once the Loan is approved, Manager can create Employee Loan record for the Employee.
> Human Resources > Employee Loan Management > Employee Loan > New Employee Loan
<img class="screenshot" alt="Employee Loan Application" src="{{docs_base_url}}/assets/img/human-resources/employee-loan.png">
#### In the Employee Loan,
* Enter Employee and Loan Application
* Check "Repay from Salary" if the loan repayment will be deducted from the salary
* Enter Disbursement Date and Account Info
* As soon as you hit save, the repayment schedule is generated.
<img class="screenshot" alt="repayment Schedule" src="{{docs_base_url}}/assets/img/human-resources/repayment-schedule.png">
#### Loan repayment deduction from Salary
To auto deduct the Loan repayment from Salary, check "Repay from Salary" in Employee Loan. It will appear as Loan repayment in Salary Slip.
<img class="screenshot" alt="Salary Slip" src="{{docs_base_url}}/assets/img/human-resources/loan-repayment-salary-slip.png">

View File

@ -15,4 +15,5 @@ holiday-list
human-resource-setup
daily-work-summary
fleet-management
employee-loan-management
articles

View File

@ -107,7 +107,6 @@ portal_menu_items = [
{"title": _("Shipments"), "route": "/shipments", "reference_doctype": "Delivery Note", "role":"Customer"},
{"title": _("Issues"), "route": "/issues", "reference_doctype": "Issue", "role":"Customer"},
{"title": _("Addresses"), "route": "/addresses", "reference_doctype": "Address"},
{"title": _("Announcements"), "route": "/announcement", "reference_doctype": "Announcement"},
{"title": _("Fees"), "route": "/fees", "reference_doctype": "Fees", "role":"Student"}
]
@ -122,18 +121,7 @@ has_website_permission = {
"Sales Invoice": "erpnext.controllers.website_list_for_contact.has_website_permission",
"Supplier Quotation": "erpnext.controllers.website_list_for_contact.has_website_permission",
"Delivery Note": "erpnext.controllers.website_list_for_contact.has_website_permission",
"Issue": "erpnext.support.doctype.issue.issue.has_website_permission",
"Discussion": "erpnext.schools.web_form.discussion.discussion.has_website_permission"
}
permission_query_conditions = {
"Contact": "erpnext.utilities.address_and_contact.get_permission_query_conditions_for_contact",
"Address": "erpnext.utilities.address_and_contact.get_permission_query_conditions_for_address"
}
has_permission = {
"Contact": "erpnext.utilities.address_and_contact.has_permission",
"Address": "erpnext.utilities.address_and_contact.has_permission"
"Issue": "erpnext.support.doctype.issue.issue.has_website_permission"
}
dump_report_map = "erpnext.startup.report_data_map.data_map"
@ -153,7 +141,7 @@ doc_events = {
"after_insert": "frappe.email.doctype.contact.contact.update_contact",
"validate": "erpnext.hr.doctype.employee.employee.validate_employee_role",
"on_update": "erpnext.hr.doctype.employee.employee.update_user_permissions",
"on_update": "erpnext.utilities.address_and_contact.set_default_role"
"on_update": "frappe.geo.address_and_contact.set_default_role"
},
("Sales Taxes and Charges Template", 'Price List'): {
"on_update": "erpnext.shopping_cart.doctype.shopping_cart_settings.shopping_cart_settings.validate_cart_settings"

View File

@ -0,0 +1,88 @@
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on('Employee Loan', {
onload: function(frm) {
frm.set_query("employee_loan_application", function() {
return {
"filters": {
"employee": frm.doc.employee,
"docstatus": 1,
"status": "Approved"
}
};
});
$.each(["payment_account", "employee_loan_account"], function(i, field) {
frm.set_query(field, function() {
return {
"filters": {
"company": frm.doc.company,
"root_type": "Asset",
"is_group": 0
}
};
});
})
},
mode_of_payment: function(frm){
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);
}
}
});
},
refresh: function(frm) {
frm.trigger("toggle_fields");
if(frm.doc.docstatus==1) {
frm.add_custom_button(__('Ledger'), function() {
frappe.route_options = {
"voucher_no": frm.doc.name,
"from_date": frm.doc.posting_date,
"to_date": frm.doc.posting_date,
"company": frm.doc.company,
group_by_voucher: 0
};
frappe.set_route("query-report", "General Ledger");
}, "fa fa-table");
}
},
employee_loan_application: function(frm) {
return frm.call({
method: "erpnext.hr.doctype.employee_loan.employee_loan.get_employee_loan_application",
args: {
"employee_loan_application": frm.doc.employee_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")
}
});

View File

@ -0,0 +1,892 @@
{
"allow_copy": 0,
"allow_import": 1,
"allow_rename": 0,
"autoname": "ELN.####",
"beta": 0,
"creation": "2016-12-02 10:11:49.673604",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "Document",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"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_list_view": 0,
"in_standard_filter": 0,
"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,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "employee_name",
"fieldtype": "Read Only",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Employee Name",
"length": 0,
"no_copy": 0,
"options": "employee.employee_name",
"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": "employee_loan_application",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Employee Loan Application",
"length": 0,
"no_copy": 0,
"options": "Employee Loan Application",
"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": "loan_type",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 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,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_3",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 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": "posting_date",
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Posting Date",
"length": 0,
"no_copy": 1,
"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": "company",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 1,
"in_list_view": 0,
"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": 1,
"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": "status",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Status",
"length": 0,
"no_copy": 0,
"options": "Loan Unpaid\nLoan Paid\nEMI in progress\nLoan Repaid",
"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": "repay_from_salary",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Repay from Salary",
"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,
"fieldname": "section_break_8",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Loan 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,
"unique": 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_list_view": 0,
"in_standard_filter": 0,
"label": "Loan Amount",
"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,
"default": "",
"fieldname": "rate_of_interest",
"fieldtype": "Percent",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Rate of Interest (%) / Year",
"length": 0,
"no_copy": 0,
"options": "loan_type.rate_of_interest",
"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,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "disbursement_date",
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Disbursement 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,
"unique": 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_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": "Repay Over Number of Periods",
"fieldname": "repayment_method",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 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,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "",
"depends_on": "",
"fieldname": "repayment_periods",
"fieldtype": "Int",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 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,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "",
"depends_on": "",
"fieldname": "monthly_repayment_amount",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Repayment Amount",
"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,
"fieldname": "account_info",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Account 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,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "mode_of_payment",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Mode of Payment",
"length": 0,
"no_copy": 0,
"options": "Mode of Payment",
"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": "payment_account",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Payment Account",
"length": 0,
"no_copy": 0,
"options": "Account",
"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_9",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 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,
"fieldname": "employee_loan_account",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Employee Loan Account",
"length": 0,
"no_copy": 0,
"options": "Account",
"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": "section_break_15",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Repayment Schedule",
"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,
"fieldname": "repayment_schedule",
"fieldtype": "Table",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Repayment Schedule",
"length": 0,
"no_copy": 1,
"options": "Repayment Schedule",
"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": "section_break_17",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Totals",
"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": "0",
"fieldname": "total_payment",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Total Payment",
"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,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_19",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 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": "0",
"fieldname": "total_interest_payable",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Total Interest Payable",
"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,
"unique": 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_list_view": 0,
"in_standard_filter": 0,
"label": "Amended From",
"length": 0,
"no_copy": 1,
"options": "Employee Loan",
"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,
"unique": 0
}
],
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 1,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-01-17 07:13:10.704520",
"modified_by": "Administrator",
"module": "HR",
"name": "Employee Loan",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"is_custom": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "HR Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 1,
"write": 1
}
],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"search_fields": "posting_date",
"sort_field": "creation",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0
}

View File

@ -0,0 +1,128 @@
# -*- 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
import erpnext
from frappe import _
from frappe.utils import flt, rounded, add_months, nowdate
from erpnext.controllers.accounts_controller import AccountsController
from erpnext.accounts.general_ledger import make_gl_entries
class EmployeeLoan(AccountsController):
def validate(self):
check_repayment_method(self.repayment_method, self.loan_amount, self.monthly_repayment_amount, self.repayment_periods)
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)
self.make_repayment_schedule()
self.set_repayment_period()
self.calculate_totals()
def on_submit(self):
self.make_gl_entries()
def on_cancel(self):
self.make_gl_entries()
def make_gl_entries(self):
gl_entries = []
# Gl entries for employee loan account
gl_entries.append(
self.get_gl_dict({
"account": self.employee_loan_account,
"party_type": "Employee",
"party": self.employee,
"debit": self.loan_amount,
"debit_in_account_currency": self.loan_amount
})
)
# Gl entries for payment account
gl_entries.append(
self.get_gl_dict({
"account": self.payment_account,
"credit": self.loan_amount,
"credit_in_account_currency": self.loan_amount
})
)
make_gl_entries(gl_entries, cancel=(self.docstatus == 2))
def make_repayment_schedule(self):
self.repayment_schedule = []
payment_date = self.disbursement_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
for data in self.repayment_schedule:
self.total_payment += data.total_payment
self.total_interest_payable +=data.interest_amount
def update_status(self):
if self.disbursement_date:
self.status = "Loan Paid"
if len(self.repayment_schedule)>0:
self.status = "repayment in progress"
def check_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 repayment_method == "Repay Over Number of 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_employee_loan_application(employee_loan_application):
employee_loan = frappe.get_doc("Employee Loan Application", employee_loan_application)
if employee_loan:
return employee_loan

View File

@ -0,0 +1,68 @@
# -*- 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
from erpnext.hr.doctype.salary_structure.test_salary_structure import make_employee
class TestEmployeeLoan(unittest.TestCase):
def setUp(self):
create_loan_type("Personal Loan", 500000, 8.4)
self.employee = make_employee("robert_loan@loan.com")
create_employee_loan(self.employee, "Personal Loan", 280000, "Repay Over Number of Periods", 20)
def test_employee_loan(self):
employee_loan = frappe.get_doc("Employee Loan", {"employee":self.employee})
self.assertEquals(employee_loan.monthly_repayment_amount, 15052)
self.assertEquals(employee_loan.total_interest_payable, 21034)
self.assertEquals(employee_loan.total_payment, 301034)
schedule = employee_loan.repayment_schedule
self.assertEquals(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.assertEquals(schedule[idx].principal_amount, principal_amount)
self.assertEquals(schedule[idx].interest_amount, interest_amount)
self.assertEquals(schedule[idx].balance_loan_amount, balance_loan_amount)
employee_loan.repayment_method = "Repay Fixed Amount per Period"
employee_loan.monthly_repayment_amount = 14000
employee_loan.save()
self.assertEquals(len(employee_loan.repayment_schedule), 22)
self.assertEquals(employee_loan.total_interest_payable, 22712)
self.assertEquals(employee_loan.total_payment, 302712)
def create_loan_type(loan_name, maximum_loan_amount, rate_of_interest):
if not frappe.db.get_value("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_employee_loan(employee, loan_type, loan_amount, repayment_method, repayment_periods):
if not frappe.db.get_value("Employee Loan", {"employee":employee}):
employee_loan = frappe.new_doc("Employee Loan")
employee_loan.update({
"employee": employee,
"loan_type": loan_type,
"loan_amount": loan_amount,
"repayment_method": repayment_method,
"repayment_periods": repayment_periods,
"disbursement_date": nowdate(),
"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"),
"employee_loan_account": frappe.db.get_value('Account', {'account_type': 'Cash', 'company': erpnext.get_default_company(),'is_group':0}, "name")
})
employee_loan.insert()
return employee_loan
else:
return frappe.get_doc("Employee Loan", {"employee":employee})

View File

@ -0,0 +1,16 @@
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on('Employee Loan Application', {
refresh: function(frm) {
frm.trigger("toggle_fields")
},
repayment_method: function(frm) {
frm.trigger("toggle_fields")
},
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")
}
});

View File

@ -0,0 +1,681 @@
{
"allow_copy": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "ELA/.#####",
"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_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_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,
"unique": 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_list_view": 0,
"in_standard_filter": 0,
"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,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "employee_name",
"fieldtype": "Read Only",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 1,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Employee Name",
"length": 0,
"no_copy": 0,
"options": "employee.employee_name",
"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,
"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_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": 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_list_view": 0,
"in_standard_filter": 0,
"label": "Status",
"length": 0,
"no_copy": 0,
"options": "Open\nApproved\nRejected",
"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": "company",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"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,
"unique": 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_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,
"unique": 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_list_view": 0,
"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,
"unique": 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_list_view": 0,
"in_standard_filter": 0,
"label": "Loan Amount",
"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": "required_by_date",
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 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,
"unique": 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_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,
"fieldname": "description",
"fieldtype": "Small Text",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 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,
"unique": 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_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,
"unique": 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_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,
"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_list_view": 0,
"in_standard_filter": 0,
"label": "Rate of Interest",
"length": 0,
"no_copy": 0,
"options": "loan_type.rate_of_interest",
"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,
"unique": 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_list_view": 0,
"in_standard_filter": 0,
"label": "Total Payable Interest",
"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,
"unique": 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_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,
"depends_on": "",
"fieldname": "repayment_amount",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Repayment Amount",
"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,
"depends_on": "",
"fieldname": "repayment_periods",
"fieldtype": "Int",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 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,
"unique": 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_list_view": 0,
"in_standard_filter": 0,
"label": "Total Payable Amount",
"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,
"unique": 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_list_view": 0,
"in_standard_filter": 0,
"label": "Amended From",
"length": 0,
"no_copy": 1,
"options": "Employee 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,
"unique": 0
}
],
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 1,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-01-09 12:02:36.562577",
"modified_by": "Administrator",
"module": "HR",
"name": "Employee Loan Application",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"is_custom": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "HR Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 1,
"write": 1
},
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"is_custom": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Employee",
"set_user_permissions": 0,
"share": 1,
"submit": 1,
"write": 1
}
],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"search_fields": "employee, employee_name, loan_type, loan_amount",
"sort_field": "modified",
"sort_order": "DESC",
"timeline_field": "employee",
"title_field": "employee_name",
"track_changes": 1,
"track_seen": 0
}

View File

@ -0,0 +1,34 @@
# -*- 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
from frappe.model.document import Document
from erpnext.hr.doctype.employee_loan.employee_loan import get_monthly_repayment_amount, check_repayment_method
class EmployeeLoanApplication(Document):
def validate(self):
check_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 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)
self.repayment_periods = math.ceil((math.log(self.repayment_amount) - math.log(self.repayment_amount - \
(self.loan_amount*monthly_interest_rate)))/(math.log(1+monthly_interest_rate)))
self.total_payable_amount = self.repayment_amount * self.repayment_periods
self.total_payable_interest = self.total_payable_amount - self.loan_amount

View File

@ -0,0 +1,51 @@
# -*- 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 TestEmployeeLoanApplication(unittest.TestCase):
def setUp(self):
self.create_loan_type()
self.employee = 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("Employee Loan Application", {"employee":self.employee}, "name"):
loan_application = frappe.new_doc("Employee Loan Application")
loan_application.update({
"employee": self.employee,
"loan_type": "Home Loan",
"rate_of_interest": 9.2,
"loan_amount": 250000,
"repayment_method": "Repay Over Number of Periods",
"repayment_periods": 24
})
loan_application.insert()
def test_loan_totals(self):
loan_application = frappe.get_doc("Employee Loan Application", {"employee":self.employee})
self.assertEquals(loan_application.repayment_amount, 11445)
self.assertEquals(loan_application.total_payable_interest, 24680)
self.assertEquals(loan_application.total_payable_amount, 274680)
loan_application.repayment_method = "Repay Fixed Amount per Period"
loan_application.repayment_amount = 15000
loan_application.save()
self.assertEquals(loan_application.repayment_periods, 18)
self.assertEquals(loan_application.total_payable_interest, 20000)
self.assertEquals(loan_application.total_payable_amount, 270000)

View File

@ -0,0 +1,7 @@
// 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) {
}
});

View File

@ -1,15 +1,15 @@
{
"allow_copy": 0,
"allow_import": 1,
"allow_import": 0,
"allow_rename": 0,
"autoname": "Discussion.####",
"autoname": "field:loan_name",
"beta": 0,
"creation": "2016-06-13 07:57:38.326925",
"creation": "2016-12-02 10:41:40.732843",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "Document",
"editable_grid": 0,
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
@ -17,7 +17,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "subject",
"fieldname": "loan_name",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
@ -25,7 +25,7 @@
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Subject",
"label": "Loan Name",
"length": 0,
"no_copy": 0,
"permlevel": 0,
@ -36,7 +36,7 @@
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
@ -45,18 +45,17 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "course",
"fieldtype": "Link",
"fieldname": "maximum_loan_amount",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Course",
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Maximum Loan Amount",
"length": 0,
"no_copy": 0,
"options": "Course",
"permlevel": 0,
"precision": "",
"print_hide": 0,
@ -65,7 +64,92 @@
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 1,
"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_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_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_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
},
@ -96,34 +180,6 @@
"search_index": 0,
"set_only_once": 0,
"unique": 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_list_view": 0,
"in_standard_filter": 0,
"label": "Amended From",
"length": 0,
"no_copy": 1,
"options": "Discussion",
"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,
"unique": 0
}
],
"hide_heading": 0,
@ -132,14 +188,14 @@
"image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 1,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2016-11-07 05:28:34.032169",
"modified": "2016-12-29 15:54:17.716285",
"modified_by": "Administrator",
"module": "Schools",
"name": "Discussion",
"module": "HR",
"name": "Loan Type",
"name_case": "",
"owner": "Administrator",
"permissions": [
@ -158,18 +214,17 @@
"print": 1,
"read": 1,
"report": 1,
"role": "Academics User",
"role": "HR Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 1,
"submit": 0,
"write": 1
}
],
"quick_entry": 1,
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "subject",
"track_seen": 1
"track_seen": 0
}

View File

@ -0,0 +1,10 @@
# -*- 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
from frappe.model.document import Document
class LoanType(Document):
pass

View File

@ -0,0 +1,12 @@
# -*- 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

View File

@ -1,60 +1,30 @@
{
"allow_copy": 0,
"allow_import": 0,
"allow_rename": 1,
"autoname": "Topic.####",
"allow_rename": 0,
"beta": 0,
"creation": "2016-06-28 07:06:38.749398",
"creation": "2016-12-20 15:32:25.078334",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "Document",
"editable_grid": 0,
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "course",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Course",
"length": 0,
"no_copy": 0,
"options": "Course",
"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": 1,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "topic_name",
"fieldtype": "Data",
"columns": 2,
"fieldname": "payment_date",
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Topic Name",
"label": "Payment Date",
"length": 0,
"no_copy": 0,
"permlevel": 0,
@ -64,7 +34,7 @@
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
@ -73,26 +43,26 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "introduction",
"fieldtype": "Text",
"columns": 2,
"fieldname": "principal_amount",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Introduction",
"label": "Principal Amount",
"length": 0,
"no_copy": 0,
"no_copy": 1,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
@ -101,26 +71,82 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "content",
"fieldtype": "Text Editor",
"columns": 2,
"fieldname": "interest_amount",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Content",
"label": "Interest Amount",
"length": 0,
"no_copy": 1,
"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,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 2,
"fieldname": "total_payment",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Total Payment",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 2,
"fieldname": "balance_loan_amount",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Balance Loan Amount",
"length": 0,
"no_copy": 1,
"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,
"unique": 0
@ -134,42 +160,20 @@
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2016-11-07 05:29:20.531725",
"modified": "2017-01-09 12:00:10.818772",
"modified_by": "Administrator",
"module": "Schools",
"name": "Topic",
"module": "HR",
"name": "Repayment Schedule",
"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,
"is_custom": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Academics User",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
}
],
"permissions": [],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "course",
"track_changes": 1,
"track_seen": 0
}

View File

@ -0,0 +1,10 @@
# -*- 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
from frappe.model.document import Document
class RepaymentSchedule(Document):
pass

View File

@ -110,6 +110,7 @@ cur_frm.cscript.depends_on_lwp = function(doc,dt,dn){
calculate_earning_total(doc, dt, dn, true);
calculate_ded_total(doc, dt, dn, true);
calculate_net_pay(doc, dt, dn);
refresh_many(['amount','gross_pay', 'rounded_total', 'net_pay', 'loan_repayment']);
};
// Calculate earning total

View File

@ -1266,7 +1266,35 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"description": "Gross Pay + Arrear Amount +Encashment Amount - Total Deduction",
"fieldname": "loan_repayment",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Loan Repayment",
"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,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"description": "Gross Pay + Arrear Amount + Encashment Amount - Total Deduction - Loan Repayment",
"fieldname": "net_pay",
"fieldtype": "Currency",
"hidden": 0,
@ -1362,7 +1390,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2016-12-14 08:26:31.400930",
"modified": "2017-01-09 12:37:03.802501",
"modified_by": "Administrator",
"module": "HR",
"name": "Salary Slip",
@ -1413,7 +1441,7 @@
},
{
"amend": 0,
"apply_user_permissions": 0,
"apply_user_permissions": 1,
"cancel": 0,
"create": 0,
"delete": 0,
@ -1430,6 +1458,7 @@
"set_user_permissions": 0,
"share": 0,
"submit": 0,
"user_permission_doctypes": "[\"Employee\"]",
"write": 0
}
],
@ -1440,5 +1469,6 @@
"sort_order": "DESC",
"timeline_field": "employee",
"title_field": "employee_name",
"track_changes": 0,
"track_seen": 0
}

View File

@ -330,10 +330,20 @@ class SalarySlip(TransactionBase):
self.sum_components('earnings', 'gross_pay')
self.sum_components('deductions', 'total_deduction')
self.net_pay = flt(self.gross_pay) - flt(self.total_deduction)
self.set_loan_repayment()
self.net_pay = flt(self.gross_pay) - (flt(self.total_deduction) + flt(self.loan_repayment))
self.rounded_total = rounded(self.net_pay,
self.precision("net_pay") if disable_rounded_total else 0)
def set_loan_repayment(self):
employee_loan = frappe.db.sql("""select sum(total_payment) as loan_repayment from `tabRepayment Schedule`
where payment_date between %s and %s and parent in (select name from `tabEmployee Loan`
where employee = %s and repay_from_salary = 1 and docstatus = 1)""",
(self.start_date, self.end_date, self.employee), as_dict=True)
if employee_loan:
self.loan_repayment = employee_loan[0].loan_repayment
def on_submit(self):
if self.net_pay < 0:
frappe.throw(_("Net Pay cannot be less than 0"))

View File

@ -7,7 +7,7 @@ import frappe
import erpnext
import calendar
from erpnext.accounts.utils import get_fiscal_year
from frappe.utils import getdate, nowdate, add_days
from frappe.utils import getdate, nowdate, add_days, flt
from erpnext.hr.doctype.salary_structure.salary_structure import make_salary_slip
from erpnext.hr.doctype.process_payroll.test_process_payroll import get_salary_component_account
from erpnext.hr.doctype.process_payroll.process_payroll import get_month_details
@ -24,7 +24,6 @@ class TestSalarySlip(unittest.TestCase):
frappe.db.set_value("Company", erpnext.get_default_company(), "default_holiday_list", "Salary Slip Test Holiday List")
def tearDown(self):
frappe.db.set_value("HR Settings", None, "include_holidays_in_total_working_days", 0)
frappe.set_user("Administrator")
@ -40,12 +39,12 @@ class TestSalarySlip(unittest.TestCase):
self.assertEquals(ss.total_working_days, no_of_days[0])
self.assertEquals(ss.payment_days, no_of_days[0])
self.assertEquals(ss.earnings[0].amount, 5000)
self.assertEquals(ss.earnings[0].amount, 25000)
self.assertEquals(ss.earnings[1].amount, 3000)
self.assertEquals(ss.deductions[0].amount, 5000)
self.assertEquals(ss.deductions[1].amount, 2500)
self.assertEquals(ss.gross_pay, 10500)
self.assertEquals(ss.net_pay, 3000)
self.assertEquals(ss.deductions[1].amount, 5000)
self.assertEquals(ss.gross_pay, 40500)
self.assertEquals(ss.net_pay, 29918)
def test_salary_slip_with_holidays_excluded(self):
no_of_days = self.get_no_of_days()
@ -58,13 +57,13 @@ class TestSalarySlip(unittest.TestCase):
self.assertEquals(ss.total_working_days, no_of_days[0] - no_of_days[1])
self.assertEquals(ss.payment_days, no_of_days[0] - no_of_days[1])
self.assertEquals(ss.earnings[0].amount, 5000)
self.assertEquals(ss.earnings[0].default_amount, 5000)
self.assertEquals(ss.earnings[0].amount, 25000)
self.assertEquals(ss.earnings[0].default_amount, 25000)
self.assertEquals(ss.earnings[1].amount, 3000)
self.assertEquals(ss.deductions[0].amount, 5000)
self.assertEquals(ss.deductions[1].amount, 2500)
self.assertEquals(ss.gross_pay, 10500)
self.assertEquals(ss.net_pay, 3000)
self.assertEquals(ss.deductions[1].amount, 5000)
self.assertEquals(ss.gross_pay, 40500)
self.assertEquals(ss.net_pay, 29918)
def test_payment_days(self):
no_of_days = self.get_no_of_days()
@ -130,9 +129,23 @@ class TestSalarySlip(unittest.TestCase):
ss = frappe.get_doc("Salary Slip",
self.make_employee_salary_slip("test_employee@salary.com", "Monthly"))
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.employee_loan.test_employee_loan import create_loan_type, create_employee_loan
employee = self.make_employee("test_employee@salary.com")
create_loan_type("Car Loan", 500000, 6.4)
employee_loan = create_employee_loan(employee, "Car Loan", 11000, "Repay Over Number of Periods", 20)
employee_loan.repay_from_salary = 1
employee_loan.submit()
ss = frappe.get_doc("Salary Slip",
self.make_employee_salary_slip("test_employee@salary.com", "Monthly"))
ss.submit()
self.assertEquals(ss.loan_repayment, 582)
self.assertEquals(ss.net_pay, (flt(ss.gross_pay) - (flt(ss.total_deduction) + flt(ss.loan_repayment))))
def test_payroll_frequency(self):
fiscal_year = get_fiscal_year(nowdate(), company="_Test Company")[0]
month = "%02d" % getdate(nowdate()).month
@ -167,7 +180,7 @@ class TestSalarySlip(unittest.TestCase):
}).insert()
if not frappe.db.get_value("Employee", {"user_id": user}):
frappe.get_doc({
employee = frappe.get_doc({
"doctype": "Employee",
"naming_series": "EMP-",
"employee_name": user,
@ -183,6 +196,9 @@ class TestSalarySlip(unittest.TestCase):
"status": "Active",
"employment_type": "Intern"
}).insert()
return employee.name
else:
return frappe.get_value("Employee", {"employee_name":user}, "name")
def make_holiday_list(self):
fiscal_year = get_fiscal_year(nowdate(), company="_Test Company")
@ -278,7 +294,7 @@ def make_salary_structure(sal_struct, payroll_frequency, employee):
def get_employee_details(employee):
return [{"employee": employee,
"base": 25000,
"base": 50000,
"variable": 5000
}
]
@ -289,14 +305,14 @@ def get_earnings_component():
"salary_component": 'Basic Salary',
"abbr":'BS',
"condition": 'base > 10000',
"formula": 'base*.2',
"formula": 'base*.5',
"idx": 1
},
{
"salary_component": 'Basic Salary',
"abbr":'BS',
"condition": 'base < 10000',
"formula": 'base*.1',
"formula": 'base*.2',
"idx": 2
},
{
@ -320,13 +336,13 @@ def get_deductions_component():
"salary_component": 'Professional Tax',
"abbr":'PT',
"condition": 'base > 10000',
"formula": 'base*.2',
"formula": 'base*.1',
"idx": 1
},
{
"salary_component": 'TDS',
"abbr":'T',
"formula": 'base*.5',
"formula": 'base*.1',
"idx": 2
},
{

View File

@ -19,8 +19,8 @@ class TestSalaryStructure(unittest.TestCase):
frappe.db.set_value("Company", erpnext.get_default_company(), "default_holiday_list", "Salary Structure Test Holiday List")
make_earning_salary_component(["Basic Salary", "Allowance", "HRA"])
make_deduction_salary_component(["Professional Tax", "TDS"])
self.make_employee("test_employee@salary.com")
self.make_employee("test_employee_2@salary.com")
make_employee("test_employee@salary.com")
make_employee("test_employee_2@salary.com")
def make_holiday_list(self):
if not frappe.db.get_value("Holiday List", "Salary Structure Test Holiday List"):
@ -34,7 +34,18 @@ class TestSalaryStructure(unittest.TestCase):
holiday_list.get_weekly_off_dates()
holiday_list.save()
def make_employee(self, user):
def test_amount_totals(self):
sal_slip = frappe.get_value("Salary Slip", {"employee_name":"test_employee@salary.com"})
if not sal_slip:
sal_slip = make_salary_slip_from_salary_structure(employee=frappe.get_value("Employee", {"employee_name":"test_employee@salary.com"}))
self.assertEquals(sal_slip.get("salary_structure"), 'Salary Structure Sample')
self.assertEquals(sal_slip.get("earnings")[0].amount, 5000)
self.assertEquals(sal_slip.get("deductions")[0].amount, 5000)
self.assertEquals(sal_slip.get("deductions")[1].amount, 2500)
self.assertEquals(sal_slip.get("total_deduction"), 7500)
self.assertEquals(sal_slip.get("net_pay"), 7500)
def make_employee(user):
if not frappe.db.get_value("User", user):
frappe.get_doc({
"doctype": "User",
@ -65,18 +76,6 @@ class TestSalaryStructure(unittest.TestCase):
else:
return frappe.get_value("Employee", {"employee_name":user}, "name")
def test_amount_totals(self):
sal_slip = frappe.get_value("Salary Slip", {"employee_name":"test_employee@salary.com"})
if not sal_slip:
sal_slip = make_salary_slip_from_salary_structure(employee=frappe.get_value("Employee", {"employee_name":"test_employee@salary.com"}))
self.assertEquals(sal_slip.get("salary_structure"), 'Salary Structure Sample')
self.assertEquals(sal_slip.get("earnings")[0].amount, 5000)
self.assertEquals(sal_slip.get("deductions")[0].amount, 5000)
self.assertEquals(sal_slip.get("deductions")[1].amount, 2500)
self.assertEquals(sal_slip.get("total_deduction"), 7500)
self.assertEquals(sal_slip.get("net_pay"), 7500)
def make_salary_slip_from_salary_structure(employee):
sal_struct = make_salary_structure('Salary Structure Sample')
sal_slip = make_salary_slip(sal_struct, employee = employee)

View File

@ -30,15 +30,18 @@ def get_columns(leave_types):
return columns
def get_data(filters, leave_types):
user = frappe.session.user
allocation_records_based_on_to_date = get_leave_allocation_records(filters.to_date)
active_employees = frappe.get_all("Employee",
filters = { "status": "Active", "company": filters.company},
fields = ["name", "employee_name", "department"])
fields = ["name", "employee_name", "department", "user_id"])
data = []
for employee in active_employees:
leave_approvers = [l.leave_approver for l in frappe.db.sql("""select leave_approver from `tabEmployee Leave Approver` where parent = %s""",
(employee.name),as_dict=True)]
if (len(leave_approvers) and user in leave_approvers) or (user in ["Administrator", employee.user_id]) or ("HR Manager" in frappe.get_roles(user)):
row = [employee.name, employee.employee_name, employee.department]
for leave_type in leave_types:

View File

@ -8,7 +8,7 @@ from erpnext.setup.setup_wizard import domainify
def execute():
frappe.reload_doctype('Role')
for dt in ("assessment", "announcement", "course", "fees"):
for dt in ("assessment", "course", "fees"):
frappe.reload_doc("schools", "doctype", dt)
frappe.reload_doc('website', 'doctype', 'portal_menu_item')

View File

@ -20,7 +20,8 @@ def execute():
frappe.reload_doc("schools", "doctype", "evaluation_criteria")
for assessment in frappe.get_all("Assessment Plan", fields=["name", "grading_scale"]):
for assessment in frappe.get_all("Assessment Plan", fields=["name", "grading_scale"], filters = [["docstatus", "!=", 2 ]]):
print assessment
for stud_result in frappe.db.sql("select * from `tabAssessment Result` where parent= %s", assessment.name, as_dict=True):
if stud_result.result:
assessment_result = frappe.new_doc("Assessment Result")
@ -30,6 +31,7 @@ def execute():
assessment_result.grading_scale = assessment.grading_scale
assessment_result.total_score = stud_result.result
assessment_result.flags.ignore_validate = True
assessment_result.flags.ignore_mandatory = True
assessment_result.save()
frappe.db.sql("""delete from `tabAssessment Result` where parent != '' or parent is not null""")

View File

@ -1,7 +1,7 @@
{
"allow_copy": 0,
"allow_import": 1,
"allow_rename": 0,
"allow_rename": 1,
"autoname": "TASK.#####",
"beta": 0,
"creation": "2013-01-29 19:25:50",
@ -944,7 +944,7 @@
"istable": 0,
"max_attachments": 5,
"menu_index": 0,
"modified": "2016-11-07 05:12:23.294476",
"modified": "2017-01-27 01:26:24.985167",
"modified_by": "Administrator",
"module": "Projects",
"name": "Task",
@ -960,7 +960,6 @@
"export": 0,
"if_owner": 0,
"import": 0,
"is_custom": 0,
"permlevel": 0,
"print": 1,
"read": 1,
@ -979,5 +978,6 @@
"sort_order": "DESC",
"timeline_field": "project",
"title_field": "subject",
"track_changes": 0,
"track_seen": 1
}

View File

@ -31,7 +31,8 @@ erpnext.financial_statements = {
"account": data.account,
"company": frappe.query_report_filters_by_name.company.get_value(),
"from_date": data.from_date || data.year_start_date,
"to_date": data.to_date || data.year_end_date
"to_date": data.to_date || data.year_end_date,
"project": $.grep(frappe.query_report.filters, function(e){ return e.df.fieldname == 'project'; })[0].$input.val()
};
frappe.set_route("query-report", "General Ledger");
},

View File

@ -36,7 +36,7 @@ $.extend(erpnext.queries, {
customer_filter: function(doc) {
if(!doc.customer) {
frappe.throw(__("Please set {0}", __(frappe.meta.get_label(doc.doctype, "customer", doc.name))));
frappe.throw(__("Please set {0}", [__(frappe.meta.get_label(doc.doctype, "customer", doc.name))]));
}
return { filters: { customer: doc.customer } };
@ -45,8 +45,8 @@ $.extend(erpnext.queries, {
contact_query: function(doc) {
if(frappe.dynamic_link) {
if(!doc[frappe.dynamic_link.fieldname]) {
frappe.throw(__("Please set {0}", __(frappe.meta.get_label(doc.doctype,
frappe.dynamic_link.fieldname, doc.name))));
frappe.throw(__("Please set {0}",
[__(frappe.meta.get_label(doc.doctype, frappe.dynamic_link.fieldname, doc.name))]));
}
return {
@ -58,8 +58,8 @@ $.extend(erpnext.queries, {
address_query: function(doc) {
if(frappe.dynamic_link) {
if(!doc[frappe.dynamic_link.fieldname]) {
frappe.throw(__("Please set {0}", __(frappe.meta.get_label(doc.doctype,
frappe.dynamic_link.fieldname, doc.name))));
frappe.throw(__("Please set {0}",
[__(frappe.meta.get_label(doc.doctype, frappe.dynamic_link.fieldname, doc.name))]));
}
return {
@ -68,9 +68,16 @@ $.extend(erpnext.queries, {
}
},
company_address_query: function(doc) {
return {
query: 'frappe.geo.doctype.address.address.address_query',
filters: { is_your_company_address: 1, link_doctype: 'Company', link_name: doc.company || '' }
};
},
supplier_filter: function(doc) {
if(!doc.supplier) {
frappe.throw(__("Please set {0}", __(frappe.meta.get_label(doc.doctype, "supplier", doc.name))));
frappe.throw(__("Please set {0}", [__(frappe.meta.get_label(doc.doctype, "supplier", doc.name))]));
}
return { filters: { supplier: doc.supplier } };
@ -78,8 +85,8 @@ $.extend(erpnext.queries, {
lead_filter: function(doc) {
if(!doc.lead) {
frappe.throw(__("Please specify a") + " " +
__(frappe.meta.get_label(doc.doctype, "lead", doc.name)));
frappe.throw(__("Please specify a {0}",
[__(frappe.meta.get_label(doc.doctype, "lead", doc.name))]));
}
return { filters: { lead: doc.lead } };

View File

@ -82,32 +82,6 @@ $.extend(erpnext, {
$.extend(erpnext.utils, {
clear_address_and_contact: function(frm) {
$(frm.fields_dict['address_html'].wrapper).html("");
frm.fields_dict['contact_html'] && $(frm.fields_dict['contact_html'].wrapper).html("");
},
render_address_and_contact: function(frm) {
// render address
$(frm.fields_dict['address_html'].wrapper)
.html(frappe.render_template("address_list",
cur_frm.doc.__onload))
.find(".btn-address").on("click", function() {
frappe.new_doc("Address");
});
// render contact
if(frm.fields_dict['contact_html']) {
$(frm.fields_dict['contact_html'].wrapper)
.html(frappe.render_template("contact_list",
cur_frm.doc.__onload))
.find(".btn-contact").on("click", function() {
frappe.new_doc("Contact");
}
);
}
},
set_party_dashboard_indicators: function(frm) {
if(frm.doc.__onload && frm.doc.__onload.dashboard_info) {
var info = frm.doc.__onload.dashboard_info;

View File

@ -1,9 +0,0 @@
// Copyright (c) 2016, Frappe and contributors
// For license information, please see license.txt
frappe.ui.form.on('Announcement', {
onload: function(frm) {
frm.add_fetch('instructor', 'instructor_name' , 'posted_by');
}
});

View File

@ -1,315 +0,0 @@
{
"allow_copy": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "announcement.#####",
"beta": 0,
"creation": "2016-06-23 05:37:33.996289",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "Document",
"editable_grid": 0,
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"default": "",
"fieldname": "receiver",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Receiver",
"length": 0,
"no_copy": 0,
"options": "Student\nStudent Group\nAll Students",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 1,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "instructor",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Instructor",
"length": 0,
"no_copy": 0,
"options": "Instructor",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "column_break_3",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"depends_on": "eval: doc.receiver == \"Student\"",
"fieldname": "student",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Student",
"length": 0,
"no_copy": 0,
"options": "Student",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 1,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"depends_on": "eval: doc.receiver == \"Student Group\"",
"fieldname": "student_group",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Student Group",
"length": 0,
"no_copy": 0,
"options": "Student Group",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 1,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "posted_by",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Posted By",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "section_break_5",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "subject",
"fieldtype": "Small Text",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Subject",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "description",
"fieldtype": "Text Editor",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Description",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "amended_from",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Amended From",
"length": 0,
"no_copy": 1,
"options": "Announcement",
"permlevel": 0,
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}
],
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 1,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2016-07-21 06:30:12.825629",
"modified_by": "r@r.com",
"module": "Schools",
"name": "Announcement",
"name_case": "",
"owner": "demo@erpnext.com",
"permissions": [
{
"amend": 1,
"apply_user_permissions": 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": "Academics User",
"set_user_permissions": 0,
"share": 1,
"submit": 1,
"write": 1
}
],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "subject",
"track_seen": 1
}

View File

@ -1,75 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
from frappe import _
class Announcement(Document):
def validate(self):
self.validate_receiver()
self.set_posted_by()
def validate_receiver(self):
if self.receiver == "Student":
if not self.student:
frappe.throw(_("Please select a Student"))
self.student_group = None
elif self.receiver == "Student Group":
if not self.student_group:
frappe.throw(_("Please select a Student Group"))
self.student = None
else:
self.student_group = None
self.student = None
def set_posted_by(self):
if self.instructor:
self.posted_by = frappe.db.get_value("Instructor", self.instructor, "instructor_name")
else:
self.posted_by = frappe.session.user
def get_message_list(doctype, txt, filters, limit_start, limit_page_length=20):
user = frappe.session.user
student = frappe.db.sql("select name from `tabStudent` where student_email_id= %s", user)
if student:
sg_list = frappe.db.sql("""select parent from `tabStudent Group Student` as sgs
where sgs.student = %s """,(student))
data = frappe.db.sql("""select name, receiver, subject, description, posted_by, modified,
student, student_group
from `tabAnnouncement` as announce
where (announce.receiver = "Student" and announce.student = %s)
or (announce.receiver = "Student Group" and announce.student_group in %s)
or announce.receiver = "All Students"
and announce.docstatus = 1
order by announce.idx asc limit {0} , {1}"""
.format(limit_start, limit_page_length), (student,sg_list), as_dict = True)
for announcement in data:
try:
num_attachments = frappe.db.sql(""" select count(file_url) from tabFile as file
where file.attached_to_name=%s
and file.attached_to_doctype=%s""",(announcement.name,"Announcement"))
except IOError or frappe.DoesNotExistError:
pass
frappe.local.message_log.pop()
announcement.num_attachments = num_attachments[0][0]
return data
def get_list_context(context=None):
return {
"show_sidebar": True,
'no_breadcrumbs': True,
"title": _("Announcements"),
"get_list": get_message_list,
"row_template": "templates/includes/announcement/announcement_row.html"
}

View File

@ -1,12 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe and Contributors
# See license.txt
from __future__ import unicode_literals
import frappe
import unittest
# test_records = frappe.get_test_records('Announcement')
class TestAnnouncement(unittest.TestCase):
pass

View File

@ -13,7 +13,7 @@ class AssessmentPlan(Document):
frappe.throw(_("Please select Student Group or Student Batch"))
self.validate_student_batch()
self.validate_overlap()
self.validate_max_score()
def validate_overlap(self):
"""Validates overlap for Student Group/Student Batch, Instructor, Room"""
@ -43,3 +43,10 @@ class AssessmentPlan(Document):
def validate_student_batch(self):
if self.student_group:
self.student_batch = frappe.db.get_value("Student Group", self.student_group, "student_batch")
def validate_max_score(self):
max_score = 0
for d in self.evaluation_criterias:
max_score += d.maximum_score
if self.maximum_assessment_score != max_score:
frappe.throw(_("Sum of Scores of Evaluation Criterias needs to be {0}.".format(self.maximum_assessment_score)))

View File

@ -1,8 +0,0 @@
// Copyright (c) 2016, Frappe and contributors
// For license information, please see license.txt
frappe.ui.form.on('Discussion', {
refresh: function(frm) {
}
});

View File

@ -1,37 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe import _
from frappe.model.document import Document
class Discussion(Document):
def validate(self):
if not self.owner== frappe.session.user:
frappe.throw(_("Not Permitted"))
def get_discussions(doctype, txt, filters, limit_start, limit_page_length=20):
from frappe.www.list import get_list
if not filters:
filters = []
filters.append(("Discussion", "course", "=", frappe.form_dict.course))
return get_list(doctype, txt, filters, limit_start, limit_page_length, ignore_permissions=True)
def get_list_context(context=None):
course_name = frappe.form_dict.course
portal_items = [{'reference_doctype': u'Topic', 'route': u"/topic?course=" + str(course_name), 'show_always': 0L, 'title': u'Topics'},
{'reference_doctype': u'Discussion', 'route': u"/discussion?course=" + str(course_name), 'show_always': 0L, 'title': u'Discussions'},
]
sidebar_title = course_name
return {
"show_sidebar": True,
'no_breadcrumbs': True,
"get_list" : get_discussions,
"title": _("Discussions"),
"sidebar_items" : portal_items,
"sidebar_title" : sidebar_title,
"row_template": "templates/includes/discussion/discussion_row.html"
}

View File

@ -1,12 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe and Contributors
# See license.txt
from __future__ import unicode_literals
import frappe
import unittest
# test_records = frappe.get_test_records('Discussion')
class TestDiscussion(unittest.TestCase):
pass

View File

@ -1,12 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe and Contributors
# See license.txt
from __future__ import unicode_literals
import frappe
import unittest
# test_records = frappe.get_test_records('Topic')
class TestTopic(unittest.TestCase):
pass

View File

@ -1,8 +0,0 @@
// Copyright (c) 2016, Frappe and contributors
// For license information, please see license.txt
frappe.ui.form.on('Topic', {
refresh: function(frm) {
}
});

View File

@ -1,49 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
from frappe import _
class Topic(Document):
pass
def get_topic_list(doctype, txt, filters, limit_start, limit_page_length=20):
user = frappe.session.user
student = frappe.db.sql("select name from `tabStudent` where student_email_id= %s", user)
if student:
data = frappe. db.sql('''select name, course, modified,topic_name, introduction, content from `tabTopic` as topic
where topic.course = %s
order by idx asc limit {0} , {1}'''.format(limit_start, limit_page_length),filters.course,as_dict = True)
for topic in data:
try:
num_attachments = frappe.db.sql(""" select count(file_url) from tabFile as file
where file.attached_to_name=%s
and file.attached_to_doctype=%s""",(topic.name,"Topic"))
except IOError or frappe.DoesNotExistError:
pass
frappe.local.message_log.pop()
topic.num_attachments = num_attachments[0][0]
return data
def get_list_context(context=None):
course = frappe.get_doc('Course', frappe.form_dict.course)
portal_items = [{'reference_doctype': u'Topic', 'route': u"/topic?course=" + str(course.name), 'show_always': 0L, 'title': u'Topics'},
{'reference_doctype': u'Discussion', 'route': u"/discussion?course=" + str(course.name), 'show_always': 0L, 'title': u'Discussions'},
]
return {
"show_sidebar": True,
"title": _("Topic"),
'no_breadcrumbs': True,
"sidebar_items" : portal_items,
"sidebar_title" : course.name,
"get_list": get_topic_list,
"row_template": "templates/includes/topic/topic_row.html"
}

View File

@ -61,7 +61,7 @@ def get_attendance_list(from_date, to_date, student_batch, students_list):
students_with_leave_application = get_students_with_leave_application(from_date, to_date, students_list)
for d in attendance_list:
att_map.setdefault(d.student, frappe._dict()).setdefault(d.date, "")
if students_with_leave_application and d.student in students_with_leave_application.get(d.date,[]):
if students_with_leave_application and d.student in students_with_leave_application.get(d.date):
att_map[d.student][d.date] = "Present"
else:
att_map[d.student][d.date] = d.status

View File

@ -37,7 +37,7 @@ frappe.ui.form.on("Customer", {
frm.toggle_display(['address_html','contact_html'], !frm.doc.__islocal);
if(!frm.doc.__islocal) {
erpnext.utils.render_address_and_contact(frm);
frappe.geo.render_address_and_contact(frm);
// custom buttons
frm.add_custom_button(__('Accounting Ledger'), function() {
@ -53,7 +53,7 @@ frappe.ui.form.on("Customer", {
erpnext.utils.set_party_dashboard_indicators(frm);
} else {
erpnext.utils.clear_address_and_contact(frm);
frappe.geo.clear_address_and_contact(frm);
}
var grid = cur_frm.get_field("sales_team").grid;

View File

@ -9,7 +9,7 @@ import frappe.defaults
from frappe.utils import flt, cint, cstr
from frappe.desk.reportview import build_match_conditions
from erpnext.utilities.transaction_base import TransactionBase
from erpnext.utilities.address_and_contact import load_address_and_contact, delete_contact_and_address
from frappe.geo.address_and_contact import load_address_and_contact, delete_contact_and_address
from erpnext.accounts.party import validate_party_accounts, get_timeline_data # keep this
from erpnext.accounts.party_status import get_party_status
from erpnext import get_default_currency
@ -95,9 +95,14 @@ class Customer(TransactionBase):
def create_lead_address_contact(self):
if self.lead_name:
# assign lead address to customer (if already not set)
address_name = frappe.get_value('Dynamic Link', dict(parenttype='Address', link_doctype='Lead', link_name=self.name))
if address_name:
address = frappe.get_doc('Address', address_name)
address_names = frappe.get_all('Dynamic Link', filters={
"parenttype":"Address",
"link_doctype":"Lead",
"link_name":self.lead_name
}, fields=["parent as name"])
for address_name in address_names:
address = frappe.get_doc('Address', address_name.get('name'))
if not address.has_link('Customer', self.name):
address.append('links', dict(link_doctype='Customer', link_name=self.name))
address.save()
@ -105,17 +110,17 @@ class Customer(TransactionBase):
lead = frappe.db.get_value("Lead", self.lead_name, ["lead_name", "email_id", "phone", "mobile_no"], as_dict=True)
# create contact from lead
c = frappe.new_doc('Contact')
c.first_name = lead.lead_name
c.email_id = lead.email_id
c.phone = lead.phone
c.mobile_no = lead.mobile_no
c.is_primary_contact = 1
c.append('links', dict(link_doctype='Customer', link_name=self.name))
c.flags.ignore_permissions = self.flags.ignore_permissions
c.autoname()
if not frappe.db.exists("Contact", c.name):
c.insert()
contact = frappe.new_doc('Contact')
contact.first_name = lead.lead_name
contact.email_id = lead.email_id
contact.phone = lead.phone
contact.mobile_no = lead.mobile_no
contact.is_primary_contact = 1
contact.append('links', dict(link_doctype='Customer', link_name=self.name))
contact.flags.ignore_permissions = self.flags.ignore_permissions
contact.autoname()
if not frappe.db.exists("Contact", contact.name):
contact.insert()
def validate_name_with_customer_group(self):
if frappe.db.exists("Customer Group", self.name):
@ -133,7 +138,7 @@ class Customer(TransactionBase):
def on_trash(self):
delete_contact_and_address('Customer', self.name)
if self.lead_name:
frappe.db.sql("update `tabLead` set status='Interested' where name=%s",self.lead_name)
frappe.db.sql("update `tabLead` set status='Interested' where name=%s", self.lead_name)
def after_rename(self, olddn, newdn, merge=False):
if frappe.defaults.get_global_default('cust_master_name') == 'Customer Name':

View File

@ -12,9 +12,9 @@ def get_funnel_data(from_date, to_date):
where (date(`modified`) between %s and %s)
and status != "Do Not Contact" """, (from_date, to_date))[0][0]
active_leads += frappe.db.sql("""select count(distinct customer) from `tabContact`
where (date(`modified`) between %s and %s)
and status != "Passive" """, (from_date, to_date))[0][0]
active_leads += frappe.db.sql("""select count(distinct contact.name) from `tabContact` contact
left join `tabDynamic Link` dl on (dl.parent=contact.name) where dl.link_doctype='Customer'
and (date(contact.modified) between %s and %s) and status != "Passive" """, (from_date, to_date))[0][0]
opportunities = frappe.db.sql("""select count(*) from `tabOpportunity`
where (date(`creation`) between %s and %s)

View File

@ -1,19 +0,0 @@
{
"add_total_row": 0,
"apply_user_permissions": 1,
"creation": "2012-10-04 18:45:27",
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 1,
"is_standard": "Yes",
"modified": "2015-08-24 11:44:00.711112",
"modified_by": "Administrator",
"module": "Selling",
"name": "Customer Addresses And Contacts",
"owner": "Administrator",
"query": "SELECT\n\t`tabCustomer`.name as \"Customer ID:Link/Customer\",\n\t`tabCustomer`.customer_name as \"Customer Name\",\n\t`tabCustomer`.customer_group as \"Customer Group:Link/Customer Group\",\n\t`tabAddress`.address_line1 as \"Address Line 1\",\n\t`tabAddress`.address_line2 as \"Address Line 2\",\n\t`tabAddress`.city as \"City\",\n\t`tabAddress`.state as \"State\",\n\t`tabAddress`.pincode as \"Postal Code\",\n\t`tabAddress`.country as \"Country\",\n\t`tabAddress`.is_primary_address as \"Is Primary Address:Check\", \n\t`tabContact`.first_name as \"First Name\",\n\t`tabContact`.last_name as \"Last Name\",\n\t`tabContact`.phone as \"Phone\",\n\t`tabContact`.mobile_no as \"Mobile No\",\n\t`tabContact`.email_id as \"Email Address\",\n\t`tabContact`.is_primary_contact as \"Is Primary Contact:Check\"\nFROM\n\t`tabCustomer`\n\tleft join `tabAddress` on (\n\t\t`tabAddress`.customer=`tabCustomer`.name\n\t)\n\tleft join `tabContact` on (\n\t\t`tabContact`.customer=`tabCustomer`.name\n\t)\nWHERE\n\t`tabCustomer`.docstatus<2\nORDER BY\n\t`tabCustomer`.name asc",
"ref_doctype": "Customer",
"report_name": "Customer Addresses And Contacts",
"report_type": "Query Report"
}

View File

@ -1,16 +1,18 @@
{
"add_total_row": 0,
"apply_user_permissions": 1,
"creation": "2013-10-22 11:58:16",
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 1,
"is_standard": "Yes",
"modified": "2015-02-02 11:39:57.231750",
"modified": "2017-01-19 15:44:59.742195",
"modified_by": "Administrator",
"module": "Selling",
"name": "Lead Details",
"owner": "Administrator",
"query": "SELECT\n `tabLead`.name as \"Lead Id:Link/Lead:120\",\n `tabLead`.lead_name as \"Lead Name::120\",\n\t`tabLead`.company_name as \"Company Name::120\",\n\t`tabLead`.status as \"Status::120\",\n\tconcat_ws(', ', \n\t\ttrim(',' from `tabAddress`.address_line1), \n\t\ttrim(',' from tabAddress.address_line2)\n\t) as 'Address::180',\n\t`tabAddress`.state as \"State::100\",\n\t`tabAddress`.pincode as \"Pincode::70\",\n\t`tabAddress`.country as \"Country::100\",\n\t`tabLead`.phone as \"Phone::100\",\n\t`tabLead`.mobile_no as \"Mobile No::100\",\n\t`tabLead`.email_id as \"Email Address::120\",\n\t`tabLead`.lead_owner as \"Lead Owner::120\",\n\t`tabLead`.source as \"Source::120\",\n\t`tabLead`.territory as \"Territory::120\",\n `tabLead`.owner as \"Owner:Link/User:120\"\nFROM\n\t`tabLead`\n\tleft join `tabAddress` on (\n\t\t`tabAddress`.lead=`tabLead`.name\n\t)\nWHERE\n\t`tabLead`.docstatus<2\nORDER BY\n\t`tabLead`.name asc",
"query": "SELECT\n `tabLead`.name as \"Lead Id:Link/Lead:120\",\n `tabLead`.lead_name as \"Lead Name::120\",\n\t`tabLead`.company_name as \"Company Name::120\",\n\t`tabLead`.status as \"Status::120\",\n\tconcat_ws(', ', \n\t\ttrim(',' from `tabAddress`.address_line1), \n\t\ttrim(',' from tabAddress.address_line2)\n\t) as 'Address::180',\n\t`tabAddress`.state as \"State::100\",\n\t`tabAddress`.pincode as \"Pincode::70\",\n\t`tabAddress`.country as \"Country::100\",\n\t`tabLead`.phone as \"Phone::100\",\n\t`tabLead`.mobile_no as \"Mobile No::100\",\n\t`tabLead`.email_id as \"Email Id::120\",\n\t`tabLead`.lead_owner as \"Lead Owner::120\",\n\t`tabLead`.source as \"Source::120\",\n\t`tabLead`.territory as \"Territory::120\",\n `tabLead`.owner as \"Owner:Link/User:120\"\nFROM\n\t`tabLead`\n\tleft join `tabDynamic Link` on (\n\t\t`tabDynamic Link`.link_name=`tabLead`.name\n\t)\n\tleft join `tabAddress` on (\n\t\t`tabAddress`.name=`tabDynamic Link`.parent\n\t)\nWHERE\n\t`tabLead`.docstatus<2\nORDER BY\n\t`tabLead`.name asc",
"ref_doctype": "Lead",
"report_name": "Lead Details",
"report_type": "Query Report"

View File

@ -73,11 +73,23 @@ def delete_bins(company_name):
def delete_lead_addresses(company_name):
"""Delete addresses to which leads are linked"""
for lead in frappe.get_all("Lead", filters={"company": company_name}):
frappe.db.sql("""delete from `tabAddress`
where lead=%s and (customer='' or customer is null) and (supplier='' or supplier is null)""", lead.name)
leads = frappe.get_all("Lead", filters={"company": company_name})
leads = [ "'%s'"%row.get("name") for row in leads ]
addresses = []
if leads:
addresses = frappe.db.sql_list("""select parent from `tabDynamic Link` where link_name
in ({leads})""".format(leads=",".join(leads)), debug=True)
addresses = ["'%s'"%addr for addr in addresses]
frappe.db.sql("""update `tabAddress` set lead=null, lead_name=null where lead=%s""", lead.name)
frappe.db.sql("""delete from tabAddress where name in ({addresses}) and
name not in (select distinct dl1.parent from `tabDynamic Link` dl1
inner join `tabDynamic Link` dl2 on dl1.parent=dl2.parent
and dl1.link_doctype<>dl2.link_doctype)""".format(addresses=",".join(addresses)), debug=True)
frappe.db.sql("""delete from `tabDynamic Link` where link_doctype='Lead' and parenttype='Address'
and link_name in ({leads})""".format(leads=",".join(leads)), debug=True)
frappe.db.sql("""update tabCustomer set lead_name=NULL where lead_name in ({leads})""".format(leads=",".join(leads)), debug=True)
def delete_communications(doctype, company_name, company_fieldname):
frappe.db.sql("""

View File

@ -6,12 +6,12 @@ frappe.ui.form.on('Sales Partner', {
frappe.dynamic_link = {doc: frm.doc, fieldname: 'name', doctype: 'Sales Person'}
if(frm.doc.__islocal){
hide_field(['address_html', 'contact_html']);
erpnext.utils.clear_address_and_contact(frm);
hide_field(['address_html', 'contact_html', 'address_contacts']);
frappe.geo.clear_address_and_contact(frm);
}
else{
unhide_field(['address_html', 'contact_html']);
erpnext.utils.render_address_and_contact(frm);
unhide_field(['address_html', 'contact_html', 'address_contacts']);
frappe.geo.render_address_and_contact(frm);
}
}
});

View File

@ -5,7 +5,7 @@ from __future__ import unicode_literals
import frappe
from frappe.utils import cstr, filter_strip_join
from frappe.website.website_generator import WebsiteGenerator
from erpnext.utilities.address_and_contact import load_address_and_contact
from frappe.geo.address_and_contact import load_address_and_contact
class SalesPartner(WebsiteGenerator):
website = frappe._dict(
@ -28,15 +28,6 @@ class SalesPartner(WebsiteGenerator):
if self.partner_website and not self.partner_website.startswith("http"):
self.partner_website = "http://" + self.partner_website
def get_contacts(self, nm):
if nm:
return frappe.db.convert_to_lists(frappe.db.sql("""
select name, CONCAT(IFNULL(first_name,''),
' ',IFNULL(last_name,'')),contact_no,email_id
from `tabContact` where sales_partner = %s""", nm))
else:
return ''
def get_context(self, context):
address = frappe.db.get_value("Address",
{"sales_partner": self.name, "is_primary_address": 1},

View File

@ -1,20 +0,0 @@
{% extends "templates/web.html" %}
{% block header %}
<h1> {{doc.subject}} </h1>
{% endblock %}
{% block page_content %}
<p class="post-description"> {{doc.description}} </p>
<p class="post-by text-muted small">
{% for file in attached_files%}
<a href="{{file.file_url}}" target="_new">{{file.file_name}}</a>
<br>
{% endfor %}
<br>
<i>{{ doc.posted_by }}</i>
<i class="blog-dot"></i> {{ frappe.format_date(doc.modified) }}
</p>
{% endblock %}

View File

@ -1,18 +0,0 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
def get_context(context):
announcement = frappe.get_doc('Announcement', frappe.form_dict.announcement)
context.no_cache = 1
context.show_sidebar = True
announcement.has_permission('read')
context.doc = announcement
attachments = frappe.db.sql("""select file_url, file_name from tabFile as file
where file.attached_to_name=%s """,(announcement.name), as_dict = True)
context.attached_files = attachments

View File

@ -15,14 +15,6 @@ def get_context(context):
course = frappe.get_doc('Course', frappe.form_dict.course)
course.has_permission('read')
context.doc = course
portal_items = [{'reference_doctype': u'Topic', 'route': u"/topic?course=" + str(course.name), 'show_always': 0L, 'title': u'Topics'},
{'reference_doctype': u'Discussion', 'route': u"/discussion?course=" + str(course.name), 'show_always': 0L, 'title': u'Discussions'},
]
context.sidebar_items = portal_items
context.sidebar_title = sidebar_title
context.intro = course.course_intro

View File

@ -1,15 +0,0 @@
{% extends "templates/web.html" %}
{% block header %}
<h2> {{doc.subject}} </h2>
<p> {{doc.description}} </p>
<p class="text-muted small">Started by: {{doc.owner}} </p>
{% endblock %}
{% block page_content %}
<div>
{% include 'templates/includes/comments/comments.html' %}
</div>
{% endblock %}

View File

@ -1,21 +0,0 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
from frappe.website.utils import get_comment_list
def get_context(context):
context.doc = frappe.get_doc('Discussion', frappe.form_dict.discussion)
portal_items = [{'reference_doctype': u'Topic', 'route': u"/topic?course=" + str(context.doc.course), 'show_always': 0L, 'title': u'Topics'},
{'reference_doctype': u'Discussion', 'route': u"/discussion?course=" + str(context.doc.course), 'show_always': 0L, 'title': u'Discussions'},
]
context.show_sidebar = True
context.sidebar_items = portal_items
context.no_cache = 1
context.doc.has_permission('read')
context.sidebar_title = context.doc.course
context.reference_doctype = "Discussion"
context.reference_name = context.doc.name
context.comment_list = get_comment_list(context.doc.doctype,context.doc.name)

View File

@ -1,12 +0,0 @@
{% extends "templates/web.html" %}
{% block header %}
<h2> {{ doc.introduction }} </h1>
{% endblock %}
{% block page_content %}
<p class="post-description"> {{ doc.content }} </p>
{% endblock %}

View File

@ -1,15 +0,0 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
def get_context(context):
topic = frappe.get_doc('Topic', frappe.form_dict.topic)
context.no_cache = 1
context.show_sidebar = True
context.doc = topic
attachments = frappe.db.sql("""select file_url, file_name from tabFile as file
where file.attached_to_name=%s """,(topic.name), as_dict = True)
context.attached_files = attachments

View File

@ -1,134 +0,0 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
def load_address_and_contact(doc, key):
"""Loads address list and contact list in `__onload`"""
from frappe.geo.doctype.address.address import get_address_display
address_list = [frappe.get_value('Address', a.parent, '*')
for a in frappe.get_all('Dynamic Link', fields='parent',
filters=dict(parenttype='Address', link_doctype=doc.doctype, link_name=doc.name))]
address_list = [a.update({"display": get_address_display(a)})
for a in address_list]
address_list = sorted(address_list,
lambda a, b:
(int(a.is_primary_address - b.is_primary_address)) or
(1 if a.modified - b.modified else 0))
doc.set_onload('addr_list', address_list)
if doc.doctype != "Lead":
contact_list = [frappe.get_value('Contact', a.parent, '*')
for a in frappe.get_all('Dynamic Link', fields='parent',
filters=dict(parenttype='Contact', link_doctype=doc.doctype, link_name=doc.name))]
contact_list = sorted(contact_list,
lambda a, b:
(int(a.is_primary_contact - b.is_primary_contact)) or
(1 if a.modified - b.modified else 0))
doc.set_onload('contact_list', contact_list)
def set_default_role(doc, method):
'''Set customer, supplier, student based on email'''
if frappe.flags.setting_role:
return
contact_name = frappe.get_value('Contact', dict(email_id=doc.email))
if contact_name:
contact = frappe.get_doc('Contact', contact_name)
for link in contact.links:
frappe.flags.setting_role = True
if link.link_doctype=='Customer':
doc.add_roles('Customer')
elif link.link_doctype=='Supplier':
doc.add_roles('Supplier')
elif frappe.get_value('Student', dict(student_email_id=doc.email)):
doc.add_roles('Student')
def has_permission(doc, ptype, user):
links = get_permitted_and_not_permitted_links(doc.doctype)
if not links.get("not_permitted_links"):
# optimization: don't determine permissions based on link fields
return True
# True if any one is True or all are empty
names = []
for df in (links.get("permitted_links") + links.get("not_permitted_links")):
doctype = df.options
name = doc.get(df.fieldname)
names.append(name)
if name and frappe.has_permission(doctype, ptype, doc=name):
return True
if not any(names):
return True
return False
def get_permission_query_conditions_for_contact(user):
return get_permission_query_conditions("Contact")
def get_permission_query_conditions_for_address(user):
return get_permission_query_conditions("Address")
def get_permission_query_conditions(doctype):
links = get_permitted_and_not_permitted_links(doctype)
if not links.get("not_permitted_links"):
# when everything is permitted, don't add additional condition
return ""
elif not links.get("permitted_links"):
conditions = []
# when everything is not permitted
for df in links.get("not_permitted_links"):
# like ifnull(customer, '')='' and ifnull(supplier, '')=''
conditions.append("ifnull(`tab{doctype}`.`{fieldname}`, '')=''".format(doctype=doctype, fieldname=df.fieldname))
return "( " + " and ".join(conditions) + " )"
else:
conditions = []
for df in links.get("permitted_links"):
# like ifnull(customer, '')!='' or ifnull(supplier, '')!=''
conditions.append("ifnull(`tab{doctype}`.`{fieldname}`, '')!=''".format(doctype=doctype, fieldname=df.fieldname))
return "( " + " or ".join(conditions) + " )"
def get_permitted_and_not_permitted_links(doctype):
permitted_links = []
not_permitted_links = []
meta = frappe.get_meta(doctype)
for df in meta.get_link_fields():
if df.options not in ("Customer", "Supplier", "Company", "Sales Partner"):
continue
if frappe.has_permission(df.options):
permitted_links.append(df)
else:
not_permitted_links.append(df)
return {
"permitted_links": permitted_links,
"not_permitted_links": not_permitted_links
}
def delete_contact_and_address(doctype, name):
for parenttype in ('Contact', 'Address'):
items = frappe.db.sql("""select parent from `tabDynamic Link`
where parenttype=%s and link_doctype=%s and link_name=%s""",
(parenttype, doctype, name))
for name in items:
doc = frappe.get_doc(parenttype, name)
if len(doc.links)==1:
doc.delete()