Merge pull request #5770 from saurabh6790/demo_expense_claim

[enhance] demo for expense claim #5731
This commit is contained in:
Rushabh Mehta 2016-07-19 21:34:30 +05:30 committed by GitHub
commit f997c6981d
13 changed files with 337 additions and 64 deletions

View File

@ -3,7 +3,8 @@ from __future__ import unicode_literals
import random, json import random, json
from erpnext.demo.domains import data from erpnext.demo.domains import data
import frappe, erpnext import frappe, erpnext
from frappe.utils import cint, flt from frappe.utils import cint, flt, now_datetime, cstr
from frappe import _
def setup_data(): def setup_data():
domain = frappe.flags.domain domain = frappe.flags.domain
@ -28,6 +29,7 @@ def setup_data():
setup_employee() setup_employee()
setup_salary_structure() setup_salary_structure()
setup_salary_structure_for_timesheet() setup_salary_structure_for_timesheet()
setup_account_to_expense_type()
setup_user_roles() setup_user_roles()
frappe.db.commit() frappe.db.commit()
frappe.clear_cache() frappe.clear_cache()
@ -64,11 +66,11 @@ def setup_demo_page():
def setup_fiscal_year(): def setup_fiscal_year():
fiscal_year = None fiscal_year = None
for year in xrange(2014, frappe.utils.now_datetime().year + 1, 1): for year in xrange(2014, now_datetime().year + 1, 1):
try: try:
fiscal_year = frappe.get_doc({ fiscal_year = frappe.get_doc({
"doctype": "Fiscal Year", "doctype": "Fiscal Year",
"year": frappe.utils.cstr(year), "year": cstr(year),
"year_start_date": "{0}-01-01".format(year), "year_start_date": "{0}-01-01".format(year),
"year_end_date": "{0}-12-31".format(year) "year_end_date": "{0}-12-31".format(year)
}).insert() }).insert()
@ -80,7 +82,7 @@ def setup_fiscal_year():
def setup_holiday_list(): def setup_holiday_list():
"""Setup Holiday List for the current year""" """Setup Holiday List for the current year"""
year = frappe.utils.now_datetime().year year = now_datetime().year
holiday_list = frappe.get_doc({ holiday_list = frappe.get_doc({
"doctype": "Holiday List", "doctype": "Holiday List",
"holiday_list_name": str(year), "holiday_list_name": str(year),
@ -306,6 +308,21 @@ def setup_account():
doc.parent_account = frappe.db.get_value('Account', {'account_name': doc.parent_account}) doc.parent_account = frappe.db.get_value('Account', {'account_name': doc.parent_account})
doc.insert() doc.insert()
def setup_account_to_expense_type():
expense_types = [{'name': _('Calls'), "account": "Sales Expenses - WPL"},
{'name': _('Food'), "account": "Entertainment Expenses - WPL"},
{'name': _('Medical'), "account": "Utility Expenses - WPL"},
{'name': _('Others'), "account": "Miscellaneous Expenses - WPL"},
{'name': _('Travel'), "account": "Travel Expenses - WPL"}]
for expense_type in expense_types:
doc = frappe.get_doc("Expense Claim Type", expense_type["name"])
doc.append("accounts", {
"company" : erpnext.get_default_company(),
"default_account" : expense_type["account"]
})
doc.save(ignore_permissions=True)
def setup_user_roles(): def setup_user_roles():
if not frappe.db.get_global('demo_hr_user'): if not frappe.db.get_global('demo_hr_user'):
user = frappe.get_doc('User', 'CharmaineGaudreau@example.com') user = frappe.get_doc('User', 'CharmaineGaudreau@example.com')

View File

@ -5,6 +5,7 @@ from frappe.utils import random_string
from erpnext.projects.doctype.timesheet.test_timesheet import make_timesheet from erpnext.projects.doctype.timesheet.test_timesheet import make_timesheet
from erpnext.projects.doctype.timesheet.timesheet import make_salary_slip, make_sales_invoice from erpnext.projects.doctype.timesheet.timesheet import make_salary_slip, make_sales_invoice
from frappe.utils.make_random import how_many, get_random from frappe.utils.make_random import how_many, get_random
from erpnext.hr.doctype.expense_claim.expense_claim import get_expense_approver, make_bank_entry
def work(): def work():
frappe.set_user(frappe.db.get_global('demo_hr_user')) frappe.set_user(frappe.db.get_global('demo_hr_user'))
@ -30,6 +31,61 @@ def work():
if frappe.db.get_global('demo_hr_user'): if frappe.db.get_global('demo_hr_user'):
make_timesheet_records() make_timesheet_records()
#expense claim
expense_claim = frappe.new_doc("Expense Claim")
expense_claim.extend('expenses', get_expenses())
expense_claim.employee = get_random("Employee")
expense_claim.company = frappe.flags.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()
rand = random.random()
if rand < 0.4:
expense_claim.approval_status = "Approved"
update_sanctioned_amount(expense_claim)
expense_claim.submit()
if random.randint(0, 1):
#make journal entry against expense claim
je = frappe.get_doc(make_bank_entry(expense_claim.name))
je.posting_date = frappe.flags.current_date
je.cheque_no = random_string(10)
je.cheque_date = frappe.flags.current_date
je.flags.ignore_permissions = 1
je.submit()
elif rand < 0.2:
expense_claim.approval_status = "Rejected"
expense_claim.submit()
def get_expenses():
expenses = []
expese_types = frappe.db.sql("""select ect.name, eca.default_account from `tabExpense Claim Type` ect,
`tabExpense Claim Account` eca where eca.parent=ect.name
and eca.company=%s """, frappe.flags.company,as_dict=1)
for expense_type in expese_types[:random.randint(1,4)]:
claim_amount = random.randint(1,20)*10
expenses.append({
"expense_date": frappe.flags.current_date,
"expense_type": expense_type.name,
"default_account": expense_type.default_account or "Miscellaneous Expenses - WPL",
"claim_amount": claim_amount,
"sanctioned_amount": claim_amount
})
return expenses
def update_sanctioned_amount(expense_claim):
for expense in expense_claim.expenses:
sanctioned_amount = random.randint(1,20)*10
if sanctioned_amount < expense.claim_amount:
expense.sanctioned_amount = sanctioned_amount
def get_timesheet_based_salary_slip_employee(): def get_timesheet_based_salary_slip_employee():
return frappe.get_all('Salary Structure', fields = ["distinct employee as name"], return frappe.get_all('Salary Structure', fields = ["distinct employee as name"],

View File

@ -7,39 +7,30 @@ erpnext.hr.ExpenseClaimController = frappe.ui.form.Controller.extend({
make_bank_entry: function() { make_bank_entry: function() {
var me = this; var me = this;
return frappe.call({ return frappe.call({
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_default_bank_cash_account", method: "erpnext.hr.doctype.expense_claim.expense_claim.make_bank_entry",
args: { args: {
"company": cur_frm.doc.company, "docname": cur_frm.doc.name,
"account_type": "Bank"
}, },
callback: function(r) { callback: function(r) {
var jv = frappe.model.make_new_doc_and_get_name('Journal Entry'); var doc = frappe.model.sync(r.message);
jv = locals['Journal Entry'][jv]; frappe.set_route('Form', 'Journal Entry', r.message.name);
jv.voucher_type = 'Bank Entry'; }
jv.company = cur_frm.doc.company; });
jv.remark = 'Payment against Expense Claim: ' + cur_frm.doc.name; },
var expense = cur_frm.doc.expenses || [];
for(var i = 0; i < expense.length; i++){ expense_type: function(frm, cdt, cdn) {
var d1 = frappe.model.add_child(jv, 'Journal Entry Account', 'accounts'); var d = locals[cdt][cdn];
d1.account = expense[i].default_account;
d1.debit_in_account_currency = expense[i].sanctioned_amount;
d1.reference_type = cur_frm.doc.doctype;
d1.reference_name = cur_frm.doc.name;
}
// credit to bank return frappe.call({
var d1 = frappe.model.add_child(jv, 'Journal Entry Account', 'accounts'); method: "erpnext.hr.doctype.expense_claim.expense_claim.get_expense_claim_account",
d1.credit_in_account_currency = cur_frm.doc.total_sanctioned_amount; args: {
d1.reference_type = cur_frm.doc.doctype; "expense_claim_type": d.expense_type,
d1.reference_name = cur_frm.doc.name; "company": frm.company
if(r.message) { },
d1.account = r.message.account; callback: function(r) {
d1.balance = r.message.balance; if (r.message) {
d1.account_currency = r.message.account_currency; d.default_account = r.message.account;
d1.account_type = r.message.account_type;
} }
frappe.set_route('Form', 'Journal Entry', jv.name);
} }
}); });
} }
@ -49,7 +40,6 @@ $.extend(cur_frm.cscript, new erpnext.hr.ExpenseClaimController({frm: cur_frm}))
cur_frm.add_fetch('employee', 'company', 'company'); cur_frm.add_fetch('employee', 'company', 'company');
cur_frm.add_fetch('employee','employee_name','employee_name'); cur_frm.add_fetch('employee','employee_name','employee_name');
cur_frm.add_fetch('expense_type', 'default_account', 'default_account');
cur_frm.cscript.onload = function(doc,cdt,cdn) { cur_frm.cscript.onload = function(doc,cdt,cdn) {
if(!doc.approval_status) if(!doc.approval_status)

View File

@ -20,6 +20,7 @@ class ExpenseClaim(Document):
self.validate_expense_approver() self.validate_expense_approver()
self.calculate_total_amount() self.calculate_total_amount()
set_employee_name(self) set_employee_name(self)
self.set_expense_account()
if self.task and not self.project: if self.task and not self.project:
self.project = frappe.db.get_value("Task", self.task, "project") self.project = frappe.db.get_value("Task", self.task, "project")
@ -59,7 +60,11 @@ class ExpenseClaim(Document):
if flt(d.sanctioned_amount) > flt(d.claim_amount): if flt(d.sanctioned_amount) > flt(d.claim_amount):
frappe.throw(_("Sanctioned Amount cannot be greater than Claim Amount in Row {0}.").format(d.idx)) frappe.throw(_("Sanctioned Amount cannot be greater than Claim Amount in Row {0}.").format(d.idx))
def set_expense_account(self):
for expense in self.expenses:
if not expense.default_account:
expense.default_account = get_expense_claim_account(expense.expense_type, self.company)["account"]
@frappe.whitelist() @frappe.whitelist()
def get_expense_approver(doctype, txt, searchfield, start, page_len, filters): def get_expense_approver(doctype, txt, searchfield, start, page_len, filters):
return frappe.db.sql(""" return frappe.db.sql("""
@ -68,3 +73,48 @@ def get_expense_approver(doctype, txt, searchfield, start, page_len, filters):
where u.name = r.parent and r.role = 'Expense Approver' where u.name = r.parent and r.role = 'Expense Approver'
and u.enabled = 1 and u.name like %s and u.enabled = 1 and u.name like %s
""", ("%" + txt + "%")) """, ("%" + txt + "%"))
@frappe.whitelist()
def make_bank_entry(docname):
from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account
expense_claim = frappe.get_doc("Expense Claim", docname)
default_bank_cash_account = get_default_bank_cash_account(expense_claim.company, "Bank")
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": default_bank_cash_account.account,
"credit_in_account_currency": expense_claim.total_sanctioned_amount,
"reference_type": "Expense Claim",
"reference_name": expense_claim.name,
"balance": default_bank_cash_account.balance,
"account_currency": default_bank_cash_account.account_currency,
"account_type": default_bank_cash_account.account_type
})
return je.as_dict()
@frappe.whitelist()
def get_expense_claim_account(expense_claim_type, company):
account = frappe.db.get_value("Expense Claim Account",
{"parent": expense_claim_type, "company": company}, "default_account")
if not account:
frappe.throw(_("Please set default account in Expense Claim Type {0}")
.format(expense_claim_type))
return {
"account": account
}

View File

@ -30,7 +30,7 @@ class TestExpenseClaim(unittest.TestCase):
"project": "_Test Project 1", "project": "_Test Project 1",
"task": task_name, "task": task_name,
"expenses": "expenses":
[{ "expense_type": "Food", "claim_amount": 300, "sanctioned_amount": 200 }] [{ "expense_type": "Food", "default_account": "Entertainment Expenses - _TC", "claim_amount": 300, "sanctioned_amount": 200 }]
}) })
expense_claim.submit() expense_claim.submit()
@ -44,7 +44,7 @@ class TestExpenseClaim(unittest.TestCase):
"project": "_Test Project 1", "project": "_Test Project 1",
"task": task_name, "task": task_name,
"expenses": "expenses":
[{ "expense_type": "Food", "claim_amount": 600, "sanctioned_amount": 500 }] [{ "expense_type": "Food", "default_account": "Entertainment Expenses - _TC", "claim_amount": 600, "sanctioned_amount": 500 }]
}) })
expense_claim2.submit() expense_claim2.submit()

View File

@ -0,0 +1,89 @@
{
"allow_copy": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2016-07-18 12:24:16.507860",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "company",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Company",
"length": 0,
"no_copy": 0,
"options": "Company",
"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": "default_account",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Default Account",
"length": 0,
"no_copy": 0,
"options": "Account",
"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
}
],
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2016-07-18 12:39:29.709848",
"modified_by": "Administrator",
"module": "HR",
"name": "Expense Claim Account",
"name_case": "",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"sort_field": "modified",
"sort_order": "DESC",
"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 ExpenseClaimAccount(Document):
pass

View File

@ -0,0 +1,17 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
frappe.ui.form.on("Expense Claim Type", {
refresh: function(frm){
frm.fields_dict["accounts"].grid.get_field("default_account").get_query = function(frm, cdt, cdn){
var d = locals[cdt][cdn];
return{
filters: {
"is_group": 0,
"root_type": "Expense",
'company': d.company
}
}
}
}
})

View File

@ -3,11 +3,13 @@
"allow_import": 1, "allow_import": 1,
"allow_rename": 0, "allow_rename": 0,
"autoname": "field:expense_type", "autoname": "field:expense_type",
"beta": 0,
"creation": "2012-03-27 14:35:55", "creation": "2012-03-27 14:35:55",
"custom": 0, "custom": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Setup", "document_type": "Setup",
"editable_grid": 0,
"fields": [ "fields": [
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
@ -17,6 +19,7 @@
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Expense Claim Type", "label": "Expense Claim Type",
@ -26,6 +29,7 @@
"oldfieldtype": "Data", "oldfieldtype": "Data",
"permlevel": 0, "permlevel": 0,
"print_hide": 0, "print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0, "read_only": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 1, "reqd": 1,
@ -33,30 +37,6 @@
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "default_account",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Default Account",
"length": 0,
"no_copy": 0,
"options": "Account",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
@ -65,6 +45,7 @@
"fieldtype": "Small Text", "fieldtype": "Small Text",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Description", "label": "Description",
@ -74,6 +55,7 @@
"oldfieldtype": "Small Text", "oldfieldtype": "Small Text",
"permlevel": 0, "permlevel": 0,
"print_hide": 0, "print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0, "read_only": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 0,
@ -81,19 +63,46 @@
"set_only_once": 0, "set_only_once": 0,
"unique": 0, "unique": 0,
"width": "300px" "width": "300px"
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "accounts",
"fieldtype": "Table",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Accounts",
"length": 0,
"no_copy": 0,
"options": "Expense Claim Account",
"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
} }
], ],
"hide_heading": 0, "hide_heading": 0,
"hide_toolbar": 0, "hide_toolbar": 0,
"icon": "icon-flag", "icon": "icon-flag",
"idx": 1, "idx": 1,
"image_view": 0,
"in_create": 0, "in_create": 0,
"in_dialog": 0, "in_dialog": 0,
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"modified": "2015-11-16 06:29:47.084611", "modified": "2016-07-18 12:36:10.096252",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "HR", "module": "HR",
"name": "Expense Claim Type", "name": "Expense Claim Type",
@ -140,6 +149,9 @@
"write": 0 "write": 0
} }
], ],
"quick_entry": 0,
"read_only": 0, "read_only": 0,
"read_only_onload": 0 "read_only_onload": 0,
"sort_order": "ASC",
"track_seen": 0
} }

View File

@ -3,8 +3,25 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe import _
from frappe.model.document import Document from frappe.model.document import Document
class ExpenseClaimType(Document): class ExpenseClaimType(Document):
pass def validate(self):
self.validate_accounts()
self.validate_repeating_companies()
def validate_repeating_companies(self):
"""Error when Same Company is entered multiple times in accounts"""
accounts_list = []
for entry in self.accounts:
accounts_list.append(entry.company)
if len(accounts_list)!= len(set(accounts_list)):
frappe.throw(_("Same Company is entered more than once"))
def validate_accounts(self):
for entry in self.accounts:
"""Error when Company of Ledger account doesn't match with Company Selected"""
if frappe.db.get_value("Account", entry.default_account, "company") != entry.company:
frappe.throw(_("Account does not match with Company"))

View File

@ -298,3 +298,4 @@ execute:frappe.reload_doc('projects', 'doctype', 'timesheet', force=True)
execute:frappe.delete_doc_if_exists("Report", "Employee Holiday Attendance") execute:frappe.delete_doc_if_exists("Report", "Employee Holiday Attendance")
execute:frappe.delete_doc_if_exists("DocType", "Payment Tool") execute:frappe.delete_doc_if_exists("DocType", "Payment Tool")
execute:frappe.delete_doc_if_exists("DocType", "Payment Tool Detail") execute:frappe.delete_doc_if_exists("DocType", "Payment Tool Detail")
erpnext.patches.v7_0.setup_account_table_for_expense_claim_type_if_exists

View File

@ -0,0 +1,14 @@
from __future__ import unicode_literals
import frappe
def execute():
frappe.reload_doc("hr", "doctype", "expense_claim_type")
for expense_claim_type in frappe.get_all("Expense Claim Type", fields=["name", "default_account"]):
if expense_claim_type.default_account:
doc = frappe.get_doc("Expense Claim Type", expense_claim_type.name)
doc.append("accounts", {
"company": frappe.db.get_value("Account", expense_claim_type.default_account, "company"),
"default_account": expense_claim_type.default_account,
})
doc.save(ignore_permissions=True)