Merge pull request #31467 from ruchamahabal/hr-separation
This commit is contained in:
commit
2b0b53f587
@ -49,15 +49,9 @@ class AccountingPeriod(Document):
|
||||
@frappe.whitelist()
|
||||
def get_doctypes_for_closing(self):
|
||||
docs_for_closing = []
|
||||
doctypes = [
|
||||
"Sales Invoice",
|
||||
"Purchase Invoice",
|
||||
"Journal Entry",
|
||||
"Payroll Entry",
|
||||
"Bank Clearance",
|
||||
"Asset",
|
||||
"Stock Entry",
|
||||
]
|
||||
# get period closing doctypes from all the apps
|
||||
doctypes = frappe.get_hooks("period_closing_doctypes")
|
||||
|
||||
closed_doctypes = [{"document_type": doctype, "closed": 1} for doctype in doctypes]
|
||||
for closed_doctype in closed_doctypes:
|
||||
docs_for_closing.append(closed_doctype)
|
||||
|
@ -104,7 +104,7 @@ class BankClearance(Document):
|
||||
|
||||
loan_repayment = frappe.qb.DocType("Loan Repayment")
|
||||
|
||||
loan_repayments = (
|
||||
query = (
|
||||
frappe.qb.from_(loan_repayment)
|
||||
.select(
|
||||
ConstantColumn("Loan Repayment").as_("payment_document"),
|
||||
@ -118,13 +118,17 @@ class BankClearance(Document):
|
||||
)
|
||||
.where(loan_repayment.docstatus == 1)
|
||||
.where(loan_repayment.clearance_date.isnull())
|
||||
.where(loan_repayment.repay_from_salary == 0)
|
||||
.where(loan_repayment.posting_date >= self.from_date)
|
||||
.where(loan_repayment.posting_date <= self.to_date)
|
||||
.where(loan_repayment.payment_account.isin([self.bank_account, self.account]))
|
||||
.orderby(loan_repayment.posting_date)
|
||||
.orderby(loan_repayment.name, frappe.qb.desc)
|
||||
).run(as_dict=1)
|
||||
)
|
||||
|
||||
if frappe.db.has_column("Loan Repayment", "repay_from_salary"):
|
||||
query = query.where((loan_repayment.repay_from_salary == 0))
|
||||
|
||||
query = query.orderby(loan_repayment.posting_date).orderby(loan_repayment.name, frappe.qb.desc)
|
||||
|
||||
loan_repayments = query.run(as_dict=True)
|
||||
|
||||
pos_sales_invoices, pos_purchase_invoices = [], []
|
||||
if self.include_pos_transactions:
|
||||
|
@ -10,7 +10,6 @@ from frappe.model.document import Document
|
||||
from frappe.query_builder.custom import ConstantColumn
|
||||
from frappe.utils import flt
|
||||
|
||||
from erpnext import get_company_currency
|
||||
from erpnext.accounts.doctype.bank_transaction.bank_transaction import get_paid_amount
|
||||
from erpnext.accounts.report.bank_reconciliation_statement.bank_reconciliation_statement import (
|
||||
get_amounts_not_reflected_in_system,
|
||||
@ -368,6 +367,27 @@ def get_queries(bank_account, company, transaction, document_types):
|
||||
account_from_to = "paid_to" if transaction.deposit > 0 else "paid_from"
|
||||
queries = []
|
||||
|
||||
# get matching queries from all the apps
|
||||
for method_name in frappe.get_hooks("get_matching_queries"):
|
||||
queries.extend(
|
||||
frappe.get_attr(method_name)(
|
||||
bank_account,
|
||||
company,
|
||||
transaction,
|
||||
document_types,
|
||||
amount_condition,
|
||||
account_from_to,
|
||||
)
|
||||
or []
|
||||
)
|
||||
|
||||
return queries
|
||||
|
||||
|
||||
def get_matching_queries(
|
||||
bank_account, company, transaction, document_types, amount_condition, account_from_to
|
||||
):
|
||||
queries = []
|
||||
if "payment_entry" in document_types:
|
||||
pe_amount_matching = get_pe_matching_query(amount_condition, account_from_to, transaction)
|
||||
queries.extend([pe_amount_matching])
|
||||
@ -385,10 +405,6 @@ def get_queries(bank_account, company, transaction, document_types):
|
||||
pi_amount_matching = get_pi_matching_query(amount_condition)
|
||||
queries.extend([pi_amount_matching])
|
||||
|
||||
if "expense_claim" in document_types:
|
||||
ec_amount_matching = get_ec_matching_query(bank_account, company, amount_condition)
|
||||
queries.extend([ec_amount_matching])
|
||||
|
||||
return queries
|
||||
|
||||
|
||||
@ -467,11 +483,13 @@ def get_lr_matching_query(bank_account, amount_condition, filters):
|
||||
loan_repayment.posting_date,
|
||||
)
|
||||
.where(loan_repayment.docstatus == 1)
|
||||
.where(loan_repayment.repay_from_salary == 0)
|
||||
.where(loan_repayment.clearance_date.isnull())
|
||||
.where(loan_repayment.payment_account == bank_account)
|
||||
)
|
||||
|
||||
if frappe.db.has_column("Loan Repayment", "repay_from_salary"):
|
||||
query = query.where((loan_repayment.repay_from_salary == 0))
|
||||
|
||||
if amount_condition:
|
||||
query.where(loan_repayment.amount_paid == filters.get("amount"))
|
||||
else:
|
||||
@ -602,37 +620,3 @@ def get_pi_matching_query(amount_condition):
|
||||
AND ifnull(clearance_date, '') = ""
|
||||
AND cash_bank_account = %(bank_account)s
|
||||
"""
|
||||
|
||||
|
||||
def get_ec_matching_query(bank_account, company, amount_condition):
|
||||
# get matching Expense Claim query
|
||||
mode_of_payments = [
|
||||
x["parent"]
|
||||
for x in frappe.db.get_all(
|
||||
"Mode of Payment Account", filters={"default_account": bank_account}, fields=["parent"]
|
||||
)
|
||||
]
|
||||
mode_of_payments = "('" + "', '".join(mode_of_payments) + "' )"
|
||||
company_currency = get_company_currency(company)
|
||||
return f"""
|
||||
SELECT
|
||||
( CASE WHEN employee = %(party)s THEN 1 ELSE 0 END
|
||||
+ 1 ) AS rank,
|
||||
'Expense Claim' as doctype,
|
||||
name,
|
||||
total_sanctioned_amount as paid_amount,
|
||||
'' as reference_no,
|
||||
'' as reference_date,
|
||||
employee as party,
|
||||
'Employee' as party_type,
|
||||
posting_date,
|
||||
'{company_currency}' as currency
|
||||
FROM
|
||||
`tabExpense Claim`
|
||||
WHERE
|
||||
total_sanctioned_amount {amount_condition} %(amount)s
|
||||
AND docstatus = 1
|
||||
AND is_paid = 1
|
||||
AND ifnull(clearance_date, '') = ""
|
||||
AND mode_of_payment in {mode_of_payments}
|
||||
"""
|
||||
|
@ -3,28 +3,21 @@
|
||||
|
||||
frappe.ui.form.on("Bank Transaction", {
|
||||
onload(frm) {
|
||||
frm.set_query("payment_document", "payment_entries", function () {
|
||||
frm.set_query("payment_document", "payment_entries", function() {
|
||||
const payment_doctypes = frm.events.get_payment_doctypes(frm);
|
||||
return {
|
||||
filters: {
|
||||
name: [
|
||||
"in",
|
||||
[
|
||||
"Payment Entry",
|
||||
"Journal Entry",
|
||||
"Sales Invoice",
|
||||
"Purchase Invoice",
|
||||
"Expense Claim",
|
||||
],
|
||||
],
|
||||
name: ["in", payment_doctypes],
|
||||
},
|
||||
};
|
||||
});
|
||||
},
|
||||
bank_account: function (frm) {
|
||||
|
||||
bank_account: function(frm) {
|
||||
set_bank_statement_filter(frm);
|
||||
},
|
||||
|
||||
setup: function (frm) {
|
||||
setup: function(frm) {
|
||||
frm.set_query("party_type", function () {
|
||||
return {
|
||||
filters: {
|
||||
@ -33,6 +26,16 @@ frappe.ui.form.on("Bank Transaction", {
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
get_payment_doctypes: function() {
|
||||
// get payment doctypes from all the apps
|
||||
return [
|
||||
"Payment Entry",
|
||||
"Journal Entry",
|
||||
"Sales Invoice",
|
||||
"Purchase Invoice",
|
||||
];
|
||||
}
|
||||
});
|
||||
|
||||
frappe.ui.form.on("Bank Transaction Payments", {
|
||||
|
@ -58,18 +58,10 @@ class BankTransaction(StatusUpdater):
|
||||
|
||||
def clear_linked_payment_entries(self, for_cancel=False):
|
||||
for payment_entry in self.payment_entries:
|
||||
if payment_entry.payment_document in [
|
||||
"Payment Entry",
|
||||
"Journal Entry",
|
||||
"Purchase Invoice",
|
||||
"Expense Claim",
|
||||
"Loan Repayment",
|
||||
"Loan Disbursement",
|
||||
]:
|
||||
self.clear_simple_entry(payment_entry, for_cancel=for_cancel)
|
||||
|
||||
elif payment_entry.payment_document == "Sales Invoice":
|
||||
if payment_entry.payment_document == "Sales Invoice":
|
||||
self.clear_sales_invoice(payment_entry, for_cancel=for_cancel)
|
||||
elif payment_entry.payment_document in get_doctypes_for_bank_reconciliation():
|
||||
self.clear_simple_entry(payment_entry, for_cancel=for_cancel)
|
||||
|
||||
def clear_simple_entry(self, payment_entry, for_cancel=False):
|
||||
if payment_entry.payment_document == "Payment Entry":
|
||||
@ -95,6 +87,12 @@ class BankTransaction(StatusUpdater):
|
||||
)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_doctypes_for_bank_reconciliation():
|
||||
"""Get Bank Reconciliation doctypes from all the apps"""
|
||||
return frappe.get_hooks("bank_reconciliation_doctypes")
|
||||
|
||||
|
||||
def get_reconciled_bank_transactions(payment_entry):
|
||||
reconciled_bank_transactions = frappe.get_all(
|
||||
"Bank Transaction Payments",
|
||||
|
@ -5,6 +5,7 @@ import json
|
||||
import unittest
|
||||
|
||||
import frappe
|
||||
from frappe.tests.utils import FrappeTestCase
|
||||
|
||||
from erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool import (
|
||||
get_linked_payments,
|
||||
@ -18,28 +19,21 @@ from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sal
|
||||
test_dependencies = ["Item", "Cost Center"]
|
||||
|
||||
|
||||
class TestBankTransaction(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
class TestBankTransaction(FrappeTestCase):
|
||||
def setUp(self):
|
||||
for dt in [
|
||||
"Loan Repayment",
|
||||
"Bank Transaction",
|
||||
"Payment Entry",
|
||||
"Payment Entry Reference",
|
||||
"POS Profile",
|
||||
]:
|
||||
frappe.db.delete(dt)
|
||||
|
||||
make_pos_profile()
|
||||
add_transactions()
|
||||
add_vouchers()
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
for bt in frappe.get_all("Bank Transaction"):
|
||||
doc = frappe.get_doc("Bank Transaction", bt.name)
|
||||
if doc.docstatus == 1:
|
||||
doc.cancel()
|
||||
doc.delete()
|
||||
|
||||
# Delete directly in DB to avoid validation errors for countries not allowing deletion
|
||||
frappe.db.sql("""delete from `tabPayment Entry Reference`""")
|
||||
frappe.db.sql("""delete from `tabPayment Entry`""")
|
||||
|
||||
# Delete POS Profile
|
||||
frappe.db.sql("delete from `tabPOS Profile`")
|
||||
|
||||
# This test checks if ERPNext is able to provide a linked payment for a bank transaction based on the amount of the bank transaction.
|
||||
def test_linked_payments(self):
|
||||
bank_transaction = frappe.get_doc(
|
||||
@ -155,6 +149,35 @@ class TestBankTransaction(unittest.TestCase):
|
||||
is not None
|
||||
)
|
||||
|
||||
def test_matching_loan_repayment(self):
|
||||
from erpnext.loan_management.doctype.loan.test_loan import create_loan_accounts
|
||||
|
||||
create_loan_accounts()
|
||||
bank_account = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Bank Account",
|
||||
"account_name": "Payment Account",
|
||||
"bank": "Citi Bank",
|
||||
"account": "Payment Account - _TC",
|
||||
}
|
||||
).insert(ignore_if_duplicate=True)
|
||||
|
||||
bank_transaction = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Bank Transaction",
|
||||
"description": "Loan Repayment - OPSKATTUZWXXX AT776000000098709837 Herr G",
|
||||
"date": "2018-10-27",
|
||||
"deposit": 500,
|
||||
"currency": "INR",
|
||||
"bank_account": bank_account.name,
|
||||
}
|
||||
).submit()
|
||||
|
||||
repayment_entry = create_loan_and_repayment()
|
||||
|
||||
linked_payments = get_linked_payments(bank_transaction.name, ["loan_repayment", "exact_match"])
|
||||
self.assertEqual(linked_payments[0][2], repayment_entry.name)
|
||||
|
||||
|
||||
def create_bank_account(bank_name="Citi Bank", account_name="_Test Bank - _TC"):
|
||||
try:
|
||||
@ -364,3 +387,59 @@ def add_vouchers():
|
||||
)
|
||||
si.insert()
|
||||
si.submit()
|
||||
|
||||
|
||||
def create_loan_and_repayment():
|
||||
from erpnext.loan_management.doctype.loan.test_loan import (
|
||||
create_loan,
|
||||
create_loan_type,
|
||||
create_repayment_entry,
|
||||
make_loan_disbursement_entry,
|
||||
)
|
||||
from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import (
|
||||
process_loan_interest_accrual_for_term_loans,
|
||||
)
|
||||
from erpnext.setup.doctype.employee.test_employee import make_employee
|
||||
|
||||
create_loan_type(
|
||||
"Personal Loan",
|
||||
500000,
|
||||
8.4,
|
||||
is_term_loan=1,
|
||||
mode_of_payment="Cash",
|
||||
disbursement_account="Disbursement Account - _TC",
|
||||
payment_account="Payment Account - _TC",
|
||||
loan_account="Loan Account - _TC",
|
||||
interest_income_account="Interest Income Account - _TC",
|
||||
penalty_income_account="Penalty Income Account - _TC",
|
||||
)
|
||||
|
||||
applicant = make_employee("test_bank_reco@loan.com", company="_Test Company")
|
||||
loan = create_loan(applicant, "Personal Loan", 5000, "Repay Over Number of Periods", 20)
|
||||
loan = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Loan",
|
||||
"applicant_type": "Employee",
|
||||
"company": "_Test Company",
|
||||
"applicant": applicant,
|
||||
"loan_type": "Personal Loan",
|
||||
"loan_amount": 5000,
|
||||
"repayment_method": "Repay Fixed Amount per Period",
|
||||
"monthly_repayment_amount": 500,
|
||||
"repayment_start_date": "2018-09-27",
|
||||
"is_term_loan": 1,
|
||||
"posting_date": "2018-09-27",
|
||||
}
|
||||
).insert()
|
||||
|
||||
make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date="2018-09-27")
|
||||
process_loan_interest_accrual_for_term_loans(posting_date="2018-10-27")
|
||||
|
||||
repayment_entry = create_repayment_entry(
|
||||
loan.name,
|
||||
applicant,
|
||||
"2018-10-27",
|
||||
500,
|
||||
)
|
||||
repayment_entry.submit()
|
||||
return repayment_entry
|
||||
|
@ -224,25 +224,6 @@ erpnext.accounts.JournalEntry = class JournalEntry extends frappe.ui.form.Contro
|
||||
me.frm.set_query("reference_name", "accounts", function(doc, cdt, cdn) {
|
||||
var jvd = frappe.get_doc(cdt, cdn);
|
||||
|
||||
// expense claim
|
||||
if(jvd.reference_type==="Expense Claim") {
|
||||
return {
|
||||
filters: {
|
||||
'total_sanctioned_amount': ['>', 0],
|
||||
'status': ['!=', 'Paid'],
|
||||
'docstatus': 1
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if(jvd.reference_type==="Employee Advance") {
|
||||
return {
|
||||
filters: {
|
||||
'docstatus': 1
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// journal entry
|
||||
if(jvd.reference_type==="Journal Entry") {
|
||||
frappe.model.validate_missing(jvd, "account");
|
||||
@ -255,13 +236,6 @@ erpnext.accounts.JournalEntry = class JournalEntry extends frappe.ui.form.Contro
|
||||
};
|
||||
}
|
||||
|
||||
// payroll entry
|
||||
if(jvd.reference_type==="Payroll Entry") {
|
||||
return {
|
||||
query: "erpnext.payroll.doctype.payroll_entry.payroll_entry.get_payroll_entries_for_jv",
|
||||
};
|
||||
}
|
||||
|
||||
var out = {
|
||||
filters: [
|
||||
[jvd.reference_type, "docstatus", "=", 1]
|
||||
|
@ -24,7 +24,6 @@ from erpnext.accounts.utils import (
|
||||
get_stock_and_account_balance,
|
||||
)
|
||||
from erpnext.controllers.accounts_controller import AccountsController
|
||||
from erpnext.hr.doctype.expense_claim.expense_claim import update_reimbursed_amount
|
||||
|
||||
|
||||
class StockAccountInvalidTransaction(frappe.ValidationError):
|
||||
@ -66,7 +65,6 @@ class JournalEntry(AccountsController):
|
||||
self.set_against_account()
|
||||
self.create_remarks()
|
||||
self.set_print_format_fields()
|
||||
self.validate_expense_claim()
|
||||
self.validate_credit_debit_note()
|
||||
self.validate_empty_accounts_table()
|
||||
self.set_account_and_party_balance()
|
||||
@ -83,27 +81,21 @@ class JournalEntry(AccountsController):
|
||||
self.check_credit_limit()
|
||||
self.make_gl_entries()
|
||||
self.update_advance_paid()
|
||||
self.update_expense_claim()
|
||||
self.update_inter_company_jv()
|
||||
self.update_invoice_discounting()
|
||||
self.update_status_for_full_and_final_statement()
|
||||
|
||||
def on_cancel(self):
|
||||
from erpnext.accounts.utils import unlink_ref_doc_from_payment_entries
|
||||
from erpnext.payroll.doctype.salary_slip.salary_slip import unlink_ref_doc_from_salary_slip
|
||||
|
||||
unlink_ref_doc_from_payment_entries(self)
|
||||
unlink_ref_doc_from_salary_slip(self.name)
|
||||
self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry", "Payment Ledger Entry")
|
||||
self.make_gl_entries(1)
|
||||
self.update_advance_paid()
|
||||
self.update_expense_claim()
|
||||
self.unlink_advance_entry_reference()
|
||||
self.unlink_asset_reference()
|
||||
self.unlink_inter_company_jv()
|
||||
self.unlink_asset_adjustment_entry()
|
||||
self.update_invoice_discounting()
|
||||
self.update_status_for_full_and_final_statement()
|
||||
|
||||
def get_title(self):
|
||||
return self.pay_to_recd_from or self.accounts[0].account
|
||||
@ -112,21 +104,13 @@ class JournalEntry(AccountsController):
|
||||
advance_paid = frappe._dict()
|
||||
for d in self.get("accounts"):
|
||||
if d.is_advance:
|
||||
if d.reference_type in ("Sales Order", "Purchase Order", "Employee Advance"):
|
||||
if d.reference_type in frappe.get_hooks("advance_payment_doctypes"):
|
||||
advance_paid.setdefault(d.reference_type, []).append(d.reference_name)
|
||||
|
||||
for voucher_type, order_list in advance_paid.items():
|
||||
for voucher_no in list(set(order_list)):
|
||||
frappe.get_doc(voucher_type, voucher_no).set_total_advance_paid()
|
||||
|
||||
def update_status_for_full_and_final_statement(self):
|
||||
for entry in self.accounts:
|
||||
if entry.reference_type == "Full and Final Statement":
|
||||
if self.docstatus == 1:
|
||||
frappe.db.set_value("Full and Final Statement", entry.reference_name, "status", "Paid")
|
||||
elif self.docstatus == 2:
|
||||
frappe.db.set_value("Full and Final Statement", entry.reference_name, "status", "Unpaid")
|
||||
|
||||
def validate_inter_company_accounts(self):
|
||||
if (
|
||||
self.voucher_type == "Inter Company Journal Entry"
|
||||
@ -935,29 +919,6 @@ class JournalEntry(AccountsController):
|
||||
as_dict=True,
|
||||
)
|
||||
|
||||
def update_expense_claim(self):
|
||||
for d in self.accounts:
|
||||
if d.reference_type == "Expense Claim" and d.reference_name:
|
||||
doc = frappe.get_doc("Expense Claim", d.reference_name)
|
||||
if self.docstatus == 2:
|
||||
update_reimbursed_amount(doc, -1 * d.debit)
|
||||
else:
|
||||
update_reimbursed_amount(doc, d.debit)
|
||||
|
||||
def validate_expense_claim(self):
|
||||
for d in self.accounts:
|
||||
if d.reference_type == "Expense Claim":
|
||||
sanctioned_amount, reimbursed_amount = frappe.db.get_value(
|
||||
"Expense Claim", d.reference_name, ("total_sanctioned_amount", "total_amount_reimbursed")
|
||||
)
|
||||
pending_amount = flt(sanctioned_amount) - flt(reimbursed_amount)
|
||||
if d.debit > pending_amount:
|
||||
frappe.throw(
|
||||
_(
|
||||
"Row No {0}: Amount cannot be greater than Pending Amount against Expense Claim {1}. Pending Amount is {2}"
|
||||
).format(d.idx, d.reference_name, pending_amount)
|
||||
)
|
||||
|
||||
def validate_credit_debit_note(self):
|
||||
if self.stock_entry:
|
||||
if frappe.db.get_value("Stock Entry", self.stock_entry, "docstatus") != 1:
|
||||
|
@ -110,8 +110,6 @@ frappe.ui.form.on('Payment Entry', {
|
||||
var doctypes = ["Sales Order", "Sales Invoice", "Journal Entry", "Dunning"];
|
||||
} else if (frm.doc.party_type == "Supplier") {
|
||||
var doctypes = ["Purchase Order", "Purchase Invoice", "Journal Entry"];
|
||||
} else if (frm.doc.party_type == "Employee") {
|
||||
var doctypes = ["Expense Claim", "Journal Entry"];
|
||||
} else {
|
||||
var doctypes = ["Journal Entry"];
|
||||
}
|
||||
@ -140,17 +138,12 @@ frappe.ui.form.on('Payment Entry', {
|
||||
const child = locals[cdt][cdn];
|
||||
const filters = {"docstatus": 1, "company": doc.company};
|
||||
const party_type_doctypes = ['Sales Invoice', 'Sales Order', 'Purchase Invoice',
|
||||
'Purchase Order', 'Expense Claim', 'Fees', 'Dunning'];
|
||||
'Purchase Order', 'Dunning'];
|
||||
|
||||
if (in_list(party_type_doctypes, child.reference_doctype)) {
|
||||
filters[doc.party_type.toLowerCase()] = doc.party;
|
||||
}
|
||||
|
||||
if(child.reference_doctype == "Expense Claim") {
|
||||
filters["docstatus"] = 1;
|
||||
filters["is_paid"] = 0;
|
||||
}
|
||||
|
||||
return {
|
||||
filters: filters
|
||||
};
|
||||
@ -730,7 +723,7 @@ frappe.ui.form.on('Payment Entry', {
|
||||
c.payment_term = d.payment_term;
|
||||
c.allocated_amount = d.allocated_amount;
|
||||
|
||||
if(!in_list(["Sales Order", "Purchase Order", "Expense Claim", "Fees"], d.voucher_type)) {
|
||||
if(!in_list(frm.events.get_order_doctypes(frm), d.voucher_type)) {
|
||||
if(flt(d.outstanding_amount) > 0)
|
||||
total_positive_outstanding += flt(d.outstanding_amount);
|
||||
else
|
||||
@ -745,7 +738,7 @@ frappe.ui.form.on('Payment Entry', {
|
||||
} else {
|
||||
c.exchange_rate = 1;
|
||||
}
|
||||
if (in_list(['Sales Invoice', 'Purchase Invoice', "Expense Claim", "Fees"], d.reference_doctype)){
|
||||
if (in_list(frm.events.get_invoice_doctypes(frm), d.reference_doctype)){
|
||||
c.due_date = d.due_date;
|
||||
}
|
||||
});
|
||||
@ -776,6 +769,14 @@ frappe.ui.form.on('Payment Entry', {
|
||||
});
|
||||
},
|
||||
|
||||
get_order_doctypes: function(frm) {
|
||||
return ["Sales Order", "Purchase Order"];
|
||||
},
|
||||
|
||||
get_invoice_doctypes: function(frm) {
|
||||
return ["Sales Invoice", "Purchase Invoice"];
|
||||
},
|
||||
|
||||
allocate_party_amount_against_ref_docs: function(frm, paid_amount, paid_amount_change) {
|
||||
var total_positive_outstanding_including_order = 0;
|
||||
var total_negative_outstanding = 0;
|
||||
@ -946,14 +947,6 @@ frappe.ui.form.on('Payment Entry', {
|
||||
frappe.msgprint(__("Row #{0}: Reference Document Type must be one of Purchase Order, Purchase Invoice or Journal Entry", [row.idx]));
|
||||
return false;
|
||||
}
|
||||
|
||||
if(frm.doc.party_type=="Employee" &&
|
||||
!in_list(["Expense Claim", "Journal Entry"], row.reference_doctype)
|
||||
) {
|
||||
frappe.model.set_value(row.doctype, row.name, "against_voucher_type", null);
|
||||
frappe.msgprint(__("Row #{0}: Reference Document Type must be one of Expense Claim or Journal Entry", [row.idx]));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (row) {
|
||||
|
@ -29,7 +29,6 @@ from erpnext.controllers.accounts_controller import (
|
||||
get_supplier_block_status,
|
||||
validate_taxes_and_charges,
|
||||
)
|
||||
from erpnext.hr.doctype.expense_claim.expense_claim import update_reimbursed_amount
|
||||
from erpnext.setup.utils import get_exchange_rate
|
||||
|
||||
|
||||
@ -88,7 +87,6 @@ class PaymentEntry(AccountsController):
|
||||
if self.difference_amount:
|
||||
frappe.throw(_("Difference Amount must be zero"))
|
||||
self.make_gl_entries()
|
||||
self.update_expense_claim()
|
||||
self.update_outstanding_amounts()
|
||||
self.update_advance_paid()
|
||||
self.update_payment_schedule()
|
||||
@ -97,7 +95,6 @@ class PaymentEntry(AccountsController):
|
||||
def on_cancel(self):
|
||||
self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry", "Payment Ledger Entry")
|
||||
self.make_gl_entries(cancel=1)
|
||||
self.update_expense_claim()
|
||||
self.update_outstanding_amounts()
|
||||
self.update_advance_paid()
|
||||
self.delink_advance_entry_references()
|
||||
@ -296,14 +293,7 @@ class PaymentEntry(AccountsController):
|
||||
frappe.throw(_("{0} is mandatory").format(self.meta.get_label(field)))
|
||||
|
||||
def validate_reference_documents(self):
|
||||
if self.party_type == "Customer":
|
||||
valid_reference_doctypes = ("Sales Order", "Sales Invoice", "Journal Entry", "Dunning")
|
||||
elif self.party_type == "Supplier":
|
||||
valid_reference_doctypes = ("Purchase Order", "Purchase Invoice", "Journal Entry")
|
||||
elif self.party_type == "Employee":
|
||||
valid_reference_doctypes = ("Expense Claim", "Journal Entry", "Employee Advance", "Gratuity")
|
||||
elif self.party_type == "Shareholder":
|
||||
valid_reference_doctypes = "Journal Entry"
|
||||
valid_reference_doctypes = self.get_valid_reference_doctypes()
|
||||
|
||||
for d in self.get("references"):
|
||||
if not d.allocated_amount:
|
||||
@ -329,7 +319,7 @@ class PaymentEntry(AccountsController):
|
||||
else:
|
||||
self.validate_journal_entry()
|
||||
|
||||
if d.reference_doctype in ("Sales Invoice", "Purchase Invoice", "Expense Claim", "Fees"):
|
||||
if d.reference_doctype in frappe.get_hooks("invoice_doctypes"):
|
||||
if self.party_type == "Customer":
|
||||
ref_party_account = (
|
||||
get_party_account_based_on_invoice_discounting(d.reference_name) or ref_doc.debit_to
|
||||
@ -355,6 +345,16 @@ class PaymentEntry(AccountsController):
|
||||
if ref_doc.docstatus != 1:
|
||||
frappe.throw(_("{0} {1} must be submitted").format(d.reference_doctype, d.reference_name))
|
||||
|
||||
def get_valid_reference_doctypes(self):
|
||||
if self.party_type == "Customer":
|
||||
return ("Sales Order", "Sales Invoice", "Journal Entry", "Dunning")
|
||||
elif self.party_type == "Supplier":
|
||||
return ("Purchase Order", "Purchase Invoice", "Journal Entry")
|
||||
elif self.party_type == "Shareholder":
|
||||
return ("Journal Entry",)
|
||||
elif self.party_type == "Employee":
|
||||
return ("Journal Entry",)
|
||||
|
||||
def validate_paid_invoices(self):
|
||||
no_oustanding_refs = {}
|
||||
|
||||
@ -980,24 +980,9 @@ class PaymentEntry(AccountsController):
|
||||
def update_advance_paid(self):
|
||||
if self.payment_type in ("Receive", "Pay") and self.party:
|
||||
for d in self.get("references"):
|
||||
if d.allocated_amount and d.reference_doctype in (
|
||||
"Sales Order",
|
||||
"Purchase Order",
|
||||
"Employee Advance",
|
||||
"Gratuity",
|
||||
):
|
||||
if d.allocated_amount and d.reference_doctype in frappe.get_hooks("advance_payment_doctypes"):
|
||||
frappe.get_doc(d.reference_doctype, d.reference_name).set_total_advance_paid()
|
||||
|
||||
def update_expense_claim(self):
|
||||
if self.payment_type in ("Pay") and self.party:
|
||||
for d in self.get("references"):
|
||||
if d.reference_doctype == "Expense Claim" and d.reference_name:
|
||||
doc = frappe.get_doc("Expense Claim", d.reference_name)
|
||||
if self.docstatus == 2:
|
||||
update_reimbursed_amount(doc, -1 * d.allocated_amount)
|
||||
else:
|
||||
update_reimbursed_amount(doc, d.allocated_amount)
|
||||
|
||||
def on_recurring(self, reference_doc, auto_repeat_doc):
|
||||
self.reference_no = reference_doc.name
|
||||
self.reference_date = nowdate()
|
||||
@ -1191,7 +1176,6 @@ def validate_inclusive_tax(tax, doc):
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_outstanding_reference_documents(args):
|
||||
|
||||
if isinstance(args, str):
|
||||
args = json.loads(args)
|
||||
|
||||
@ -1260,7 +1244,7 @@ def get_outstanding_reference_documents(args):
|
||||
for d in outstanding_invoices:
|
||||
d["exchange_rate"] = 1
|
||||
if party_account_currency != company_currency:
|
||||
if d.voucher_type in ("Sales Invoice", "Purchase Invoice", "Expense Claim"):
|
||||
if d.voucher_type in frappe.get_hooks("invoice_doctypes"):
|
||||
d["exchange_rate"] = frappe.db.get_value(d.voucher_type, d.voucher_no, "conversion_rate")
|
||||
elif d.voucher_type == "Journal Entry":
|
||||
d["exchange_rate"] = get_exchange_rate(
|
||||
@ -1587,20 +1571,17 @@ def get_outstanding_on_journal_entry(name):
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_reference_details(reference_doctype, reference_name, party_account_currency):
|
||||
total_amount = outstanding_amount = exchange_rate = bill_no = None
|
||||
total_amount = outstanding_amount = exchange_rate = None
|
||||
|
||||
ref_doc = frappe.get_doc(reference_doctype, reference_name)
|
||||
company_currency = ref_doc.get("company_currency") or erpnext.get_company_currency(
|
||||
ref_doc.company
|
||||
)
|
||||
|
||||
if reference_doctype == "Fees":
|
||||
total_amount = ref_doc.get("grand_total")
|
||||
if reference_doctype == "Dunning":
|
||||
total_amount = outstanding_amount = ref_doc.get("dunning_amount")
|
||||
exchange_rate = 1
|
||||
outstanding_amount = ref_doc.get("outstanding_amount")
|
||||
elif reference_doctype == "Dunning":
|
||||
total_amount = ref_doc.get("dunning_amount")
|
||||
exchange_rate = 1
|
||||
outstanding_amount = ref_doc.get("dunning_amount")
|
||||
|
||||
elif reference_doctype == "Journal Entry" and ref_doc.docstatus == 1:
|
||||
total_amount = ref_doc.get("total_amount")
|
||||
if ref_doc.multi_currency:
|
||||
@ -1610,16 +1591,8 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre
|
||||
else:
|
||||
exchange_rate = 1
|
||||
outstanding_amount = get_outstanding_on_journal_entry(reference_name)
|
||||
|
||||
elif reference_doctype != "Journal Entry":
|
||||
if ref_doc.doctype == "Expense Claim":
|
||||
total_amount = flt(ref_doc.total_sanctioned_amount) + flt(ref_doc.total_taxes_and_charges)
|
||||
elif ref_doc.doctype == "Employee Advance":
|
||||
total_amount = ref_doc.advance_amount
|
||||
exchange_rate = ref_doc.get("exchange_rate")
|
||||
if party_account_currency != ref_doc.currency:
|
||||
total_amount = flt(total_amount) * flt(exchange_rate)
|
||||
elif ref_doc.doctype == "Gratuity":
|
||||
total_amount = ref_doc.amount
|
||||
if not total_amount:
|
||||
if party_account_currency == company_currency:
|
||||
total_amount = ref_doc.base_grand_total
|
||||
@ -1632,26 +1605,12 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre
|
||||
exchange_rate = ref_doc.get("conversion_rate") or get_exchange_rate(
|
||||
party_account_currency, company_currency, ref_doc.posting_date
|
||||
)
|
||||
|
||||
if reference_doctype in ("Sales Invoice", "Purchase Invoice"):
|
||||
outstanding_amount = ref_doc.get("outstanding_amount")
|
||||
bill_no = ref_doc.get("bill_no")
|
||||
elif reference_doctype == "Expense Claim":
|
||||
outstanding_amount = (
|
||||
flt(ref_doc.get("total_sanctioned_amount"))
|
||||
+ flt(ref_doc.get("total_taxes_and_charges"))
|
||||
- flt(ref_doc.get("total_amount_reimbursed"))
|
||||
- flt(ref_doc.get("total_advance_amount"))
|
||||
)
|
||||
elif reference_doctype == "Employee Advance":
|
||||
outstanding_amount = flt(ref_doc.advance_amount) - flt(ref_doc.paid_amount)
|
||||
if party_account_currency != ref_doc.currency:
|
||||
outstanding_amount = flt(outstanding_amount) * flt(exchange_rate)
|
||||
if party_account_currency == company_currency:
|
||||
exchange_rate = 1
|
||||
elif reference_doctype == "Gratuity":
|
||||
outstanding_amount = ref_doc.amount - flt(ref_doc.paid_amount)
|
||||
else:
|
||||
outstanding_amount = flt(total_amount) - flt(ref_doc.advance_paid)
|
||||
|
||||
else:
|
||||
# Get the exchange rate based on the posting date of the ref doc.
|
||||
exchange_rate = get_exchange_rate(party_account_currency, company_currency, ref_doc.posting_date)
|
||||
@ -1662,114 +1621,11 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre
|
||||
"total_amount": flt(total_amount),
|
||||
"outstanding_amount": flt(outstanding_amount),
|
||||
"exchange_rate": flt(exchange_rate),
|
||||
"bill_no": bill_no,
|
||||
"bill_no": ref_doc.get("bill_no"),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def get_amounts_based_on_reference_doctype(
|
||||
reference_doctype, ref_doc, party_account_currency, company_currency, reference_name
|
||||
):
|
||||
total_amount = outstanding_amount = exchange_rate = None
|
||||
if reference_doctype == "Fees":
|
||||
total_amount = ref_doc.get("grand_total")
|
||||
exchange_rate = 1
|
||||
outstanding_amount = ref_doc.get("outstanding_amount")
|
||||
elif reference_doctype == "Dunning":
|
||||
total_amount = ref_doc.get("dunning_amount")
|
||||
exchange_rate = 1
|
||||
outstanding_amount = ref_doc.get("dunning_amount")
|
||||
elif reference_doctype == "Journal Entry" and ref_doc.docstatus == 1:
|
||||
total_amount = ref_doc.get("total_amount")
|
||||
if ref_doc.multi_currency:
|
||||
exchange_rate = get_exchange_rate(
|
||||
party_account_currency, company_currency, ref_doc.posting_date
|
||||
)
|
||||
else:
|
||||
exchange_rate = 1
|
||||
outstanding_amount = get_outstanding_on_journal_entry(reference_name)
|
||||
|
||||
return total_amount, outstanding_amount, exchange_rate
|
||||
|
||||
|
||||
def get_amounts_based_on_ref_doc(
|
||||
reference_doctype, ref_doc, party_account_currency, company_currency
|
||||
):
|
||||
total_amount = outstanding_amount = exchange_rate = None
|
||||
if ref_doc.doctype == "Expense Claim":
|
||||
total_amount = flt(ref_doc.total_sanctioned_amount) + flt(ref_doc.total_taxes_and_charges)
|
||||
elif ref_doc.doctype == "Employee Advance":
|
||||
total_amount, exchange_rate = get_total_amount_exchange_rate_for_employee_advance(
|
||||
party_account_currency, ref_doc
|
||||
)
|
||||
|
||||
if not total_amount:
|
||||
total_amount, exchange_rate = get_total_amount_exchange_rate_base_on_currency(
|
||||
party_account_currency, company_currency, ref_doc
|
||||
)
|
||||
|
||||
if not exchange_rate:
|
||||
# Get the exchange rate from the original ref doc
|
||||
# or get it based on the posting date of the ref doc
|
||||
exchange_rate = ref_doc.get("conversion_rate") or get_exchange_rate(
|
||||
party_account_currency, company_currency, ref_doc.posting_date
|
||||
)
|
||||
|
||||
outstanding_amount, exchange_rate, bill_no = get_bill_no_and_update_amounts(
|
||||
reference_doctype, ref_doc, total_amount, exchange_rate, party_account_currency, company_currency
|
||||
)
|
||||
|
||||
return total_amount, outstanding_amount, exchange_rate, bill_no
|
||||
|
||||
|
||||
def get_total_amount_exchange_rate_for_employee_advance(party_account_currency, ref_doc):
|
||||
total_amount = ref_doc.advance_amount
|
||||
exchange_rate = ref_doc.get("exchange_rate")
|
||||
if party_account_currency != ref_doc.currency:
|
||||
total_amount = flt(total_amount) * flt(exchange_rate)
|
||||
|
||||
return total_amount, exchange_rate
|
||||
|
||||
|
||||
def get_total_amount_exchange_rate_base_on_currency(
|
||||
party_account_currency, company_currency, ref_doc
|
||||
):
|
||||
exchange_rate = None
|
||||
if party_account_currency == company_currency:
|
||||
total_amount = ref_doc.base_grand_total
|
||||
exchange_rate = 1
|
||||
else:
|
||||
total_amount = ref_doc.grand_total
|
||||
|
||||
return total_amount, exchange_rate
|
||||
|
||||
|
||||
def get_bill_no_and_update_amounts(
|
||||
reference_doctype, ref_doc, total_amount, exchange_rate, party_account_currency, company_currency
|
||||
):
|
||||
outstanding_amount = bill_no = None
|
||||
if reference_doctype in ("Sales Invoice", "Purchase Invoice"):
|
||||
outstanding_amount = ref_doc.get("outstanding_amount")
|
||||
bill_no = ref_doc.get("bill_no")
|
||||
elif reference_doctype == "Expense Claim":
|
||||
outstanding_amount = (
|
||||
flt(ref_doc.get("total_sanctioned_amount"))
|
||||
+ flt(ref_doc.get("total_taxes_and_charges"))
|
||||
- flt(ref_doc.get("total_amount_reimbursed"))
|
||||
- flt(ref_doc.get("total_advance_amount"))
|
||||
)
|
||||
elif reference_doctype == "Employee Advance":
|
||||
outstanding_amount = flt(ref_doc.advance_amount) - flt(ref_doc.paid_amount)
|
||||
if party_account_currency != ref_doc.currency:
|
||||
outstanding_amount = flt(outstanding_amount) * flt(exchange_rate)
|
||||
if party_account_currency == company_currency:
|
||||
exchange_rate = 1
|
||||
else:
|
||||
outstanding_amount = flt(total_amount) - flt(ref_doc.advance_paid)
|
||||
|
||||
return outstanding_amount, exchange_rate, bill_no
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=None):
|
||||
reference_doc = None
|
||||
@ -1888,8 +1744,6 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=
|
||||
pe.set_missing_values()
|
||||
|
||||
if party_account and bank:
|
||||
if dt == "Employee Advance":
|
||||
reference_doc = doc
|
||||
pe.set_exchange_rate(ref_doc=reference_doc)
|
||||
pe.set_amounts()
|
||||
if discount_amount:
|
||||
@ -1924,8 +1778,6 @@ def set_party_type(dt):
|
||||
party_type = "Customer"
|
||||
elif dt in ("Purchase Invoice", "Purchase Order"):
|
||||
party_type = "Supplier"
|
||||
elif dt in ("Expense Claim", "Employee Advance", "Gratuity"):
|
||||
party_type = "Employee"
|
||||
return party_type
|
||||
|
||||
|
||||
@ -1936,12 +1788,6 @@ def set_party_account(dt, dn, doc, party_type):
|
||||
party_account = doc.credit_to
|
||||
elif dt == "Fees":
|
||||
party_account = doc.receivable_account
|
||||
elif dt == "Employee Advance":
|
||||
party_account = doc.advance_account
|
||||
elif dt == "Expense Claim":
|
||||
party_account = doc.payable_account
|
||||
elif dt == "Gratuity":
|
||||
party_account = doc.payable_account
|
||||
else:
|
||||
party_account = get_party_account(party_type, doc.get(party_type.lower()), doc.company)
|
||||
return party_account
|
||||
@ -1976,24 +1822,12 @@ def set_grand_total_and_outstanding_amount(party_amount, dt, party_account_curre
|
||||
else:
|
||||
grand_total = doc.rounded_total or doc.grand_total
|
||||
outstanding_amount = doc.outstanding_amount
|
||||
elif dt in ("Expense Claim"):
|
||||
grand_total = doc.total_sanctioned_amount + doc.total_taxes_and_charges
|
||||
outstanding_amount = doc.grand_total - doc.total_amount_reimbursed
|
||||
elif dt == "Employee Advance":
|
||||
grand_total = flt(doc.advance_amount)
|
||||
outstanding_amount = flt(doc.advance_amount) - flt(doc.paid_amount)
|
||||
if party_account_currency != doc.currency:
|
||||
grand_total = flt(doc.advance_amount) * flt(doc.exchange_rate)
|
||||
outstanding_amount = (flt(doc.advance_amount) - flt(doc.paid_amount)) * flt(doc.exchange_rate)
|
||||
elif dt == "Fees":
|
||||
grand_total = doc.grand_total
|
||||
outstanding_amount = doc.outstanding_amount
|
||||
elif dt == "Dunning":
|
||||
grand_total = doc.grand_total
|
||||
outstanding_amount = doc.grand_total
|
||||
elif dt == "Gratuity":
|
||||
grand_total = doc.amount
|
||||
outstanding_amount = flt(doc.amount) - flt(doc.paid_amount)
|
||||
else:
|
||||
if party_account_currency == doc.company_currency:
|
||||
grand_total = flt(doc.get("base_rounded_total") or doc.base_grand_total)
|
||||
@ -2015,8 +1849,6 @@ def set_paid_amount_and_received_amount(
|
||||
received_amount = bank_amount
|
||||
else:
|
||||
received_amount = paid_amount * doc.get("conversion_rate", 1)
|
||||
if dt == "Employee Advance":
|
||||
received_amount = paid_amount * doc.get("exchange_rate", 1)
|
||||
else:
|
||||
received_amount = abs(outstanding_amount)
|
||||
if bank_amount:
|
||||
@ -2024,8 +1856,6 @@ def set_paid_amount_and_received_amount(
|
||||
else:
|
||||
# if party account currency and bank currency is different then populate paid amount as well
|
||||
paid_amount = received_amount * doc.get("conversion_rate", 1)
|
||||
if dt == "Employee Advance":
|
||||
paid_amount = received_amount * doc.get("exchange_rate", 1)
|
||||
|
||||
return paid_amount, received_amount
|
||||
|
||||
|
@ -19,8 +19,8 @@ from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import (
|
||||
create_sales_invoice,
|
||||
create_sales_invoice_against_cost_center,
|
||||
)
|
||||
from erpnext.hr.doctype.expense_claim.test_expense_claim import make_expense_claim
|
||||
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
|
||||
from erpnext.setup.doctype.employee.test_employee import make_employee
|
||||
|
||||
test_dependencies = ["Item"]
|
||||
|
||||
@ -297,31 +297,6 @@ class TestPaymentEntry(FrappeTestCase):
|
||||
self.assertEqual(flt(outstanding_amount), 250)
|
||||
self.assertEqual(status, "Unpaid")
|
||||
|
||||
def test_payment_entry_against_ec(self):
|
||||
|
||||
payable = frappe.get_cached_value("Company", "_Test Company", "default_payable_account")
|
||||
ec = make_expense_claim(payable, 300, 300, "_Test Company", "Travel Expenses - _TC")
|
||||
pe = get_payment_entry(
|
||||
"Expense Claim", ec.name, bank_account="_Test Bank USD - _TC", bank_amount=300
|
||||
)
|
||||
pe.reference_no = "1"
|
||||
pe.reference_date = "2016-01-01"
|
||||
pe.source_exchange_rate = 1
|
||||
pe.paid_to = payable
|
||||
pe.insert()
|
||||
pe.submit()
|
||||
|
||||
expected_gle = dict(
|
||||
(d[0], d) for d in [[payable, 300, 0, ec.name], ["_Test Bank USD - _TC", 0, 300, None]]
|
||||
)
|
||||
|
||||
self.validate_gl_entries(pe.name, expected_gle)
|
||||
|
||||
outstanding_amount = flt(
|
||||
frappe.db.get_value("Expense Claim", ec.name, "total_sanctioned_amount")
|
||||
) - flt(frappe.db.get_value("Expense Claim", ec.name, "total_amount_reimbursed"))
|
||||
self.assertEqual(outstanding_amount, 0)
|
||||
|
||||
def test_payment_entry_against_si_usd_to_inr(self):
|
||||
si = create_sales_invoice(
|
||||
customer="_Test Customer USD",
|
||||
@ -762,6 +737,10 @@ class TestPaymentEntry(FrappeTestCase):
|
||||
|
||||
self.assertTrue("is on hold" in str(err.exception).lower())
|
||||
|
||||
def test_payment_entry_for_employee(self):
|
||||
employee = make_employee("test_payment_entry@salary.com", company="_Test Company")
|
||||
create_payment_entry(party_type="Employee", party=employee, save=True)
|
||||
|
||||
|
||||
def create_payment_entry(**args):
|
||||
payment_entry = frappe.new_doc("Payment Entry")
|
||||
|
@ -1,38 +0,0 @@
|
||||
{
|
||||
"actions": [],
|
||||
"creation": "2016-07-27 17:24:24.956896",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"company",
|
||||
"account"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "company",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Company",
|
||||
"options": "Company"
|
||||
},
|
||||
{
|
||||
"description": "Default Bank / Cash account will be automatically updated in Salary Journal Entry when this mode is selected.",
|
||||
"fieldname": "account",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Account",
|
||||
"options": "Account"
|
||||
}
|
||||
],
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-10-18 17:57:57.110257",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Salary Component Account",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC"
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
|
||||
from frappe.model.document import Document
|
||||
|
||||
|
||||
class SalaryComponentAccount(Document):
|
||||
pass
|
@ -198,12 +198,10 @@ def get_loan_entries(filters):
|
||||
amount_field = (loan_doc.disbursed_amount).as_("credit")
|
||||
posting_date = (loan_doc.disbursement_date).as_("posting_date")
|
||||
account = loan_doc.disbursement_account
|
||||
salary_condition = loan_doc.docstatus == 1
|
||||
else:
|
||||
amount_field = (loan_doc.amount_paid).as_("debit")
|
||||
posting_date = (loan_doc.posting_date).as_("posting_date")
|
||||
account = loan_doc.payment_account
|
||||
salary_condition = loan_doc.repay_from_salary == 0
|
||||
|
||||
query = (
|
||||
frappe.qb.from_(loan_doc)
|
||||
@ -216,12 +214,14 @@ def get_loan_entries(filters):
|
||||
posting_date,
|
||||
)
|
||||
.where(loan_doc.docstatus == 1)
|
||||
.where(salary_condition)
|
||||
.where(account == filters.get("account"))
|
||||
.where(posting_date <= getdate(filters.get("report_date")))
|
||||
.where(ifnull(loan_doc.clearance_date, "4000-01-01") > getdate(filters.get("report_date")))
|
||||
)
|
||||
|
||||
if doctype == "Loan Repayment" and frappe.db.has_column("Loan Repayment", "repay_from_salary"):
|
||||
query = query.where((loan_doc.repay_from_salary == 0))
|
||||
|
||||
entries = query.run(as_dict=1)
|
||||
loan_docs.extend(entries)
|
||||
|
||||
@ -267,23 +267,24 @@ def get_loan_amount(filters):
|
||||
amount_field = Sum(loan_doc.disbursed_amount)
|
||||
posting_date = (loan_doc.disbursement_date).as_("posting_date")
|
||||
account = loan_doc.disbursement_account
|
||||
salary_condition = loan_doc.docstatus == 1
|
||||
else:
|
||||
amount_field = Sum(loan_doc.amount_paid)
|
||||
posting_date = (loan_doc.posting_date).as_("posting_date")
|
||||
account = loan_doc.payment_account
|
||||
salary_condition = loan_doc.repay_from_salary == 0
|
||||
amount = (
|
||||
|
||||
query = (
|
||||
frappe.qb.from_(loan_doc)
|
||||
.select(amount_field)
|
||||
.where(loan_doc.docstatus == 1)
|
||||
.where(salary_condition)
|
||||
.where(account == filters.get("account"))
|
||||
.where(posting_date > getdate(filters.get("report_date")))
|
||||
.where(ifnull(loan_doc.clearance_date, "4000-01-01") <= getdate(filters.get("report_date")))
|
||||
.run()[0][0]
|
||||
)
|
||||
|
||||
if doctype == "Loan Repayment" and frappe.db.has_column("Loan Repayment", "repay_from_salary"):
|
||||
query = query.where((loan_doc.repay_from_salary == 0))
|
||||
|
||||
amount = query.run()[0][0]
|
||||
total_amount += flt(amount)
|
||||
|
||||
return total_amount
|
||||
|
@ -0,0 +1,40 @@
|
||||
# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
|
||||
import frappe
|
||||
from frappe.tests.utils import FrappeTestCase
|
||||
|
||||
from erpnext.accounts.doctype.bank_transaction.test_bank_transaction import (
|
||||
create_loan_and_repayment,
|
||||
)
|
||||
from erpnext.accounts.report.bank_reconciliation_statement.bank_reconciliation_statement import (
|
||||
execute,
|
||||
)
|
||||
from erpnext.loan_management.doctype.loan.test_loan import create_loan_accounts
|
||||
|
||||
|
||||
class TestBankReconciliationStatement(FrappeTestCase):
|
||||
def setUp(self):
|
||||
for dt in [
|
||||
"Loan Repayment",
|
||||
"Loan Disbursement",
|
||||
"Journal Entry",
|
||||
"Journal Entry Account",
|
||||
"Payment Entry",
|
||||
]:
|
||||
frappe.db.delete(dt)
|
||||
|
||||
def test_loan_entries_in_bank_reco_statement(self):
|
||||
create_loan_accounts()
|
||||
repayment_entry = create_loan_and_repayment()
|
||||
|
||||
filters = frappe._dict(
|
||||
{
|
||||
"company": "Test Company",
|
||||
"account": "Payment Account - _TC",
|
||||
"report_date": "2018-10-30",
|
||||
}
|
||||
)
|
||||
result = execute(filters)
|
||||
|
||||
self.assertEqual(result[1][0].payment_entry, repayment_entry.name)
|
@ -1,13 +0,0 @@
|
||||
// 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",
|
||||
"options": "Employee"
|
||||
}
|
||||
]
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
{
|
||||
"add_total_row": 0,
|
||||
"apply_user_permissions": 1,
|
||||
"creation": "2017-01-04 16:26:18.309717",
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"idx": 2,
|
||||
"is_standard": "Yes",
|
||||
"modified": "2017-02-24 19:59:29.747039",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Unpaid Expense Claim",
|
||||
"owner": "Administrator",
|
||||
"ref_doctype": "Expense Claim",
|
||||
"report_name": "Unpaid Expense Claim",
|
||||
"report_type": "Script Report",
|
||||
"roles": [
|
||||
{
|
||||
"role": "HR Manager"
|
||||
},
|
||||
{
|
||||
"role": "Expense Approver"
|
||||
},
|
||||
{
|
||||
"role": "HR User"
|
||||
}
|
||||
]
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
|
||||
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,
|
||||
)
|
@ -420,7 +420,7 @@ def add_cc(args=None):
|
||||
return cc.name
|
||||
|
||||
|
||||
def reconcile_against_document(args):
|
||||
def reconcile_against_document(args): # nosemgrep
|
||||
"""
|
||||
Cancel PE or JV, Update against document, split if required and resubmit
|
||||
"""
|
||||
@ -461,7 +461,8 @@ def reconcile_against_document(args):
|
||||
frappe.flags.ignore_party_validation = False
|
||||
|
||||
if entry.voucher_type in ("Payment Entry", "Journal Entry"):
|
||||
doc.update_expense_claim()
|
||||
if hasattr(doc, "update_expense_claim"):
|
||||
doc.update_expense_claim()
|
||||
|
||||
|
||||
def check_if_advance_entry_modified(args):
|
||||
|
@ -7,7 +7,7 @@ import frappe
|
||||
from frappe.utils import now
|
||||
|
||||
from erpnext.assets.doctype.asset.test_asset import create_asset_data
|
||||
from erpnext.hr.doctype.employee.test_employee import make_employee
|
||||
from erpnext.setup.doctype.employee.test_employee import make_employee
|
||||
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
|
||||
|
||||
|
||||
|
@ -7,8 +7,8 @@ from frappe.desk.form import assign_to
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import add_days, flt, unique
|
||||
|
||||
from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee
|
||||
from erpnext.hr.doctype.holiday_list.holiday_list import is_holiday
|
||||
from erpnext.setup.doctype.employee.employee import get_holiday_list_for_employee
|
||||
from erpnext.setup.doctype.holiday_list.holiday_list import is_holiday
|
||||
|
||||
|
||||
class EmployeeBoardingController(Document):
|
||||
|
@ -80,13 +80,14 @@ calendars = [
|
||||
"Holiday List",
|
||||
]
|
||||
|
||||
website_generators = ["Item Group", "Website Item", "BOM", "Sales Partner", "Job Opening"]
|
||||
website_generators = ["Item Group", "Website Item", "BOM", "Sales Partner"]
|
||||
|
||||
website_context = {
|
||||
"favicon": "/assets/erpnext/images/erpnext-favicon.svg",
|
||||
"splash_image": "/assets/erpnext/images/erpnext-logo.svg",
|
||||
}
|
||||
|
||||
# nosemgrep
|
||||
website_route_rules = [
|
||||
{"from_route": "/orders", "to_route": "Sales Order"},
|
||||
{
|
||||
@ -163,7 +164,6 @@ website_route_rules = [
|
||||
"to_route": "addresses",
|
||||
"defaults": {"doctype": "Address", "parents": [{"label": _("Addresses"), "route": "addresses"}]},
|
||||
},
|
||||
{"from_route": "/jobs", "to_route": "Job Opening"},
|
||||
{"from_route": "/boms", "to_route": "BOM"},
|
||||
{"from_route": "/timesheets", "to_route": "Timesheet"},
|
||||
{"from_route": "/material-requests", "to_route": "Material Request"},
|
||||
@ -256,7 +256,9 @@ sounds = [
|
||||
{"name": "call-disconnect", "src": "/assets/erpnext/sounds/call-disconnect.mp3", "volume": 0.2},
|
||||
]
|
||||
|
||||
has_upload_permission = {"Employee": "erpnext.hr.doctype.employee.employee.has_upload_permission"}
|
||||
has_upload_permission = {
|
||||
"Employee": "erpnext.setup.doctype.employee.employee.has_upload_permission"
|
||||
}
|
||||
|
||||
has_website_permission = {
|
||||
"Sales Order": "erpnext.controllers.website_list_for_contact.has_website_permission",
|
||||
@ -289,9 +291,9 @@ doc_events = {
|
||||
},
|
||||
"User": {
|
||||
"after_insert": "frappe.contacts.doctype.contact.contact.update_contact",
|
||||
"validate": "erpnext.hr.doctype.employee.employee.validate_employee_role",
|
||||
"validate": "erpnext.setup.doctype.employee.employee.validate_employee_role",
|
||||
"on_update": [
|
||||
"erpnext.hr.doctype.employee.employee.update_user_permissions",
|
||||
"erpnext.setup.doctype.employee.employee.update_user_permissions",
|
||||
"erpnext.portal.utils.set_default_role",
|
||||
],
|
||||
},
|
||||
@ -366,8 +368,6 @@ auto_cancel_exempted_doctypes = [
|
||||
"Payment Entry",
|
||||
]
|
||||
|
||||
after_migrate = ["erpnext.setup.install.update_select_perm_after_install"]
|
||||
|
||||
scheduler_events = {
|
||||
"cron": {
|
||||
"0/5 * * * *": [
|
||||
@ -387,16 +387,13 @@ scheduler_events = {
|
||||
},
|
||||
"all": [
|
||||
"erpnext.projects.doctype.project.project.project_status_update_reminder",
|
||||
"erpnext.hr.doctype.interview.interview.send_interview_reminder",
|
||||
"erpnext.crm.doctype.social_media_post.social_media_post.process_scheduled_social_media_posts",
|
||||
],
|
||||
"hourly": [
|
||||
"erpnext.hr.doctype.daily_work_summary_group.daily_work_summary_group.trigger_emails",
|
||||
"erpnext.accounts.doctype.subscription.subscription.process_all",
|
||||
"erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.automatic_synchronization",
|
||||
"erpnext.projects.doctype.project.project.hourly_reminder",
|
||||
"erpnext.projects.doctype.project.project.collect_project_status",
|
||||
"erpnext.hr.doctype.shift_type.shift_type.process_auto_attendance_for_all_shifts",
|
||||
],
|
||||
"hourly_long": [
|
||||
"erpnext.stock.doctype.repost_item_valuation.repost_item_valuation.repost_entries",
|
||||
@ -407,11 +404,8 @@ scheduler_events = {
|
||||
"erpnext.crm.doctype.opportunity.opportunity.auto_close_opportunity",
|
||||
"erpnext.controllers.accounts_controller.update_invoice_status",
|
||||
"erpnext.accounts.doctype.fiscal_year.fiscal_year.auto_create_fiscal_year",
|
||||
"erpnext.hr.doctype.employee.employee_reminders.send_work_anniversary_reminders",
|
||||
"erpnext.hr.doctype.employee.employee_reminders.send_birthday_reminders",
|
||||
"erpnext.projects.doctype.task.task.set_tasks_as_overdue",
|
||||
"erpnext.assets.doctype.asset.depreciation.post_depreciation_entries",
|
||||
"erpnext.hr.doctype.daily_work_summary_group.daily_work_summary_group.send_summary",
|
||||
"erpnext.stock.doctype.serial_no.serial_no.update_maintenance_status",
|
||||
"erpnext.buying.doctype.supplier_scorecard.supplier_scorecard.refresh_scorecards",
|
||||
"erpnext.setup.doctype.company.company.cache_companies_monthly_sales_history",
|
||||
@ -427,20 +421,14 @@ scheduler_events = {
|
||||
"erpnext.selling.doctype.quotation.quotation.set_expired_status",
|
||||
"erpnext.buying.doctype.supplier_quotation.supplier_quotation.set_expired_status",
|
||||
"erpnext.accounts.doctype.process_statement_of_accounts.process_statement_of_accounts.send_auto_email",
|
||||
"erpnext.hr.doctype.interview.interview.send_daily_feedback_reminder",
|
||||
],
|
||||
"daily_long": [
|
||||
"erpnext.setup.doctype.email_digest.email_digest.send",
|
||||
"erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool.auto_update_latest_price_in_all_boms",
|
||||
"erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry.process_expired_allocation",
|
||||
"erpnext.hr.utils.generate_leave_encashment",
|
||||
"erpnext.hr.utils.allocate_earned_leaves",
|
||||
"erpnext.loan_management.doctype.process_loan_security_shortfall.process_loan_security_shortfall.create_process_loan_security_shortfall",
|
||||
"erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual.process_loan_interest_accrual_for_term_loans",
|
||||
"erpnext.crm.utils.open_leads_opportunities_based_on_todays_event",
|
||||
],
|
||||
"weekly": ["erpnext.hr.doctype.employee.employee_reminders.send_reminders_in_advance_weekly"],
|
||||
"monthly": ["erpnext.hr.doctype.employee.employee_reminders.send_reminders_in_advance_monthly"],
|
||||
"monthly_long": [
|
||||
"erpnext.accounts.deferred_revenue.process_deferred_accounting",
|
||||
"erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual.process_loan_interest_accrual_for_demand_loans",
|
||||
@ -472,6 +460,28 @@ payment_gateway_enabled = "erpnext.accounts.utils.create_payment_gateway_account
|
||||
|
||||
communication_doctypes = ["Customer", "Supplier"]
|
||||
|
||||
advance_payment_doctypes = ["Sales Order", "Purchase Order"]
|
||||
|
||||
invoice_doctypes = ["Sales Invoice", "Purchase Invoice"]
|
||||
|
||||
period_closing_doctypes = [
|
||||
"Sales Invoice",
|
||||
"Purchase Invoice",
|
||||
"Journal Entry",
|
||||
"Bank Clearance",
|
||||
"Asset",
|
||||
"Stock Entry",
|
||||
]
|
||||
|
||||
bank_reconciliation_doctypes = [
|
||||
"Payment Entry",
|
||||
"Journal Entry",
|
||||
"Purchase Invoice",
|
||||
"Sales Invoice",
|
||||
"Loan Repayment",
|
||||
"Loan Disbursement",
|
||||
]
|
||||
|
||||
accounting_dimension_doctypes = [
|
||||
"GL Entry",
|
||||
"Payment Ledger Entry",
|
||||
@ -479,12 +489,8 @@ accounting_dimension_doctypes = [
|
||||
"Purchase Invoice",
|
||||
"Payment Entry",
|
||||
"Asset",
|
||||
"Expense Claim",
|
||||
"Expense Claim Detail",
|
||||
"Expense Taxes and Charges",
|
||||
"Stock Entry",
|
||||
"Budget",
|
||||
"Payroll Entry",
|
||||
"Delivery Note",
|
||||
"Sales Invoice Item",
|
||||
"Purchase Invoice Item",
|
||||
@ -502,7 +508,6 @@ accounting_dimension_doctypes = [
|
||||
"Asset Value Adjustment",
|
||||
"Loyalty Program",
|
||||
"Stock Reconciliation",
|
||||
"Travel Request",
|
||||
"POS Profile",
|
||||
"Opening Invoice Creation Tool",
|
||||
"Opening Invoice Creation Tool Item",
|
||||
@ -515,14 +520,15 @@ accounting_dimension_doctypes = [
|
||||
"Sales Order",
|
||||
]
|
||||
|
||||
# get matching queries for Bank Reconciliation
|
||||
get_matching_queries = (
|
||||
"erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.get_matching_queries"
|
||||
)
|
||||
|
||||
regional_overrides = {
|
||||
"France": {
|
||||
"erpnext.tests.test_regional.test_method": "erpnext.regional.france.utils.test_method"
|
||||
},
|
||||
"India": {
|
||||
"erpnext.hr.utils.calculate_annual_eligible_hra_exemption": "erpnext.regional.india.utils.calculate_annual_eligible_hra_exemption",
|
||||
"erpnext.hr.utils.calculate_hra_exemption_for_period": "erpnext.regional.india.utils.calculate_hra_exemption_for_period",
|
||||
},
|
||||
"United Arab Emirates": {
|
||||
"erpnext.controllers.taxes_and_totals.update_itemised_tax_data": "erpnext.regional.united_arab_emirates.utils.update_itemised_tax_data",
|
||||
"erpnext.accounts.doctype.purchase_invoice.purchase_invoice.make_regional_gl_entries": "erpnext.regional.united_arab_emirates.utils.make_regional_gl_entries",
|
||||
@ -570,9 +576,6 @@ global_search_doctypes = {
|
||||
{"doctype": "Material Request", "index": 16},
|
||||
{"doctype": "Delivery Trip", "index": 17},
|
||||
{"doctype": "Pick List", "index": 18},
|
||||
{"doctype": "Salary Slip", "index": 19},
|
||||
{"doctype": "Leave Application", "index": 20},
|
||||
{"doctype": "Expense Claim", "index": 21},
|
||||
{"doctype": "Payment Entry", "index": 22},
|
||||
{"doctype": "Lead", "index": 23},
|
||||
{"doctype": "Opportunity", "index": 24},
|
||||
@ -588,13 +591,7 @@ global_search_doctypes = {
|
||||
{"doctype": "Batch", "index": 34},
|
||||
{"doctype": "Branch", "index": 35},
|
||||
{"doctype": "Department", "index": 36},
|
||||
{"doctype": "Employee Grade", "index": 37},
|
||||
{"doctype": "Designation", "index": 38},
|
||||
{"doctype": "Job Opening", "index": 39},
|
||||
{"doctype": "Job Applicant", "index": 40},
|
||||
{"doctype": "Job Offer", "index": 41},
|
||||
{"doctype": "Salary Structure Assignment", "index": 42},
|
||||
{"doctype": "Appraisal", "index": 43},
|
||||
{"doctype": "Loan", "index": 44},
|
||||
{"doctype": "Maintenance Schedule", "index": 45},
|
||||
{"doctype": "Maintenance Visit", "index": 46},
|
||||
|
@ -1,6 +0,0 @@
|
||||
Key features:
|
||||
|
||||
- Leave and Attendance
|
||||
- Payroll
|
||||
- Appraisal
|
||||
- Expense Claim
|
@ -1,27 +0,0 @@
|
||||
{
|
||||
"chart_name": "Attendance Count",
|
||||
"chart_type": "Report",
|
||||
"creation": "2020-07-22 11:56:32.730068",
|
||||
"custom_options": "{\n\t\t\"type\": \"line\",\n\t\t\"axisOptions\": {\n\t\t\t\"shortenYAxisNumbers\": 1\n\t\t},\n\t\t\"tooltipOptions\": {}\n\t}",
|
||||
"docstatus": 0,
|
||||
"doctype": "Dashboard Chart",
|
||||
"dynamic_filters_json": "{\"month\":\"frappe.datetime.str_to_obj(frappe.datetime.get_today()).getMonth() + 1\",\"year\":\"frappe.datetime.str_to_obj(frappe.datetime.get_today()).getFullYear();\",\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\"}",
|
||||
"filters_json": "{}",
|
||||
"group_by_type": "Count",
|
||||
"idx": 0,
|
||||
"is_public": 1,
|
||||
"is_standard": 1,
|
||||
"modified": "2020-07-22 14:32:40.334424",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Attendance Count",
|
||||
"number_of_groups": 0,
|
||||
"owner": "Administrator",
|
||||
"report_name": "Monthly Attendance Sheet",
|
||||
"time_interval": "Yearly",
|
||||
"timeseries": 0,
|
||||
"timespan": "Last Year",
|
||||
"type": "Line",
|
||||
"use_report_chart": 1,
|
||||
"y_axis": []
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
{
|
||||
"chart_name": "Department Wise Employee Count",
|
||||
"chart_type": "Group By",
|
||||
"creation": "2020-07-22 11:56:32.760730",
|
||||
"custom_options": "",
|
||||
"docstatus": 0,
|
||||
"doctype": "Dashboard Chart",
|
||||
"document_type": "Employee",
|
||||
"dynamic_filters_json": "[[\"Employee\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
|
||||
"filters_json": "[[\"Employee\",\"status\",\"=\",\"Active\",false]]",
|
||||
"group_by_based_on": "department",
|
||||
"group_by_type": "Count",
|
||||
"idx": 0,
|
||||
"is_public": 1,
|
||||
"is_standard": 1,
|
||||
"last_synced_on": "2020-07-22 14:27:40.574194",
|
||||
"modified": "2020-07-22 14:33:38.036794",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Department Wise Employee Count",
|
||||
"number_of_groups": 0,
|
||||
"owner": "Administrator",
|
||||
"time_interval": "Yearly",
|
||||
"timeseries": 0,
|
||||
"timespan": "Last Year",
|
||||
"type": "Donut",
|
||||
"use_report_chart": 0,
|
||||
"y_axis": []
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
{
|
||||
"aggregate_function_based_on": "planned_vacancies",
|
||||
"chart_name": "Department Wise Openings",
|
||||
"chart_type": "Group By",
|
||||
"creation": "2020-07-22 11:56:32.849775",
|
||||
"custom_options": "",
|
||||
"docstatus": 0,
|
||||
"doctype": "Dashboard Chart",
|
||||
"document_type": "Job Opening",
|
||||
"filters_json": "[]",
|
||||
"group_by_based_on": "department",
|
||||
"group_by_type": "Sum",
|
||||
"idx": 0,
|
||||
"is_public": 1,
|
||||
"is_standard": 1,
|
||||
"last_synced_on": "2020-07-22 14:33:44.834801",
|
||||
"modified": "2020-07-22 14:34:45.273591",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Department Wise Openings",
|
||||
"number_of_groups": 0,
|
||||
"owner": "Administrator",
|
||||
"time_interval": "Monthly",
|
||||
"timeseries": 0,
|
||||
"timespan": "Last Year",
|
||||
"type": "Bar",
|
||||
"use_report_chart": 0,
|
||||
"y_axis": []
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
{
|
||||
"chart_name": "Designation Wise Employee Count",
|
||||
"chart_type": "Group By",
|
||||
"creation": "2020-07-22 11:56:32.790337",
|
||||
"custom_options": "",
|
||||
"docstatus": 0,
|
||||
"doctype": "Dashboard Chart",
|
||||
"document_type": "Employee",
|
||||
"dynamic_filters_json": "[[\"Employee\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
|
||||
"filters_json": "[[\"Employee\",\"status\",\"=\",\"Active\",false]]",
|
||||
"group_by_based_on": "designation",
|
||||
"group_by_type": "Count",
|
||||
"idx": 0,
|
||||
"is_public": 1,
|
||||
"is_standard": 1,
|
||||
"last_synced_on": "2020-07-22 14:27:40.602783",
|
||||
"modified": "2020-07-22 14:31:49.665555",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Designation Wise Employee Count",
|
||||
"number_of_groups": 0,
|
||||
"owner": "Administrator",
|
||||
"time_interval": "Yearly",
|
||||
"timeseries": 0,
|
||||
"timespan": "Last Year",
|
||||
"type": "Donut",
|
||||
"use_report_chart": 0,
|
||||
"y_axis": []
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
{
|
||||
"aggregate_function_based_on": "planned_vacancies",
|
||||
"chart_name": "Designation Wise Openings",
|
||||
"chart_type": "Group By",
|
||||
"creation": "2020-07-22 11:56:32.820217",
|
||||
"custom_options": "",
|
||||
"docstatus": 0,
|
||||
"doctype": "Dashboard Chart",
|
||||
"document_type": "Job Opening",
|
||||
"dynamic_filters_json": "",
|
||||
"filters_json": "[]",
|
||||
"group_by_based_on": "designation",
|
||||
"group_by_type": "Sum",
|
||||
"idx": 0,
|
||||
"is_public": 1,
|
||||
"is_standard": 1,
|
||||
"last_synced_on": "2020-07-22 14:33:44.806626",
|
||||
"modified": "2020-07-22 14:34:32.711881",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Designation Wise Openings",
|
||||
"number_of_groups": 0,
|
||||
"owner": "Administrator",
|
||||
"time_interval": "Monthly",
|
||||
"timeseries": 0,
|
||||
"timespan": "Last Year",
|
||||
"type": "Bar",
|
||||
"use_report_chart": 0,
|
||||
"y_axis": []
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
{
|
||||
"chart_name": "Gender Diversity Ratio",
|
||||
"chart_type": "Group By",
|
||||
"creation": "2020-07-22 11:56:32.667291",
|
||||
"custom_options": "",
|
||||
"docstatus": 0,
|
||||
"doctype": "Dashboard Chart",
|
||||
"document_type": "Employee",
|
||||
"dynamic_filters_json": "[[\"Employee\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
|
||||
"filters_json": "[[\"Employee\",\"status\",\"=\",\"Active\",false]]",
|
||||
"group_by_based_on": "gender",
|
||||
"group_by_type": "Count",
|
||||
"idx": 0,
|
||||
"is_public": 1,
|
||||
"is_standard": 1,
|
||||
"last_synced_on": "2020-07-22 14:27:40.143783",
|
||||
"modified": "2020-07-22 14:32:50.962459",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Gender Diversity Ratio",
|
||||
"number_of_groups": 0,
|
||||
"owner": "Administrator",
|
||||
"time_interval": "Yearly",
|
||||
"timeseries": 0,
|
||||
"timespan": "Last Year",
|
||||
"type": "Pie",
|
||||
"use_report_chart": 0,
|
||||
"y_axis": []
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
{
|
||||
"chart_name": "Job Application Status",
|
||||
"chart_type": "Group By",
|
||||
"creation": "2020-07-22 11:56:32.699696",
|
||||
"custom_options": "",
|
||||
"docstatus": 0,
|
||||
"doctype": "Dashboard Chart",
|
||||
"document_type": "Job Applicant",
|
||||
"dynamic_filters_json": "",
|
||||
"filters_json": "[[\"Job Applicant\",\"creation\",\"Timespan\",\"last month\",false]]",
|
||||
"group_by_based_on": "status",
|
||||
"group_by_type": "Count",
|
||||
"idx": 0,
|
||||
"is_public": 1,
|
||||
"is_standard": 1,
|
||||
"last_synced_on": "2020-07-28 16:19:12.109979",
|
||||
"modified": "2020-07-28 16:19:45.279490",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Job Application Status",
|
||||
"number_of_groups": 0,
|
||||
"owner": "Administrator",
|
||||
"time_interval": "Yearly",
|
||||
"timeseries": 0,
|
||||
"timespan": "Last Year",
|
||||
"type": "Pie",
|
||||
"use_report_chart": 0,
|
||||
"y_axis": []
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Appointment Letter', {
|
||||
appointment_letter_template: function(frm){
|
||||
if (frm.doc.appointment_letter_template){
|
||||
frappe.call({
|
||||
method: 'erpnext.hr.doctype.appointment_letter.appointment_letter.get_appointment_letter_details',
|
||||
args : {
|
||||
template : frm.doc.appointment_letter_template
|
||||
},
|
||||
callback: function(r){
|
||||
if(r.message){
|
||||
let message_body = r.message;
|
||||
frm.set_value("introduction", message_body[0].introduction);
|
||||
frm.set_value("closing_notes", message_body[0].closing_notes);
|
||||
frm.doc.terms = []
|
||||
for (var i in message_body[1].description){
|
||||
frm.add_child("terms");
|
||||
frm.fields_dict.terms.get_value()[i].title = message_body[1].description[i].title;
|
||||
frm.fields_dict.terms.get_value()[i].description = message_body[1].description[i].description;
|
||||
}
|
||||
frm.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
@ -1,128 +0,0 @@
|
||||
{
|
||||
"actions": [],
|
||||
"autoname": "HR-APP-LETTER-.#####",
|
||||
"creation": "2019-12-26 12:35:49.574828",
|
||||
"default_print_format": "Standard Appointment Letter",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"job_applicant",
|
||||
"applicant_name",
|
||||
"column_break_3",
|
||||
"company",
|
||||
"appointment_date",
|
||||
"appointment_letter_template",
|
||||
"body_section",
|
||||
"introduction",
|
||||
"terms",
|
||||
"closing_notes"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fetch_from": "job_applicant.applicant_name",
|
||||
"fieldname": "applicant_name",
|
||||
"fieldtype": "Data",
|
||||
"in_global_search": 1,
|
||||
"in_list_view": 1,
|
||||
"label": "Applicant Name",
|
||||
"read_only": 1,
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "appointment_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Appointment Date",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "appointment_letter_template",
|
||||
"fieldtype": "Link",
|
||||
"label": "Appointment Letter Template",
|
||||
"options": "Appointment Letter Template",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fetch_from": "appointment_letter_template.introduction",
|
||||
"fieldname": "introduction",
|
||||
"fieldtype": "Long Text",
|
||||
"label": "Introduction",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "body_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Body"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_3",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "job_applicant",
|
||||
"fieldtype": "Link",
|
||||
"label": "Job Applicant",
|
||||
"options": "Job Applicant",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "company",
|
||||
"fieldtype": "Link",
|
||||
"label": "Company",
|
||||
"options": "Company",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "closing_notes",
|
||||
"fieldtype": "Text",
|
||||
"label": "Closing Notes"
|
||||
},
|
||||
{
|
||||
"fieldname": "terms",
|
||||
"fieldtype": "Table",
|
||||
"label": "Terms",
|
||||
"options": "Appointment Letter content",
|
||||
"reqd": 1
|
||||
}
|
||||
],
|
||||
"links": [],
|
||||
"modified": "2022-01-18 19:27:35.649424",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Appointment Letter",
|
||||
"name_case": "Title Case",
|
||||
"naming_rule": "Expression (old style)",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "HR Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"search_fields": "applicant_name, company",
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"states": [],
|
||||
"title_field": "applicant_name",
|
||||
"track_changes": 1
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
|
||||
class AppointmentLetter(Document):
|
||||
pass
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_appointment_letter_details(template):
|
||||
body = []
|
||||
intro = frappe.get_list(
|
||||
"Appointment Letter Template",
|
||||
fields=["introduction", "closing_notes"],
|
||||
filters={"name": template},
|
||||
)[0]
|
||||
content = frappe.get_all(
|
||||
"Appointment Letter content",
|
||||
fields=["title", "description"],
|
||||
filters={"parent": template},
|
||||
order_by="idx",
|
||||
)
|
||||
body.append(intro)
|
||||
body.append({"description": content})
|
||||
return body
|
@ -1,9 +0,0 @@
|
||||
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
|
||||
# import frappe
|
||||
import unittest
|
||||
|
||||
|
||||
class TestAppointmentLetter(unittest.TestCase):
|
||||
pass
|
@ -1,39 +0,0 @@
|
||||
{
|
||||
"actions": [],
|
||||
"creation": "2019-12-26 12:22:16.575767",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"title",
|
||||
"description"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "title",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
"label": "Title",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "description",
|
||||
"fieldtype": "Long Text",
|
||||
"in_list_view": 1,
|
||||
"label": "Description",
|
||||
"reqd": 1
|
||||
}
|
||||
],
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2019-12-26 12:24:09.824084",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Appointment Letter content",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
|
||||
# import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
|
||||
class AppointmentLettercontent(Document):
|
||||
pass
|
@ -1,8 +0,0 @@
|
||||
// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Appointment Letter Template', {
|
||||
// refresh: function(frm) {
|
||||
|
||||
// }
|
||||
});
|
@ -1,81 +0,0 @@
|
||||
{
|
||||
"actions": [],
|
||||
"autoname": "field:template_name",
|
||||
"creation": "2019-12-26 12:20:14.219578",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"template_name",
|
||||
"introduction",
|
||||
"terms",
|
||||
"closing_notes"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "introduction",
|
||||
"fieldtype": "Long Text",
|
||||
"in_list_view": 1,
|
||||
"label": "Introduction",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "closing_notes",
|
||||
"fieldtype": "Text",
|
||||
"label": "Closing Notes"
|
||||
},
|
||||
{
|
||||
"fieldname": "terms",
|
||||
"fieldtype": "Table",
|
||||
"label": "Terms",
|
||||
"options": "Appointment Letter content",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "template_name",
|
||||
"fieldtype": "Data",
|
||||
"label": "Template Name",
|
||||
"reqd": 1,
|
||||
"unique": 1
|
||||
}
|
||||
],
|
||||
"links": [],
|
||||
"modified": "2022-01-18 19:25:14.614616",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Appointment Letter Template",
|
||||
"naming_rule": "By fieldname",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "HR Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"search_fields": "template_name",
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"states": [],
|
||||
"title_field": "template_name",
|
||||
"track_changes": 1
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
|
||||
# import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
|
||||
class AppointmentLetterTemplate(Document):
|
||||
pass
|
@ -1,9 +0,0 @@
|
||||
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
|
||||
# import frappe
|
||||
import unittest
|
||||
|
||||
|
||||
class TestAppointmentLetterTemplate(unittest.TestCase):
|
||||
pass
|
@ -1 +0,0 @@
|
||||
Performance of an Employee in a Time Period against given goals.
|
@ -1,79 +0,0 @@
|
||||
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
// License: GNU General Public License v3. See license.txt
|
||||
|
||||
frappe.ui.form.on('Appraisal', {
|
||||
setup: function(frm) {
|
||||
frm.add_fetch('employee', 'company', 'company');
|
||||
frm.add_fetch('employee', 'employee_name', 'employee_name');
|
||||
frm.fields_dict.employee.get_query = function(doc,cdt,cdn) {
|
||||
return{ query: "erpnext.controllers.queries.employee_query" }
|
||||
};
|
||||
},
|
||||
|
||||
onload: function(frm) {
|
||||
if(!frm.doc.status) {
|
||||
frm.set_value('status', 'Draft');
|
||||
}
|
||||
},
|
||||
|
||||
kra_template: function(frm) {
|
||||
frm.doc.goals = [];
|
||||
erpnext.utils.map_current_doc({
|
||||
method: "erpnext.hr.doctype.appraisal.appraisal.fetch_appraisal_template",
|
||||
source_name: frm.doc.kra_template,
|
||||
frm: frm
|
||||
});
|
||||
},
|
||||
|
||||
calculate_total: function(frm) {
|
||||
let goals = frm.doc.goals || [];
|
||||
let total = 0;
|
||||
|
||||
if (goals == []) {
|
||||
frm.set_value('total_score', 0);
|
||||
return;
|
||||
}
|
||||
for (let i = 0; i<goals.length; i++) {
|
||||
total = flt(total)+flt(goals[i].score_earned)
|
||||
}
|
||||
if (!isNaN(total)) {
|
||||
frm.set_value('total_score', total);
|
||||
frm.refresh_field('calculate_total');
|
||||
}
|
||||
},
|
||||
|
||||
set_score_earned: function(frm) {
|
||||
let goals = frm.doc.goals || [];
|
||||
for (let i = 0; i<goals.length; i++) {
|
||||
var d = locals[goals[i].doctype][goals[i].name];
|
||||
if (d.score && d.per_weightage) {
|
||||
d.score_earned = flt(d.per_weightage*d.score, precision("score_earned", d))/100;
|
||||
}
|
||||
else {
|
||||
d.score_earned = 0;
|
||||
}
|
||||
refresh_field('score_earned', d.name, 'goals');
|
||||
}
|
||||
frm.trigger('calculate_total');
|
||||
}
|
||||
});
|
||||
|
||||
frappe.ui.form.on('Appraisal Goal', {
|
||||
score: function(frm, cdt, cdn) {
|
||||
var d = locals[cdt][cdn];
|
||||
if (flt(d.score) > 5) {
|
||||
frappe.msgprint(__("Score must be less than or equal to 5"));
|
||||
d.score = 0;
|
||||
refresh_field('score', d.name, 'goals');
|
||||
}
|
||||
else {
|
||||
frm.trigger('set_score_earned');
|
||||
}
|
||||
},
|
||||
per_weightage: function(frm) {
|
||||
frm.trigger('set_score_earned');
|
||||
},
|
||||
goals_remove: function(frm) {
|
||||
frm.trigger('set_score_earned');
|
||||
}
|
||||
});
|
@ -1,254 +0,0 @@
|
||||
{
|
||||
"actions": [],
|
||||
"autoname": "naming_series:",
|
||||
"creation": "2013-01-10 16:34:12",
|
||||
"doctype": "DocType",
|
||||
"document_type": "Setup",
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"employee_details",
|
||||
"naming_series",
|
||||
"kra_template",
|
||||
"employee",
|
||||
"employee_name",
|
||||
"column_break0",
|
||||
"status",
|
||||
"start_date",
|
||||
"end_date",
|
||||
"department",
|
||||
"section_break0",
|
||||
"goals",
|
||||
"total_score",
|
||||
"section_break1",
|
||||
"remarks",
|
||||
"other_details",
|
||||
"company",
|
||||
"column_break_17",
|
||||
"amended_from"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "employee_details",
|
||||
"fieldtype": "Section Break",
|
||||
"oldfieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "naming_series",
|
||||
"fieldtype": "Select",
|
||||
"label": "Series",
|
||||
"no_copy": 1,
|
||||
"options": "HR-APR-.YY.-.MM.",
|
||||
"print_hide": 1,
|
||||
"reqd": 1,
|
||||
"set_only_once": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "kra_template",
|
||||
"fieldtype": "Link",
|
||||
"in_standard_filter": 1,
|
||||
"label": "Appraisal Template",
|
||||
"oldfieldname": "kra_template",
|
||||
"oldfieldtype": "Link",
|
||||
"options": "Appraisal Template",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "kra_template",
|
||||
"fieldname": "employee",
|
||||
"fieldtype": "Link",
|
||||
"in_global_search": 1,
|
||||
"in_standard_filter": 1,
|
||||
"label": "For Employee",
|
||||
"oldfieldname": "employee",
|
||||
"oldfieldtype": "Link",
|
||||
"options": "Employee",
|
||||
"reqd": 1,
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "kra_template",
|
||||
"fieldname": "employee_name",
|
||||
"fieldtype": "Data",
|
||||
"in_global_search": 1,
|
||||
"label": "For Employee Name",
|
||||
"oldfieldname": "employee_name",
|
||||
"oldfieldtype": "Data",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "kra_template",
|
||||
"fieldname": "column_break0",
|
||||
"fieldtype": "Column Break",
|
||||
"oldfieldtype": "Column Break",
|
||||
"width": "50%"
|
||||
},
|
||||
{
|
||||
"default": "Draft",
|
||||
"depends_on": "kra_template",
|
||||
"fieldname": "status",
|
||||
"fieldtype": "Select",
|
||||
"in_standard_filter": 1,
|
||||
"label": "Status",
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "status",
|
||||
"oldfieldtype": "Select",
|
||||
"options": "\nDraft\nSubmitted\nCompleted\nCancelled",
|
||||
"read_only": 1,
|
||||
"reqd": 1,
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "kra_template",
|
||||
"fieldname": "start_date",
|
||||
"fieldtype": "Date",
|
||||
"in_list_view": 1,
|
||||
"label": "Start Date",
|
||||
"oldfieldname": "start_date",
|
||||
"oldfieldtype": "Date",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "kra_template",
|
||||
"fieldname": "end_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "End Date",
|
||||
"oldfieldname": "end_date",
|
||||
"oldfieldtype": "Date",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fetch_from": "employee.department",
|
||||
"fieldname": "department",
|
||||
"fieldtype": "Link",
|
||||
"label": "Department",
|
||||
"options": "Department",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "kra_template",
|
||||
"fieldname": "section_break0",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Goals",
|
||||
"oldfieldtype": "Section Break",
|
||||
"options": "Simple"
|
||||
},
|
||||
{
|
||||
"fieldname": "goals",
|
||||
"fieldtype": "Table",
|
||||
"label": "Goals",
|
||||
"oldfieldname": "appraisal_details",
|
||||
"oldfieldtype": "Table",
|
||||
"options": "Appraisal Goal"
|
||||
},
|
||||
{
|
||||
"fieldname": "total_score",
|
||||
"fieldtype": "Float",
|
||||
"in_list_view": 1,
|
||||
"label": "Total Score (Out of 5)",
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "total_score",
|
||||
"oldfieldtype": "Currency",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "kra_template",
|
||||
"fieldname": "section_break1",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"description": "Any other remarks, noteworthy effort that should go in the records.",
|
||||
"fieldname": "remarks",
|
||||
"fieldtype": "Text",
|
||||
"label": "Remarks"
|
||||
},
|
||||
{
|
||||
"depends_on": "kra_template",
|
||||
"fieldname": "other_details",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "company",
|
||||
"fieldtype": "Link",
|
||||
"label": "Company",
|
||||
"oldfieldname": "company",
|
||||
"oldfieldtype": "Link",
|
||||
"options": "Company",
|
||||
"remember_last_selected_value": 1,
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_17",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "amended_from",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 1,
|
||||
"label": "Amended From",
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "amended_from",
|
||||
"oldfieldtype": "Data",
|
||||
"options": "Appraisal",
|
||||
"print_hide": 1,
|
||||
"read_only": 1,
|
||||
"report_hide": 1,
|
||||
"width": "150px"
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-thumbs-up",
|
||||
"idx": 1,
|
||||
"index_web_pages_for_search": 1,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-10-03 21:48:33.297065",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Appraisal",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"create": 1,
|
||||
"email": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Employee",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"amend": 1,
|
||||
"cancel": 1,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"share": 1,
|
||||
"submit": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"amend": 1,
|
||||
"cancel": 1,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "HR User",
|
||||
"share": 1,
|
||||
"submit": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"search_fields": "status, employee, employee_name",
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"timeline_field": "employee",
|
||||
"title_field": "employee_name"
|
||||
}
|
@ -1,94 +0,0 @@
|
||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from frappe.model.mapper import get_mapped_doc
|
||||
from frappe.utils import flt, getdate
|
||||
|
||||
from erpnext.hr.utils import set_employee_name, validate_active_employee
|
||||
|
||||
|
||||
class Appraisal(Document):
|
||||
def validate(self):
|
||||
if not self.status:
|
||||
self.status = "Draft"
|
||||
|
||||
if not self.goals:
|
||||
frappe.throw(_("Goals cannot be empty"))
|
||||
|
||||
validate_active_employee(self.employee)
|
||||
set_employee_name(self)
|
||||
self.validate_dates()
|
||||
self.validate_existing_appraisal()
|
||||
self.calculate_total()
|
||||
|
||||
def get_employee_name(self):
|
||||
self.employee_name = frappe.db.get_value("Employee", self.employee, "employee_name")
|
||||
return self.employee_name
|
||||
|
||||
def validate_dates(self):
|
||||
if getdate(self.start_date) > getdate(self.end_date):
|
||||
frappe.throw(_("End Date can not be less than Start Date"))
|
||||
|
||||
def validate_existing_appraisal(self):
|
||||
chk = frappe.db.sql(
|
||||
"""select name from `tabAppraisal` where employee=%s
|
||||
and (status='Submitted' or status='Completed')
|
||||
and ((start_date>=%s and start_date<=%s)
|
||||
or (end_date>=%s and end_date<=%s))""",
|
||||
(self.employee, self.start_date, self.end_date, self.start_date, self.end_date),
|
||||
)
|
||||
if chk:
|
||||
frappe.throw(
|
||||
_("Appraisal {0} created for Employee {1} in the given date range").format(
|
||||
chk[0][0], self.employee_name
|
||||
)
|
||||
)
|
||||
|
||||
def calculate_total(self):
|
||||
total, total_w = 0, 0
|
||||
for d in self.get("goals"):
|
||||
if d.score:
|
||||
d.score_earned = flt(d.score) * flt(d.per_weightage) / 100
|
||||
total = total + d.score_earned
|
||||
total_w += flt(d.per_weightage)
|
||||
|
||||
if int(total_w) != 100:
|
||||
frappe.throw(
|
||||
_("Total weightage assigned should be 100%.<br>It is {0}").format(str(total_w) + "%")
|
||||
)
|
||||
|
||||
if (
|
||||
frappe.db.get_value("Employee", self.employee, "user_id") != frappe.session.user and total == 0
|
||||
):
|
||||
frappe.throw(_("Total cannot be zero"))
|
||||
|
||||
self.total_score = total
|
||||
|
||||
def on_submit(self):
|
||||
frappe.db.set(self, "status", "Submitted")
|
||||
|
||||
def on_cancel(self):
|
||||
frappe.db.set(self, "status", "Cancelled")
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def fetch_appraisal_template(source_name, target_doc=None):
|
||||
target_doc = get_mapped_doc(
|
||||
"Appraisal Template",
|
||||
source_name,
|
||||
{
|
||||
"Appraisal Template": {
|
||||
"doctype": "Appraisal",
|
||||
},
|
||||
"Appraisal Template Goal": {
|
||||
"doctype": "Appraisal Goal",
|
||||
},
|
||||
},
|
||||
target_doc,
|
||||
)
|
||||
|
||||
return target_doc
|
@ -1,10 +0,0 @@
|
||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors
|
||||
# See license.txt
|
||||
|
||||
import unittest
|
||||
|
||||
# test_records = frappe.get_test_records('Appraisal')
|
||||
|
||||
|
||||
class TestAppraisal(unittest.TestCase):
|
||||
pass
|
@ -1 +0,0 @@
|
||||
Goal for the parent Appraisal.
|
@ -1,220 +0,0 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"autoname": "hash",
|
||||
"beta": 0,
|
||||
"creation": "2013-02-22 01:27:44",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"fields": [
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"description": "Key Responsibility Area",
|
||||
"fieldname": "kra",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Goal",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "kra",
|
||||
"oldfieldtype": "Small Text",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"print_width": "240px",
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0,
|
||||
"width": "240px"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "section_break_2",
|
||||
"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": "per_weightage",
|
||||
"fieldtype": "Float",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Weightage (%)",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "per_weightage",
|
||||
"oldfieldtype": "Currency",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"print_width": "70px",
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0,
|
||||
"width": "70px"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "column_break_4",
|
||||
"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,
|
||||
"fieldname": "score",
|
||||
"fieldtype": "Float",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Score (0-5)",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "score",
|
||||
"oldfieldtype": "Select",
|
||||
"options": "",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"print_width": "70px",
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0,
|
||||
"width": "70px"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "section_break_6",
|
||||
"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": "score_earned",
|
||||
"fieldtype": "Float",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Score Earned",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "score_earned",
|
||||
"oldfieldtype": "Currency",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"print_width": "70px",
|
||||
"read_only": 1,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0,
|
||||
"width": "70px"
|
||||
}
|
||||
],
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 1,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"max_attachments": 0,
|
||||
"modified": "2020-09-18 17:26:09.703215",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Appraisal Goal",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 0,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"track_seen": 0
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
|
||||
from frappe.model.document import Document
|
||||
|
||||
|
||||
class AppraisalGoal(Document):
|
||||
pass
|
@ -1 +0,0 @@
|
||||
Standard set of goals for an Employee / Designation / Job Profile. New Appraisal transactions can be created from the Template.
|
@ -1,8 +0,0 @@
|
||||
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Appraisal Template', {
|
||||
refresh: function(frm) {
|
||||
|
||||
}
|
||||
});
|
@ -1,170 +0,0 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_import": 1,
|
||||
"allow_rename": 1,
|
||||
"autoname": "field:kra_title",
|
||||
"beta": 0,
|
||||
"creation": "2012-07-03 13:30:39",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "Setup",
|
||||
"editable_grid": 0,
|
||||
"fields": [
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "kra_title",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Appraisal Template Title",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "kra_title",
|
||||
"oldfieldtype": "Data",
|
||||
"permlevel": 0,
|
||||
"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": "description",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Description",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "description",
|
||||
"oldfieldtype": "Small Text",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"print_width": "300px",
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0,
|
||||
"width": "300px"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "goals",
|
||||
"fieldtype": "Table",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Goals",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "kra_sheet",
|
||||
"oldfieldtype": "Table",
|
||||
"options": "Appraisal Template Goal",
|
||||
"permlevel": 0,
|
||||
"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
|
||||
}
|
||||
],
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"icon": "icon-file-text",
|
||||
"idx": 1,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2020-09-18 17:26:09.703215",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Appraisal Template",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 0,
|
||||
"email": 1,
|
||||
"export": 0,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"is_custom": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "HR User",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 0,
|
||||
"delete": 0,
|
||||
"email": 0,
|
||||
"export": 0,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"is_custom": 0,
|
||||
"permlevel": 0,
|
||||
"print": 0,
|
||||
"read": 1,
|
||||
"report": 0,
|
||||
"role": "Employee",
|
||||
"set_user_permissions": 0,
|
||||
"share": 0,
|
||||
"submit": 0,
|
||||
"write": 0
|
||||
}
|
||||
],
|
||||
"quick_entry": 0,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"sort_order": "DESC",
|
||||
"track_seen": 0
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import cint, flt
|
||||
|
||||
|
||||
class AppraisalTemplate(Document):
|
||||
def validate(self):
|
||||
self.check_total_points()
|
||||
|
||||
def check_total_points(self):
|
||||
total_points = 0
|
||||
for d in self.get("goals"):
|
||||
total_points += flt(d.per_weightage)
|
||||
|
||||
if cint(total_points) != 100:
|
||||
frappe.throw(_("Sum of points for all goals should be 100. It is {0}").format(total_points))
|
@ -1,7 +0,0 @@
|
||||
def get_data():
|
||||
return {
|
||||
"fieldname": "kra_template",
|
||||
"transactions": [
|
||||
{"items": ["Appraisal"]},
|
||||
],
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors
|
||||
# See license.txt
|
||||
|
||||
import unittest
|
||||
|
||||
# test_records = frappe.get_test_records('Appraisal Template')
|
||||
|
||||
|
||||
class TestAppraisalTemplate(unittest.TestCase):
|
||||
pass
|
@ -1 +0,0 @@
|
||||
Goal details for the parent Appraisal Template.
|
@ -1,91 +0,0 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"autoname": "hash",
|
||||
"beta": 0,
|
||||
"creation": "2013-02-22 01:27:44",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"fields": [
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"description": "Key Performance Area",
|
||||
"fieldname": "kra",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "KRA",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "kra",
|
||||
"oldfieldtype": "Small Text",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"print_width": "200px",
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0,
|
||||
"width": "200px"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "per_weightage",
|
||||
"fieldtype": "Float",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Weightage (%)",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "per_weightage",
|
||||
"oldfieldtype": "Currency",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"print_width": "100px",
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0,
|
||||
"width": "100px"
|
||||
}
|
||||
],
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 1,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"max_attachments": 0,
|
||||
"modified": "2020-09-18 17:26:09.703215",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Appraisal Template Goal",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 0,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"track_seen": 0
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
|
||||
from frappe.model.document import Document
|
||||
|
||||
|
||||
class AppraisalTemplateGoal(Document):
|
||||
pass
|
@ -1 +0,0 @@
|
||||
Attendance record of an Employee on a particular date.
|
@ -1,15 +0,0 @@
|
||||
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
// License: GNU General Public License v3. See license.txt
|
||||
|
||||
cur_frm.add_fetch('employee', 'company', 'company');
|
||||
cur_frm.add_fetch('employee', 'employee_name', 'employee_name');
|
||||
|
||||
cur_frm.cscript.onload = function(doc, cdt, cdn) {
|
||||
if(doc.__islocal) cur_frm.set_value("attendance_date", frappe.datetime.get_today());
|
||||
}
|
||||
|
||||
cur_frm.fields_dict.employee.get_query = function(doc,cdt,cdn) {
|
||||
return{
|
||||
query: "erpnext.controllers.queries.employee_query"
|
||||
}
|
||||
}
|
@ -1,260 +0,0 @@
|
||||
{
|
||||
"actions": [],
|
||||
"allow_import": 1,
|
||||
"autoname": "naming_series:",
|
||||
"creation": "2013-01-10 16:34:13",
|
||||
"doctype": "DocType",
|
||||
"document_type": "Setup",
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"attendance_details",
|
||||
"naming_series",
|
||||
"employee",
|
||||
"employee_name",
|
||||
"working_hours",
|
||||
"status",
|
||||
"leave_type",
|
||||
"leave_application",
|
||||
"column_break0",
|
||||
"attendance_date",
|
||||
"company",
|
||||
"department",
|
||||
"attendance_request",
|
||||
"details_section",
|
||||
"shift",
|
||||
"in_time",
|
||||
"out_time",
|
||||
"column_break_18",
|
||||
"late_entry",
|
||||
"early_exit",
|
||||
"amended_from"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "attendance_details",
|
||||
"fieldtype": "Section Break",
|
||||
"oldfieldtype": "Section Break",
|
||||
"options": "Simple"
|
||||
},
|
||||
{
|
||||
"fieldname": "naming_series",
|
||||
"fieldtype": "Select",
|
||||
"label": "Series",
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "naming_series",
|
||||
"oldfieldtype": "Select",
|
||||
"options": "HR-ATT-.YYYY.-",
|
||||
"reqd": 1,
|
||||
"set_only_once": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "employee",
|
||||
"fieldtype": "Link",
|
||||
"in_global_search": 1,
|
||||
"in_standard_filter": 1,
|
||||
"label": "Employee",
|
||||
"oldfieldname": "employee",
|
||||
"oldfieldtype": "Link",
|
||||
"options": "Employee",
|
||||
"reqd": 1,
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"fetch_from": "employee.employee_name",
|
||||
"fieldname": "employee_name",
|
||||
"fieldtype": "Data",
|
||||
"in_global_search": 1,
|
||||
"label": "Employee Name",
|
||||
"oldfieldname": "employee_name",
|
||||
"oldfieldtype": "Data",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "working_hours",
|
||||
"fieldname": "working_hours",
|
||||
"fieldtype": "Float",
|
||||
"label": "Working Hours",
|
||||
"precision": "1",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"default": "Present",
|
||||
"fieldname": "status",
|
||||
"fieldtype": "Select",
|
||||
"in_standard_filter": 1,
|
||||
"label": "Status",
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "status",
|
||||
"oldfieldtype": "Select",
|
||||
"options": "\nPresent\nAbsent\nOn Leave\nHalf Day\nWork From Home",
|
||||
"reqd": 1,
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:in_list([\"On Leave\", \"Half Day\"], doc.status)",
|
||||
"fieldname": "leave_type",
|
||||
"fieldtype": "Link",
|
||||
"in_standard_filter": 1,
|
||||
"label": "Leave Type",
|
||||
"mandatory_depends_on": "eval:in_list([\"On Leave\", \"Half Day\"], doc.status)",
|
||||
"oldfieldname": "leave_type",
|
||||
"oldfieldtype": "Link",
|
||||
"options": "Leave Type"
|
||||
},
|
||||
{
|
||||
"fieldname": "leave_application",
|
||||
"fieldtype": "Link",
|
||||
"label": "Leave Application",
|
||||
"no_copy": 1,
|
||||
"options": "Leave Application",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break0",
|
||||
"fieldtype": "Column Break",
|
||||
"oldfieldtype": "Column Break",
|
||||
"width": "50%"
|
||||
},
|
||||
{
|
||||
"fieldname": "attendance_date",
|
||||
"fieldtype": "Date",
|
||||
"in_list_view": 1,
|
||||
"label": "Attendance Date",
|
||||
"oldfieldname": "attendance_date",
|
||||
"oldfieldtype": "Date",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "company",
|
||||
"fieldtype": "Link",
|
||||
"label": "Company",
|
||||
"oldfieldname": "company",
|
||||
"oldfieldtype": "Link",
|
||||
"options": "Company",
|
||||
"remember_last_selected_value": 1,
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fetch_from": "employee.department",
|
||||
"fieldname": "department",
|
||||
"fieldtype": "Link",
|
||||
"label": "Department",
|
||||
"options": "Department",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "shift",
|
||||
"fieldtype": "Link",
|
||||
"label": "Shift",
|
||||
"options": "Shift Type"
|
||||
},
|
||||
{
|
||||
"fieldname": "attendance_request",
|
||||
"fieldtype": "Link",
|
||||
"label": "Attendance Request",
|
||||
"options": "Attendance Request",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "amended_from",
|
||||
"fieldtype": "Link",
|
||||
"ignore_user_permissions": 1,
|
||||
"label": "Amended From",
|
||||
"no_copy": 1,
|
||||
"options": "Attendance",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "late_entry",
|
||||
"fieldtype": "Check",
|
||||
"label": "Late Entry"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "early_exit",
|
||||
"fieldtype": "Check",
|
||||
"label": "Early Exit"
|
||||
},
|
||||
{
|
||||
"fieldname": "details_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Details"
|
||||
},
|
||||
{
|
||||
"depends_on": "shift",
|
||||
"fieldname": "in_time",
|
||||
"fieldtype": "Datetime",
|
||||
"label": "In Time",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "shift",
|
||||
"fieldname": "out_time",
|
||||
"fieldtype": "Datetime",
|
||||
"label": "Out Time",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_18",
|
||||
"fieldtype": "Column Break"
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-ok",
|
||||
"idx": 1,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-09-18 17:26:09.703215",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Attendance",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"cancel": 1,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"share": 1,
|
||||
"submit": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"cancel": 1,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"import": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "HR User",
|
||||
"share": 1,
|
||||
"submit": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"cancel": 1,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "HR Manager",
|
||||
"share": 1,
|
||||
"submit": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"search_fields": "employee,employee_name,attendance_date,status",
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"title_field": "employee_name"
|
||||
}
|
@ -1,390 +0,0 @@
|
||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from frappe.query_builder import Criterion
|
||||
from frappe.utils import cint, cstr, formatdate, get_datetime, get_link_to_form, getdate, nowdate
|
||||
|
||||
from erpnext.hr.doctype.shift_assignment.shift_assignment import has_overlapping_timings
|
||||
from erpnext.hr.utils import get_holiday_dates_for_employee, validate_active_employee
|
||||
|
||||
|
||||
class DuplicateAttendanceError(frappe.ValidationError):
|
||||
pass
|
||||
|
||||
|
||||
class OverlappingShiftAttendanceError(frappe.ValidationError):
|
||||
pass
|
||||
|
||||
|
||||
class Attendance(Document):
|
||||
def validate(self):
|
||||
from erpnext.controllers.status_updater import validate_status
|
||||
|
||||
validate_status(self.status, ["Present", "Absent", "On Leave", "Half Day", "Work From Home"])
|
||||
validate_active_employee(self.employee)
|
||||
self.validate_attendance_date()
|
||||
self.validate_duplicate_record()
|
||||
self.validate_overlapping_shift_attendance()
|
||||
self.validate_employee_status()
|
||||
self.check_leave_record()
|
||||
|
||||
def on_cancel(self):
|
||||
self.unlink_attendance_from_checkins()
|
||||
|
||||
def validate_attendance_date(self):
|
||||
date_of_joining = frappe.db.get_value("Employee", self.employee, "date_of_joining")
|
||||
|
||||
# leaves can be marked for future dates
|
||||
if (
|
||||
self.status != "On Leave"
|
||||
and not self.leave_application
|
||||
and getdate(self.attendance_date) > getdate(nowdate())
|
||||
):
|
||||
frappe.throw(_("Attendance can not be marked for future dates"))
|
||||
elif date_of_joining and getdate(self.attendance_date) < getdate(date_of_joining):
|
||||
frappe.throw(_("Attendance date can not be less than employee's joining date"))
|
||||
|
||||
def validate_duplicate_record(self):
|
||||
duplicate = get_duplicate_attendance_record(
|
||||
self.employee, self.attendance_date, self.shift, self.name
|
||||
)
|
||||
|
||||
if duplicate:
|
||||
frappe.throw(
|
||||
_("Attendance for employee {0} is already marked for the date {1}: {2}").format(
|
||||
frappe.bold(self.employee),
|
||||
frappe.bold(self.attendance_date),
|
||||
get_link_to_form("Attendance", duplicate[0].name),
|
||||
),
|
||||
title=_("Duplicate Attendance"),
|
||||
exc=DuplicateAttendanceError,
|
||||
)
|
||||
|
||||
def validate_overlapping_shift_attendance(self):
|
||||
attendance = get_overlapping_shift_attendance(
|
||||
self.employee, self.attendance_date, self.shift, self.name
|
||||
)
|
||||
|
||||
if attendance:
|
||||
frappe.throw(
|
||||
_("Attendance for employee {0} is already marked for an overlapping shift {1}: {2}").format(
|
||||
frappe.bold(self.employee),
|
||||
frappe.bold(attendance.shift),
|
||||
get_link_to_form("Attendance", attendance.name),
|
||||
),
|
||||
title=_("Overlapping Shift Attendance"),
|
||||
exc=OverlappingShiftAttendanceError,
|
||||
)
|
||||
|
||||
def validate_employee_status(self):
|
||||
if frappe.db.get_value("Employee", self.employee, "status") == "Inactive":
|
||||
frappe.throw(_("Cannot mark attendance for an Inactive employee {0}").format(self.employee))
|
||||
|
||||
def check_leave_record(self):
|
||||
leave_record = frappe.db.sql(
|
||||
"""
|
||||
select leave_type, half_day, half_day_date
|
||||
from `tabLeave Application`
|
||||
where employee = %s
|
||||
and %s between from_date and to_date
|
||||
and status = 'Approved'
|
||||
and docstatus = 1
|
||||
""",
|
||||
(self.employee, self.attendance_date),
|
||||
as_dict=True,
|
||||
)
|
||||
if leave_record:
|
||||
for d in leave_record:
|
||||
self.leave_type = d.leave_type
|
||||
if d.half_day_date == getdate(self.attendance_date):
|
||||
self.status = "Half Day"
|
||||
frappe.msgprint(
|
||||
_("Employee {0} on Half day on {1}").format(self.employee, formatdate(self.attendance_date))
|
||||
)
|
||||
else:
|
||||
self.status = "On Leave"
|
||||
frappe.msgprint(
|
||||
_("Employee {0} is on Leave on {1}").format(self.employee, formatdate(self.attendance_date))
|
||||
)
|
||||
|
||||
if self.status in ("On Leave", "Half Day"):
|
||||
if not leave_record:
|
||||
frappe.msgprint(
|
||||
_("No leave record found for employee {0} on {1}").format(
|
||||
self.employee, formatdate(self.attendance_date)
|
||||
),
|
||||
alert=1,
|
||||
)
|
||||
elif self.leave_type:
|
||||
self.leave_type = None
|
||||
self.leave_application = None
|
||||
|
||||
def validate_employee(self):
|
||||
emp = frappe.db.sql(
|
||||
"select name from `tabEmployee` where name = %s and status = 'Active'", self.employee
|
||||
)
|
||||
if not emp:
|
||||
frappe.throw(_("Employee {0} is not active or does not exist").format(self.employee))
|
||||
|
||||
def unlink_attendance_from_checkins(self):
|
||||
EmployeeCheckin = frappe.qb.DocType("Employee Checkin")
|
||||
linked_logs = (
|
||||
frappe.qb.from_(EmployeeCheckin)
|
||||
.select(EmployeeCheckin.name)
|
||||
.where(EmployeeCheckin.attendance == self.name)
|
||||
.for_update()
|
||||
.run(as_dict=True)
|
||||
)
|
||||
|
||||
if linked_logs:
|
||||
(
|
||||
frappe.qb.update(EmployeeCheckin)
|
||||
.set("attendance", "")
|
||||
.where(EmployeeCheckin.attendance == self.name)
|
||||
).run()
|
||||
|
||||
frappe.msgprint(
|
||||
msg=_("Unlinked Attendance record from Employee Checkins: {}").format(
|
||||
", ".join(get_link_to_form("Employee Checkin", log.name) for log in linked_logs)
|
||||
),
|
||||
title=_("Unlinked logs"),
|
||||
indicator="blue",
|
||||
is_minimizable=True,
|
||||
wide=True,
|
||||
)
|
||||
|
||||
|
||||
def get_duplicate_attendance_record(employee, attendance_date, shift, name=None):
|
||||
attendance = frappe.qb.DocType("Attendance")
|
||||
query = (
|
||||
frappe.qb.from_(attendance)
|
||||
.select(attendance.name)
|
||||
.where((attendance.employee == employee) & (attendance.docstatus < 2))
|
||||
)
|
||||
|
||||
if shift:
|
||||
query = query.where(
|
||||
Criterion.any(
|
||||
[
|
||||
Criterion.all(
|
||||
[
|
||||
((attendance.shift.isnull()) | (attendance.shift == "")),
|
||||
(attendance.attendance_date == attendance_date),
|
||||
]
|
||||
),
|
||||
Criterion.all(
|
||||
[
|
||||
((attendance.shift.isnotnull()) | (attendance.shift != "")),
|
||||
(attendance.attendance_date == attendance_date),
|
||||
(attendance.shift == shift),
|
||||
]
|
||||
),
|
||||
]
|
||||
)
|
||||
)
|
||||
else:
|
||||
query = query.where((attendance.attendance_date == attendance_date))
|
||||
|
||||
if name:
|
||||
query = query.where(attendance.name != name)
|
||||
|
||||
return query.run(as_dict=True)
|
||||
|
||||
|
||||
def get_overlapping_shift_attendance(employee, attendance_date, shift, name=None):
|
||||
if not shift:
|
||||
return {}
|
||||
|
||||
attendance = frappe.qb.DocType("Attendance")
|
||||
query = (
|
||||
frappe.qb.from_(attendance)
|
||||
.select(attendance.name, attendance.shift)
|
||||
.where(
|
||||
(attendance.employee == employee)
|
||||
& (attendance.docstatus < 2)
|
||||
& (attendance.attendance_date == attendance_date)
|
||||
& (attendance.shift != shift)
|
||||
)
|
||||
)
|
||||
|
||||
if name:
|
||||
query = query.where(attendance.name != name)
|
||||
|
||||
overlapping_attendance = query.run(as_dict=True)
|
||||
|
||||
if overlapping_attendance and has_overlapping_timings(shift, overlapping_attendance[0].shift):
|
||||
return overlapping_attendance[0]
|
||||
return {}
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_events(start, end, filters=None):
|
||||
events = []
|
||||
|
||||
employee = frappe.db.get_value("Employee", {"user_id": frappe.session.user})
|
||||
|
||||
if not employee:
|
||||
return events
|
||||
|
||||
from frappe.desk.reportview import get_filters_cond
|
||||
|
||||
conditions = get_filters_cond("Attendance", filters, [])
|
||||
add_attendance(events, start, end, conditions=conditions)
|
||||
return events
|
||||
|
||||
|
||||
def add_attendance(events, start, end, conditions=None):
|
||||
query = """select name, attendance_date, status
|
||||
from `tabAttendance` where
|
||||
attendance_date between %(from_date)s and %(to_date)s
|
||||
and docstatus < 2"""
|
||||
if conditions:
|
||||
query += conditions
|
||||
|
||||
for d in frappe.db.sql(query, {"from_date": start, "to_date": end}, as_dict=True):
|
||||
e = {
|
||||
"name": d.name,
|
||||
"doctype": "Attendance",
|
||||
"start": d.attendance_date,
|
||||
"end": d.attendance_date,
|
||||
"title": cstr(d.status),
|
||||
"docstatus": d.docstatus,
|
||||
}
|
||||
if e not in events:
|
||||
events.append(e)
|
||||
|
||||
|
||||
def mark_attendance(
|
||||
employee,
|
||||
attendance_date,
|
||||
status,
|
||||
shift=None,
|
||||
leave_type=None,
|
||||
ignore_validate=False,
|
||||
late_entry=False,
|
||||
early_exit=False,
|
||||
):
|
||||
if get_duplicate_attendance_record(employee, attendance_date, shift):
|
||||
return
|
||||
|
||||
if get_overlapping_shift_attendance(employee, attendance_date, shift):
|
||||
return
|
||||
|
||||
company = frappe.db.get_value("Employee", employee, "company")
|
||||
attendance = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Attendance",
|
||||
"employee": employee,
|
||||
"attendance_date": attendance_date,
|
||||
"status": status,
|
||||
"company": company,
|
||||
"shift": shift,
|
||||
"leave_type": leave_type,
|
||||
"late_entry": late_entry,
|
||||
"early_exit": early_exit,
|
||||
}
|
||||
)
|
||||
attendance.flags.ignore_validate = ignore_validate
|
||||
attendance.insert()
|
||||
attendance.submit()
|
||||
return attendance.name
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def mark_bulk_attendance(data):
|
||||
import json
|
||||
|
||||
if isinstance(data, str):
|
||||
data = json.loads(data)
|
||||
data = frappe._dict(data)
|
||||
company = frappe.get_value("Employee", data.employee, "company")
|
||||
if not data.unmarked_days:
|
||||
frappe.throw(_("Please select a date."))
|
||||
return
|
||||
|
||||
for date in data.unmarked_days:
|
||||
doc_dict = {
|
||||
"doctype": "Attendance",
|
||||
"employee": data.employee,
|
||||
"attendance_date": get_datetime(date),
|
||||
"status": data.status,
|
||||
"company": company,
|
||||
}
|
||||
attendance = frappe.get_doc(doc_dict).insert()
|
||||
attendance.submit()
|
||||
|
||||
|
||||
def get_month_map():
|
||||
return frappe._dict(
|
||||
{
|
||||
"January": 1,
|
||||
"February": 2,
|
||||
"March": 3,
|
||||
"April": 4,
|
||||
"May": 5,
|
||||
"June": 6,
|
||||
"July": 7,
|
||||
"August": 8,
|
||||
"September": 9,
|
||||
"October": 10,
|
||||
"November": 11,
|
||||
"December": 12,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_unmarked_days(employee, month, exclude_holidays=0):
|
||||
import calendar
|
||||
|
||||
month_map = get_month_map()
|
||||
today = get_datetime()
|
||||
|
||||
joining_date, relieving_date = frappe.get_cached_value(
|
||||
"Employee", employee, ["date_of_joining", "relieving_date"]
|
||||
)
|
||||
start_day = 1
|
||||
end_day = calendar.monthrange(today.year, month_map[month])[1] + 1
|
||||
|
||||
if joining_date and joining_date.month == month_map[month]:
|
||||
start_day = joining_date.day
|
||||
|
||||
if relieving_date and relieving_date.month == month_map[month]:
|
||||
end_day = relieving_date.day + 1
|
||||
|
||||
dates_of_month = [
|
||||
"{}-{}-{}".format(today.year, month_map[month], r) for r in range(start_day, end_day)
|
||||
]
|
||||
month_start, month_end = dates_of_month[0], dates_of_month[-1]
|
||||
|
||||
records = frappe.get_all(
|
||||
"Attendance",
|
||||
fields=["attendance_date", "employee"],
|
||||
filters=[
|
||||
["attendance_date", ">=", month_start],
|
||||
["attendance_date", "<=", month_end],
|
||||
["employee", "=", employee],
|
||||
["docstatus", "!=", 2],
|
||||
],
|
||||
)
|
||||
|
||||
marked_days = [get_datetime(record.attendance_date) for record in records]
|
||||
if cint(exclude_holidays):
|
||||
holiday_dates = get_holiday_dates_for_employee(employee, month_start, month_end)
|
||||
holidays = [get_datetime(record) for record in holiday_dates]
|
||||
marked_days.extend(holidays)
|
||||
|
||||
unmarked_days = []
|
||||
|
||||
for date in dates_of_month:
|
||||
date_time = get_datetime(date)
|
||||
if today.day <= date_time.day and today.month <= date_time.month:
|
||||
break
|
||||
if date_time not in marked_days:
|
||||
unmarked_days.append(date)
|
||||
|
||||
return unmarked_days
|
@ -1,12 +0,0 @@
|
||||
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
frappe.views.calendar["Attendance"] = {
|
||||
options: {
|
||||
header: {
|
||||
left: 'prev,next today',
|
||||
center: 'title',
|
||||
right: 'month'
|
||||
}
|
||||
},
|
||||
get_events_method: "erpnext.hr.doctype.attendance.attendance.get_events"
|
||||
};
|
@ -1,2 +0,0 @@
|
||||
def get_data():
|
||||
return {"fieldname": "attendance", "transactions": [{"label": "", "items": ["Employee Checkin"]}]}
|
@ -1,164 +0,0 @@
|
||||
frappe.listview_settings['Attendance'] = {
|
||||
add_fields: ["status", "attendance_date"],
|
||||
get_indicator: function (doc) {
|
||||
if (["Present", "Work From Home"].includes(doc.status)) {
|
||||
return [__(doc.status), "green", "status,=," + doc.status];
|
||||
} else if (["Absent", "On Leave"].includes(doc.status)) {
|
||||
return [__(doc.status), "red", "status,=," + doc.status];
|
||||
} else if (doc.status == "Half Day") {
|
||||
return [__(doc.status), "orange", "status,=," + doc.status];
|
||||
}
|
||||
},
|
||||
|
||||
onload: function(list_view) {
|
||||
let me = this;
|
||||
const months = moment.months();
|
||||
list_view.page.add_inner_button(__("Mark Attendance"), function() {
|
||||
let dialog = new frappe.ui.Dialog({
|
||||
title: __("Mark Attendance"),
|
||||
fields: [{
|
||||
fieldname: 'employee',
|
||||
label: __('For Employee'),
|
||||
fieldtype: 'Link',
|
||||
options: 'Employee',
|
||||
get_query: () => {
|
||||
return {query: "erpnext.controllers.queries.employee_query"};
|
||||
},
|
||||
reqd: 1,
|
||||
onchange: function() {
|
||||
dialog.set_df_property("unmarked_days", "hidden", 1);
|
||||
dialog.set_df_property("status", "hidden", 1);
|
||||
dialog.set_df_property("exclude_holidays", "hidden", 1);
|
||||
dialog.set_df_property("month", "value", '');
|
||||
dialog.set_df_property("unmarked_days", "options", []);
|
||||
dialog.no_unmarked_days_left = false;
|
||||
}
|
||||
},
|
||||
{
|
||||
label: __("For Month"),
|
||||
fieldtype: "Select",
|
||||
fieldname: "month",
|
||||
options: months,
|
||||
reqd: 1,
|
||||
onchange: function() {
|
||||
if (dialog.fields_dict.employee.value && dialog.fields_dict.month.value) {
|
||||
dialog.set_df_property("status", "hidden", 0);
|
||||
dialog.set_df_property("exclude_holidays", "hidden", 0);
|
||||
dialog.set_df_property("unmarked_days", "options", []);
|
||||
dialog.no_unmarked_days_left = false;
|
||||
me.get_multi_select_options(
|
||||
dialog.fields_dict.employee.value,
|
||||
dialog.fields_dict.month.value,
|
||||
dialog.fields_dict.exclude_holidays.get_value()
|
||||
).then(options => {
|
||||
if (options.length > 0) {
|
||||
dialog.set_df_property("unmarked_days", "hidden", 0);
|
||||
dialog.set_df_property("unmarked_days", "options", options);
|
||||
} else {
|
||||
dialog.no_unmarked_days_left = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: __("Status"),
|
||||
fieldtype: "Select",
|
||||
fieldname: "status",
|
||||
options: ["Present", "Absent", "Half Day", "Work From Home"],
|
||||
hidden: 1,
|
||||
reqd: 1,
|
||||
|
||||
},
|
||||
{
|
||||
label: __("Exclude Holidays"),
|
||||
fieldtype: "Check",
|
||||
fieldname: "exclude_holidays",
|
||||
hidden: 1,
|
||||
onchange: function() {
|
||||
if (dialog.fields_dict.employee.value && dialog.fields_dict.month.value) {
|
||||
dialog.set_df_property("status", "hidden", 0);
|
||||
dialog.set_df_property("unmarked_days", "options", []);
|
||||
dialog.no_unmarked_days_left = false;
|
||||
me.get_multi_select_options(
|
||||
dialog.fields_dict.employee.value,
|
||||
dialog.fields_dict.month.value,
|
||||
dialog.fields_dict.exclude_holidays.get_value()
|
||||
).then(options => {
|
||||
if (options.length > 0) {
|
||||
dialog.set_df_property("unmarked_days", "hidden", 0);
|
||||
dialog.set_df_property("unmarked_days", "options", options);
|
||||
} else {
|
||||
dialog.no_unmarked_days_left = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: __("Unmarked Attendance for days"),
|
||||
fieldname: "unmarked_days",
|
||||
fieldtype: "MultiCheck",
|
||||
options: [],
|
||||
columns: 2,
|
||||
hidden: 1
|
||||
}],
|
||||
primary_action(data) {
|
||||
if (cur_dialog.no_unmarked_days_left) {
|
||||
frappe.msgprint(__("Attendance for the month of {0} , has already been marked for the Employee {1}",
|
||||
[dialog.fields_dict.month.value, dialog.fields_dict.employee.value]));
|
||||
} else {
|
||||
frappe.confirm(__('Mark attendance as {0} for {1} on selected dates?', [data.status, data.month]), () => {
|
||||
frappe.call({
|
||||
method: "erpnext.hr.doctype.attendance.attendance.mark_bulk_attendance",
|
||||
args: {
|
||||
data: data
|
||||
},
|
||||
callback: function (r) {
|
||||
if (r.message === 1) {
|
||||
frappe.show_alert({
|
||||
message: __("Attendance Marked"),
|
||||
indicator: 'blue'
|
||||
});
|
||||
cur_dialog.hide();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
dialog.hide();
|
||||
list_view.refresh();
|
||||
},
|
||||
primary_action_label: __('Mark Attendance')
|
||||
|
||||
});
|
||||
dialog.show();
|
||||
});
|
||||
},
|
||||
|
||||
get_multi_select_options: function(employee, month, exclude_holidays) {
|
||||
return new Promise(resolve => {
|
||||
frappe.call({
|
||||
method: 'erpnext.hr.doctype.attendance.attendance.get_unmarked_days',
|
||||
async: false,
|
||||
args: {
|
||||
employee: employee,
|
||||
month: month,
|
||||
exclude_holidays: exclude_holidays
|
||||
}
|
||||
}).then(r => {
|
||||
var options = [];
|
||||
for (var d in r.message) {
|
||||
var momentObj = moment(r.message[d], 'YYYY-MM-DD');
|
||||
var date = momentObj.format('DD-MM-YYYY');
|
||||
options.push({
|
||||
"label": date,
|
||||
"value": r.message[d],
|
||||
"checked": 1
|
||||
});
|
||||
}
|
||||
resolve(options);
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
@ -1,232 +0,0 @@
|
||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors
|
||||
# See license.txt
|
||||
|
||||
import frappe
|
||||
from frappe.tests.utils import FrappeTestCase
|
||||
from frappe.utils import (
|
||||
add_days,
|
||||
add_months,
|
||||
get_last_day,
|
||||
get_year_ending,
|
||||
get_year_start,
|
||||
getdate,
|
||||
nowdate,
|
||||
)
|
||||
|
||||
from erpnext.hr.doctype.attendance.attendance import (
|
||||
DuplicateAttendanceError,
|
||||
OverlappingShiftAttendanceError,
|
||||
get_month_map,
|
||||
get_unmarked_days,
|
||||
mark_attendance,
|
||||
)
|
||||
from erpnext.hr.doctype.employee.test_employee import make_employee
|
||||
from erpnext.hr.doctype.leave_application.test_leave_application import get_first_sunday
|
||||
|
||||
test_records = frappe.get_test_records("Attendance")
|
||||
|
||||
|
||||
class TestAttendance(FrappeTestCase):
|
||||
def setUp(self):
|
||||
from erpnext.payroll.doctype.salary_slip.test_salary_slip import make_holiday_list
|
||||
|
||||
from_date = get_year_start(getdate())
|
||||
to_date = get_year_ending(getdate())
|
||||
self.holiday_list = make_holiday_list(from_date=from_date, to_date=to_date)
|
||||
frappe.db.delete("Attendance")
|
||||
|
||||
def test_duplicate_attendance(self):
|
||||
employee = make_employee("test_duplicate_attendance@example.com", company="_Test Company")
|
||||
date = nowdate()
|
||||
|
||||
mark_attendance(employee, date, "Present")
|
||||
attendance = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Attendance",
|
||||
"employee": employee,
|
||||
"attendance_date": date,
|
||||
"status": "Absent",
|
||||
"company": "_Test Company",
|
||||
}
|
||||
)
|
||||
|
||||
self.assertRaises(DuplicateAttendanceError, attendance.insert)
|
||||
|
||||
def test_duplicate_attendance_with_shift(self):
|
||||
from erpnext.hr.doctype.shift_type.test_shift_type import setup_shift_type
|
||||
|
||||
employee = make_employee("test_duplicate_attendance@example.com", company="_Test Company")
|
||||
date = nowdate()
|
||||
|
||||
shift_1 = setup_shift_type(shift_type="Shift 1", start_time="08:00:00", end_time="10:00:00")
|
||||
mark_attendance(employee, date, "Present", shift=shift_1.name)
|
||||
|
||||
# attendance record with shift
|
||||
attendance = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Attendance",
|
||||
"employee": employee,
|
||||
"attendance_date": date,
|
||||
"status": "Absent",
|
||||
"company": "_Test Company",
|
||||
"shift": shift_1.name,
|
||||
}
|
||||
)
|
||||
|
||||
self.assertRaises(DuplicateAttendanceError, attendance.insert)
|
||||
|
||||
# attendance record without any shift
|
||||
attendance = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Attendance",
|
||||
"employee": employee,
|
||||
"attendance_date": date,
|
||||
"status": "Absent",
|
||||
"company": "_Test Company",
|
||||
}
|
||||
)
|
||||
|
||||
self.assertRaises(DuplicateAttendanceError, attendance.insert)
|
||||
|
||||
def test_overlapping_shift_attendance_validation(self):
|
||||
from erpnext.hr.doctype.shift_type.test_shift_type import setup_shift_type
|
||||
|
||||
employee = make_employee("test_overlap_attendance@example.com", company="_Test Company")
|
||||
date = nowdate()
|
||||
|
||||
shift_1 = setup_shift_type(shift_type="Shift 1", start_time="08:00:00", end_time="10:00:00")
|
||||
shift_2 = setup_shift_type(shift_type="Shift 2", start_time="09:30:00", end_time="11:00:00")
|
||||
|
||||
mark_attendance(employee, date, "Present", shift=shift_1.name)
|
||||
|
||||
# attendance record with overlapping shift
|
||||
attendance = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Attendance",
|
||||
"employee": employee,
|
||||
"attendance_date": date,
|
||||
"status": "Absent",
|
||||
"company": "_Test Company",
|
||||
"shift": shift_2.name,
|
||||
}
|
||||
)
|
||||
|
||||
self.assertRaises(OverlappingShiftAttendanceError, attendance.insert)
|
||||
|
||||
def test_allow_attendance_with_different_shifts(self):
|
||||
# allows attendance with 2 different non-overlapping shifts
|
||||
from erpnext.hr.doctype.shift_type.test_shift_type import setup_shift_type
|
||||
|
||||
employee = make_employee("test_duplicate_attendance@example.com", company="_Test Company")
|
||||
date = nowdate()
|
||||
|
||||
shift_1 = setup_shift_type(shift_type="Shift 1", start_time="08:00:00", end_time="10:00:00")
|
||||
shift_2 = setup_shift_type(shift_type="Shift 2", start_time="11:00:00", end_time="12:00:00")
|
||||
|
||||
mark_attendance(employee, date, "Present", shift_1.name)
|
||||
frappe.get_doc(
|
||||
{
|
||||
"doctype": "Attendance",
|
||||
"employee": employee,
|
||||
"attendance_date": date,
|
||||
"status": "Absent",
|
||||
"company": "_Test Company",
|
||||
"shift": shift_2.name,
|
||||
}
|
||||
).insert()
|
||||
|
||||
def test_mark_absent(self):
|
||||
employee = make_employee("test_mark_absent@example.com")
|
||||
date = nowdate()
|
||||
|
||||
attendance = mark_attendance(employee, date, "Absent")
|
||||
fetch_attendance = frappe.get_value(
|
||||
"Attendance", {"employee": employee, "attendance_date": date, "status": "Absent"}
|
||||
)
|
||||
self.assertEqual(attendance, fetch_attendance)
|
||||
|
||||
def test_unmarked_days(self):
|
||||
first_sunday = get_first_sunday(
|
||||
self.holiday_list, for_date=get_last_day(add_months(getdate(), -1))
|
||||
)
|
||||
attendance_date = add_days(first_sunday, 1)
|
||||
|
||||
employee = make_employee(
|
||||
"test_unmarked_days@example.com", date_of_joining=add_days(attendance_date, -1)
|
||||
)
|
||||
frappe.db.set_value("Employee", employee, "holiday_list", self.holiday_list)
|
||||
|
||||
mark_attendance(employee, attendance_date, "Present")
|
||||
month_name = get_month_name(attendance_date)
|
||||
|
||||
unmarked_days = get_unmarked_days(employee, month_name)
|
||||
unmarked_days = [getdate(date) for date in unmarked_days]
|
||||
|
||||
# attendance already marked for the day
|
||||
self.assertNotIn(attendance_date, unmarked_days)
|
||||
# attendance unmarked
|
||||
self.assertIn(getdate(add_days(attendance_date, 1)), unmarked_days)
|
||||
# holiday considered in unmarked days
|
||||
self.assertIn(first_sunday, unmarked_days)
|
||||
|
||||
def test_unmarked_days_excluding_holidays(self):
|
||||
first_sunday = get_first_sunday(
|
||||
self.holiday_list, for_date=get_last_day(add_months(getdate(), -1))
|
||||
)
|
||||
attendance_date = add_days(first_sunday, 1)
|
||||
|
||||
employee = make_employee(
|
||||
"test_unmarked_days@example.com", date_of_joining=add_days(attendance_date, -1)
|
||||
)
|
||||
frappe.db.set_value("Employee", employee, "holiday_list", self.holiday_list)
|
||||
|
||||
mark_attendance(employee, attendance_date, "Present")
|
||||
month_name = get_month_name(attendance_date)
|
||||
|
||||
unmarked_days = get_unmarked_days(employee, month_name, exclude_holidays=True)
|
||||
unmarked_days = [getdate(date) for date in unmarked_days]
|
||||
|
||||
# attendance already marked for the day
|
||||
self.assertNotIn(attendance_date, unmarked_days)
|
||||
# attendance unmarked
|
||||
self.assertIn(getdate(add_days(attendance_date, 1)), unmarked_days)
|
||||
# holidays not considered in unmarked days
|
||||
self.assertNotIn(first_sunday, unmarked_days)
|
||||
|
||||
def test_unmarked_days_as_per_joining_and_relieving_dates(self):
|
||||
first_sunday = get_first_sunday(
|
||||
self.holiday_list, for_date=get_last_day(add_months(getdate(), -1))
|
||||
)
|
||||
date = add_days(first_sunday, 1)
|
||||
|
||||
doj = add_days(date, 1)
|
||||
relieving_date = add_days(date, 5)
|
||||
employee = make_employee(
|
||||
"test_unmarked_days_as_per_doj@example.com", date_of_joining=doj, relieving_date=relieving_date
|
||||
)
|
||||
|
||||
frappe.db.set_value("Employee", employee, "holiday_list", self.holiday_list)
|
||||
|
||||
attendance_date = add_days(date, 2)
|
||||
mark_attendance(employee, attendance_date, "Present")
|
||||
month_name = get_month_name(attendance_date)
|
||||
|
||||
unmarked_days = get_unmarked_days(employee, month_name)
|
||||
unmarked_days = [getdate(date) for date in unmarked_days]
|
||||
|
||||
# attendance already marked for the day
|
||||
self.assertNotIn(attendance_date, unmarked_days)
|
||||
# date before doj not in unmarked days
|
||||
self.assertNotIn(add_days(doj, -1), unmarked_days)
|
||||
# date after relieving not in unmarked days
|
||||
self.assertNotIn(add_days(relieving_date, 1), unmarked_days)
|
||||
|
||||
def tearDown(self):
|
||||
frappe.db.rollback()
|
||||
|
||||
|
||||
def get_month_name(date):
|
||||
month_number = date.month
|
||||
for month, number in get_month_map().items():
|
||||
if number == month_number:
|
||||
return month
|
@ -1,10 +0,0 @@
|
||||
[
|
||||
{
|
||||
"doctype": "Attendance",
|
||||
"name": "_Test Attendance 1",
|
||||
"employee": "_T-Employee-00001",
|
||||
"status": "Present",
|
||||
"attendance_date": "2014-02-01",
|
||||
"company": "_Test Company"
|
||||
}
|
||||
]
|
@ -1,14 +0,0 @@
|
||||
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
cur_frm.add_fetch('employee', 'company', 'company');
|
||||
|
||||
frappe.ui.form.on('Attendance Request', {
|
||||
half_day: function(frm) {
|
||||
if(frm.doc.half_day == 1){
|
||||
frm.set_df_property('half_day_date', 'reqd', true);
|
||||
}
|
||||
else{
|
||||
frm.set_df_property('half_day_date', 'reqd', false);
|
||||
}
|
||||
}
|
||||
});
|
@ -1,190 +0,0 @@
|
||||
{
|
||||
"actions": [],
|
||||
"allow_import": 1,
|
||||
"allow_rename": 1,
|
||||
"autoname": "HR-ARQ-.YY.-.MM.-.#####",
|
||||
"creation": "2018-04-13 15:37:40.918990",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"employee",
|
||||
"employee_name",
|
||||
"department",
|
||||
"column_break_5",
|
||||
"company",
|
||||
"from_date",
|
||||
"to_date",
|
||||
"half_day",
|
||||
"half_day_date",
|
||||
"reason_section",
|
||||
"reason",
|
||||
"column_break_4",
|
||||
"explanation",
|
||||
"amended_from"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "employee",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Employee",
|
||||
"options": "Employee",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fetch_from": "employee.employee_name",
|
||||
"fieldname": "employee_name",
|
||||
"fieldtype": "Data",
|
||||
"label": "Employee Name",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fetch_from": "employee.department",
|
||||
"fieldname": "department",
|
||||
"fieldtype": "Link",
|
||||
"label": "Department",
|
||||
"options": "Department",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "company",
|
||||
"fieldtype": "Link",
|
||||
"label": "Company",
|
||||
"options": "Company",
|
||||
"remember_last_selected_value": 1,
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_5",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "from_date",
|
||||
"fieldtype": "Date",
|
||||
"in_list_view": 1,
|
||||
"label": "From Date",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "to_date",
|
||||
"fieldtype": "Date",
|
||||
"in_list_view": 1,
|
||||
"label": "To Date",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "half_day",
|
||||
"fieldtype": "Check",
|
||||
"label": "Half Day"
|
||||
},
|
||||
{
|
||||
"depends_on": "half_day",
|
||||
"fieldname": "half_day_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Half Day Date"
|
||||
},
|
||||
{
|
||||
"fieldname": "reason_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Reason"
|
||||
},
|
||||
{
|
||||
"fieldname": "reason",
|
||||
"fieldtype": "Select",
|
||||
"in_list_view": 1,
|
||||
"label": "Reason",
|
||||
"options": "Work From Home\nOn Duty",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_4",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "explanation",
|
||||
"fieldtype": "Small Text",
|
||||
"label": "Explanation"
|
||||
},
|
||||
{
|
||||
"fieldname": "amended_from",
|
||||
"fieldtype": "Link",
|
||||
"label": "Amended From",
|
||||
"no_copy": 1,
|
||||
"options": "Attendance Request",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2019-12-16 11:49:26.943173",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Attendance Request",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 1,
|
||||
"cancel": 1,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"share": 1,
|
||||
"submit": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"amend": 1,
|
||||
"cancel": 1,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "HR Manager",
|
||||
"share": 1,
|
||||
"submit": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"amend": 1,
|
||||
"cancel": 1,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "HR User",
|
||||
"share": 1,
|
||||
"submit": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Employee",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"title_field": "employee_name",
|
||||
"track_changes": 1
|
||||
}
|
@ -1,78 +0,0 @@
|
||||
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import add_days, date_diff, getdate
|
||||
|
||||
from erpnext.hr.doctype.employee.employee import is_holiday
|
||||
from erpnext.hr.utils import validate_active_employee, validate_dates
|
||||
|
||||
|
||||
class AttendanceRequest(Document):
|
||||
def validate(self):
|
||||
validate_active_employee(self.employee)
|
||||
validate_dates(self, self.from_date, self.to_date)
|
||||
if self.half_day:
|
||||
if not getdate(self.from_date) <= getdate(self.half_day_date) <= getdate(self.to_date):
|
||||
frappe.throw(_("Half day date should be in between from date and to date"))
|
||||
|
||||
def on_submit(self):
|
||||
self.create_attendance()
|
||||
|
||||
def on_cancel(self):
|
||||
attendance_list = frappe.get_list(
|
||||
"Attendance", {"employee": self.employee, "attendance_request": self.name}
|
||||
)
|
||||
if attendance_list:
|
||||
for attendance in attendance_list:
|
||||
attendance_obj = frappe.get_doc("Attendance", attendance["name"])
|
||||
attendance_obj.cancel()
|
||||
|
||||
def create_attendance(self):
|
||||
request_days = date_diff(self.to_date, self.from_date) + 1
|
||||
for number in range(request_days):
|
||||
attendance_date = add_days(self.from_date, number)
|
||||
skip_attendance = self.validate_if_attendance_not_applicable(attendance_date)
|
||||
if not skip_attendance:
|
||||
attendance = frappe.new_doc("Attendance")
|
||||
attendance.employee = self.employee
|
||||
attendance.employee_name = self.employee_name
|
||||
if self.half_day and date_diff(getdate(self.half_day_date), getdate(attendance_date)) == 0:
|
||||
attendance.status = "Half Day"
|
||||
elif self.reason == "Work From Home":
|
||||
attendance.status = "Work From Home"
|
||||
else:
|
||||
attendance.status = "Present"
|
||||
attendance.attendance_date = attendance_date
|
||||
attendance.company = self.company
|
||||
attendance.attendance_request = self.name
|
||||
attendance.save(ignore_permissions=True)
|
||||
attendance.submit()
|
||||
|
||||
def validate_if_attendance_not_applicable(self, attendance_date):
|
||||
# Check if attendance_date is a Holiday
|
||||
if is_holiday(self.employee, attendance_date):
|
||||
frappe.msgprint(
|
||||
_("Attendance not submitted for {0} as it is a Holiday.").format(attendance_date), alert=1
|
||||
)
|
||||
return True
|
||||
|
||||
# Check if employee on Leave
|
||||
leave_record = frappe.db.sql(
|
||||
"""select half_day from `tabLeave Application`
|
||||
where employee = %s and %s between from_date and to_date
|
||||
and docstatus = 1""",
|
||||
(self.employee, attendance_date),
|
||||
as_dict=True,
|
||||
)
|
||||
if leave_record:
|
||||
frappe.msgprint(
|
||||
_("Attendance not submitted for {0} as {1} on leave.").format(attendance_date, self.employee),
|
||||
alert=1,
|
||||
)
|
||||
return True
|
||||
|
||||
return False
|
@ -1,2 +0,0 @@
|
||||
def get_data():
|
||||
return {"fieldname": "attendance_request", "transactions": [{"items": ["Attendance"]}]}
|
@ -1,100 +0,0 @@
|
||||
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
|
||||
import unittest
|
||||
from datetime import date
|
||||
|
||||
import frappe
|
||||
from frappe.utils import nowdate
|
||||
|
||||
test_dependencies = ["Employee"]
|
||||
|
||||
|
||||
class TestAttendanceRequest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
for doctype in ["Attendance Request", "Attendance"]:
|
||||
frappe.db.sql("delete from `tab{doctype}`".format(doctype=doctype))
|
||||
|
||||
def tearDown(self):
|
||||
frappe.db.rollback()
|
||||
|
||||
def test_on_duty_attendance_request(self):
|
||||
"Test creation/updation of Attendace from Attendance Request, on duty."
|
||||
today = nowdate()
|
||||
employee = get_employee()
|
||||
attendance_request = frappe.new_doc("Attendance Request")
|
||||
attendance_request.employee = employee.name
|
||||
attendance_request.from_date = date(date.today().year, 1, 1)
|
||||
attendance_request.to_date = date(date.today().year, 1, 2)
|
||||
attendance_request.reason = "On Duty"
|
||||
attendance_request.company = "_Test Company"
|
||||
attendance_request.insert()
|
||||
attendance_request.submit()
|
||||
|
||||
attendance = frappe.db.get_value(
|
||||
"Attendance",
|
||||
filters={
|
||||
"attendance_request": attendance_request.name,
|
||||
"attendance_date": date(date.today().year, 1, 1),
|
||||
},
|
||||
fieldname=["status", "docstatus"],
|
||||
as_dict=True,
|
||||
)
|
||||
self.assertEqual(attendance.status, "Present")
|
||||
self.assertEqual(attendance.docstatus, 1)
|
||||
|
||||
# cancelling attendance request cancels linked attendances
|
||||
attendance_request.cancel()
|
||||
|
||||
# cancellation alters docname
|
||||
# fetch attendance value again to avoid stale docname
|
||||
attendance_docstatus = frappe.db.get_value(
|
||||
"Attendance",
|
||||
filters={
|
||||
"attendance_request": attendance_request.name,
|
||||
"attendance_date": date(date.today().year, 1, 1),
|
||||
},
|
||||
fieldname="docstatus",
|
||||
)
|
||||
self.assertEqual(attendance_docstatus, 2)
|
||||
|
||||
def test_work_from_home_attendance_request(self):
|
||||
"Test creation/updation of Attendace from Attendance Request, work from home."
|
||||
today = nowdate()
|
||||
employee = get_employee()
|
||||
attendance_request = frappe.new_doc("Attendance Request")
|
||||
attendance_request.employee = employee.name
|
||||
attendance_request.from_date = date(date.today().year, 1, 1)
|
||||
attendance_request.to_date = date(date.today().year, 1, 2)
|
||||
attendance_request.reason = "Work From Home"
|
||||
attendance_request.company = "_Test Company"
|
||||
attendance_request.insert()
|
||||
attendance_request.submit()
|
||||
|
||||
attendance_status = frappe.db.get_value(
|
||||
"Attendance",
|
||||
filters={
|
||||
"attendance_request": attendance_request.name,
|
||||
"attendance_date": date(date.today().year, 1, 1),
|
||||
},
|
||||
fieldname="status",
|
||||
)
|
||||
self.assertEqual(attendance_status, "Work From Home")
|
||||
|
||||
attendance_request.cancel()
|
||||
|
||||
# cancellation alters docname
|
||||
# fetch attendance value again to avoid stale docname
|
||||
attendance_docstatus = frappe.db.get_value(
|
||||
"Attendance",
|
||||
filters={
|
||||
"attendance_request": attendance_request.name,
|
||||
"attendance_date": date(date.today().year, 1, 1),
|
||||
},
|
||||
fieldname="docstatus",
|
||||
)
|
||||
self.assertEqual(attendance_docstatus, 2)
|
||||
|
||||
|
||||
def get_employee():
|
||||
return frappe.get_doc("Employee", "_T-Employee-00001")
|
@ -1,103 +0,0 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_import": 1,
|
||||
"allow_rename": 1,
|
||||
"autoname": "field:branch",
|
||||
"beta": 0,
|
||||
"creation": "2013-01-10 16:34:13",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "Setup",
|
||||
"editable_grid": 0,
|
||||
"fields": [
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "branch",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Branch",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "branch",
|
||||
"oldfieldtype": "Data",
|
||||
"permlevel": 0,
|
||||
"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,
|
||||
"icon": "fa fa-code-fork",
|
||||
"idx": 1,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2016-07-25 05:24:26.534086",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Branch",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 0,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "HR User",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 0,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "HR Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"quick_entry": 1,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"track_seen": 0
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Compensatory Leave Request', {
|
||||
refresh: function(frm) {
|
||||
frm.set_query("leave_type", function() {
|
||||
return {
|
||||
filters: {
|
||||
"is_compensatory": true
|
||||
}
|
||||
};
|
||||
});
|
||||
},
|
||||
half_day: function(frm) {
|
||||
if(frm.doc.half_day == 1){
|
||||
frm.set_df_property('half_day_date', 'reqd', true);
|
||||
}
|
||||
else{
|
||||
frm.set_df_property('half_day_date', 'reqd', false);
|
||||
}
|
||||
}
|
||||
});
|
@ -1,575 +0,0 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 1,
|
||||
"allow_rename": 0,
|
||||
"autoname": "HR-CMP-.YY.-.MM.-.#####",
|
||||
"beta": 0,
|
||||
"creation": "2018-04-13 14:51:39.326768",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "employee",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 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,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_from": "employee.employee_name",
|
||||
"fieldname": "employee_name",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Employee Name",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_from": "employee.department",
|
||||
"fieldname": "department",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Department",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Department",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "column_break_2",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "leave_type",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Leave Type",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Leave 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": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "leave_allocation",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Leave Allocation",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Leave Allocation",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "worked_on",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Worked On Holiday",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "work_from_date",
|
||||
"fieldtype": "Date",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Work From Date",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "work_end_date",
|
||||
"fieldtype": "Date",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Work End Date",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "half_day",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Half Day",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "half_day",
|
||||
"fieldname": "half_day_date",
|
||||
"fieldtype": "Date",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Half Day Date",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "column_break_4",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "reason",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Reason",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "amended_from",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Amended From",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"options": "Compensatory Leave Request",
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"is_submittable": 1,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-08-21 16:15:36.266924",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Compensatory Leave Request",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 1,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "HR Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "HR User",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Employee",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"quick_entry": 0,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"title_field": "employee_name",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0,
|
||||
"track_views": 0
|
||||
}
|
@ -1,155 +0,0 @@
|
||||
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import add_days, cint, date_diff, format_date, getdate
|
||||
|
||||
from erpnext.hr.utils import (
|
||||
create_additional_leave_ledger_entry,
|
||||
get_holiday_dates_for_employee,
|
||||
get_leave_period,
|
||||
validate_active_employee,
|
||||
validate_dates,
|
||||
validate_overlap,
|
||||
)
|
||||
|
||||
|
||||
class CompensatoryLeaveRequest(Document):
|
||||
def validate(self):
|
||||
validate_active_employee(self.employee)
|
||||
validate_dates(self, self.work_from_date, self.work_end_date)
|
||||
if self.half_day:
|
||||
if not self.half_day_date:
|
||||
frappe.throw(_("Half Day Date is mandatory"))
|
||||
if (
|
||||
not getdate(self.work_from_date) <= getdate(self.half_day_date) <= getdate(self.work_end_date)
|
||||
):
|
||||
frappe.throw(_("Half Day Date should be in between Work From Date and Work End Date"))
|
||||
validate_overlap(self, self.work_from_date, self.work_end_date)
|
||||
self.validate_holidays()
|
||||
self.validate_attendance()
|
||||
if not self.leave_type:
|
||||
frappe.throw(_("Leave Type is madatory"))
|
||||
|
||||
def validate_attendance(self):
|
||||
attendance = frappe.get_all(
|
||||
"Attendance",
|
||||
filters={
|
||||
"attendance_date": ["between", (self.work_from_date, self.work_end_date)],
|
||||
"status": "Present",
|
||||
"docstatus": 1,
|
||||
"employee": self.employee,
|
||||
},
|
||||
fields=["attendance_date", "status"],
|
||||
)
|
||||
|
||||
if len(attendance) < date_diff(self.work_end_date, self.work_from_date) + 1:
|
||||
frappe.throw(_("You are not present all day(s) between compensatory leave request days"))
|
||||
|
||||
def validate_holidays(self):
|
||||
holidays = get_holiday_dates_for_employee(self.employee, self.work_from_date, self.work_end_date)
|
||||
if len(holidays) < date_diff(self.work_end_date, self.work_from_date) + 1:
|
||||
if date_diff(self.work_end_date, self.work_from_date):
|
||||
msg = _("The days between {0} to {1} are not valid holidays.").format(
|
||||
frappe.bold(format_date(self.work_from_date)), frappe.bold(format_date(self.work_end_date))
|
||||
)
|
||||
else:
|
||||
msg = _("{0} is not a holiday.").format(frappe.bold(format_date(self.work_from_date)))
|
||||
|
||||
frappe.throw(msg)
|
||||
|
||||
def on_submit(self):
|
||||
company = frappe.db.get_value("Employee", self.employee, "company")
|
||||
date_difference = date_diff(self.work_end_date, self.work_from_date) + 1
|
||||
if self.half_day:
|
||||
date_difference -= 0.5
|
||||
leave_period = get_leave_period(self.work_from_date, self.work_end_date, company)
|
||||
if leave_period:
|
||||
leave_allocation = self.get_existing_allocation_for_period(leave_period)
|
||||
if leave_allocation:
|
||||
leave_allocation.new_leaves_allocated += date_difference
|
||||
leave_allocation.validate()
|
||||
leave_allocation.db_set("new_leaves_allocated", leave_allocation.total_leaves_allocated)
|
||||
leave_allocation.db_set("total_leaves_allocated", leave_allocation.total_leaves_allocated)
|
||||
|
||||
# generate additional ledger entry for the new compensatory leaves off
|
||||
create_additional_leave_ledger_entry(
|
||||
leave_allocation, date_difference, add_days(self.work_end_date, 1)
|
||||
)
|
||||
|
||||
else:
|
||||
leave_allocation = self.create_leave_allocation(leave_period, date_difference)
|
||||
self.db_set("leave_allocation", leave_allocation.name)
|
||||
else:
|
||||
frappe.throw(
|
||||
_("There is no leave period in between {0} and {1}").format(
|
||||
format_date(self.work_from_date), format_date(self.work_end_date)
|
||||
)
|
||||
)
|
||||
|
||||
def on_cancel(self):
|
||||
if self.leave_allocation:
|
||||
date_difference = date_diff(self.work_end_date, self.work_from_date) + 1
|
||||
if self.half_day:
|
||||
date_difference -= 0.5
|
||||
leave_allocation = frappe.get_doc("Leave Allocation", self.leave_allocation)
|
||||
if leave_allocation:
|
||||
leave_allocation.new_leaves_allocated -= date_difference
|
||||
if leave_allocation.new_leaves_allocated - date_difference <= 0:
|
||||
leave_allocation.new_leaves_allocated = 0
|
||||
leave_allocation.validate()
|
||||
leave_allocation.db_set("new_leaves_allocated", leave_allocation.total_leaves_allocated)
|
||||
leave_allocation.db_set("total_leaves_allocated", leave_allocation.total_leaves_allocated)
|
||||
|
||||
# create reverse entry on cancelation
|
||||
create_additional_leave_ledger_entry(
|
||||
leave_allocation, date_difference * -1, add_days(self.work_end_date, 1)
|
||||
)
|
||||
|
||||
def get_existing_allocation_for_period(self, leave_period):
|
||||
leave_allocation = frappe.db.sql(
|
||||
"""
|
||||
select name
|
||||
from `tabLeave Allocation`
|
||||
where employee=%(employee)s and leave_type=%(leave_type)s
|
||||
and docstatus=1
|
||||
and (from_date between %(from_date)s and %(to_date)s
|
||||
or to_date between %(from_date)s and %(to_date)s
|
||||
or (from_date < %(from_date)s and to_date > %(to_date)s))
|
||||
""",
|
||||
{
|
||||
"from_date": leave_period[0].from_date,
|
||||
"to_date": leave_period[0].to_date,
|
||||
"employee": self.employee,
|
||||
"leave_type": self.leave_type,
|
||||
},
|
||||
as_dict=1,
|
||||
)
|
||||
|
||||
if leave_allocation:
|
||||
return frappe.get_doc("Leave Allocation", leave_allocation[0].name)
|
||||
else:
|
||||
return False
|
||||
|
||||
def create_leave_allocation(self, leave_period, date_difference):
|
||||
is_carry_forward = frappe.db.get_value("Leave Type", self.leave_type, "is_carry_forward")
|
||||
allocation = frappe.get_doc(
|
||||
dict(
|
||||
doctype="Leave Allocation",
|
||||
employee=self.employee,
|
||||
employee_name=self.employee_name,
|
||||
leave_type=self.leave_type,
|
||||
from_date=add_days(self.work_end_date, 1),
|
||||
to_date=leave_period[0].to_date,
|
||||
carry_forward=cint(is_carry_forward),
|
||||
new_leaves_allocated=date_difference,
|
||||
total_leaves_allocated=date_difference,
|
||||
description=self.reason,
|
||||
)
|
||||
)
|
||||
allocation.insert(ignore_permissions=True)
|
||||
allocation.submit()
|
||||
return allocation
|
@ -1,157 +0,0 @@
|
||||
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
|
||||
import unittest
|
||||
|
||||
import frappe
|
||||
from frappe.utils import add_days, add_months, today
|
||||
|
||||
from erpnext.hr.doctype.attendance_request.test_attendance_request import get_employee
|
||||
from erpnext.hr.doctype.leave_application.leave_application import get_leave_balance_on
|
||||
from erpnext.hr.doctype.leave_period.test_leave_period import create_leave_period
|
||||
|
||||
test_dependencies = ["Employee"]
|
||||
|
||||
|
||||
class TestCompensatoryLeaveRequest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
frappe.db.sql(""" delete from `tabCompensatory Leave Request`""")
|
||||
frappe.db.sql(""" delete from `tabLeave Ledger Entry`""")
|
||||
frappe.db.sql(""" delete from `tabLeave Allocation`""")
|
||||
frappe.db.sql(
|
||||
""" delete from `tabAttendance` where attendance_date in {0} """.format(
|
||||
(today(), add_days(today(), -1))
|
||||
)
|
||||
) # nosec
|
||||
create_leave_period(add_months(today(), -3), add_months(today(), 3), "_Test Company")
|
||||
create_holiday_list()
|
||||
|
||||
employee = get_employee()
|
||||
employee.holiday_list = "_Test Compensatory Leave"
|
||||
employee.save()
|
||||
|
||||
def test_leave_balance_on_submit(self):
|
||||
"""check creation of leave allocation on submission of compensatory leave request"""
|
||||
employee = get_employee()
|
||||
mark_attendance(employee)
|
||||
compensatory_leave_request = get_compensatory_leave_request(employee.name)
|
||||
|
||||
before = get_leave_balance_on(employee.name, compensatory_leave_request.leave_type, today())
|
||||
compensatory_leave_request.submit()
|
||||
|
||||
self.assertEqual(
|
||||
get_leave_balance_on(
|
||||
employee.name, compensatory_leave_request.leave_type, add_days(today(), 1)
|
||||
),
|
||||
before + 1,
|
||||
)
|
||||
|
||||
def test_leave_allocation_update_on_submit(self):
|
||||
employee = get_employee()
|
||||
mark_attendance(employee, date=add_days(today(), -1))
|
||||
compensatory_leave_request = get_compensatory_leave_request(
|
||||
employee.name, leave_date=add_days(today(), -1)
|
||||
)
|
||||
compensatory_leave_request.submit()
|
||||
|
||||
# leave allocation creation on submit
|
||||
leaves_allocated = frappe.db.get_value(
|
||||
"Leave Allocation",
|
||||
{"name": compensatory_leave_request.leave_allocation},
|
||||
["total_leaves_allocated"],
|
||||
)
|
||||
self.assertEqual(leaves_allocated, 1)
|
||||
|
||||
mark_attendance(employee)
|
||||
compensatory_leave_request = get_compensatory_leave_request(employee.name)
|
||||
compensatory_leave_request.submit()
|
||||
|
||||
# leave allocation updates on submission of second compensatory leave request
|
||||
leaves_allocated = frappe.db.get_value(
|
||||
"Leave Allocation",
|
||||
{"name": compensatory_leave_request.leave_allocation},
|
||||
["total_leaves_allocated"],
|
||||
)
|
||||
self.assertEqual(leaves_allocated, 2)
|
||||
|
||||
def test_creation_of_leave_ledger_entry_on_submit(self):
|
||||
"""check creation of leave ledger entry on submission of leave request"""
|
||||
employee = get_employee()
|
||||
mark_attendance(employee)
|
||||
compensatory_leave_request = get_compensatory_leave_request(employee.name)
|
||||
compensatory_leave_request.submit()
|
||||
|
||||
filters = dict(transaction_name=compensatory_leave_request.leave_allocation)
|
||||
leave_ledger_entry = frappe.get_all("Leave Ledger Entry", fields="*", filters=filters)
|
||||
|
||||
self.assertEqual(len(leave_ledger_entry), 1)
|
||||
self.assertEqual(leave_ledger_entry[0].employee, compensatory_leave_request.employee)
|
||||
self.assertEqual(leave_ledger_entry[0].leave_type, compensatory_leave_request.leave_type)
|
||||
self.assertEqual(leave_ledger_entry[0].leaves, 1)
|
||||
|
||||
# check reverse leave ledger entry on cancellation
|
||||
compensatory_leave_request.cancel()
|
||||
leave_ledger_entry = frappe.get_all(
|
||||
"Leave Ledger Entry", fields="*", filters=filters, order_by="creation desc"
|
||||
)
|
||||
|
||||
self.assertEqual(len(leave_ledger_entry), 2)
|
||||
self.assertEqual(leave_ledger_entry[0].employee, compensatory_leave_request.employee)
|
||||
self.assertEqual(leave_ledger_entry[0].leave_type, compensatory_leave_request.leave_type)
|
||||
self.assertEqual(leave_ledger_entry[0].leaves, -1)
|
||||
|
||||
|
||||
def get_compensatory_leave_request(employee, leave_date=today()):
|
||||
prev_comp_leave_req = frappe.db.get_value(
|
||||
"Compensatory Leave Request",
|
||||
dict(
|
||||
leave_type="Compensatory Off",
|
||||
work_from_date=leave_date,
|
||||
work_end_date=leave_date,
|
||||
employee=employee,
|
||||
),
|
||||
"name",
|
||||
)
|
||||
if prev_comp_leave_req:
|
||||
return frappe.get_doc("Compensatory Leave Request", prev_comp_leave_req)
|
||||
|
||||
return frappe.get_doc(
|
||||
dict(
|
||||
doctype="Compensatory Leave Request",
|
||||
employee=employee,
|
||||
leave_type="Compensatory Off",
|
||||
work_from_date=leave_date,
|
||||
work_end_date=leave_date,
|
||||
reason="test",
|
||||
)
|
||||
).insert()
|
||||
|
||||
|
||||
def mark_attendance(employee, date=today(), status="Present"):
|
||||
if not frappe.db.exists(
|
||||
dict(doctype="Attendance", employee=employee.name, attendance_date=date, status="Present")
|
||||
):
|
||||
attendance = frappe.get_doc(
|
||||
{"doctype": "Attendance", "employee": employee.name, "attendance_date": date, "status": status}
|
||||
)
|
||||
attendance.save()
|
||||
attendance.submit()
|
||||
|
||||
|
||||
def create_holiday_list():
|
||||
if frappe.db.exists("Holiday List", "_Test Compensatory Leave"):
|
||||
return
|
||||
|
||||
holiday_list = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Holiday List",
|
||||
"from_date": add_months(today(), -3),
|
||||
"to_date": add_months(today(), 3),
|
||||
"holidays": [
|
||||
{"description": "Test Holiday", "holiday_date": today()},
|
||||
{"description": "Test Holiday 1", "holiday_date": add_days(today(), -1)},
|
||||
],
|
||||
"holiday_list_name": "_Test Compensatory Leave",
|
||||
}
|
||||
)
|
||||
holiday_list.save()
|
@ -1,8 +0,0 @@
|
||||
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Daily Work Summary', {
|
||||
refresh: function (frm) {
|
||||
|
||||
}
|
||||
});
|
@ -1,175 +0,0 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"beta": 0,
|
||||
"creation": "2016-11-08 04:58:20.001780",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "Document",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "daily_work_summary_group",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Daily Work Summary Group",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Daily Work Summary Group",
|
||||
"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_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "Open",
|
||||
"fieldname": "status",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Status",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Open\nSent",
|
||||
"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_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "email_sent_to",
|
||||
"fieldtype": "Code",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Email Sent To",
|
||||
"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
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"image_view": 0,
|
||||
"in_create": 1,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-02-16 11:02:06.727749",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Daily Work Summary",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 0,
|
||||
"delete": 0,
|
||||
"email": 0,
|
||||
"export": 0,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 0,
|
||||
"read": 1,
|
||||
"report": 0,
|
||||
"role": "Employee",
|
||||
"set_user_permissions": 0,
|
||||
"share": 0,
|
||||
"submit": 0,
|
||||
"write": 0
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 0,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "HR User",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 0
|
||||
}
|
||||
],
|
||||
"quick_entry": 1,
|
||||
"read_only": 1,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 0,
|
||||
"track_seen": 0
|
||||
}
|
@ -1,119 +0,0 @@
|
||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
|
||||
import frappe
|
||||
from email_reply_parser import EmailReplyParser
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import global_date_format
|
||||
|
||||
|
||||
class DailyWorkSummary(Document):
|
||||
def send_mails(self, dws_group, emails):
|
||||
"""Send emails to get daily work summary to all users \
|
||||
in selected daily work summary group"""
|
||||
incoming_email_account = frappe.db.get_value(
|
||||
"Email Account", dict(enable_incoming=1, default_incoming=1), "email_id"
|
||||
)
|
||||
|
||||
self.db_set("email_sent_to", "\n".join(emails))
|
||||
frappe.sendmail(
|
||||
recipients=emails,
|
||||
message=dws_group.message,
|
||||
subject=dws_group.subject,
|
||||
reference_doctype=self.doctype,
|
||||
reference_name=self.name,
|
||||
reply_to=incoming_email_account,
|
||||
)
|
||||
|
||||
def send_summary(self):
|
||||
"""Send summary of all replies. Called at midnight"""
|
||||
args = self.get_message_details()
|
||||
emails = get_user_emails_from_group(self.daily_work_summary_group)
|
||||
frappe.sendmail(
|
||||
recipients=emails,
|
||||
template="daily_work_summary",
|
||||
args=args,
|
||||
subject=_(self.daily_work_summary_group),
|
||||
reference_doctype=self.doctype,
|
||||
reference_name=self.name,
|
||||
)
|
||||
|
||||
self.db_set("status", "Sent")
|
||||
|
||||
def get_message_details(self):
|
||||
"""Return args for template"""
|
||||
dws_group = frappe.get_doc("Daily Work Summary Group", self.daily_work_summary_group)
|
||||
|
||||
replies = frappe.get_all(
|
||||
"Communication",
|
||||
fields=["content", "text_content", "sender"],
|
||||
filters=dict(
|
||||
reference_doctype=self.doctype,
|
||||
reference_name=self.name,
|
||||
communication_type="Communication",
|
||||
sent_or_received="Received",
|
||||
),
|
||||
order_by="creation asc",
|
||||
)
|
||||
|
||||
did_not_reply = self.email_sent_to.split()
|
||||
|
||||
for d in replies:
|
||||
user = frappe.db.get_values(
|
||||
"User", {"email": d.sender}, ["full_name", "user_image"], as_dict=True
|
||||
)
|
||||
|
||||
d.sender_name = user[0].full_name if user else d.sender
|
||||
d.image = user[0].image if user and user[0].image else None
|
||||
|
||||
original_image = d.image
|
||||
# make thumbnail image
|
||||
try:
|
||||
if original_image:
|
||||
file_name = frappe.get_list("File", {"file_url": original_image})
|
||||
|
||||
if file_name:
|
||||
file_name = file_name[0].name
|
||||
file_doc = frappe.get_doc("File", file_name)
|
||||
thumbnail_image = file_doc.make_thumbnail(
|
||||
set_as_thumbnail=False, width=100, height=100, crop=True
|
||||
)
|
||||
d.image = thumbnail_image
|
||||
except Exception:
|
||||
d.image = original_image
|
||||
|
||||
if d.sender in did_not_reply:
|
||||
did_not_reply.remove(d.sender)
|
||||
if d.text_content:
|
||||
d.content = frappe.utils.md_to_html(EmailReplyParser.parse_reply(d.text_content))
|
||||
|
||||
did_not_reply = [
|
||||
(frappe.db.get_value("User", {"email": email}, "full_name") or email) for email in did_not_reply
|
||||
]
|
||||
|
||||
return dict(
|
||||
replies=replies,
|
||||
original_message=dws_group.message,
|
||||
title=_("Work Summary for {0}").format(global_date_format(self.creation)),
|
||||
did_not_reply=", ".join(did_not_reply) or "",
|
||||
did_not_reply_title=_("No replies from"),
|
||||
)
|
||||
|
||||
|
||||
def get_user_emails_from_group(group):
|
||||
"""Returns list of email of enabled users from the given group
|
||||
|
||||
:param group: Daily Work Summary Group `name`"""
|
||||
group_doc = group
|
||||
if isinstance(group_doc, str):
|
||||
group_doc = frappe.get_doc("Daily Work Summary Group", group)
|
||||
|
||||
emails = get_users_email(group_doc)
|
||||
|
||||
return emails
|
||||
|
||||
|
||||
def get_users_email(doc):
|
||||
return [d.email for d in doc.users if frappe.db.get_value("User", d.user, "enabled")]
|
@ -1,105 +0,0 @@
|
||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
|
||||
import os
|
||||
import unittest
|
||||
|
||||
import frappe
|
||||
import frappe.utils
|
||||
|
||||
# test_records = frappe.get_test_records('Daily Work Summary')
|
||||
|
||||
|
||||
class TestDailyWorkSummary(unittest.TestCase):
|
||||
def test_email_trigger(self):
|
||||
self.setup_and_prepare_test()
|
||||
for d in self.users:
|
||||
# check that email is sent to users
|
||||
if d.message:
|
||||
self.assertTrue(
|
||||
d.email in [d.recipient for d in self.emails if self.groups.subject in d.message]
|
||||
)
|
||||
|
||||
def test_email_trigger_failed(self):
|
||||
hour = "00:00"
|
||||
if frappe.utils.nowtime().split(":")[0] == "00":
|
||||
hour = "01:00"
|
||||
|
||||
self.setup_and_prepare_test(hour)
|
||||
|
||||
for d in self.users:
|
||||
# check that email is not sent to users
|
||||
self.assertFalse(
|
||||
d.email in [d.recipient for d in self.emails if self.groups.subject in d.message]
|
||||
)
|
||||
|
||||
def test_incoming(self):
|
||||
# get test mail with message-id as in-reply-to
|
||||
self.setup_and_prepare_test()
|
||||
with open(os.path.join(os.path.dirname(__file__), "test_data", "test-reply.raw"), "r") as f:
|
||||
if not self.emails:
|
||||
return
|
||||
test_mails = [
|
||||
f.read()
|
||||
.replace("{{ sender }}", self.users[-1].email)
|
||||
.replace("{{ message_id }}", self.emails[-1].message_id)
|
||||
]
|
||||
|
||||
# pull the mail
|
||||
email_account = frappe.get_doc("Email Account", "_Test Email Account 1")
|
||||
email_account.db_set("enable_incoming", 1)
|
||||
email_account.receive(test_mails=test_mails)
|
||||
|
||||
daily_work_summary = frappe.get_doc(
|
||||
"Daily Work Summary", frappe.get_all("Daily Work Summary")[0].name
|
||||
)
|
||||
|
||||
args = daily_work_summary.get_message_details()
|
||||
|
||||
self.assertTrue("I built Daily Work Summary!" in args.get("replies")[0].content)
|
||||
|
||||
def setup_and_prepare_test(self, hour=None):
|
||||
frappe.db.sql("delete from `tabDaily Work Summary`")
|
||||
frappe.db.sql("delete from `tabEmail Queue`")
|
||||
frappe.db.sql("delete from `tabEmail Queue Recipient`")
|
||||
frappe.db.sql("delete from `tabCommunication`")
|
||||
frappe.db.sql("delete from `tabDaily Work Summary Group`")
|
||||
|
||||
self.users = frappe.get_all(
|
||||
"User", fields=["email"], filters=dict(email=("!=", "test@example.com"))
|
||||
)
|
||||
self.setup_groups(hour)
|
||||
|
||||
from erpnext.hr.doctype.daily_work_summary_group.daily_work_summary_group import trigger_emails
|
||||
|
||||
trigger_emails()
|
||||
|
||||
# check if emails are created
|
||||
|
||||
self.emails = frappe.db.sql(
|
||||
"""select r.recipient, q.message, q.message_id \
|
||||
from `tabEmail Queue` as q, `tabEmail Queue Recipient` as r \
|
||||
where q.name = r.parent""",
|
||||
as_dict=1,
|
||||
)
|
||||
|
||||
def setup_groups(self, hour=None):
|
||||
# setup email to trigger at this hour
|
||||
if not hour:
|
||||
hour = frappe.utils.nowtime().split(":")[0]
|
||||
hour = hour + ":00"
|
||||
|
||||
groups = frappe.get_doc(
|
||||
dict(
|
||||
doctype="Daily Work Summary Group",
|
||||
name="Daily Work Summary",
|
||||
users=self.users,
|
||||
send_emails_at=hour,
|
||||
subject="this is a subject for testing summary emails",
|
||||
message="this is a message for testing summary emails",
|
||||
)
|
||||
)
|
||||
groups.insert()
|
||||
|
||||
self.groups = groups
|
||||
self.groups.save()
|
@ -1,75 +0,0 @@
|
||||
From: {{ sender }}
|
||||
Content-Type: multipart/alternative;
|
||||
boundary="Apple-Mail=_29597CF7-20DD-4184-B3FA-85582C5C4361"
|
||||
Message-Id: <07D687F6-10AA-4B9F-82DE-27753096164E@gmail.com>
|
||||
Mime-Version: 1.0 (Mac OS X Mail 9.3 \(3124\))
|
||||
X-Smtp-Server: 73CC8281-7E8F-4B47-8324-D5DA86EEDD4F
|
||||
Subject: Re: What did you work on today?
|
||||
Date: Thu, 10 Nov 2016 16:04:43 +0530
|
||||
X-Universally-Unique-Identifier: A4D9669F-179C-42D8-A3D3-AA6A8C49A6F2
|
||||
References: <{{ message_id }}>
|
||||
To: test_in@iwebnotes.com
|
||||
In-Reply-To: <{{ message_id }}>
|
||||
|
||||
|
||||
--Apple-Mail=_29597CF7-20DD-4184-B3FA-85582C5C4361
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
Content-Type: text/plain;
|
||||
charset=us-ascii
|
||||
|
||||
I built Daily Work Summary!
|
||||
|
||||
> On 10-Nov-2016, at 3:20 PM, Frappe <test@erpnext.com> wrote:
|
||||
>=20
|
||||
> Please share what did you do today. If you reply by midnight, your =
|
||||
response will be recorded!
|
||||
>=20
|
||||
> This email was sent to rmehta@gmail.com
|
||||
> Unsubscribe from this list =
|
||||
<http://demo-test.erpnext.com.dev/api/method/frappe.email.queue.unsubscrib=
|
||||
e?email=3Drmehta%40gmail.com&name=3D26cc3e5a5d&doctype=3DDaily+Work+Summar=
|
||||
y&_signature=3D2c7ab37e6d775e5a481e9b4376154a41>
|
||||
> Sent via ERPNext <https://erpnext.com/?source=3Dvia_email_footer>
|
||||
|
||||
|
||||
--Apple-Mail=_29597CF7-20DD-4184-B3FA-85582C5C4361
|
||||
Content-Transfer-Encoding: 7bit
|
||||
Content-Type: text/html;
|
||||
charset=us-ascii
|
||||
|
||||
<html><head><meta http-equiv="Content-Type" content="text/html charset=us-ascii"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class="">I built Daily Work Summary!<div class=""><br class=""><div><blockquote type="cite" class=""><div class="">On 10-Nov-2016, at 3:20 PM, Frappe <<a href="mailto:test@erpnext.com" class="">test@erpnext.com</a>> wrote:</div><br class="Apple-interchange-newline"><div class="">
|
||||
|
||||
|
||||
<meta name="viewport" content="width=device-width" class="">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" class="">
|
||||
<title class="">What did you work on today?</title>
|
||||
|
||||
<div style="line-height: 1.5; color: #36414C;" class="">
|
||||
<!-- body -->
|
||||
<div style="font-family: -apple-system, BlinkMacSystemFont,
|
||||
" segoe="" ui",="" "roboto",="" "oxygen",="" "ubuntu",="" "cantarell",="" "fira="" sans",="" "droid="" "helvetica="" neue",="" sans-serif;="" font-size:="" 14px;="" padding:="" 10px;"="" class=""><p class="">Please share what did you do today. If you reply by midnight, your response will be recorded!</p>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- footer -->
|
||||
<div style="margin-top: 30px; font-family: Helvetica, Arial, sans-serif; font-size: 11px;
|
||||
margin-bottom: 15px; border-top: 1px solid #d1d8dd;" data-email-footer="true" class="">
|
||||
<div style="margin: 15px auto; padding: 0px 7px; text-align: center; color: #8d99a6;" class="">
|
||||
This email was sent to <a href="mailto:rmehta@gmail.com" class="">rmehta@gmail.com</a>
|
||||
<p style="margin: 15px auto;" class="">
|
||||
<a href="http://demo-test.erpnext.com.dev/api/method/frappe.email.queue.unsubscribe?email=rmehta%40gmail.com&name=26cc3e5a5d&doctype=Daily+Work+Summary&_signature=2c7ab37e6d775e5a481e9b4376154a41" style="color: #8d99a6; text-decoration: underline;
|
||||
target=" _blank"="" class="">Unsubscribe from this list
|
||||
</a>
|
||||
</p>
|
||||
</div><div style="margin: 15px auto;" class=""><div style="text-align: center;" class="">
|
||||
<a href="https://erpnext.com/?source=via_email_footer" target="_blank" style="color: #8d99a6;" class="">
|
||||
Sent via ERPNext
|
||||
</a>
|
||||
</div></div>
|
||||
</div>
|
||||
<!-- /footer -->
|
||||
|
||||
<div class="print-html"></div>
|
||||
</div>
|
||||
</div></blockquote></div><br class=""></div></body></html>
|
||||
--Apple-Mail=_29597CF7-20DD-4184-B3FA-85582C5C4361--
|
@ -1,12 +0,0 @@
|
||||
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Daily Work Summary Group', {
|
||||
refresh: function (frm) {
|
||||
if (!frm.is_new()) {
|
||||
frm.add_custom_button(__('Daily Work Summary'), function () {
|
||||
frappe.set_route('List', 'Daily Work Summary');
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
@ -1,309 +0,0 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"autoname": "Prompt",
|
||||
"beta": 0,
|
||||
"creation": "2018-02-12 15:06:18.767239",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "1",
|
||||
"fieldname": "enabled",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Enabled",
|
||||
"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_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "select_users",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Select Users",
|
||||
"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_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "users",
|
||||
"fieldtype": "Table",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Users",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Daily Work Summary Group User",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "send_emails_at",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Send Emails At",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "00:00\n01:00\n02:00\n03:00\n04:00\n05:00\n06:00\n07:00\n08:00\n09:00\n10:00\n11:00\n12:00\n13:00\n14:00\n15:00\n16:00\n17:00\n18:00\n19:00\n20:00\n21:00\n22:00\n23:00",
|
||||
"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_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "holiday_list",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Holiday List",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Holiday List",
|
||||
"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_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "mail_details",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Reminder",
|
||||
"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_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "What did you work on today?",
|
||||
"fieldname": "subject",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Subject",
|
||||
"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_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "<p>Please share what did you do today. If you reply by midnight, your response will be recorded!</p>",
|
||||
"fieldname": "message",
|
||||
"fieldtype": "Text Editor",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Message",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-02-16 10:56:03.998495",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Daily Work Summary Group",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "HR Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"quick_entry": 1,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 0,
|
||||
"track_seen": 0
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
# # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# # For license information, please see license.txt
|
||||
|
||||
|
||||
import frappe
|
||||
import frappe.utils
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
|
||||
from erpnext.hr.doctype.daily_work_summary.daily_work_summary import get_user_emails_from_group
|
||||
from erpnext.hr.doctype.holiday_list.holiday_list import is_holiday
|
||||
|
||||
|
||||
class DailyWorkSummaryGroup(Document):
|
||||
def validate(self):
|
||||
if self.users:
|
||||
if not frappe.flags.in_test and not is_incoming_account_enabled():
|
||||
frappe.throw(
|
||||
_("Please enable default incoming account before creating Daily Work Summary Group")
|
||||
)
|
||||
|
||||
|
||||
def trigger_emails():
|
||||
"""Send emails to Employees at the given hour asking
|
||||
them what did they work on today"""
|
||||
groups = frappe.get_all("Daily Work Summary Group")
|
||||
for d in groups:
|
||||
group_doc = frappe.get_doc("Daily Work Summary Group", d)
|
||||
if (
|
||||
is_current_hour(group_doc.send_emails_at)
|
||||
and not is_holiday(group_doc.holiday_list)
|
||||
and group_doc.enabled
|
||||
):
|
||||
emails = get_user_emails_from_group(group_doc)
|
||||
# find emails relating to a company
|
||||
if emails:
|
||||
daily_work_summary = frappe.get_doc(
|
||||
dict(doctype="Daily Work Summary", daily_work_summary_group=group_doc.name)
|
||||
).insert()
|
||||
daily_work_summary.send_mails(group_doc, emails)
|
||||
|
||||
|
||||
def is_current_hour(hour):
|
||||
return frappe.utils.nowtime().split(":")[0] == hour.split(":")[0]
|
||||
|
||||
|
||||
def send_summary():
|
||||
"""Send summary to everyone"""
|
||||
for d in frappe.get_all("Daily Work Summary", dict(status="Open")):
|
||||
daily_work_summary = frappe.get_doc("Daily Work Summary", d.name)
|
||||
daily_work_summary.send_summary()
|
||||
|
||||
|
||||
def is_incoming_account_enabled():
|
||||
return frappe.db.get_value("Email Account", dict(enable_incoming=1, default_incoming=1))
|
@ -1,106 +0,0 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"beta": 0,
|
||||
"creation": "2018-02-12 14:57:38.332692",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "user",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "User",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "User",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_from": "user.email",
|
||||
"fieldname": "email",
|
||||
"fieldtype": "Read Only",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "email",
|
||||
"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,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-05-16 22:43:00.481019",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Daily Work Summary Group User",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 1,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
|
||||
from frappe.model.document import Document
|
||||
|
||||
|
||||
class DailyWorkSummaryGroupUser(Document):
|
||||
pass
|
@ -1,76 +0,0 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"autoname": "hash",
|
||||
"beta": 0,
|
||||
"creation": "2018-04-08 16:31:02.433252",
|
||||
"custom": 0,
|
||||
"description": "",
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "approver",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 1,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Approver",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "User",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0,
|
||||
"width": "200"
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-04-17 11:05:46.294340",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Department Approver",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 0,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 0,
|
||||
"track_seen": 0
|
||||
}
|
@ -1,93 +0,0 @@
|
||||
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
|
||||
|
||||
class DepartmentApprover(Document):
|
||||
pass
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
@frappe.validate_and_sanitize_search_inputs
|
||||
def get_approvers(doctype, txt, searchfield, start, page_len, filters):
|
||||
|
||||
if not filters.get("employee"):
|
||||
frappe.throw(_("Please select Employee first."))
|
||||
|
||||
approvers = []
|
||||
department_details = {}
|
||||
department_list = []
|
||||
employee = frappe.get_value(
|
||||
"Employee",
|
||||
filters.get("employee"),
|
||||
["employee_name", "department", "leave_approver", "expense_approver", "shift_request_approver"],
|
||||
as_dict=True,
|
||||
)
|
||||
|
||||
employee_department = filters.get("department") or employee.department
|
||||
if employee_department:
|
||||
department_details = frappe.db.get_value(
|
||||
"Department", {"name": employee_department}, ["lft", "rgt"], as_dict=True
|
||||
)
|
||||
if department_details:
|
||||
department_list = frappe.db.sql(
|
||||
"""select name from `tabDepartment` where lft <= %s
|
||||
and rgt >= %s
|
||||
and disabled=0
|
||||
order by lft desc""",
|
||||
(department_details.lft, department_details.rgt),
|
||||
as_list=True,
|
||||
)
|
||||
|
||||
if filters.get("doctype") == "Leave Application" and employee.leave_approver:
|
||||
approvers.append(
|
||||
frappe.db.get_value("User", employee.leave_approver, ["name", "first_name", "last_name"])
|
||||
)
|
||||
|
||||
if filters.get("doctype") == "Expense Claim" and employee.expense_approver:
|
||||
approvers.append(
|
||||
frappe.db.get_value("User", employee.expense_approver, ["name", "first_name", "last_name"])
|
||||
)
|
||||
|
||||
if filters.get("doctype") == "Shift Request" and employee.shift_request_approver:
|
||||
approvers.append(
|
||||
frappe.db.get_value(
|
||||
"User", employee.shift_request_approver, ["name", "first_name", "last_name"]
|
||||
)
|
||||
)
|
||||
|
||||
if filters.get("doctype") == "Leave Application":
|
||||
parentfield = "leave_approvers"
|
||||
field_name = "Leave Approver"
|
||||
elif filters.get("doctype") == "Expense Claim":
|
||||
parentfield = "expense_approvers"
|
||||
field_name = "Expense Approver"
|
||||
elif filters.get("doctype") == "Shift Request":
|
||||
parentfield = "shift_request_approver"
|
||||
field_name = "Shift Request Approver"
|
||||
if department_list:
|
||||
for d in department_list:
|
||||
approvers += frappe.db.sql(
|
||||
"""select user.name, user.first_name, user.last_name from
|
||||
tabUser user, `tabDepartment Approver` approver where
|
||||
approver.parent = %s
|
||||
and user.name like %s
|
||||
and approver.parentfield = %s
|
||||
and approver.approver=user.name""",
|
||||
(d, "%" + txt + "%", parentfield),
|
||||
as_list=True,
|
||||
)
|
||||
|
||||
if len(approvers) == 0:
|
||||
error_msg = _("Please set {0} for the Employee: {1}").format(
|
||||
field_name, frappe.bold(employee.employee_name)
|
||||
)
|
||||
if department_list:
|
||||
error_msg += " " + _("or for Department: {0}").format(frappe.bold(employee_department))
|
||||
frappe.throw(error_msg, title=_(field_name + " Missing"))
|
||||
|
||||
return set(tuple(approver) for approver in approvers)
|
@ -1,198 +0,0 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_events_in_timeline": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 1,
|
||||
"allow_rename": 1,
|
||||
"autoname": "field:designation_name",
|
||||
"beta": 0,
|
||||
"creation": "2013-01-10 16:34:13",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "Setup",
|
||||
"editable_grid": 0,
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "designation_name",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Designation",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "designation_name",
|
||||
"oldfieldtype": "Data",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "description",
|
||||
"fieldtype": "Text",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Description",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "required_skills_section",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Required Skills",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "skills",
|
||||
"fieldtype": "Table",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Skills",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Designation Skill",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_toolbar": 0,
|
||||
"icon": "fa fa-bookmark",
|
||||
"idx": 1,
|
||||
"in_create": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"menu_index": 0,
|
||||
"modified": "2019-04-16 10:02:23.277734",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Designation",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 0,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "HR User",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"read": 1,
|
||||
"role": "Sales User"
|
||||
}
|
||||
],
|
||||
"quick_entry": 1,
|
||||
"read_only": 0,
|
||||
"show_name_in_global_search": 1,
|
||||
"sort_order": "ASC",
|
||||
"track_changes": 0,
|
||||
"track_seen": 0,
|
||||
"track_views": 0
|
||||
}
|
@ -1,74 +0,0 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_events_in_timeline": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"beta": 0,
|
||||
"creation": "2019-04-16 10:01:05.259881",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "skill",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Skill",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Skill",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"in_create": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"max_attachments": 0,
|
||||
"modified": "2019-04-16 13:42:10.760449",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Designation Skill",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 1,
|
||||
"read_only": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "ASC",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0,
|
||||
"track_views": 0
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
|
||||
# import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
|
||||
class DesignationSkill(Document):
|
||||
pass
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user