Merge branch 'develop' into remove-party-status

This commit is contained in:
Rushabh Mehta 2017-02-02 15:42:04 +05:30 committed by GitHub
commit a3d0bea375
126 changed files with 4557 additions and 1621 deletions

View File

@ -2,7 +2,7 @@
from __future__ import unicode_literals
import frappe
__version__ = '7.2.17'
__version__ = '7.2.18'
def get_default_company(user=None):
'''Get default company for user'''

View File

@ -86,9 +86,9 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
};
});
me.frm.set_query("party_type", "accounts", function(doc, cdt, cdn) {
return {
filters: {"name": ["in", ["Customer", "Supplier"]]}
me.frm.set_query("party_type", "accounts", function() {
return{
query: "erpnext.setup.doctype.party_type.party_type.get_party_type"
}
});

View File

@ -9,6 +9,7 @@ from erpnext.controllers.accounts_controller import AccountsController
from erpnext.accounts.utils import get_balance_on, get_account_currency
from erpnext.setup.utils import get_company_currency
from erpnext.accounts.party import get_party_account
from erpnext.hr.doctype.expense_claim.expense_claim import update_reimbursed_amount
class JournalEntry(AccountsController):
def __init__(self, arg1, arg2=None):
@ -375,7 +376,7 @@ class JournalEntry(AccountsController):
bank_amount = party_amount = total_amount = 0.0
currency = bank_account_currency = party_account_currency = pay_to_recd_from= None
for d in self.get('accounts'):
if d.party_type and d.party:
if d.party_type in ['Customer', 'Supplier'] and d.party:
if not pay_to_recd_from:
pay_to_recd_from = frappe.db.get_value(d.party_type, d.party,
"customer_name" if d.party_type=="Customer" else "supplier_name")
@ -503,11 +504,9 @@ class JournalEntry(AccountsController):
def update_expense_claim(self):
for d in self.accounts:
if d.reference_type=="Expense Claim":
amt = frappe.db.sql("""select sum(debit) as amt from `tabJournal Entry Account`
where reference_type = "Expense Claim" and
reference_name = %s and docstatus = 1""", d.reference_name ,as_dict=1)[0].amt
frappe.db.set_value("Expense Claim", d.reference_name , "total_amount_reimbursed", amt)
if d.reference_type=="Expense Claim" and d.party:
doc = frappe.get_doc("Expense Claim", d.reference_name)
update_reimbursed_amount(doc)
def validate_expense_claim(self):
for d in self.accounts:

View File

@ -26,6 +26,12 @@ frappe.ui.form.on('Payment Entry', {
}
});
frm.set_query("party_type", function() {
return{
query: "erpnext.setup.doctype.party_type.party_type.get_party_type"
}
});
frm.set_query("paid_to", function() {
var account_types = in_list(["Receive", "Internal Transfer"], frm.doc.payment_type) ?
["Bank", "Cash"] : party_account_type;

View File

@ -123,7 +123,7 @@
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
},
{
"allow_on_submit": 0,
"bold": 1,
@ -247,7 +247,7 @@
"columns": 0,
"depends_on": "eval:in_list([\"Receive\", \"Pay\"], doc.payment_type)",
"fieldname": "party_type",
"fieldtype": "Select",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
@ -257,7 +257,7 @@
"label": "Party Type",
"length": 0,
"no_copy": 0,
"options": "Customer\nSupplier",
"options": "DocType",
"permlevel": 0,
"precision": "",
"print_hide": 1,

View File

@ -6,12 +6,10 @@ frappe.provide("erpnext.accounts");
erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.extend({
onload: function() {
var me = this
this.frm.set_query('party_type', function() {
return {
filters: {
"name": ["in", ["Customer", "Supplier"]]
}
};
this.frm.set_query("party_type", function() {
return{
query: "erpnext.setup.doctype.party_type.party_type.get_party_type"
}
});
this.frm.set_query('receivable_payable_account', function() {

View File

@ -30,6 +30,7 @@ def get_pos_data():
'doc': doc,
'default_customer': pos_profile.get('customer'),
'items': get_items_list(pos_profile),
'item_groups': get_item_group(pos_profile),
'customers': get_customers_list(pos_profile),
'serial_no_data': get_serial_no_data(pos_profile, doc.company),
'batch_no_data': get_batch_no_data(),
@ -140,6 +141,15 @@ def get_items_list(pos_profile):
disabled = 0 and has_variants = 0 and is_sales_item = 1 and {cond}
""".format(cond=cond), tuple(item_groups), as_dict=1)
def get_item_group(pos_profile):
if pos_profile.get('item_groups'):
item_groups = []
for d in pos_profile.get('item_groups'):
item_groups.extend(get_child_nodes('Item Group', d.item_group))
return item_groups
else:
return frappe.db.sql_list("""Select name from `tabItem Group` order by name""")
def get_customers_list(pos_profile):
cond = "1=1"
customer_groups = []
@ -276,11 +286,33 @@ def validate_customer(doc):
customer_doc = frappe.new_doc('Customer')
customer_doc.customer_name = doc.get('customer')
customer_doc.customer_type = 'Company'
customer_doc.customer_group = doc.get('customer_group')
customer_doc.territory = doc.get('territory')
customer_doc.customer_group = frappe.db.get_single_value('Selling Settings', 'customer_group')
customer_doc.territory = frappe.db.get_single_value('Selling Settings', 'territory')
customer_doc.flags.ignore_mandatory = True
customer_doc.save(ignore_permissions = True)
frappe.db.commit()
doc['customer'] = customer_doc.name
if doc.get('contact_details'):
args = json.loads(doc.get("contact_details"))
make_address(doc, args, customer_doc.name)
def make_address(doc, args, customer):
if args.get("address_line1"):
address = frappe.new_doc('Address')
address.address_line1 = args.get('address_line1')
address.address_line2 = args.get('address_line2')
address.city = args.get('city')
address.state = args.get('state')
address.zip_code = args.get('zip_code')
address.email_id = args.get('email_id')
address.flags.ignore_mandatory = True
address.country = frappe.db.get_value('Company', doc.get('company'), 'country')
address.append('links',{
'link_doctype': 'Customer',
'link_name': customer
})
address.save(ignore_permissions = True)
frappe.db.commit()
def validate_item(doc):
for item in doc.get('items'):

View File

@ -28,7 +28,7 @@
"label": "",
"length": 0,
"no_copy": 0,
"options": "fa fa-user",
"options": "icon-user",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
@ -232,7 +232,7 @@
"in_standard_filter": 0,
"label": "Offline POS Name",
"length": 0,
"no_copy": 0,
"no_copy": 1,
"permlevel": 0,
"precision": "",
"print_hide": 0,
@ -1035,7 +1035,7 @@
"length": 0,
"no_copy": 0,
"oldfieldtype": "Section Break",
"options": "fa fa-shopping-cart",
"options": "icon-shopping-cart",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
@ -1122,7 +1122,7 @@
"label": "Packing List",
"length": 0,
"no_copy": 0,
"options": "fa fa-suitcase",
"options": "icon-suitcase",
"permlevel": 0,
"print_hide": 1,
"print_hide_if_no_value": 0,
@ -1462,7 +1462,7 @@
"length": 0,
"no_copy": 0,
"oldfieldtype": "Section Break",
"options": "fa fa-money",
"options": "icon-money",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
@ -1941,7 +1941,7 @@
"length": 0,
"no_copy": 0,
"oldfieldtype": "Section Break",
"options": "fa fa-money",
"options": "icon-money",
"permlevel": 0,
"print_hide": 1,
"print_hide_if_no_value": 0,
@ -2238,7 +2238,7 @@
"length": 0,
"no_copy": 0,
"oldfieldtype": "Section Break",
"options": "fa fa-money",
"options": "icon-money",
"permlevel": 0,
"print_hide": 1,
"print_hide_if_no_value": 0,
@ -2327,7 +2327,7 @@
"label": "Payments",
"length": 0,
"no_copy": 0,
"options": "fa fa-money",
"options": "icon-money",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
@ -3252,7 +3252,7 @@
"length": 0,
"no_copy": 0,
"oldfieldtype": "Section Break",
"options": "fa fa-file-text",
"options": "icon-file-text",
"permlevel": 0,
"print_hide": 1,
"print_hide_if_no_value": 0,
@ -3515,7 +3515,7 @@
"length": 0,
"no_copy": 0,
"oldfieldtype": "Section Break",
"options": "fa fa-group",
"options": "icon-group",
"permlevel": 0,
"print_hide": 1,
"print_hide_if_no_value": 0,
@ -3720,7 +3720,7 @@
"label": "Recurring",
"length": 0,
"no_copy": 0,
"options": "fa fa-time",
"options": "icon-time",
"permlevel": 0,
"print_hide": 1,
"print_hide_if_no_value": 0,
@ -4173,7 +4173,7 @@
],
"hide_heading": 0,
"hide_toolbar": 0,
"icon": "fa fa-file-text",
"icon": "icon-file-text",
"idx": 181,
"image_view": 0,
"in_create": 0,
@ -4282,5 +4282,6 @@
"sort_order": "DESC",
"timeline_field": "customer",
"title_field": "title",
"track_changes": 1,
"track_seen": 0
}

File diff suppressed because it is too large Load Diff

View File

@ -171,13 +171,13 @@ def get_party_account(party_type, party, company):
account = frappe.db.get_value("Party Account",
{"parenttype": party_type, "parent": party, "company": company}, "account")
if not account:
if not account and party_type in ['Customer', 'Supplier']:
party_group_doctype = "Customer Group" if party_type=="Customer" else "Supplier Type"
group = frappe.db.get_value(party_type, party, scrub(party_group_doctype))
account = frappe.db.get_value("Party Account",
{"parenttype": party_group_doctype, "parent": group, "company": company}, "account")
if not account:
if not account and party_type in ['Customer', 'Supplier']:
default_account_name = "default_receivable_account" \
if party_type=="Customer" else "default_payable_account"
account = frappe.db.get_value("Company", company, default_account_name)
@ -249,7 +249,7 @@ def validate_party_accounts(doc):
if existing_gle_currency and party_account_currency != existing_gle_currency:
frappe.throw(_("Accounting entries have already been made in currency {0} for company {1}. Please select a receivable or payable account with currency {0}.").format(existing_gle_currency, account.company))
if doc.default_currency and party_account_currency and company_default_currency:
if doc.get("default_currency") and party_account_currency and company_default_currency:
if doc.default_currency != party_account_currency and doc.default_currency != company_default_currency:
frappe.throw(_("Billing currency must be equal to either default comapany's currency or party account currency"))
@ -337,13 +337,18 @@ 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:
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:
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)
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.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'''

View File

@ -59,8 +59,8 @@ frappe.query_reports["General Ledger"] = {
{
"fieldname":"party_type",
"label": __("Party Type"),
"fieldtype": "Select",
"options": ["", "Customer", "Supplier"],
"fieldtype": "Link",
"options": "Party Type",
"default": ""
},
{

View File

@ -0,0 +1,12 @@
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.query_reports["Unpaid Expense Claim"] = {
"filters": [
{
"fieldname":"employee",
"label": __("Employee"),
"fieldtype": "Link"
}
]
}

View File

@ -0,0 +1,18 @@
{
"add_total_row": 0,
"apply_user_permissions": 1,
"creation": "2017-01-04 16:26:18.309717",
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 0,
"is_standard": "Yes",
"modified": "2017-01-04 16:26:18.309717",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Unpaid Expense Claim",
"owner": "Administrator",
"ref_doctype": "Expense Claim",
"report_name": "Unpaid Expense Claim",
"report_type": "Script Report"
}

View File

@ -0,0 +1,34 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe import _
def execute(filters=None):
columns, data = [], []
columns = get_columns()
data = get_unclaimed_expese_claims(filters)
return columns, data
def get_columns():
return [_("Employee") + ":Link/Employee:120", _("Employee Name") + "::120",_("Expense Claim") + ":Link/Expense Claim:120",
_("Sanctioned Amount") + ":Currency:120", _("Paid Amount") + ":Currency:120", _("Outstanding Amount") + ":Currency:150"]
def get_unclaimed_expese_claims(filters):
cond = "1=1"
if filters.get("employee"):
cond = "ec.employee = %(employee)s"
return frappe.db.sql("""
select
ec.employee, ec.employee_name, ec.name, ec.total_sanctioned_amount, ec.total_amount_reimbursed,
sum(gle.credit_in_account_currency - gle.debit_in_account_currency) as outstanding_amt
from
`tabExpense Claim` ec, `tabGL Entry` gle
where
gle.against_voucher_type = "Expense Claim" and gle.against_voucher = ec.name
and gle.party is not null and ec.docstatus = 1 and ec.is_paid = 0 and {cond} group by ec.name
having
outstanding_amt > 0
""".format(cond=cond), filters, as_list=1)

View File

@ -146,6 +146,11 @@ def get_data():
"name": "Appraisal Template",
"description": _("Template for performance appraisals.")
},
{
"type": "page",
"name": "team-updates",
"label": _("Team Updates")
},
]
},
@ -166,7 +171,7 @@ def get_data():
},
]
},
{
"label": _("Fleet Management"),
"items": [
@ -180,7 +185,7 @@ def get_data():
},
]
},
{
"label": _("Setup"),
@ -265,6 +270,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

@ -49,6 +49,11 @@ def get_data():
"name": "Stock Projected Qty",
"doctype": "Item",
},
{
"type": "page",
"name": "stock-balance",
"label": _("Stock Summary")
},
{
"type": "report",
"is_query_report": True,

View File

@ -93,8 +93,10 @@ class StockController(AccountsController):
sle.voucher_type, sle.voucher_no)
sle.stock_value = flt(sle.qty_after_transaction) * flt(sle.valuation_rate)
sle.stock_value_difference = sle.stock_value
sle.doctype="Stock Ledger Entry"
frappe.get_doc(sle).db_update()
if sle.name:
frappe.db.sql(""" update `tabStock Ledger Entry` set stock_value = %(stock_value)s,
valuation_rate = %(valuation_rate)s, stock_value_difference = %(stock_value_difference)s
where name = %(name)s""", (sle))
def validate_negative_stock(self, sle):
if sle.qty_after_transaction < 0 and sle.actual_qty < 0:
@ -148,7 +150,7 @@ class StockController(AccountsController):
def get_stock_ledger_details(self):
stock_ledger = {}
for sle in frappe.db.sql("""select warehouse, stock_value_difference,
for sle in frappe.db.sql("""select name, warehouse, stock_value_difference,
voucher_detail_no, item_code, posting_date, posting_time, actual_qty, qty_after_transaction
from `tabStock Ledger Entry` where voucher_type=%s and voucher_no=%s""",
(self.doctype, self.name), as_dict=True):

View File

@ -6,6 +6,7 @@ from frappe.utils import random_string, add_days, get_last_day, getdate
from erpnext.projects.doctype.timesheet.test_timesheet import make_timesheet
from erpnext.projects.doctype.timesheet.timesheet import make_salary_slip, make_sales_invoice
from frappe.utils.make_random import get_random
from erpnext.hr.doctype.expense_claim.test_expense_claim import get_payable_account
from erpnext.hr.doctype.expense_claim.expense_claim import get_expense_approver, make_bank_entry
from erpnext.hr.doctype.leave_application.leave_application import (get_leave_balance_on,
OverlapError, AttendanceAlreadyMarkedError)
@ -50,6 +51,7 @@ def work():
expense_claim.extend('expenses', get_expenses())
expense_claim.employee = get_random("Employee")
expense_claim.company = frappe.flags.company
expense_claim.payable_account = get_payable_account(expense_claim.company)
expense_claim.posting_date = frappe.flags.current_date
expense_claim.exp_approver = filter((lambda x: x[0] != 'Administrator'), get_expense_approver(None, '', None, 0, 20, None))[0][0]
expense_claim.insert()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 220 KiB

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 226 KiB

After

Width:  |  Height:  |  Size: 99 KiB

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: 71 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 KiB

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 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: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 424 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 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

@ -9,6 +9,10 @@ To make a new Expense Claim, go to:
Set the Employee ID, date and the list of expenses that are to be claimed and
“Submit” the record.
### Set Account for Employee
Set employee's expense account on the employee form, system books an expense amount of an employee under this account.
<img class="screenshot" alt="Expense Claim" src="{{docs_base_url}}/assets/img/human-resources/employee_account.png">
### Approving Expenses
Approver for the Expense Claim is selected by an Employee himself. Users to whom `Expense Approver` role is assigned will shown in the Expense Claim Approver field.
@ -18,11 +22,25 @@ After saving Expense Claim, Employee should [Assign document to Approver]({{docs
Expense Claim Approver can update the “Sanctioned Amounts” against Claimed Amount of an Employee. If submitting, Approval Status should be submitted to Approved or Rejected. If Approved, then Expense Claim gets submitted. If rejected, then Expen
Comments can be added in the Comments section explaining why the claim was approved or rejected.
### Booking the Expense and Reimbursement
### Booking the Expense
The approved Expense Claim must then be converted into a Journal Entry and a
payment must be made. Note: This amount should not be clubbed with Salary
because the amount will then be taxable to the Employee.
On submission of Expense Claim, system books an expense against the expense account and the employee account
<img class="screenshot" alt="Expense Claim" src="{{docs_base_url}}/assets/img/human-resources/expense_claim_book.png">
User can view unpaid expense claim using report "Unclaimed Expense Claims"
<img class="screenshot" alt="Expense Claim" src="{{docs_base_url}}/assets/img/human-resources/unclaimed_expense_claims.png">
### Payment for Expense Claim
To make payment against the expense claim, user has to click on Make > Bank Entry
#### Expense Claim
<img class="screenshot" alt="Expense Claim" src="{{docs_base_url}}/assets/img/human-resources/payment.png">
#### Payment Entry
<img class="screenshot" alt="Expense Claim" src="{{docs_base_url}}/assets/img/human-resources/payment_entry.png">
Note: This amount should not be clubbed with Salary because the amount will then be taxable to the Employee.
### Linking with Task & Project

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,8 +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"
"Issue": "erpnext.support.doctype.issue.issue.has_website_permission"
}
dump_report_map = "erpnext.startup.report_data_map.data_map"

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

@ -158,6 +158,21 @@ erpnext.expense_claim = {
}
}
frappe.ui.form.on("Expense Claim", {
refresh: function(frm) {
if(frm.doc.docstatus == 1) {
frm.add_custom_button(__('Accounting Ledger'), function() {
frappe.route_options = {
voucher_no: frm.doc.name,
company: frm.doc.company,
group_by_voucher: false
};
frappe.set_route("query-report", "General Ledger");
}, __("View"));
}
}
})
frappe.ui.form.on("Expense Claim Detail", {
claim_amount: function(frm, cdt, cdn) {
var child = locals[cdt][cdn];
@ -176,6 +191,47 @@ frappe.ui.form.on("Expense Claim Detail", {
}
})
frappe.ui.form.on("Expense Claim",{
setup: function(frm) {
frm.trigger("set_query_for_cost_center")
frm.trigger("set_query_for_payable_account")
frm.add_fetch("company", "cost_center", "cost_center");
frm.add_fetch("company", "default_payable_account", "payable_account");
},
refresh: function(frm) {
frm.trigger("toggle_fields")
},
set_query_for_cost_center: function(frm) {
frm.fields_dict["cost_center"].get_query = function() {
return {
filters: {
"company": frm.doc.company
}
}
}
},
set_query_for_payable_account: function(frm) {
frm.fields_dict["payable_account"].get_query = function() {
return {
filters: {
"root_type": "Liability",
"account_type": "Payable"
}
}
}
},
is_paid: function(frm) {
frm.trigger("toggle_fields")
},
toggle_fields: function(frm) {
frm.toggle_reqd("mode_of_payment", frm.doc.is_paid)
}
});
frappe.ui.form.on("Expense Claim", "employee_name", function(frm) {
erpnext.expense_claim.set_title(frm);

View File

@ -41,6 +41,34 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "is_paid",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Is Paid",
"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,
@ -55,7 +83,7 @@
"ignore_xss_filter": 0,
"in_filter": 1,
"in_list_view": 0,
"in_standard_filter": 1,
"in_standard_filter": 0,
"label": "Approval Status",
"length": 0,
"no_copy": 1,
@ -86,7 +114,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 1,
"in_standard_filter": 0,
"label": "Approver",
"length": 0,
"no_copy": 0,
@ -175,7 +203,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Total Sanctioned Amount",
"length": 0,
@ -322,7 +350,7 @@
"ignore_xss_filter": 0,
"in_filter": 1,
"in_list_view": 0,
"in_standard_filter": 1,
"in_standard_filter": 0,
"label": "From Employee",
"length": 0,
"no_copy": 0,
@ -370,36 +398,6 @@
"unique": 0,
"width": "150px"
},
{
"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": 1,
"label": "Company",
"length": 0,
"no_copy": 0,
"oldfieldname": "company",
"oldfieldtype": "Link",
"options": "Company",
"permlevel": 0,
"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,
@ -429,90 +427,6 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "cb1",
"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,
"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_amount_reimbursed",
"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 Amount Reimbursed",
"length": 0,
"no_copy": 1,
"options": "Company:company:default_currency",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "remark",
"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": "Remark",
"length": 0,
"no_copy": 1,
"oldfieldname": "remark",
"oldfieldtype": "Small Text",
"permlevel": 0,
"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,
@ -571,6 +485,90 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "cb1",
"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,
"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_amount_reimbursed",
"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 Amount Reimbursed",
"length": 0,
"no_copy": 1,
"options": "Company:company:default_currency",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "remark",
"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": "Remark",
"length": 0,
"no_copy": 1,
"oldfieldname": "remark",
"oldfieldtype": "Small Text",
"permlevel": 0,
"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,
@ -613,7 +611,7 @@
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Employees Email Address",
"label": "Employees Email Id",
"length": 0,
"no_copy": 0,
"oldfieldname": "email_id",
@ -629,6 +627,238 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "accounting_details",
"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": "Accounting 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": "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,
"oldfieldname": "company",
"oldfieldtype": "Link",
"options": "Company",
"permlevel": 0,
"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,
"depends_on": "is_paid",
"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": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_24",
"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": "payable_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": "Payable 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": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "cost_center",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Employees Email Address",
"length": 0,
"no_copy": 0,
"options": "Cost Center",
"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": 1,
"columns": 0,
"fieldname": "more_details",
"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": "More 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,
"default": "Draft",
"fieldname": "status",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 1,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Status",
"length": 0,
"no_copy": 1,
"options": "Draft\nPaid\nUnpaid\nSubmitted\nCancelled",
"permlevel": 0,
"precision": "",
"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
},
{
"allow_on_submit": 0,
"bold": 0,
@ -663,7 +893,7 @@
],
"hide_heading": 0,
"hide_toolbar": 0,
"icon": "fa fa-money",
"icon": "icon-money",
"idx": 1,
"image_view": 0,
"in_create": 0,
@ -673,10 +903,11 @@
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
"modified": "2016-11-07 05:52:48.548201",
"modified": "2017-01-19 18:15:35.968292",
"modified_by": "Administrator",
"module": "HR",
"name": "Expense Claim",
"name_case": "Title Case",
"owner": "harshada@webnotestech.com",
"permissions": [
{
@ -689,7 +920,6 @@
"export": 1,
"if_owner": 0,
"import": 0,
"is_custom": 0,
"permlevel": 0,
"print": 1,
"read": 1,
@ -710,7 +940,6 @@
"export": 0,
"if_owner": 0,
"import": 0,
"is_custom": 0,
"permlevel": 0,
"print": 1,
"read": 1,
@ -732,7 +961,6 @@
"export": 0,
"if_owner": 0,
"import": 0,
"is_custom": 0,
"permlevel": 0,
"print": 1,
"read": 1,
@ -754,7 +982,6 @@
"export": 0,
"if_owner": 0,
"import": 0,
"is_custom": 0,
"permlevel": 0,
"print": 1,
"read": 1,
@ -775,5 +1002,6 @@
"sort_order": "DESC",
"timeline_field": "employee",
"title_field": "title",
"track_changes": 0,
"track_seen": 0
}

View File

@ -4,13 +4,17 @@
from __future__ import unicode_literals
import frappe
from frappe import _
from frappe.utils import get_fullname, flt
from frappe.utils import get_fullname, flt, cstr
from frappe.model.document import Document
from erpnext.hr.utils import set_employee_name
from erpnext.accounts.party import get_party_account
from erpnext.accounts.general_ledger import make_gl_entries
from erpnext.accounts.doctype.sales_invoice.sales_invoice import get_bank_cash_account
from erpnext.controllers.accounts_controller import AccountsController
class InvalidExpenseApproverError(frappe.ValidationError): pass
class ExpenseClaim(Document):
class ExpenseClaim(AccountsController):
def get_feed(self):
return _("{0}: From {0} for {1}").format(self.approval_status,
self.employee_name, self.total_claimed_amount)
@ -21,16 +25,53 @@ class ExpenseClaim(Document):
self.calculate_total_amount()
set_employee_name(self)
self.set_expense_account()
self.set_payable_account()
self.set_cost_center()
self.set_status()
if self.task and not self.project:
self.project = frappe.db.get_value("Task", self.task, "project")
def set_status(self):
self.status = {
"0": "Draft",
"1": "Submitted",
"2": "Cancelled"
}[cstr(self.docstatus or 0)]
if self.total_sanctioned_amount == self.total_amount_reimbursed and self.docstatus == 1:
self.status = "Paid"
elif self.docstatus == 1:
self.status = "Unpaid"
def set_payable_account(self):
if not self.payable_account and not self.is_paid:
self.payable_account = frappe.db.get_value("Company", self.company, "default_payable_account")
def set_cost_center(self):
if not self.cost_center:
self.cost_center = frappe.db.get_value('Company', self.company, 'cost_center')
def on_submit(self):
if self.approval_status=="Draft":
frappe.throw(_("""Approval Status must be 'Approved' or 'Rejected'"""))
self.update_task_and_project()
self.make_gl_entries()
if self.is_paid:
update_reimbursed_amount(self)
self.set_status()
def on_cancel(self):
self.update_task_and_project()
if self.payable_account:
self.make_gl_entries(cancel=True)
if self.is_paid:
update_reimbursed_amount(self)
self.set_status()
def update_task_and_project(self):
if self.task:
@ -38,6 +79,79 @@ class ExpenseClaim(Document):
elif self.project:
frappe.get_doc("Project", self.project).update_project()
def make_gl_entries(self, cancel = False):
if flt(self.total_sanctioned_amount) > 0:
gl_entries = self.get_gl_entries()
make_gl_entries(gl_entries, cancel)
def get_gl_entries(self):
gl_entry = []
self.validate_account_details()
# payable entry
gl_entry.append(
self.get_gl_dict({
"account": self.payable_account,
"credit": self.total_sanctioned_amount,
"credit_in_account_currency": self.total_sanctioned_amount,
"against": ",".join([d.default_account for d in self.expenses]),
"party_type": "Employee",
"party": self.employee,
"against_voucher_type": self.doctype,
"against_voucher": self.name
})
)
# expense entries
for data in self.expenses:
gl_entry.append(
self.get_gl_dict({
"account": data.default_account,
"debit": data.sanctioned_amount,
"debit_in_account_currency": data.sanctioned_amount,
"against": self.employee,
"cost_center": self.cost_center
})
)
if self.is_paid:
# payment entry
payment_account = get_bank_cash_account(self.mode_of_payment, self.company).get("account")
gl_entry.append(
self.get_gl_dict({
"account": payment_account,
"credit": self.total_sanctioned_amount,
"credit_in_account_currency": self.total_sanctioned_amount,
"against": self.employee
})
)
gl_entry.append(
self.get_gl_dict({
"account": self.payable_account,
"party_type": "Employee",
"party": self.employee,
"against": payment_account,
"debit": self.total_sanctioned_amount,
"debit_in_account_currency": self.total_sanctioned_amount,
"against_voucher": self.name,
"against_voucher_type": self.doctype,
})
)
return gl_entry
def validate_account_details(self):
if not self.cost_center:
frappe.throw(_("Cost center is required to book an expense claim"))
if not self.payable_account:
frappe.throw(_("Please set default payable account in the employee {0}").format(self.employee))
if self.is_paid:
if not self.mode_of_payment:
frappe.throw(_("Mode of payment is required to make a payment").format(self.employee))
def calculate_total_amount(self):
self.total_claimed_amount = 0
self.total_sanctioned_amount = 0
@ -64,7 +178,18 @@ class ExpenseClaim(Document):
for expense in self.expenses:
if not expense.default_account:
expense.default_account = get_expense_claim_account(expense.expense_type, self.company)["account"]
def update_reimbursed_amount(doc):
amt = frappe.db.sql("""select ifnull(sum(debit_in_account_currency), 0) as amt
from `tabGL Entry` where against_voucher_type = 'Expense Claim' and against_voucher = %s
and party = %s """, (doc.name, doc.employee) ,as_dict=1)[0].amt
doc.total_amount_reimbursed = amt
frappe.db.set_value("Expense Claim", doc.name , "total_amount_reimbursed", amt)
doc.set_status()
frappe.db.set_value("Expense Claim", doc.name , "status", doc.status)
@frappe.whitelist()
def get_expense_approver(doctype, txt, searchfield, start, page_len, filters):
return frappe.db.sql("""
@ -80,23 +205,26 @@ def make_bank_entry(docname):
expense_claim = frappe.get_doc("Expense Claim", docname)
default_bank_cash_account = get_default_bank_cash_account(expense_claim.company, "Bank")
if not default_bank_cash_account:
default_bank_cash_account = get_default_bank_cash_account(expense_claim.company, "Cash")
je = frappe.new_doc("Journal Entry")
je.voucher_type = 'Bank Entry'
je.company = expense_claim.company
je.remark = 'Payment against Expense Claim: ' + docname;
for expense in expense_claim.expenses:
je.append("accounts", {
"account": expense.default_account,
"debit_in_account_currency": expense.sanctioned_amount,
"reference_type": "Expense Claim",
"reference_name": expense_claim.name
})
je.append("accounts", {
"account": expense_claim.payable_account,
"debit_in_account_currency": flt(expense_claim.total_sanctioned_amount - expense_claim.total_amount_reimbursed),
"reference_type": "Expense Claim",
"party_type": "Employee",
"party": expense_claim.employee,
"reference_name": expense_claim.name
})
je.append("accounts", {
"account": default_bank_cash_account.account,
"credit_in_account_currency": expense_claim.total_sanctioned_amount,
"credit_in_account_currency": flt(expense_claim.total_sanctioned_amount - expense_claim.total_amount_reimbursed),
"reference_type": "Expense Claim",
"reference_name": expense_claim.name,
"balance": default_bank_cash_account.balance,

View File

@ -2,7 +2,10 @@ frappe.listview_settings['Expense Claim'] = {
add_fields: ["approval_status", "total_claimed_amount", "docstatus"],
filters:[["approval_status","!=", "Rejected"]],
get_indicator: function(doc) {
return [__(doc.approval_status), frappe.utils.guess_colour(doc.approval_status),
"approval_status,=," + doc.approval_status];
if(doc.status == "Paid") {
return [__("Paid"), "green", "status,=,'Paid'"];
} else {
return [__("Unpaid"), "orange"];
}
}
};

View File

@ -4,6 +4,8 @@ from __future__ import unicode_literals
import frappe
import unittest
from frappe.utils import random_string, nowdate
from erpnext.hr.doctype.expense_claim.expense_claim import make_bank_entry
test_records = frappe.get_test_records('Expense Claim')
@ -11,8 +13,6 @@ class TestExpenseClaim(unittest.TestCase):
def test_total_expense_claim_for_project(self):
frappe.db.sql("""delete from `tabTask` where project = "_Test Project 1" """)
frappe.db.sql("""delete from `tabProject` where name = "_Test Project 1" """)
frappe.db.sql("""delete from `tabExpense Claim`""")
frappe.db.sql("""delete from `tabExpense Claim Detail`""")
frappe.get_doc({
"project_name": "_Test Project 1",
@ -22,15 +22,17 @@ class TestExpenseClaim(unittest.TestCase):
}).save()
task_name = frappe.db.get_value("Task", {"project": "_Test Project 1"})
payable_account = get_payable_account("Wind Power LLC")
expense_claim = frappe.get_doc({
"doctype": "Expense Claim",
"employee": "_T-Employee-0001",
"payable_account": payable_account,
"approval_status": "Approved",
"project": "_Test Project 1",
"task": task_name,
"expenses":
[{ "expense_type": "Food", "default_account": "Entertainment Expenses - _TC", "claim_amount": 300, "sanctioned_amount": 200 }]
[{ "expense_type": "Travel", "default_account": "Travel Expenses - WP", "claim_amount": 300, "sanctioned_amount": 200 }]
})
expense_claim.submit()
@ -44,7 +46,7 @@ class TestExpenseClaim(unittest.TestCase):
"project": "_Test Project 1",
"task": task_name,
"expenses":
[{ "expense_type": "Food", "default_account": "Entertainment Expenses - _TC", "claim_amount": 600, "sanctioned_amount": 500 }]
[{ "expense_type": "Travel", "default_account": "Travel Expenses - WP", "claim_amount": 600, "sanctioned_amount": 500 }]
})
expense_claim2.submit()
@ -52,6 +54,64 @@ class TestExpenseClaim(unittest.TestCase):
self.assertEqual(frappe.db.get_value("Project", "_Test Project 1", "total_expense_claim"), 700)
expense_claim2.cancel()
frappe.delete_doc("Expenses Claim", expense_claim2.name)
self.assertEqual(frappe.db.get_value("Task", task_name, "total_expense_claim"), 200)
self.assertEqual(frappe.db.get_value("Project", "_Test Project 1", "total_expense_claim"), 200)
def test_expense_claim_status(self):
payable_account = get_payable_account("Wind Power LLC")
expense_claim = frappe.get_doc({
"doctype": "Expense Claim",
"employee": "_T-Employee-0001",
"payable_account": payable_account,
"approval_status": "Approved",
"expenses":
[{ "expense_type": "Travel", "default_account": "Travel Expenses - WP", "claim_amount": 300, "sanctioned_amount": 200 }]
})
expense_claim.submit()
je_dict = make_bank_entry(expense_claim.name)
je = frappe.get_doc(je_dict)
je.posting_date = nowdate()
je.cheque_no = random_string(5)
je.cheque_date = nowdate()
je.submit()
expense_claim = frappe.get_doc("Expense Claim", expense_claim.name)
self.assertEqual(expense_claim.status, "Paid")
je.cancel()
expense_claim = frappe.get_doc("Expense Claim", expense_claim.name)
self.assertEqual(expense_claim.status, "Unpaid")
def test_expense_claim_gl_entry(self):
payable_account = get_payable_account("Wind Power LLC")
expense_claim = frappe.get_doc({
"doctype": "Expense Claim",
"employee": "_T-Employee-0001",
"payable_account": payable_account,
"approval_status": "Approved",
"expenses":
[{ "expense_type": "Travel", "default_account": "Travel Expenses - WP", "claim_amount": 300, "sanctioned_amount": 200 }]
})
expense_claim.submit()
gl_entries = frappe.db.sql("""select account, debit, credit
from `tabGL Entry` where voucher_type='Expense Claim' and voucher_no=%s
order by account asc""", expense_claim.name, as_dict=1)
self.assertTrue(gl_entries)
expected_values = dict((d[0], d) for d in [
[payable_account, 0.0, 200.0],
["Travel Expenses - WP", 200.0, 0.0]
])
for gle in gl_entries:
self.assertEquals(expected_values[gle.account][0], gle.account)
self.assertEquals(expected_values[gle.account][1], gle.debit)
self.assertEquals(expected_values[gle.account][2], gle.credit)
def get_payable_account(company):
return frappe.db.get_value('Company', company, 'default_payable_account')

View File

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

@ -89,8 +89,8 @@ class SalarySlip(TransactionBase):
frappe.throw(_("Name error: {0}".format(err)))
except SyntaxError as err:
frappe.throw(_("Syntax error in formula or condition: {0}".format(err)))
except:
frappe.throw(_("Error in formula or condition"))
except Exception, e:
frappe.throw(_("Error in formula or condition: {0}".format(e)))
raise
def get_data_for_eval(self):
@ -99,7 +99,7 @@ class SalarySlip(TransactionBase):
for d in self._salary_structure_doc.employees:
if d.employee == self.employee:
data.base, data.variable = d.base, d.variable
data.update(frappe.get_doc("Salary Structure Employee", {"employee": self.employee}).as_dict())
data.update(frappe.get_doc("Employee", self.employee).as_dict())
data.update(self.as_dict())
@ -108,7 +108,6 @@ class SalarySlip(TransactionBase):
salary_components = frappe.get_all("Salary Component", fields=["salary_component_abbr"])
for salary_component in salary_components:
data[salary_component.salary_component_abbr] = 0
return data
@ -329,11 +328,21 @@ class SalarySlip(TransactionBase):
self.sum_components('earnings', 'gross_pay')
self.sum_components('deductions', 'total_deduction')
self.set_loan_repayment()
self.net_pay = flt(self.gross_pay) - flt(self.total_deduction)
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

@ -21,7 +21,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
@ -48,7 +47,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 1,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Company",
@ -76,7 +74,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Letter Head",
@ -107,7 +104,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Payroll Frequency",
@ -136,7 +132,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
@ -164,7 +159,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 1,
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Is Active",
@ -195,7 +189,6 @@
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Is Default",
@ -224,7 +217,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "From Date",
@ -253,7 +245,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "To Date",
@ -282,7 +273,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
@ -310,7 +300,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Employees",
@ -339,7 +328,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "",
@ -368,7 +356,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Salary Slip Based on Timesheet",
@ -396,7 +383,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
@ -425,7 +411,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Salary Component",
@ -455,7 +440,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Hour Rate",
@ -485,7 +469,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "",
@ -515,7 +498,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Earning",
@ -546,7 +528,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Earnings",
@ -576,7 +557,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Deduction",
@ -606,7 +586,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Deductions",
@ -637,7 +616,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
@ -664,7 +642,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
@ -691,7 +668,6 @@
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Total Earning",
@ -721,7 +697,6 @@
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Total Deduction",
@ -751,7 +726,6 @@
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Net Pay",
@ -779,7 +753,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Account",
@ -807,7 +780,6 @@
"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",
@ -836,7 +808,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
@ -863,7 +834,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Payment Account",
@ -894,7 +864,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-01-11 02:02:10.848614",
"modified": "2017-02-02 01:23:49.179497",
"modified_by": "Administrator",
"module": "HR",
"name": "Salary Structure",
@ -910,7 +880,6 @@
"export": 0,
"if_owner": 0,
"import": 0,
"is_custom": 0,
"permlevel": 0,
"print": 1,
"read": 1,
@ -928,10 +897,9 @@
"create": 1,
"delete": 1,
"email": 1,
"export": 0,
"export": 1,
"if_owner": 0,
"import": 0,
"is_custom": 0,
"import": 1,
"permlevel": 0,
"print": 1,
"read": 1,
@ -950,5 +918,6 @@
"sort_order": "DESC",
"timeline_field": "",
"title_field": "",
"track_changes": 0,
"track_seen": 0
}
}

View File

@ -4,8 +4,7 @@
from __future__ import unicode_literals
import frappe
from frappe.utils import cstr, flt, getdate, cint
from frappe.model.naming import make_autoname
from frappe.utils import flt, cint
from frappe import _
from frappe.model.mapper import get_mapped_doc
from frappe.model.document import Document
@ -15,7 +14,6 @@ class SalaryStructure(Document):
def validate(self):
self.validate_amount()
self.validate_joining_date()
for e in self.get('employees'):
set_employee_name(e)
@ -29,12 +27,6 @@ class SalaryStructure(Document):
def validate_amount(self):
if flt(self.net_pay) < 0 and self.salary_slip_based_on_timesheet:
frappe.throw(_("Net pay cannot be negative"))
def validate_joining_date(self):
for e in self.get('employees'):
joining_date = getdate(frappe.db.get_value("Employee", e.employee, "date_of_joining"))
if getdate(self.from_date) < joining_date:
frappe.throw(_("From Date in Salary Structure cannot be lesser than Employee Joining Date."))
@frappe.whitelist()
@ -42,6 +34,7 @@ def make_salary_slip(source_name, target_doc = None, employee = None, as_print =
def postprocess(source, target):
if employee:
target.employee = employee
target.employee_name = frappe.get_value("Employee",employee, "employee_name")
target.run_method('process_salary_structure')
doc = get_mapped_doc("Salary Structure", source_name, {

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"):
@ -33,37 +33,6 @@ class TestSalaryStructure(unittest.TestCase):
}).insert()
holiday_list.get_weekly_off_dates()
holiday_list.save()
def make_employee(self, user):
if not frappe.db.get_value("User", user):
frappe.get_doc({
"doctype": "User",
"email": user,
"first_name": user,
"new_password": "password",
"user_roles": [{"doctype": "UserRole", "role": "Employee"}]
}).insert()
if not frappe.db.get_value("Employee", {"user_id": user}):
emp = frappe.get_doc({
"doctype": "Employee",
"naming_series": "EMP-",
"employee_name": user,
"company": erpnext.get_default_company(),
"user_id": user,
"date_of_birth": "1990-05-08",
"date_of_joining": "2013-01-01",
"relieving_date": "",
"department": frappe.get_all("Department", fields="name")[0].name,
"gender": "Female",
"company_email": user,
"status": "Active",
"employment_type": "Intern"
}).insert()
return emp.name
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"})
@ -76,6 +45,36 @@ class TestSalaryStructure(unittest.TestCase):
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",
"email": user,
"first_name": user,
"new_password": "password",
"user_roles": [{"doctype": "UserRole", "role": "Employee"}]
}).insert()
if not frappe.db.get_value("Employee", {"user_id": user}):
emp = frappe.get_doc({
"doctype": "Employee",
"naming_series": "EMP-",
"employee_name": user,
"company": erpnext.get_default_company(),
"user_id": user,
"date_of_birth": "1990-05-08",
"date_of_joining": "2013-01-01",
"relieving_date": "",
"department": frappe.get_all("Department", fields="name")[0].name,
"gender": "Female",
"company_email": user,
"status": "Active",
"employment_type": "Intern"
}).insert()
return emp.name
else:
return frappe.get_value("Employee", {"employee_name":user}, "name")
def make_salary_slip_from_salary_structure(employee):
sal_struct = make_salary_structure('Salary Structure Sample')

View File

@ -15,13 +15,14 @@
"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": 1,
"in_standard_filter": 0,
"label": "Employee",
"length": 0,
"no_copy": 0,
@ -31,6 +32,7 @@
"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,
@ -41,21 +43,24 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "employee_name",
"fieldtype": "Data",
"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": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
@ -66,13 +71,14 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "base",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Base",
"length": 0,
"no_copy": 0,
@ -81,6 +87,7 @@
"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,
@ -91,13 +98,14 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "variable",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Variable",
"length": 0,
"no_copy": 0,
@ -106,6 +114,7 @@
"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,
@ -123,7 +132,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2016-08-11 12:18:14.526977",
"modified": "2017-02-02 02:06:33.809285",
"modified_by": "Administrator",
"module": "HR",
"name": "Salary Structure Employee",
@ -135,5 +144,6 @@
"read_only_onload": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 0,
"track_seen": 0
}

View File

View File

@ -0,0 +1,15 @@
<div class="row activity-row" data-creation="{%= creation.split(" ")[0] + " 00:00:00" %}">
<div class="col-xs-3 text-right activity-date"><span class="{%= date_class %}">
{%= date_sep || "" %}</span></div>
<div class="col-xs-9 activity-message"
title="{%= by %} / {%= dateutil.str_to_user(creation) %}">
<div class="row">
<div class="col-xs-2 col-sm-1">
{{ avatar }}
</div>
<div class="col-xs-10 col-sm-11 small content">
{{ content }}
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,57 @@
.date-indicator {
background:none;
font-size:12px;
vertical-align:middle;
font-weight:bold;
color:#6c7680;
}
.date-indicator::after {
margin:0 -4px 0 12px;
content:'';
display:inline-block;
height:8px;
width:8px;
border-radius:8px;
background: #d1d8dd;
}
.date-indicator.blue {
color: #5e64ff;
}
.date-indicator.blue::after {
background: #5e64ff;
}
.activity-row {
}
.activity-message {
border-left: 1px solid #d1d8dd;
}
.activity-message .row {
padding: 15px;
margin-right: 0px;
border-bottom: 1px solid #d1d8dd;
}
.activity-row:last-child .activity-message .row {
border-bottom: none;
}
.activity-row .content {
padding-left: 0px;
padding-right: 30px;
}
.activity-date {
padding: 15px;
padding-right: 0px;
z-index: 1;
}
.for-more {
border-top: 1px solid #d1d8dd;
padding: 10px;
}

View File

@ -0,0 +1,79 @@
frappe.pages['team-updates'].on_page_load = function(wrapper) {
var page = frappe.ui.make_app_page({
parent: wrapper,
title: __('Team Updates'),
single_column: true
});
frappe.team_updates.make(page);
frappe.team_updates.run();
if(frappe.model.can_read('Daily Work Summary Settings')) {
page.add_menu_item(__('Daily Work Summary Settings'), function() {
frappe.set_route('Form', 'Daily Work Summary Settings');
});
}
}
frappe.team_updates = {
start: 0,
make: function(page) {
var me = frappe.team_updates;
me.page = page;
me.body = $('<div></div>').appendTo(me.page.main);
me.more = $('<div class="for-more"><button class="btn btn-sm btn-default btn-more">'
+ __("More") + '</button></div>').appendTo(me.page.main)
.find('.btn-more').on('click', function() {
me.start += 40;
me.run();
});
},
run: function() {
var me = frappe.team_updates;
frappe.call({
method: 'erpnext.hr.page.team_updates.team_updates.get_data',
args: {
start: me.start
},
callback: function(r) {
if(r.message) {
r.message.forEach(function(d) {
me.add_row(d);
});
} else {
frappe.show_alert({message:__('No more updates'), indicator:'darkgrey'});
me.more.parent().addClass('hidden');
}
}
});
},
add_row: function(data) {
var me = frappe.team_updates;
data.by = frappe.user.full_name(data.sender);
data.avatar = frappe.avatar(data.sender);
data.when = comment_when(data.creation);
var date = dateutil.str_to_obj(data.creation);
var last = me.last_feed_date;
if((last && dateutil.obj_to_str(last) != dateutil.obj_to_str(date)) || (!last)) {
var diff = dateutil.get_day_diff(dateutil.get_today(), dateutil.obj_to_str(date));
if(diff < 1) {
pdate = 'Today';
} else if(diff < 2) {
pdate = 'Yesterday';
} else {
pdate = dateutil.global_date_format(date);
}
data.date_sep = pdate;
data.date_class = pdate=='Today' ? "date-indicator blue" : "date-indicator";
} else {
data.date_sep = null;
data.date_class = "";
}
me.last_feed_date = date;
$(frappe.render_template('team_update_row', data)).appendTo(me.body)
}
}

View File

@ -0,0 +1,25 @@
{
"content": null,
"creation": "2017-01-31 11:02:31.614045",
"docstatus": 0,
"doctype": "Page",
"idx": 0,
"modified": "2017-01-31 11:25:01.983200",
"modified_by": "Administrator",
"module": "HR",
"name": "team-updates",
"owner": "Administrator",
"page_name": "team-updates",
"roles": [
{
"role": "Employee"
},
{
"role": "System Manager"
}
],
"script": null,
"standard": "Yes",
"style": null,
"title": "Team Updates"
}

View File

@ -0,0 +1,21 @@
from __future__ import unicode_literals
import frappe
from email_reply_parser import EmailReplyParser
from markdown2 import markdown
@frappe.whitelist()
def get_data(start=0):
#frappe.only_for('Employee', 'System Manager')
data = frappe.get_all('Communication',
fields=('content', 'text_content', 'sender', 'creation'),
filters=dict(reference_doctype='Daily Work Summary'),
order_by='creation desc', limit=40, start=start)
for d in data:
d.sender_name = frappe.db.get_value("Employee", {"user_id": d.sender},
"employee_name") or d.sender
if d.text_content:
d.content = markdown(EmailReplyParser.parse_reply(d.text_content))
return data

View File

@ -21,7 +21,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 1,
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Item",
@ -51,7 +50,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Item Name",
@ -81,7 +79,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Quantity",
@ -110,7 +107,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
@ -137,7 +133,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Is Active",
@ -167,7 +162,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Is Default",
@ -197,7 +191,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "With Operations",
@ -224,7 +217,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Rate Of Materials Based On",
@ -253,7 +245,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Price List",
@ -281,7 +272,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "",
@ -309,7 +299,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Currency",
@ -338,7 +327,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
@ -365,7 +353,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Conversion Rate",
@ -395,7 +382,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Operations",
@ -423,7 +409,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Operations",
@ -453,7 +438,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Materials",
@ -481,7 +465,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Items",
@ -511,7 +494,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Scrap",
@ -539,7 +521,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Scrap Items",
@ -568,7 +549,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Costing",
@ -596,7 +576,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Operating Cost",
@ -624,7 +603,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Raw Material Cost",
@ -652,7 +630,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Scrap Material Cost",
@ -681,7 +658,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
@ -707,7 +683,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Operating Cost (Company Currency)",
@ -736,7 +711,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Raw Material Cost(Company Currency)",
@ -765,7 +739,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Scrap Material Cost(Company Currency)",
@ -794,7 +767,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
@ -821,7 +793,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Total Cost",
@ -849,7 +820,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
@ -876,7 +846,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Total Cost(Company Currency)",
@ -905,7 +874,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "",
@ -932,7 +900,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 1,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Project",
@ -962,7 +929,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Company",
@ -991,7 +957,6 @@
"hidden": 0,
"ignore_user_permissions": 1,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Amended From",
@ -1019,7 +984,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
@ -1045,7 +1009,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Item UOM",
@ -1073,7 +1036,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
@ -1100,7 +1062,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Item Description",
@ -1127,7 +1088,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
@ -1154,7 +1114,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Image",
@ -1182,7 +1141,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Image View",
@ -1212,7 +1170,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Materials Required (Exploded)",
@ -1239,7 +1196,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Exploded_items",
@ -1271,8 +1227,8 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2016-11-21 17:06:49.349654",
"modified_by": "rohit@erpnext.com",
"modified": "2017-02-01 14:27:17.949390",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "BOM",
"owner": "Administrator",
@ -1287,7 +1243,6 @@
"export": 1,
"if_owner": 0,
"import": 0,
"is_custom": 0,
"permlevel": 0,
"print": 1,
"read": 1,
@ -1308,7 +1263,6 @@
"export": 0,
"if_owner": 0,
"import": 0,
"is_custom": 0,
"permlevel": 0,
"print": 1,
"read": 1,
@ -1329,7 +1283,6 @@
"export": 0,
"if_owner": 0,
"import": 0,
"is_custom": 0,
"permlevel": 0,
"print": 1,
"read": 1,
@ -1347,5 +1300,6 @@
"search_fields": "item",
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 0,
"track_seen": 0
}

View File

@ -500,10 +500,15 @@ def validate_bom_no(item, bom_no):
@frappe.whitelist()
def get_children():
if frappe.form_dict.parent:
return frappe.db.sql("""select item_code,
bom_no as value, qty,
if(ifnull(bom_no, "")!="", 1, 0) as expandable
from `tabBOM Item`
where parent=%s
order by idx
return frappe.db.sql("""select
bom_item.item_code,
bom_item.bom_no as value,
bom_item.qty,
if(ifnull(bom_item.bom_no, "")!="", 1, 0) as expandable,
item.image,
item.description
from `tabBOM Item` bom_item, tabItem item
where bom_item.parent=%s
and bom_item.item_code = item.name
order by bom_item.idx
""", frappe.form_dict.parent, as_dict=True)

View File

@ -0,0 +1,21 @@
<div style="padding: 15px;">
{% if data.image %}
<img class="responsive" src={{ data.image }}>
<hr style="margin: 15px -15px;">
{% endif %}
<h4>
{{ __("Description") }}
</h4>
<div style="padding-top: 10px;">
{{ data.description }}
</div>
<hr style="margin: 15px -15px;">
<p>
{% if data.value %}
<a style="margin-right: 7px; margin-bottom: 7px" class="btn btn-default btn-xs" href="#Form/BOM/{{ data.value }}">
{{ __("Open BOM {0}", [data.value.bold()]) }}</a>
{% endif %}
<a class="btn btn-default btn-xs" href="#Form/Item/{{ data.item_code }}">
{{ __("Open Item {0}", [data.item_code.bold()]) }}</a>
</p>
</div>

View File

@ -20,14 +20,14 @@ frappe.treeview_settings["BOM"] = {
}
},
toolbar: [
{toggle_btn: true},
{ toggle_btn: true },
{
label:__("Edit"),
condition: function(node) {
return node.expandable;
},
click: function(node) {
frappe.set_route("Form", "BOM", node.data.value);
}
}
@ -40,5 +40,6 @@ frappe.treeview_settings["BOM"] = {
},
condition: 'frappe.boot.user.can_create.indexOf("BOM") !== -1'
}
]
],
view_template: 'bom_item_preview'
}

View File

@ -7,7 +7,10 @@ frappe.views.calendar["Production Order"] = {
"end": "planned_end_date",
"id": "name",
"title": "name",
"allDay": "allDay"
"allDay": "allDay",
"progress": function(data) {
return flt(data.produced_qty) / data.qty * 100;
}
},
gantt: true,
get_css_class: function(data) {

View File

@ -369,3 +369,4 @@ erpnext.patches.v7_2.mark_students_active
erpnext.patches.v7_2.set_null_value_to_fields
erpnext.patches.v7_2.update_guardian_name_in_student_master
erpnext.patches.v7_2.update_abbr_in_salary_slips
erpnext.patches.v7_2.update_party_type

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

@ -0,0 +1 @@
from __future__ import unicode_literals

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

@ -0,0 +1,16 @@
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
def execute():
frappe.reload_doc('setup', 'doctype', 'party_type')
make_party_type()
def make_party_type():
for party_type in ["Customer", "Supplier", "Employee"]:
if not frappe.db.get_value("Party Type", party_type):
doc = frappe.new_doc("Party Type")
doc.party_type = party_type
doc.save(ignore_permissions=True)

View File

@ -7,7 +7,8 @@ frappe.views.calendar["Task"] = {
"end": "exp_end_date",
"id": "name",
"title": "subject",
"allDay": "allDay"
"allDay": "allDay",
"progress": "progress"
},
gantt: true,
filters: [

View File

@ -67,7 +67,6 @@
.pos-toolbar,
.pos-bill-toolbar {
padding: 10px 0px;
border-bottom: 1px solid #d1d8dd;
height: 51px;
}
.pos-item-toolbar .form-group {
@ -79,6 +78,7 @@
margin-right: -1px;
}
.pos-bill {
border-top: 1px solid #d1d8dd;
margin-left: -15px;
margin-right: -15px;
}
@ -171,6 +171,9 @@
.payment-toolbar {
padding-right: 30px;
}
.list-row-head.pos-invoice-list {
border-top: 1px solid #d1d8dd;
}
body[data-route="pos"] .modal-dialog {
width: 750px;
}

View File

@ -1,14 +1,26 @@
<div class="pos">
<div class="row">
<div class="col-sm-5 pos-bill-wrapper">
<div class="pos-bill-toolbar row">
<div class="party-area col-xs-12"></div>
<div class="col-sm-6 pos-bill-wrapper">
<div class="pos-bill-toolbar" style="display: flex;">
<div class="party-area" style="flex: 1;"></div>
<button class="btn btn-default list-customers-btn" style="margin: 0 5px 0 15px;">
<i class="fa fa-list"></i>
</button>
<button class="btn btn-default add-customer-btn">
<i class="fa fa-plus"></i>
</button>
{% if (allow_delete) { %}
<button class="btn btn-default btn-danger" style="margin: 0 5px 0 5px;display:none">
<i class="octicon octicon-trashcan"></i>
</button>
{% } %}
</div>
<div class="pos-bill">
<div class="item-cart">
<div class="row pos-bill-row pos-bill-header">
<div class="col-xs-5"><h6>{%= __("Item") %}</h6></div>
<div class="col-xs-4"><h6 class="text-right">{%= __("Quantity") %}</h6></div>
<div class="col-xs-4"><h6>{%= __("Item") %}</h6></div>
<div class="col-xs-3"><h6 class="text-right">{%= __("Quantity") %}</h6></div>
<div class="col-xs-2"><h6 class="text-right">{%= __("Discount") %}</h6></div>
<div class="col-xs-3"><h6 class="text-right">{%= __("Rate") %}</h6></div>
</div>
<div class="items"></div>
@ -48,18 +60,27 @@
</div>
</div>
</div>
<div class="list-customers">
</div>
</div>
<div class="col-sm-7 pos-items-section">
<div class="col-sm-6 pos-items-section">
<div class="row pos-item-area">
</div>
<span id="customer-results" style="color:#68a;"></span>
<div class="row pos-item-toolbar">
<div class="search-area col-xs-12"></div>
<div class="search-item-group col-xs-5"></div>
<div class="search-item col-xs-7"></div>
</div>
<div class="item-list-area">
<div class="item-list-area" style="auto">
<div class="app-listing item-list"></ul>
</div>
</div>
<div class="row">
<div class="text-right list-paging-area">
<button class="btn btn-default btn-more btn-sm" style="margin:5px 20px">{{ __("More") }}</button>
</div>
</div>
</div>
</div>

View File

@ -1,25 +1,30 @@
<div class="row pos-bill-row pos-bill-item" data-item-code="{%= item_code %}">
<div class="col-xs-5"><h6>{%= item_code || "" %}{%= item_name || "" %}</h6></div>
<div class="col-xs-4">
<div class="col-xs-4"><h6>{%= item_code || "" %}{%= __(item_name) || "" %}</h6></div>
<div class="col-xs-3">
<div class="row pos-qty-row">
<div class="col-xs-2 text-center pos-qty-btn" data-action="decrease-qty"><i class="fa fa-minus text-muted" style="font-size:12px"></i></div>
<div class="col-xs-8">
<div>
<input type="text" value="{%= qty %}" class="form-control input-sm pos-item-qty text-right">
<input type="tel" value="{%= qty %}" class="form-control pos-item-qty text-right">
</div>
{% if(actual_qty != null) { %}
<div style="margin-top: 5px;" class="text-muted small text-right">
<span title="{%= __("In Stock") %}">{%= actual_qty || 0 %}<span>
{%= __("In Stock: ") %} <span>{%= actual_qty || 0.0 %}</span>
</div>
{% } %}
</div>
<div class="col-xs-2 text-center pos-qty-btn" data-action="increase-qty"><i class="fa fa-plus text-muted" style="font-size:12px"></i></div>
</div>
</div>
<div class="col-xs-2 text-right">
<div class="row input-sm">
<input type="tel" value="{%= discount_percentage %}" class="form-control text-right pos-item-discount">
</div>
</div>
<div class="col-xs-3 text-right">
<div class="text-muted" style="margin-top: 5px;">
{% if(enabled) { %}
<input type="text" value="{%= rate %}" class="form-control input-sm pos-item-rate text-right">
<input type="tel" value="{%= rate %}" class="form-control input-sm pos-item-rate text-right">
{% } else { %}
<h6>{%= format_currency(rate) %}</h6>
{% } %}

View File

@ -82,7 +82,7 @@
.pos-toolbar, .pos-bill-toolbar {
padding: 10px 0px;
border-bottom: 1px solid #d1d8dd;
// border-bottom: 1px solid #d1d8dd;
height: 51px;
}
@ -97,6 +97,7 @@
}
.pos-bill {
border-top: 1px solid @border-color;
margin-left: -15px;
margin-right: -15px;
}
@ -213,6 +214,10 @@
padding-right: 30px;
}
.list-row-head.pos-invoice-list {
border-top: 1px solid @border-color;
}
body[data-route="pos"] .modal-dialog {
width: 750px;

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

@ -7,6 +7,17 @@ cur_frm.add_fetch("examiner", "instructor_name", "examiner_name");
cur_frm.add_fetch("supervisor", "instructor_name", "supervisor_name");
frappe.ui.form.on("Assessment Plan", {
refresh: function(frm) {
if (!frm.doc.__islocal) {
frm.add_custom_button(__("Assessment Result"), function() {
frappe.route_options = {
assessment_plan: frm.doc.name
}
frappe.set_route("Form", "Assessment Result Tool");
});
}
},
course: function(frm) {
if (frm.doc.course && frm.doc.maximum_assessment_score) {
frappe.call({

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"""
@ -42,4 +42,11 @@ 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")
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

Some files were not shown because too many files have changed in this diff Show More