Merge pull request #25251 from nextchamp-saqib/einv-ack-no-fix
fix(india): set correct ack no. on irn generation
diff --git a/erpnext/hr/workspace/hr/hr.json b/erpnext/hr/workspace/hr/hr.json
index f650b24..f4b56a0 100644
--- a/erpnext/hr/workspace/hr/hr.json
+++ b/erpnext/hr/workspace/hr/hr.json
@@ -15,6 +15,7 @@
"hide_custom": 0,
"icon": "hr",
"idx": 0,
+ "is_default": 0,
"is_standard": 1,
"label": "HR",
"links": [
@@ -227,41 +228,11 @@
"type": "Card Break"
},
{
- "dependencies": "Employee",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Leave Application",
- "link_to": "Leave Application",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "Employee",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Leave Allocation",
- "link_to": "Leave Allocation",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "Leave Type",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Leave Policy",
- "link_to": "Leave Policy",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
- "label": "Leave Period",
- "link_to": "Leave Period",
+ "label": "Holiday List",
+ "link_to": "Holiday List",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
@@ -280,8 +251,28 @@
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
- "label": "Holiday List",
- "link_to": "Holiday List",
+ "label": "Leave Period",
+ "link_to": "Leave Period",
+ "link_type": "DocType",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "dependencies": "Leave Type",
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Leave Policy",
+ "link_to": "Leave Policy",
+ "link_type": "DocType",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "dependencies": "Leave Policy",
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Leave Policy Assignment",
+ "link_to": "Leave Policy Assignment",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
@@ -290,8 +281,18 @@
"dependencies": "Employee",
"hidden": 0,
"is_query_report": 0,
- "label": "Compensatory Leave Request",
- "link_to": "Compensatory Leave Request",
+ "label": "Leave Application",
+ "link_to": "Leave Application",
+ "link_type": "DocType",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "dependencies": "Employee",
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Leave Allocation",
+ "link_to": "Leave Allocation",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
@@ -317,12 +318,12 @@
"type": "Link"
},
{
- "dependencies": "Leave Application",
+ "dependencies": "Employee",
"hidden": 0,
- "is_query_report": 1,
- "label": "Employee Leave Balance",
- "link_to": "Employee Leave Balance",
- "link_type": "Report",
+ "is_query_report": 0,
+ "label": "Compensatory Leave Request",
+ "link_to": "Compensatory Leave Request",
+ "link_type": "DocType",
"onboard": 0,
"type": "Link"
},
@@ -384,16 +385,6 @@
"type": "Link"
},
{
- "dependencies": "Attendance",
- "hidden": 0,
- "is_query_report": 1,
- "label": "Monthly Attendance Sheet",
- "link_to": "Monthly Attendance Sheet",
- "link_type": "Report",
- "onboard": 0,
- "type": "Link"
- },
- {
"hidden": 0,
"is_query_report": 0,
"label": "Expense Claims",
@@ -423,6 +414,15 @@
{
"hidden": 0,
"is_query_report": 0,
+ "label": "Travel Request",
+ "link_to": "Travel Request",
+ "link_type": "DocType",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "hidden": 0,
+ "is_query_report": 0,
"label": "Settings",
"onboard": 0,
"type": "Card Break"
@@ -465,6 +465,15 @@
"type": "Card Break"
},
{
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Driver",
+ "link_to": "Driver",
+ "link_type": "DocType",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
@@ -544,6 +553,24 @@
{
"hidden": 0,
"is_query_report": 0,
+ "label": "Appointment Letter",
+ "link_to": "Appointment Letter",
+ "link_type": "DocType",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Appointment Letter Template",
+ "link_to": "Appointment Letter Template",
+ "link_type": "DocType",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "hidden": 0,
+ "is_query_report": 0,
"label": "Loans",
"onboard": 0,
"type": "Card Break"
@@ -628,33 +655,6 @@
{
"hidden": 0,
"is_query_report": 0,
- "label": "Reports",
- "onboard": 0,
- "type": "Card Break"
- },
- {
- "dependencies": "Employee",
- "hidden": 0,
- "is_query_report": 1,
- "label": "Employee Birthday",
- "link_to": "Employee Birthday",
- "link_type": "Report",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "Employee",
- "hidden": 0,
- "is_query_report": 1,
- "label": "Employees working on a holiday",
- "link_to": "Employees working on a holiday",
- "link_type": "Report",
- "onboard": 0,
- "type": "Link"
- },
- {
- "hidden": 0,
- "is_query_report": 0,
"label": "Performance",
"onboard": 0,
"type": "Card Break"
@@ -702,7 +702,74 @@
{
"hidden": 0,
"is_query_report": 0,
- "label": "Employee Tax and Benefits",
+ "label": "Key Reports",
+ "onboard": 0,
+ "type": "Card Break"
+ },
+ {
+ "dependencies": "Attendance",
+ "hidden": 0,
+ "is_query_report": 1,
+ "label": "Monthly Attendance Sheet",
+ "link_to": "Monthly Attendance Sheet",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "dependencies": "Staffing Plan",
+ "hidden": 0,
+ "is_query_report": 1,
+ "label": "Recruitment Analytics",
+ "link_to": "Recruitment Analytics",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "dependencies": "Employee",
+ "hidden": 0,
+ "is_query_report": 1,
+ "label": "Employee Analytics",
+ "link_to": "Employee Analytics",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "dependencies": "Employee",
+ "hidden": 0,
+ "is_query_report": 1,
+ "label": "Employee Leave Balance",
+ "link_to": "Employee Leave Balance",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "dependencies": "Employee",
+ "hidden": 0,
+ "is_query_report": 1,
+ "label": "Employee Leave Balance Summary",
+ "link_to": "Employee Leave Balance Summary",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "dependencies": "Employee Advance",
+ "hidden": 0,
+ "is_query_report": 1,
+ "label": "Employee Advance Summary",
+ "link_to": "Employee Advance Summary",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Other Reports",
"onboard": 0,
"type": "Card Break"
},
@@ -710,74 +777,44 @@
"dependencies": "Employee",
"hidden": 0,
"is_query_report": 0,
- "label": "Employee Tax Exemption Declaration",
- "link_to": "Employee Tax Exemption Declaration",
- "link_type": "DocType",
+ "label": "Employee Information",
+ "link_to": "Employee Information",
+ "link_type": "Report",
"onboard": 0,
"type": "Link"
},
{
"dependencies": "Employee",
"hidden": 0,
- "is_query_report": 0,
- "label": "Employee Tax Exemption Proof Submission",
- "link_to": "Employee Tax Exemption Proof Submission",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "Employee, Payroll Period",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Employee Other Income",
- "link_to": "Employee Other Income",
- "link_type": "DocType",
+ "is_query_report": 1,
+ "label": "Employee Birthday",
+ "link_to": "Employee Birthday",
+ "link_type": "Report",
"onboard": 0,
"type": "Link"
},
{
"dependencies": "Employee",
"hidden": 0,
- "is_query_report": 0,
- "label": "Employee Benefit Application",
- "link_to": "Employee Benefit Application",
- "link_type": "DocType",
+ "is_query_report": 1,
+ "label": "Employees Working on a Holiday",
+ "link_to": "Employees working on a holiday",
+ "link_type": "Report",
"onboard": 0,
"type": "Link"
},
{
- "dependencies": "Employee",
+ "dependencies": "Daily Work Summary",
"hidden": 0,
- "is_query_report": 0,
- "label": "Employee Benefit Claim",
- "link_to": "Employee Benefit Claim",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "Employee",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Employee Tax Exemption Category",
- "link_to": "Employee Tax Exemption Category",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "Employee",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Employee Tax Exemption Sub Category",
- "link_to": "Employee Tax Exemption Sub Category",
- "link_type": "DocType",
+ "is_query_report": 1,
+ "label": "Daily Work Summary Replies",
+ "link_to": "Daily Work Summary Replies",
+ "link_type": "Report",
"onboard": 0,
"type": "Link"
}
],
- "modified": "2021-01-21 13:38:38.941001",
+ "modified": "2021-03-24 17:35:21.483297",
"modified_by": "Administrator",
"module": "HR",
"name": "HR",
diff --git a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
index a88e183..5d57ced 100644
--- a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
+++ b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
@@ -21,6 +21,7 @@
def validate(self):
amounts = calculate_amounts(self.against_loan, self.posting_date)
self.set_missing_values(amounts)
+ self.check_future_entries()
self.validate_amount()
self.allocate_amounts(amounts)
@@ -69,6 +70,13 @@
if amounts.get('due_date'):
self.due_date = amounts.get('due_date')
+ def check_future_entries(self):
+ future_repayment_date = frappe.db.get_value("Loan Repayment", {"posting_date": (">", self.posting_date),
+ "docstatus": 1, "against_loan": self.against_loan}, 'posting_date')
+
+ if future_repayment_date:
+ frappe.throw("Repayment already made till date {0}".format(getdate(future_repayment_date)))
+
def validate_amount(self):
precision = cint(frappe.db.get_default("currency_precision")) or 2
@@ -307,7 +315,9 @@
return lr
-def get_accrued_interest_entries(against_loan):
+def get_accrued_interest_entries(against_loan, posting_date=None):
+ if not posting_date:
+ posting_date = getdate()
unpaid_accrued_entries = frappe.db.sql(
"""
@@ -318,12 +328,13 @@
`tabLoan Interest Accrual`
WHERE
loan = %s
+ AND posting_date <= %s
AND (interest_amount - paid_interest_amount > 0 OR
payable_principal_amount - paid_principal_amount > 0)
AND
docstatus = 1
ORDER BY posting_date
- """, (against_loan), as_dict=1)
+ """, (against_loan, posting_date), as_dict=1)
return unpaid_accrued_entries
@@ -335,7 +346,7 @@
against_loan_doc = frappe.get_doc("Loan", against_loan)
loan_type_details = frappe.get_doc("Loan Type", against_loan_doc.loan_type)
- accrued_interest_entries = get_accrued_interest_entries(against_loan_doc.name)
+ accrued_interest_entries = get_accrued_interest_entries(against_loan_doc.name, posting_date)
pending_accrual_entries = {}
diff --git a/erpnext/loan_management/doctype/salary_slip_loan/salary_slip_loan.json b/erpnext/loan_management/doctype/salary_slip_loan/salary_slip_loan.json
index 2f4fe24..3d07081 100644
--- a/erpnext/loan_management/doctype/salary_slip_loan/salary_slip_loan.json
+++ b/erpnext/loan_management/doctype/salary_slip_loan/salary_slip_loan.json
@@ -70,7 +70,9 @@
{
"fieldname": "loan_repayment_entry",
"fieldtype": "Link",
+ "hidden": 1,
"label": "Loan Repayment Entry",
+ "no_copy": 1,
"options": "Loan Repayment",
"read_only": 1
},
@@ -83,9 +85,10 @@
"read_only": 1
}
],
+ "index_web_pages_for_search": 1,
"istable": 1,
"links": [],
- "modified": "2020-04-16 13:17:04.798335",
+ "modified": "2021-03-14 20:47:11.725818",
"modified_by": "Administrator",
"module": "Loan Management",
"name": "Salary Slip Loan",
diff --git a/erpnext/manufacturing/doctype/bom/test_bom.py b/erpnext/manufacturing/doctype/bom/test_bom.py
index 4050a7d..cd61d2a 100644
--- a/erpnext/manufacturing/doctype/bom/test_bom.py
+++ b/erpnext/manufacturing/doctype/bom/test_bom.py
@@ -5,7 +5,7 @@
from __future__ import unicode_literals
import unittest
import frappe
-from frappe.utils import cstr
+from frappe.utils import cstr, flt
from frappe.test_runner import make_test_records
from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import create_stock_reconciliation
from erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool import update_cost
@@ -81,15 +81,27 @@
bom = frappe.copy_doc(test_records[2])
bom.insert()
- # test amounts in selected currency
- self.assertEqual(bom.operating_cost, 100)
- self.assertEqual(bom.raw_material_cost, 351.68)
- self.assertEqual(bom.total_cost, 451.68)
+ raw_material_cost = 0.0
+ op_cost = 0.0
+
+ for op_row in bom.operations:
+ op_cost += op_row.operating_cost
+
+ for row in bom.items:
+ raw_material_cost += row.amount
+
+ base_raw_material_cost = raw_material_cost * flt(bom.conversion_rate, bom.precision("conversion_rate"))
+ base_op_cost = op_cost * flt(bom.conversion_rate, bom.precision("conversion_rate"))
# test amounts in selected currency
- self.assertEqual(bom.base_operating_cost, 6000)
- self.assertEqual(bom.base_raw_material_cost, 21100.80)
- self.assertEqual(bom.base_total_cost, 27100.80)
+ self.assertEqual(bom.operating_cost, op_cost)
+ self.assertEqual(bom.raw_material_cost, raw_material_cost)
+ self.assertEqual(bom.total_cost, raw_material_cost + op_cost)
+
+ # test amounts in selected currency
+ self.assertEqual(bom.base_operating_cost, base_op_cost)
+ self.assertEqual(bom.base_raw_material_cost, base_raw_material_cost)
+ self.assertEqual(bom.base_total_cost, base_raw_material_cost + base_op_cost)
def test_bom_cost_multi_uom_multi_currency_based_on_price_list(self):
frappe.db.set_value("Price List", "_Test Price List", "price_not_uom_dependent", 1)
diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py
index 8aa0ffd..92074c6 100644
--- a/erpnext/manufacturing/doctype/job_card/job_card.py
+++ b/erpnext/manufacturing/doctype/job_card/job_card.py
@@ -47,6 +47,8 @@
if d.completed_qty:
self.total_completed_qty += d.completed_qty
+ self.total_completed_qty = flt(self.total_completed_qty, self.precision("total_completed_qty"))
+
def get_overlap_for(self, args, check_next_available_slot=False):
production_capacity = 1
diff --git a/erpnext/payroll/doctype/payroll_entry/payroll_entry.js b/erpnext/payroll/doctype/payroll_entry/payroll_entry.js
index 395e56f..85bb651 100644
--- a/erpnext/payroll/doctype/payroll_entry/payroll_entry.js
+++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry.js
@@ -133,45 +133,59 @@
}
};
});
+
+ frm.set_query('employee', 'employees', () => {
+ if (!frm.doc.company) {
+ frappe.msgprint(__("Please set a Company"));
+ return [];
+ }
+ return {
+ query: "erpnext.payroll.doctype.payroll_entry.payroll_entry.employee_query",
+ filters: frm.events.get_employee_filters(frm)
+ };
+ });
+ },
+
+ get_employee_filters: function (frm) {
+ let filters = {};
+ filters['company'] = frm.doc.company;
+ filters['start_date'] = frm.doc.start_date;
+ filters['end_date'] = frm.doc.end_date;
+
+ if (frm.doc.department) {
+ filters['department'] = frm.doc.department;
+ }
+ if (frm.doc.branch) {
+ filters['branch'] = frm.doc.branch;
+ }
+ if (frm.doc.designation) {
+ filters['designation'] = frm.doc.designation;
+ }
+ if (frm.doc.employees) {
+ filters['employees'] = frm.doc.employees.filter(d => d.employee).map(d => d.employee);
+ }
+ return filters;
},
payroll_frequency: function (frm) {
frm.trigger("set_start_end_dates").then( ()=> {
frm.events.clear_employee_table(frm);
- frm.events.get_employee_with_salary_slip_and_set_query(frm);
- });
- },
-
- employee_filters: function (frm, emp_list) {
- frm.set_query('employee', 'employees', () => {
- return {
- filters: {
- name: ["not in", emp_list]
- }
- };
- });
- },
-
- get_employee_with_salary_slip_and_set_query: function (frm) {
- frappe.db.get_list('Salary Slip', {
- filters: {
- start_date: frm.doc.start_date,
- end_date: frm.doc.end_date,
- docstatus: 1,
- },
- fields: ['employee']
- }).then((emp) => {
- var emp_list = [];
- emp.forEach((employee_data) => {
- emp_list.push(Object.values(employee_data)[0]);
- });
- frm.events.employee_filters(frm, emp_list);
});
},
company: function (frm) {
frm.events.clear_employee_table(frm);
erpnext.accounts.dimensions.update_dimension(frm, frm.doctype);
+ frm.trigger("set_payable_account_and_currency");
+ },
+
+ set_payable_account_and_currency: function (frm) {
+ frappe.db.get_value("Company", {"name": frm.doc.company}, "default_currency", (r) => {
+ frm.set_value('currency', r.default_currency);
+ });
+ frappe.db.get_value("Company", {"name": frm.doc.company}, "default_payroll_payable_account", (r) => {
+ frm.set_value('payroll_payable_account', r.default_payroll_payable_account);
+ });
},
currency: function (frm) {
@@ -345,11 +359,3 @@
})
);
};
-
-frappe.ui.form.on('Payroll Employee Detail', {
- employee: function(frm) {
- if (!frm.doc.payroll_frequency) {
- frappe.throw(__("Please set a Payroll Frequency"));
- }
- }
-});
diff --git a/erpnext/payroll/doctype/payroll_entry/payroll_entry.py b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py
index 7890471..fde2e07 100644
--- a/erpnext/payroll/doctype/payroll_entry/payroll_entry.py
+++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py
@@ -10,16 +10,17 @@
from frappe import _
from erpnext.accounts.utils import get_fiscal_year
from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee
+from frappe.desk.reportview import get_match_cond, get_filters_cond
class PayrollEntry(Document):
def onload(self):
if not self.docstatus==1 or self.salary_slips_submitted:
- return
+ return
# check if salary slips were manually submitted
entries = frappe.db.count("Salary Slip", {'payroll_entry': self.name, 'docstatus': 1}, ['name'])
if cint(entries) == len(self.employees):
- self.set_onload("submitted_ss", True)
+ self.set_onload("submitted_ss", True)
def validate(self):
self.number_of_employees = len(self.employees)
@@ -59,16 +60,16 @@
condition = """and payroll_frequency = '%(payroll_frequency)s'"""% {"payroll_frequency": self.payroll_frequency}
sal_struct = frappe.db.sql_list("""
- select
- name from `tabSalary Structure`
- where
- docstatus = 1 and
- is_active = 'Yes'
- and company = %(company)s
- and currency = %(currency)s and
- ifnull(salary_slip_based_on_timesheet,0) = %(salary_slip_based_on_timesheet)s
- {condition}""".format(condition=condition),
- {"company": self.company, "currency": self.currency, "salary_slip_based_on_timesheet":self.salary_slip_based_on_timesheet})
+ select
+ name from `tabSalary Structure`
+ where
+ docstatus = 1 and
+ is_active = 'Yes'
+ and company = %(company)s
+ and currency = %(currency)s and
+ ifnull(salary_slip_based_on_timesheet,0) = %(salary_slip_based_on_timesheet)s
+ {condition}""".format(condition=condition),
+ {"company": self.company, "currency": self.currency, "salary_slip_based_on_timesheet":self.salary_slip_based_on_timesheet})
if sal_struct:
cond += "and t2.salary_structure IN %(sal_struct)s "
@@ -176,15 +177,15 @@
"""
Returns list of salary slips based on selected criteria
"""
- cond = self.get_filter_condition()
ss_list = frappe.db.sql("""
select t1.name, t1.salary_structure, t1.payroll_cost_center from `tabSalary Slip` t1
- where t1.docstatus = %s and t1.start_date >= %s and t1.end_date <= %s
- and (t1.journal_entry is null or t1.journal_entry = "") and ifnull(salary_slip_based_on_timesheet,0) = %s %s
- """ % ('%s', '%s', '%s','%s', cond), (ss_status, self.start_date, self.end_date, self.salary_slip_based_on_timesheet), as_dict=as_dict)
+ where t1.docstatus = %s and t1.start_date >= %s and t1.end_date <= %s and t1.payroll_entry = %s
+ and (t1.journal_entry is null or t1.journal_entry = "") and ifnull(salary_slip_based_on_timesheet,0) = %s
+ """, (ss_status, self.start_date, self.end_date, self.name, self.salary_slip_based_on_timesheet), as_dict=as_dict)
return ss_list
+ @frappe.whitelist()
def submit_salary_slips(self):
self.check_permission('write')
ss_list = self.get_sal_slip_list(ss_status=0)
@@ -270,26 +271,26 @@
exchange_rate, amt = self.get_amount_and_exchange_rate_for_journal_entry(acc_cc[0], amount, company_currency, currencies)
payable_amount += flt(amount, precision)
accounts.append({
- "account": acc_cc[0],
- "debit_in_account_currency": flt(amt, precision),
- "exchange_rate": flt(exchange_rate),
- "party_type": '',
- "cost_center": acc_cc[1] or self.cost_center,
- "project": self.project
- })
+ "account": acc_cc[0],
+ "debit_in_account_currency": flt(amt, precision),
+ "exchange_rate": flt(exchange_rate),
+ "party_type": '',
+ "cost_center": acc_cc[1] or self.cost_center,
+ "project": self.project
+ })
# Deductions
for acc_cc, amount in deductions.items():
exchange_rate, amt = self.get_amount_and_exchange_rate_for_journal_entry(acc_cc[0], amount, company_currency, currencies)
payable_amount -= flt(amount, precision)
accounts.append({
- "account": acc_cc[0],
- "credit_in_account_currency": flt(amt, precision),
- "exchange_rate": flt(exchange_rate),
- "cost_center": acc_cc[1] or self.cost_center,
- "party_type": '',
- "project": self.project
- })
+ "account": acc_cc[0],
+ "credit_in_account_currency": flt(amt, precision),
+ "exchange_rate": flt(exchange_rate),
+ "cost_center": acc_cc[1] or self.cost_center,
+ "party_type": '',
+ "project": self.project
+ })
# Payable amount
exchange_rate, payable_amt = self.get_amount_and_exchange_rate_for_journal_entry(payroll_payable_account, payable_amount, company_currency, currencies)
@@ -335,10 +336,9 @@
def make_payment_entry(self):
self.check_permission('write')
- cond = self.get_filter_condition()
salary_slip_name_list = frappe.db.sql(""" select t1.name from `tabSalary Slip` t1
- where t1.docstatus = 1 and start_date >= %s and end_date <= %s %s
- """ % ('%s', '%s', cond), (self.start_date, self.end_date), as_list = True)
+ where t1.docstatus = 1 and start_date >= %s and end_date <= %s and t1.payroll_entry = %s
+ """, (self.start_date, self.end_date, self.name), as_list = True)
if salary_slip_name_list and len(salary_slip_name_list) > 0:
salary_slip_total = 0
@@ -370,20 +370,20 @@
exchange_rate, amount = self.get_amount_and_exchange_rate_for_journal_entry(self.payment_account, je_payment_amount, company_currency, currencies)
accounts.append({
- "account": self.payment_account,
- "bank_account": self.bank_account,
- "credit_in_account_currency": flt(amount, precision),
- "exchange_rate": flt(exchange_rate),
- })
+ "account": self.payment_account,
+ "bank_account": self.bank_account,
+ "credit_in_account_currency": flt(amount, precision),
+ "exchange_rate": flt(exchange_rate),
+ })
exchange_rate, amount = self.get_amount_and_exchange_rate_for_journal_entry(payroll_payable_account, je_payment_amount, company_currency, currencies)
accounts.append({
- "account": payroll_payable_account,
- "debit_in_account_currency": flt(amount, precision),
- "exchange_rate": flt(exchange_rate),
- "reference_type": self.doctype,
- "reference_name": self.name
- })
+ "account": payroll_payable_account,
+ "debit_in_account_currency": flt(amount, precision),
+ "exchange_rate": flt(exchange_rate),
+ "reference_type": self.doctype,
+ "reference_name": self.name
+ })
if len(currencies) > 1:
multi_currency = 1
@@ -409,6 +409,7 @@
self.update(get_start_end_dates(self.payroll_frequency,
self.start_date or self.posting_date, self.company))
+ @frappe.whitelist()
def validate_employee_attendance(self):
employees_to_mark_attendance = []
days_in_payroll, days_holiday, days_attendance_marked = 0, 0, 0
@@ -424,7 +425,7 @@
employees_to_mark_attendance.append({
"employee": employee_detail.employee,
"employee_name": employee_detail.employee_name
- })
+ })
return employees_to_mark_attendance
def get_count_holidays_of_employee(self, employee, start_date):
@@ -441,11 +442,11 @@
def get_count_employee_attendance(self, employee, start_date):
marked_days = 0
attendances = frappe.get_all("Attendance",
- fields = ["count(*)"],
- filters = {
- "employee": employee,
- "attendance_date": ('between', [start_date, self.end_date])
- }, as_list=1)
+ fields = ["count(*)"],
+ filters = {
+ "employee": employee,
+ "attendance_date": ('between', [start_date, self.end_date])
+ }, as_list=1)
if attendances and attendances[0][0]:
marked_days = attendances[0][0]
return marked_days
@@ -553,6 +554,7 @@
def create_salary_slips_for_employees(employees, args, publish_progress=True):
salary_slips_exists_for = get_existing_salary_slips(employees, args)
count=0
+ salary_slips_not_created = []
for emp in employees:
if emp not in salary_slips_exists_for:
args.update({
@@ -566,33 +568,24 @@
frappe.publish_progress(count*100/len(set(employees) - set(salary_slips_exists_for)),
title = _("Creating Salary Slips..."))
else:
- salary_slip_name = frappe.db.sql(
- '''SELECT
- name
- FROM `tabSalary Slip`
- WHERE company=%s
- AND start_date >= %s
- AND end_date <= %s
- AND employee = %s
- ''', (args.company, args.start_date, args.end_date, emp), as_dict=True)
-
- salary_slip_doc = frappe.get_doc('Salary Slip', salary_slip_name[0].name)
- salary_slip_doc.exchange_rate = args.exchange_rate
- salary_slip_doc.set_totals()
- salary_slip_doc.db_update()
+ salary_slips_not_created.append(emp)
payroll_entry = frappe.get_doc("Payroll Entry", args.payroll_entry)
payroll_entry.db_set("salary_slips_created", 1)
payroll_entry.notify_update()
+ if salary_slips_not_created:
+ frappe.msgprint(_("Salary Slips already exists for employees {}, and will not be processed by this payroll.")
+ .format(frappe.bold(", ".join([emp for emp in salary_slips_not_created]))) , title=_("Message"), indicator="orange")
+
def get_existing_salary_slips(employees, args):
return frappe.db.sql_list("""
select distinct employee from `tabSalary Slip`
- where docstatus!= 2 and company = %s
+ where docstatus!= 2 and company = %s and payroll_entry = %s
and start_date >= %s and end_date <= %s
and employee in (%s)
- """ % ('%s', '%s', '%s', ', '.join(['%s']*len(employees))),
- [args.company, args.start_date, args.end_date] + employees)
+ """ % ('%s', '%s', '%s', '%s', ', '.join(['%s']*len(employees))),
+ [args.company, args.payroll_entry, args.start_date, args.end_date] + employees)
def submit_salary_slips_for_employees(payroll_entry, salary_slips, publish_progress=True):
submitted_ss = []
@@ -644,3 +637,61 @@
'txt': "%%%s%%" % frappe.db.escape(txt),
'start': start, 'page_len': page_len
})
+
+def get_employee_with_existing_salary_slip(start_date, end_date, company):
+ return frappe.db.sql_list("""
+ select employee from `tabSalary Slip`
+ where
+ (start_date between %(start_date)s and %(end_date)s
+ or
+ end_date between %(start_date)s and %(end_date)s
+ or
+ %(start_date)s between start_date and end_date)
+ and company = %(company)s
+ and docstatus = 1
+ """, {'start_date': start_date, 'end_date': end_date, 'company': company})
+
+@frappe.whitelist()
+@frappe.validate_and_sanitize_search_inputs
+def employee_query(doctype, txt, searchfield, start, page_len, filters):
+ filters = frappe._dict(filters)
+ conditions = []
+ exclude_employees = []
+ emp_cond = ''
+ if filters.start_date and filters.end_date:
+ employee_list = get_employee_with_existing_salary_slip(filters.start_date, filters.end_date, filters.company)
+ emp = filters.get('employees')
+ filters.pop('start_date')
+ filters.pop('end_date')
+ if filters.employees is not None:
+ filters.pop('employees')
+ if employee_list:
+ exclude_employees.extend(employee_list)
+ if emp:
+ exclude_employees.extend(emp)
+ if exclude_employees:
+ emp_cond += 'and employee not in %(exclude_employees)s'
+
+ return frappe.db.sql("""select name, employee_name from `tabEmployee`
+ where status = 'Active'
+ and docstatus < 2
+ and ({key} like %(txt)s
+ or employee_name like %(txt)s)
+ {emp_cond}
+ {fcond} {mcond}
+ order by
+ if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999),
+ if(locate(%(_txt)s, employee_name), locate(%(_txt)s, employee_name), 99999),
+ idx desc,
+ name, employee_name
+ limit %(start)s, %(page_len)s""".format(**{
+ 'key': searchfield,
+ 'fcond': get_filters_cond(doctype, filters, conditions),
+ 'mcond': get_match_cond(doctype),
+ 'emp_cond': emp_cond
+ }), {
+ 'txt': "%%%s%%" % txt,
+ '_txt': txt.replace("%", ""),
+ 'start': start,
+ 'page_len': page_len,
+ 'exclude_employees': exclude_employees})
diff --git a/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py b/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py
index 84c3814..7528bf7 100644
--- a/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py
+++ b/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py
@@ -51,21 +51,22 @@
company_doc = frappe.get_doc('Company', company)
salary_structure = make_salary_structure("_Test Multi Currency Salary Structure", "Monthly", company=company, currency='USD')
- create_salary_structure_assignment(employee, salary_structure.name, company=company)
+ create_salary_structure_assignment(employee, salary_structure.name, company=company, currency='USD')
frappe.db.sql("""delete from `tabSalary Slip` where employee=%s""",(frappe.db.get_value("Employee", {"user_id": "test_muti_currency_employee@payroll.com"})))
salary_slip = get_salary_slip("test_muti_currency_employee@payroll.com", "Monthly", "_Test Multi Currency Salary Structure")
dates = get_start_end_dates('Monthly', nowdate())
- payroll_entry = make_payroll_entry(start_date=dates.start_date, end_date=dates.end_date,
+ payroll_entry = make_payroll_entry(start_date=dates.start_date, end_date=dates.end_date,
payable_account=company_doc.default_payroll_payable_account, currency='USD', exchange_rate=70)
payroll_entry.make_payment_entry()
salary_slip.load_from_db()
payroll_je = salary_slip.journal_entry
- payroll_je_doc = frappe.get_doc('Journal Entry', payroll_je)
+ if payroll_je:
+ payroll_je_doc = frappe.get_doc('Journal Entry', payroll_je)
- self.assertEqual(salary_slip.base_gross_pay, payroll_je_doc.total_debit)
- self.assertEqual(salary_slip.base_gross_pay, payroll_je_doc.total_credit)
+ self.assertEqual(salary_slip.base_gross_pay, payroll_je_doc.total_debit)
+ self.assertEqual(salary_slip.base_gross_pay, payroll_je_doc.total_credit)
payment_entry = frappe.db.sql('''
Select ifnull(sum(je.total_debit),0) as total_debit, ifnull(sum(je.total_credit),0) as total_credit from `tabJournal Entry` je, `tabJournal Entry Account` jea
diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.js b/erpnext/payroll/doctype/salary_slip/salary_slip.js
index 3e8a213..e3993fa 100644
--- a/erpnext/payroll/doctype/salary_slip/salary_slip.js
+++ b/erpnext/payroll/doctype/salary_slip/salary_slip.js
@@ -39,7 +39,8 @@
frm.set_query("employee", function() {
return {
- query: "erpnext.controllers.queries.employee_query"
+ query: "erpnext.controllers.queries.employee_query",
+ filters: frm.doc.company
};
});
},
@@ -93,28 +94,31 @@
},
set_exchange_rate: function(frm, company_currency) {
- if (frm.doc.currency) {
- var from_currency = frm.doc.currency;
- if (from_currency != company_currency) {
- frm.events.hide_loan_section(frm);
- frappe.call({
- method: "erpnext.setup.utils.get_exchange_rate",
- args: {
- from_currency: from_currency,
- to_currency: company_currency,
- },
- callback: function(r) {
- frm.set_value("exchange_rate", flt(r.message));
- frm.set_df_property("exchange_rate", "hidden", 0);
- frm.set_df_property("exchange_rate", "description", "1 " + frm.doc.currency
- + " = [?] " + company_currency);
- }
- });
- } else {
- frm.set_value("exchange_rate", 1.0);
- frm.set_df_property("exchange_rate", "hidden", 1);
- frm.set_df_property("exchange_rate", "description", "");
- }
+ if (frm.doc.docstatus === 0) {
+ if (frm.doc.currency) {
+ var from_currency = frm.doc.currency;
+ if (from_currency != company_currency) {
+ frm.events.hide_loan_section(frm);
+ frappe.call({
+ method: "erpnext.setup.utils.get_exchange_rate",
+ args: {
+ from_currency: from_currency,
+ to_currency: company_currency,
+ },
+ callback: function(r) {
+ if (r.message) {
+ frm.set_value("exchange_rate", flt(r.message));
+ frm.set_df_property('exchange_rate', 'hidden', 0);
+ frm.set_df_property("exchange_rate", "description", "1 " + frm.doc.currency
+ + " = [?] " + company_currency);
+ }
+ }
+ });
+ } else {
+ frm.set_value("exchange_rate", 1.0);
+ frm.set_df_property('exchange_rate', 'hidden', 1);
+ frm.set_df_property("exchange_rate", "description", "" );
+ }
}
},
diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.py b/erpnext/payroll/doctype/salary_slip/salary_slip.py
index aa9acd8..f6d4c7b 100644
--- a/erpnext/payroll/doctype/salary_slip/salary_slip.py
+++ b/erpnext/payroll/doctype/salary_slip/salary_slip.py
@@ -124,9 +124,12 @@
def check_existing(self):
if not self.salary_slip_based_on_timesheet:
+ cond = ""
+ if self.payroll_entry:
+ cond += "and payroll_entry = '{0}'".format(self.payroll_entry)
ret_exist = frappe.db.sql("""select name from `tabSalary Slip`
where start_date = %s and end_date = %s and docstatus != 2
- and employee = %s and name != %s""",
+ and employee = %s and name != %s {0}""".format(cond),
(self.start_date, self.end_date, self.employee, self.name))
if ret_exist:
self.employee = ''
@@ -1053,7 +1056,7 @@
repayment_entry.save()
repayment_entry.submit()
- loan.loan_repayment_entry = repayment_entry.name
+ frappe.db.set_value("Salary Slip Loan", loan.name, "loan_repayment_entry", repayment_entry.name)
def cancel_loan_repayment_entry(self):
for loan in self.loans:
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index 21a20a7..6c2144d 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -737,28 +737,34 @@
this.frm.trigger("item_code", cdt, cdn);
}
else {
- var valid_serial_nos = [];
- var serialnos = [];
// Replacing all occurences of comma with carriage return
item.serial_no = item.serial_no.replace(/,/g, '\n');
- serialnos = item.serial_no.split("\n");
- for (var i = 0; i < serialnos.length; i++) {
- if (serialnos[i] != "") {
- valid_serial_nos.push(serialnos[i]);
- }
- }
item.conversion_factor = item.conversion_factor || 1;
-
refresh_field("serial_no", item.name, item.parentfield);
- if(!doc.is_return && cint(user_defaults.set_qty_in_transactions_based_on_serial_no_input)) {
- frappe.model.set_value(item.doctype, item.name,
- "qty", valid_serial_nos.length / item.conversion_factor);
- frappe.model.set_value(item.doctype, item.name, "stock_qty", valid_serial_nos.length);
+ if (!doc.is_return && cint(frappe.user_defaults.set_qty_in_transactions_based_on_serial_no_input)) {
+ setTimeout(() => {
+ me.update_qty(cdt, cdn);
+ }, 10000);
}
}
}
},
+ update_qty: function(cdt, cdn) {
+ var valid_serial_nos = [];
+ var serialnos = [];
+ var item = frappe.get_doc(cdt, cdn);
+ serialnos = item.serial_no.split("\n");
+ for (var i = 0; i < serialnos.length; i++) {
+ if (serialnos[i] != "") {
+ valid_serial_nos.push(serialnos[i]);
+ }
+ }
+ frappe.model.set_value(item.doctype, item.name,
+ "qty", valid_serial_nos.length / item.conversion_factor);
+ frappe.model.set_value(item.doctype, item.name, "stock_qty", valid_serial_nos.length);
+ },
+
validate: function() {
this.calculate_taxes_and_totals(false);
},
diff --git a/erpnext/setup/doctype/global_defaults/global_defaults.js b/erpnext/setup/doctype/global_defaults/global_defaults.js
index 552331a..942dd59 100644
--- a/erpnext/setup/doctype/global_defaults/global_defaults.js
+++ b/erpnext/setup/doctype/global_defaults/global_defaults.js
@@ -17,7 +17,7 @@
method: "frappe.client.get_list",
args: {
doctype: "UOM Conversion Factor",
- filters: { "category": "Length" },
+ filters: { "category": __("Length") },
fields: ["to_uom"],
limit_page_length: 500
},