Merge branch 'develop' into iff-invoicing
This commit is contained in:
commit
eb832f6861
@ -27,4 +27,4 @@ def get_vouchar_detials(column_list, doctype, docname):
|
|||||||
for col in column_list:
|
for col in column_list:
|
||||||
sanitize_searchfield(col)
|
sanitize_searchfield(col)
|
||||||
return frappe.db.sql(''' select {columns} from `tab{doctype}` where name=%s'''
|
return frappe.db.sql(''' select {columns} from `tab{doctype}` where name=%s'''
|
||||||
.format(columns=", ".join(json.loads(column_list)), doctype=doctype), docname, as_dict=1)[0]
|
.format(columns=", ".join(column_list), doctype=doctype), docname, as_dict=1)[0]
|
||||||
|
@ -24,8 +24,13 @@ class JobOffer(Document):
|
|||||||
check_vacancies = frappe.get_single("HR Settings").check_vacancies
|
check_vacancies = frappe.get_single("HR Settings").check_vacancies
|
||||||
if staffing_plan and check_vacancies:
|
if staffing_plan and check_vacancies:
|
||||||
job_offers = self.get_job_offer(staffing_plan.from_date, staffing_plan.to_date)
|
job_offers = self.get_job_offer(staffing_plan.from_date, staffing_plan.to_date)
|
||||||
if staffing_plan.vacancies - len(job_offers) <= 0:
|
|
||||||
frappe.throw(_("There are no vacancies under staffing plan {0}").format(frappe.bold(get_link_to_form("Staffing Plan", staffing_plan.parent))))
|
if not staffing_plan.get("vacancies") or staffing_plan.vacancies - len(job_offers) <= 0:
|
||||||
|
error_variable = 'for ' + frappe.bold(self.designation)
|
||||||
|
if staffing_plan.get("parent"):
|
||||||
|
error_variable = frappe.bold(get_link_to_form("Staffing Plan", staffing_plan.parent))
|
||||||
|
|
||||||
|
frappe.throw(_("There are no vacancies under staffing plan {0}").format(error_variable))
|
||||||
|
|
||||||
def on_change(self):
|
def on_change(self):
|
||||||
update_job_applicant(self.status, self.job_applicant)
|
update_job_applicant(self.status, self.job_applicant)
|
||||||
|
@ -135,10 +135,7 @@ def create_loan(source_name, target_doc=None, submit=0):
|
|||||||
"validation": {
|
"validation": {
|
||||||
"docstatus": ["=", 1]
|
"docstatus": ["=", 1]
|
||||||
},
|
},
|
||||||
"postprocess": update_accounts,
|
"postprocess": update_accounts
|
||||||
"field_no_map": [
|
|
||||||
"is_secured_loan"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}, target_doc)
|
}, target_doc)
|
||||||
|
|
||||||
|
@ -10,22 +10,20 @@ from frappe.utils import nowdate, getdate, add_days, flt
|
|||||||
from erpnext.controllers.accounts_controller import AccountsController
|
from erpnext.controllers.accounts_controller import AccountsController
|
||||||
from erpnext.accounts.general_ledger import make_gl_entries
|
from erpnext.accounts.general_ledger import make_gl_entries
|
||||||
from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual_for_demand_loans
|
from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual_for_demand_loans
|
||||||
|
from erpnext.loan_management.doctype.loan_security_unpledge.loan_security_unpledge import get_pledged_security_qty
|
||||||
|
from frappe.utils import get_datetime
|
||||||
|
|
||||||
class LoanDisbursement(AccountsController):
|
class LoanDisbursement(AccountsController):
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.set_missing_values()
|
self.set_missing_values()
|
||||||
|
|
||||||
def before_submit(self):
|
|
||||||
self.set_status_and_amounts()
|
|
||||||
|
|
||||||
def before_cancel(self):
|
|
||||||
self.set_status_and_amounts(cancel=1)
|
|
||||||
|
|
||||||
def on_submit(self):
|
def on_submit(self):
|
||||||
|
self.set_status_and_amounts()
|
||||||
self.make_gl_entries()
|
self.make_gl_entries()
|
||||||
|
|
||||||
def on_cancel(self):
|
def on_cancel(self):
|
||||||
|
self.set_status_and_amounts(cancel=1)
|
||||||
self.make_gl_entries(cancel=1)
|
self.make_gl_entries(cancel=1)
|
||||||
self.ignore_linked_doctypes = ['GL Entry']
|
self.ignore_linked_doctypes = ['GL Entry']
|
||||||
|
|
||||||
@ -45,29 +43,69 @@ class LoanDisbursement(AccountsController):
|
|||||||
def set_status_and_amounts(self, cancel=0):
|
def set_status_and_amounts(self, cancel=0):
|
||||||
|
|
||||||
loan_details = frappe.get_all("Loan",
|
loan_details = frappe.get_all("Loan",
|
||||||
fields = ["loan_amount", "disbursed_amount", "total_principal_paid", "status", "is_term_loan"],
|
fields = ["loan_amount", "disbursed_amount", "total_payment", "total_principal_paid", "total_interest_payable",
|
||||||
filters= { "name": self.against_loan }
|
"status", "is_term_loan", "is_secured_loan"], filters= { "name": self.against_loan })[0]
|
||||||
)[0]
|
|
||||||
|
|
||||||
if loan_details.status == "Disbursed" and not loan_details.is_term_loan:
|
|
||||||
process_loan_interest_accrual_for_demand_loans(posting_date=add_days(self.disbursement_date, -1),
|
|
||||||
loan=self.against_loan)
|
|
||||||
|
|
||||||
if cancel:
|
if cancel:
|
||||||
disbursed_amount = loan_details.disbursed_amount - self.disbursed_amount
|
disbursed_amount = loan_details.disbursed_amount - self.disbursed_amount
|
||||||
|
total_payment = loan_details.total_payment
|
||||||
|
|
||||||
|
if loan_details.disbursed_amount > loan_details.loan_amount:
|
||||||
|
topup_amount = loan_details.disbursed_amount - loan_details.loan_amount
|
||||||
|
if topup_amount > self.disbursed_amount:
|
||||||
|
topup_amount = self.disbursed_amount
|
||||||
|
|
||||||
|
total_payment = total_payment - topup_amount
|
||||||
|
|
||||||
if disbursed_amount == 0:
|
if disbursed_amount == 0:
|
||||||
status = "Sanctioned"
|
status = "Sanctioned"
|
||||||
elif disbursed_amount >= loan_details.disbursed_amount:
|
elif disbursed_amount >= loan_details.loan_amount:
|
||||||
status = "Disbursed"
|
status = "Disbursed"
|
||||||
else:
|
else:
|
||||||
status = "Partially Disbursed"
|
status = "Partially Disbursed"
|
||||||
else:
|
else:
|
||||||
disbursed_amount = self.disbursed_amount + loan_details.disbursed_amount
|
disbursed_amount = self.disbursed_amount + loan_details.disbursed_amount
|
||||||
|
total_payment = loan_details.total_payment
|
||||||
|
|
||||||
if flt(disbursed_amount) - flt(loan_details.total_principal_paid) > flt(loan_details.loan_amount):
|
if disbursed_amount > loan_details.loan_amount and loan_details.is_term_loan:
|
||||||
frappe.throw(_("Disbursed Amount cannot be greater than loan amount"))
|
frappe.throw(_("Disbursed Amount cannot be greater than loan amount"))
|
||||||
|
|
||||||
if flt(disbursed_amount) >= loan_details.disbursed_amount:
|
if loan_details.status == 'Disbursed':
|
||||||
|
pending_principal_amount = flt(loan_details.total_payment) - flt(loan_details.total_interest_payable) \
|
||||||
|
- flt(loan_details.total_principal_paid)
|
||||||
|
else:
|
||||||
|
pending_principal_amount = loan_details.disbursed_amount
|
||||||
|
|
||||||
|
security_value = 0.0
|
||||||
|
if loan_details.is_secured_loan:
|
||||||
|
security_value = get_total_pledged_security_value(self.against_loan)
|
||||||
|
|
||||||
|
if not security_value:
|
||||||
|
security_value = loan_details.loan_amount
|
||||||
|
|
||||||
|
if pending_principal_amount + self.disbursed_amount > flt(security_value):
|
||||||
|
allowed_amount = security_value - pending_principal_amount
|
||||||
|
if allowed_amount < 0:
|
||||||
|
allowed_amount = 0
|
||||||
|
|
||||||
|
frappe.throw(_("Disbursed Amount cannot be greater than {0}").format(allowed_amount))
|
||||||
|
|
||||||
|
if loan_details.status == "Disbursed" and not loan_details.is_term_loan:
|
||||||
|
process_loan_interest_accrual_for_demand_loans(posting_date=add_days(self.disbursement_date, -1),
|
||||||
|
loan=self.against_loan)
|
||||||
|
|
||||||
|
if disbursed_amount > loan_details.loan_amount:
|
||||||
|
topup_amount = disbursed_amount - loan_details.loan_amount
|
||||||
|
|
||||||
|
if topup_amount < 0:
|
||||||
|
topup_amount = 0
|
||||||
|
|
||||||
|
if topup_amount > self.disbursed_amount:
|
||||||
|
topup_amount = self.disbursed_amount
|
||||||
|
|
||||||
|
total_payment = total_payment + topup_amount
|
||||||
|
|
||||||
|
if flt(disbursed_amount) >= loan_details.loan_amount:
|
||||||
status = "Disbursed"
|
status = "Disbursed"
|
||||||
else:
|
else:
|
||||||
status = "Partially Disbursed"
|
status = "Partially Disbursed"
|
||||||
@ -75,7 +113,8 @@ class LoanDisbursement(AccountsController):
|
|||||||
frappe.db.set_value("Loan", self.against_loan, {
|
frappe.db.set_value("Loan", self.against_loan, {
|
||||||
"disbursement_date": self.disbursement_date,
|
"disbursement_date": self.disbursement_date,
|
||||||
"disbursed_amount": disbursed_amount,
|
"disbursed_amount": disbursed_amount,
|
||||||
"status": status
|
"status": status,
|
||||||
|
"total_payment": total_payment
|
||||||
})
|
})
|
||||||
|
|
||||||
def make_gl_entries(self, cancel=0, adv_adj=0):
|
def make_gl_entries(self, cancel=0, adv_adj=0):
|
||||||
@ -116,3 +155,24 @@ class LoanDisbursement(AccountsController):
|
|||||||
|
|
||||||
if gle_map:
|
if gle_map:
|
||||||
make_gl_entries(gle_map, cancel=cancel, adv_adj=adv_adj)
|
make_gl_entries(gle_map, cancel=cancel, adv_adj=adv_adj)
|
||||||
|
|
||||||
|
def get_total_pledged_security_value(loan):
|
||||||
|
update_time = get_datetime()
|
||||||
|
|
||||||
|
loan_security_price_map = frappe._dict(frappe.get_all("Loan Security Price",
|
||||||
|
fields=["loan_security", "loan_security_price"],
|
||||||
|
filters = {
|
||||||
|
"valid_from": ("<=", update_time),
|
||||||
|
"valid_upto": (">=", update_time)
|
||||||
|
}, as_list=1))
|
||||||
|
|
||||||
|
hair_cut_map = frappe._dict(frappe.get_all('Loan Security',
|
||||||
|
fields=["name", "haircut"], as_list=1))
|
||||||
|
|
||||||
|
security_value = 0.0
|
||||||
|
pledged_securities = get_pledged_security_qty(loan)
|
||||||
|
|
||||||
|
for security, qty in pledged_securities.items():
|
||||||
|
security_value += (loan_security_price_map.get(security) * qty * hair_cut_map.get(security))/100
|
||||||
|
|
||||||
|
return security_value
|
||||||
|
@ -85,8 +85,8 @@ def calculate_accrual_amount_for_demand_loans(loan, posting_date, process_loan_i
|
|||||||
if no_of_days <= 0:
|
if no_of_days <= 0:
|
||||||
return
|
return
|
||||||
|
|
||||||
pending_principal_amount = loan.total_payment - loan.total_interest_payable \
|
pending_principal_amount = flt(loan.total_payment) - flt(loan.total_interest_payable) \
|
||||||
- loan.total_amount_paid
|
- flt(loan.total_principal_paid)
|
||||||
|
|
||||||
interest_per_day = (pending_principal_amount * loan.rate_of_interest) / (days_in_year(get_datetime(posting_date).year) * 100)
|
interest_per_day = (pending_principal_amount * loan.rate_of_interest) / (days_in_year(get_datetime(posting_date).year) * 100)
|
||||||
payable_interest = interest_per_day * no_of_days
|
payable_interest = interest_per_day * no_of_days
|
||||||
|
@ -7,27 +7,30 @@
|
|||||||
"field_order": [
|
"field_order": [
|
||||||
"salary_component",
|
"salary_component",
|
||||||
"abbr",
|
"abbr",
|
||||||
"statistical_component",
|
|
||||||
"column_break_3",
|
"column_break_3",
|
||||||
"deduct_full_tax_on_selected_payroll_date",
|
"amount",
|
||||||
|
"section_break_5",
|
||||||
|
"additional_salary",
|
||||||
|
"statistical_component",
|
||||||
"depends_on_payment_days",
|
"depends_on_payment_days",
|
||||||
"is_tax_applicable",
|
|
||||||
"exempted_from_income_tax",
|
"exempted_from_income_tax",
|
||||||
|
"is_tax_applicable",
|
||||||
|
"column_break_11",
|
||||||
"is_flexible_benefit",
|
"is_flexible_benefit",
|
||||||
"variable_based_on_taxable_salary",
|
"variable_based_on_taxable_salary",
|
||||||
|
"do_not_include_in_total",
|
||||||
|
"deduct_full_tax_on_selected_payroll_date",
|
||||||
"section_break_2",
|
"section_break_2",
|
||||||
"condition",
|
"condition",
|
||||||
|
"column_break_18",
|
||||||
"amount_based_on_formula",
|
"amount_based_on_formula",
|
||||||
"formula",
|
"formula",
|
||||||
"amount",
|
"section_break_19",
|
||||||
"do_not_include_in_total",
|
|
||||||
"default_amount",
|
"default_amount",
|
||||||
"additional_amount",
|
"additional_amount",
|
||||||
|
"column_break_24",
|
||||||
"tax_on_flexible_benefit",
|
"tax_on_flexible_benefit",
|
||||||
"tax_on_additional_salary",
|
"tax_on_additional_salary"
|
||||||
"section_break_11",
|
|
||||||
"additional_salary",
|
|
||||||
"condition_and_formula_help"
|
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
@ -110,9 +113,11 @@
|
|||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"collapsible": 1,
|
||||||
"depends_on": "eval:doc.is_flexible_benefit != 1",
|
"depends_on": "eval:doc.is_flexible_benefit != 1",
|
||||||
"fieldname": "section_break_2",
|
"fieldname": "section_break_2",
|
||||||
"fieldtype": "Section Break"
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Condtion and formula"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 1,
|
"allow_on_submit": 1,
|
||||||
@ -181,23 +186,12 @@
|
|||||||
"label": "Tax on additional salary",
|
"label": "Tax on additional salary",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"depends_on": "eval:doc.parenttype=='Salary Structure'",
|
|
||||||
"fieldname": "section_break_11",
|
|
||||||
"fieldtype": "Column Break"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"depends_on": "eval:doc.parenttype=='Salary Structure'",
|
|
||||||
"fieldname": "condition_and_formula_help",
|
|
||||||
"fieldtype": "HTML",
|
|
||||||
"label": "Condition and Formula Help",
|
|
||||||
"options": "<h3>Condition and Formula Help</h3>\n\n<p>Notes:</p>\n\n<ol>\n<li>Use field <code>base</code> for using base salary of the Employee</li>\n<li>Use Salary Component abbreviations in conditions and formulas. <code>BS = Basic Salary</code></li>\n<li>Use field name for employee details in conditions and formulas. <code>Employment Type = employment_type</code><code>Branch = branch</code></li>\n<li>Use field name from Salary Slip in conditions and formulas. <code>Payment Days = payment_days</code><code>Leave without pay = leave_without_pay</code></li>\n<li>Direct Amount can also be entered based on Condtion. See example 3</li></ol>\n\n<h4>Examples</h4>\n<ol>\n<li>Calculating Basic Salary based on <code>base</code>\n<pre><code>Condition: base < 10000</code></pre>\n<pre><code>Formula: base * .2</code></pre></li>\n<li>Calculating HRA based on Basic Salary<code>BS</code> \n<pre><code>Condition: BS > 2000</code></pre>\n<pre><code>Formula: BS * .1</code></pre></li>\n<li>Calculating TDS based on Employment Type<code>employment_type</code> \n<pre><code>Condition: employment_type==\"Intern\"</code></pre>\n<pre><code>Amount: 1000</code></pre></li>\n</ol>"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"fieldname": "additional_salary",
|
"fieldname": "additional_salary",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Additional Salary ",
|
"label": "Additional Salary ",
|
||||||
"options": "Additional Salary"
|
"options": "Additional Salary",
|
||||||
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "0",
|
"default": "0",
|
||||||
@ -207,11 +201,43 @@
|
|||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Exempted from Income Tax",
|
"label": "Exempted from Income Tax",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"collapsible": 1,
|
||||||
|
"fieldname": "section_break_5",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Component properties and references ",
|
||||||
|
"show_days": 1,
|
||||||
|
"show_seconds": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_11",
|
||||||
|
"fieldtype": "Column Break",
|
||||||
|
"show_days": 1,
|
||||||
|
"show_seconds": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_19",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"show_days": 1,
|
||||||
|
"show_seconds": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_18",
|
||||||
|
"fieldtype": "Column Break",
|
||||||
|
"show_days": 1,
|
||||||
|
"show_seconds": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_24",
|
||||||
|
"fieldtype": "Column Break",
|
||||||
|
"show_days": 1,
|
||||||
|
"show_seconds": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-06-22 23:21:26.300951",
|
"modified": "2020-07-01 12:13:41.956495",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Payroll",
|
"module": "Payroll",
|
||||||
"name": "Salary Detail",
|
"name": "Salary Detail",
|
||||||
|
@ -123,13 +123,13 @@ frappe.ui.form.on("Salary Slip", {
|
|||||||
doc: frm.doc,
|
doc: frm.doc,
|
||||||
callback: function(r, rt) {
|
callback: function(r, rt) {
|
||||||
frm.refresh();
|
frm.refresh();
|
||||||
if (frm.doc.absent_days){
|
if (r.message){
|
||||||
frm.fields_dict.absent_days.set_description("Unmarked Days is treated as "+ r.message +". You can can change this in " + frappe.utils.get_form_link("Payroll Settings", "Payroll Settings", true));
|
frm.fields_dict.absent_days.set_description("Unmarked Days is treated as "+ r.message +". You can can change this in " + frappe.utils.get_form_link("Payroll Settings", "Payroll Settings", true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
frappe.ui.form.on('Salary Slip Timesheet', {
|
frappe.ui.form.on('Salary Slip Timesheet', {
|
||||||
time_sheet: function(frm, dt, dn) {
|
time_sheet: function(frm, dt, dn) {
|
||||||
|
@ -20,15 +20,17 @@
|
|||||||
"company",
|
"company",
|
||||||
"letter_head",
|
"letter_head",
|
||||||
"section_break_10",
|
"section_break_10",
|
||||||
"salary_slip_based_on_timesheet",
|
|
||||||
"start_date",
|
"start_date",
|
||||||
"end_date",
|
"end_date",
|
||||||
"salary_structure",
|
"salary_structure",
|
||||||
|
"column_break_18",
|
||||||
|
"salary_slip_based_on_timesheet",
|
||||||
"payroll_frequency",
|
"payroll_frequency",
|
||||||
"column_break_15",
|
"section_break_20",
|
||||||
"total_working_days",
|
"total_working_days",
|
||||||
"unmarked_days",
|
"unmarked_days",
|
||||||
"leave_without_pay",
|
"leave_without_pay",
|
||||||
|
"column_break_24",
|
||||||
"absent_days",
|
"absent_days",
|
||||||
"payment_days",
|
"payment_days",
|
||||||
"hourly_wages",
|
"hourly_wages",
|
||||||
@ -200,10 +202,6 @@
|
|||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"label": "End Date"
|
"label": "End Date"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"fieldname": "column_break_15",
|
|
||||||
"fieldtype": "Column Break"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"fieldname": "salary_structure",
|
"fieldname": "salary_structure",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
@ -490,13 +488,25 @@
|
|||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"hidden": 1,
|
"hidden": 1,
|
||||||
"label": "Unmarked days"
|
"label": "Unmarked days"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_20",
|
||||||
|
"fieldtype": "Section Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_24",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_18",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "fa fa-file-text",
|
"icon": "fa fa-file-text",
|
||||||
"idx": 9,
|
"idx": 9,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-07-22 12:41:03.659422",
|
"modified": "2020-08-11 17:37:54.274384",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Payroll",
|
"module": "Payroll",
|
||||||
"name": "Salary Slip",
|
"name": "Salary Slip",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user