Merge branch 'develop' of https://github.com/frappe/erpnext into loan_patch_and_fixes

This commit is contained in:
Deepesh Garg 2020-05-18 11:52:39 +05:30
commit a8807e2696
12 changed files with 168 additions and 38 deletions

View File

@ -162,7 +162,8 @@ def get_tds_amount(suppliers, net_total, company, tax_details, fiscal_year_detai
debit_note_amount = get_debit_note_amount(suppliers, year_start_date, year_end_date)
supplier_credit_amount -= debit_note_amount
if supplier_credit_amount >= tax_details.get('threshold', 0) or supplier_credit_amount >= tax_details.get('cumulative_threshold', 0):
if ((tax_details.get('threshold', 0) and supplier_credit_amount >= tax_details.threshold)
or (tax_details.get('cumulative_threshold', 0) and supplier_credit_amount >= tax_details.cumulative_threshold)):
if ldc and is_valid_certificate(ldc.valid_from, ldc.valid_upto, posting_date, tds_deducted, net_total,
ldc.certificate_limit):
@ -224,4 +225,4 @@ def is_valid_certificate(valid_from, valid_upto, posting_date, deducted_amount,
certificate_limit > deducted_amount):
valid = True
return valid
return valid

View File

@ -14,6 +14,8 @@
"is_group",
"disabled",
"section_break_4",
"payroll_cost_center",
"column_break_9",
"leave_block_list",
"leave_section",
"leave_approvers",
@ -125,13 +127,23 @@
{
"fieldname": "column_break_3",
"fieldtype": "Column Break"
},
{
"fieldname": "payroll_cost_center",
"fieldtype": "Link",
"label": "Payroll Cost Center",
"options": "Cost Center"
},
{
"fieldname": "column_break_9",
"fieldtype": "Column Break"
}
],
"icon": "fa fa-sitemap",
"idx": 1,
"is_tree": 1,
"links": [],
"modified": "2020-03-18 18:03:27.784362",
"modified": "2020-05-05 18:49:28.503931",
"modified_by": "Administrator",
"module": "HR",
"name": "Department",

View File

@ -60,6 +60,8 @@
"default_shift",
"salary_information",
"salary_mode",
"payroll_cost_center",
"column_break_52",
"bank_name",
"bank_ac_no",
"health_insurance_section",
@ -783,13 +785,25 @@
{
"fieldname": "column_break_19",
"fieldtype": "Column Break"
},
{
"fetch_from": "department.payroll_cost_center",
"fetch_if_empty": 1,
"fieldname": "payroll_cost_center",
"fieldtype": "Link",
"label": "Payroll Cost Center",
"options": "Cost Center"
},
{
"fieldname": "column_break_52",
"fieldtype": "Column Break"
}
],
"icon": "fa fa-user",
"idx": 24,
"image_field": "image",
"links": [],
"modified": "2020-04-08 12:25:34.306695",
"modified": "2020-05-05 18:51:03.152503",
"modified_by": "Administrator",
"module": "HR",
"name": "Employee",

View File

@ -45,7 +45,7 @@ class TestEmployee(unittest.TestCase):
employee1_doc.status = 'Left'
self.assertRaises(EmployeeLeftValidationError, employee1_doc.save)
def make_employee(user, company=None):
def make_employee(user, company=None, **kwargs):
if not frappe.db.get_value("User", user):
frappe.get_doc({
"doctype": "User",
@ -55,7 +55,7 @@ def make_employee(user, company=None):
"roles": [{"doctype": "Has Role", "role": "Employee"}]
}).insert()
if not frappe.db.get_value("Employee", { "user_id": user, "company": company or erpnext.get_default_company() }):
if not frappe.db.get_value("Employee", {"user_id": user}):
employee = frappe.get_doc({
"doctype": "Employee",
"naming_series": "EMP-",
@ -71,7 +71,10 @@ def make_employee(user, company=None):
"prefered_email": user,
"status": "Active",
"employment_type": "Intern"
}).insert()
})
if kwargs:
employee.update(kwargs)
employee.insert()
return employee.name
else:
return frappe.get_value("Employee", {"employee_name":user}, "name")

View File

@ -55,6 +55,7 @@ class PayrollEntry(Document):
ifnull(salary_slip_based_on_timesheet,0) = %(salary_slip_based_on_timesheet)s
{condition}""".format(condition=condition),
{"company": self.company, "salary_slip_based_on_timesheet":self.salary_slip_based_on_timesheet})
if sal_struct:
cond += "and t2.salary_structure IN %(sal_struct)s "
cond += "and %(from_date)s >= t2.from_date"
@ -138,7 +139,7 @@ class PayrollEntry(Document):
cond = self.get_filter_condition()
ss_list = frappe.db.sql("""
select t1.name, t1.salary_structure from `tabSalary Slip` t1
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)
@ -169,10 +170,14 @@ class PayrollEntry(Document):
def get_salary_components(self, component_type):
salary_slips = self.get_sal_slip_list(ss_status = 1, as_dict = True)
if salary_slips:
salary_components = frappe.db.sql("""select salary_component, amount, parentfield
from `tabSalary Detail` where parentfield = '%s' and parent in (%s)""" %
(component_type, ', '.join(['%s']*len(salary_slips))), tuple([d.name for d in salary_slips]), as_dict=True)
if salary_slips:
salary_components = frappe.db.sql("""
select ssd.salary_component, ssd.amount, ssd.parentfield, ss.payroll_cost_center
from `tabSalary Slip` ss, `tabSalary Detail` ssd
where ss.name = ssd.parent and ssd.parentfield = '%s' and ss.name in (%s)
""" % (component_type, ', '.join(['%s']*len(salary_slips))),
tuple([d.name for d in salary_slips]), as_dict=True)
return salary_components
def get_salary_component_total(self, component_type = None):
@ -186,15 +191,16 @@ class PayrollEntry(Document):
if is_flexible_benefit == 1 and only_tax_impact ==1:
add_component_to_accrual_jv_entry = False
if add_component_to_accrual_jv_entry:
component_dict[item['salary_component']] = component_dict.get(item['salary_component'], 0) + item['amount']
component_dict[(item.salary_component, item.payroll_cost_center)] \
= component_dict.get((item.salary_component, item.payroll_cost_center), 0) + flt(item.amount)
account_details = self.get_account(component_dict = component_dict)
return account_details
def get_account(self, component_dict = None):
account_dict = {}
for s, a in component_dict.items():
account = self.get_salary_component_account(s)
account_dict[account] = account_dict.get(account, 0) + a
account_dict = {}
for key, amount in component_dict.items():
account = self.get_salary_component_account(key[0])
account_dict[(account, key[1])] = account_dict.get((account, key[1]), 0) + amount
return account_dict
def get_default_payroll_payable_account(self):
@ -227,23 +233,23 @@ class PayrollEntry(Document):
payable_amount = 0
# Earnings
for acc, amount in earnings.items():
for acc_cc, amount in earnings.items():
payable_amount += flt(amount, precision)
accounts.append({
"account": acc,
"account": acc_cc[0],
"debit_in_account_currency": flt(amount, precision),
"party_type": '',
"cost_center": self.cost_center,
"cost_center": acc_cc[1] or self.cost_center,
"project": self.project
})
# Deductions
for acc, amount in deductions.items():
for acc_cc, amount in deductions.items():
payable_amount -= flt(amount, precision)
accounts.append({
"account": acc,
"account": acc_cc[0],
"credit_in_account_currency": flt(amount, precision),
"cost_center": self.cost_center,
"cost_center": acc_cc[1] or self.cost_center,
"party_type": '',
"project": self.project
})
@ -253,6 +259,7 @@ class PayrollEntry(Document):
"account": default_payroll_payable_account,
"credit_in_account_currency": flt(payable_amount, precision),
"party_type": '',
"cost_center": self.cost_center
})
journal_entry.set("accounts", accounts)

View File

@ -10,15 +10,16 @@ from frappe.utils import add_months
from erpnext.hr.doctype.payroll_entry.payroll_entry import get_start_end_dates, get_end_date
from erpnext.hr.doctype.employee.test_employee import make_employee
from erpnext.hr.doctype.salary_slip.test_salary_slip import get_salary_component_account, \
make_earning_salary_component, make_deduction_salary_component
make_earning_salary_component, make_deduction_salary_component, create_account
from erpnext.hr.doctype.salary_structure.test_salary_structure import make_salary_structure
from erpnext.loan_management.doctype.loan.test_loan import create_loan, 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
class TestPayrollEntry(unittest.TestCase):
def setUp(self):
for dt in ["Salary Slip", "Salary Component", "Salary Component Account", "Payroll Entry", "Salary Structure"]:
frappe.db.sql("delete from `tab%s`" % dt)
for dt in ["Salary Slip", "Salary Component", "Salary Component Account",
"Payroll Entry", "Salary Structure", "Salary Structure Assignment", "Payroll Employee Detail", "Additional Salary"]:
frappe.db.sql("delete from `tab%s`" % dt)
make_earning_salary_component(setup=True, company_list=["_Test Company"])
make_deduction_salary_component(setup=True, company_list=["_Test Company"])
@ -33,11 +34,59 @@ class TestPayrollEntry(unittest.TestCase):
get_salary_component_account(data.name)
employee = frappe.db.get_value("Employee", {'company': company})
make_salary_structure("_Test Salary Structure", "Monthly", employee)
make_salary_structure("_Test Salary Structure", "Monthly", employee, company=company)
dates = get_start_end_dates('Monthly', nowdate())
if not frappe.db.get_value("Salary Slip", {"start_date": dates.start_date, "end_date": dates.end_date}):
make_payroll_entry(start_date=dates.start_date, end_date=dates.end_date)
def test_payroll_entry_with_employee_cost_center(self): # pylint: disable=no-self-use
for data in frappe.get_all('Salary Component', fields = ["name"]):
if not frappe.db.get_value('Salary Component Account',
{'parent': data.name, 'company': "_Test Company"}, 'name'):
get_salary_component_account(data.name)
if not frappe.db.exists('Department', "cc - _TC"):
frappe.get_doc({
'doctype': 'Department',
'department_name': "cc",
"company": "_Test Company"
}).insert()
employee1 = make_employee("test_employee1@example.com", payroll_cost_center="_Test Cost Center - _TC",
department="cc - _TC", company="_Test Company")
employee2 = make_employee("test_employee2@example.com", payroll_cost_center="_Test Cost Center 2 - _TC",
department="cc - _TC", company="_Test Company")
make_salary_structure("_Test Salary Structure 1", "Monthly", employee1, company="_Test Company")
make_salary_structure("_Test Salary Structure 2", "Monthly", employee2, company="_Test Company")
if not frappe.db.exists("Account", "_Test Payroll Payable - _TC"):
create_account(account_name="_Test Payroll Payable",
company="_Test Company", parent_account="Current Liabilities - _TC")
frappe.db.set_value("Company", "_Test Company", "default_payroll_payable_account",
"_Test Payroll Payable - _TC")
dates = get_start_end_dates('Monthly', nowdate())
if not frappe.db.get_value("Salary Slip", {"start_date": dates.start_date, "end_date": dates.end_date}):
pe = make_payroll_entry(start_date=dates.start_date, end_date=dates.end_date,
department="cc - _TC", company="_Test Company", payment_account="Cash - _TC", cost_center="Main - _TC")
je = frappe.db.get_value("Salary Slip", {"payroll_entry": pe.name}, "journal_entry")
je_entries = frappe.db.sql("""
select account, cost_center, debit, credit
from `tabJournal Entry Account`
where parent=%s
order by account, cost_center
""", je)
expected_je = (
('_Test Payroll Payable - _TC', 'Main - _TC', 0.0, 155600.0),
('Salary - _TC', '_Test Cost Center - _TC', 78000.0, 0.0),
('Salary - _TC', '_Test Cost Center 2 - _TC', 78000.0, 0.0),
('Salary Deductions - _TC', '_Test Cost Center - _TC', 0.0, 200.0),
('Salary Deductions - _TC', '_Test Cost Center 2 - _TC', 0.0, 200.0)
)
self.assertEqual(je_entries, expected_je)
def test_get_end_date(self):
self.assertEqual(get_end_date('2017-01-01', 'monthly'), {'end_date': '2017-01-31'})
self.assertEqual(get_end_date('2017-02-01', 'monthly'), {'end_date': '2017-02-28'})
@ -49,7 +98,6 @@ class TestPayrollEntry(unittest.TestCase):
self.assertEqual(get_end_date('2017-02-15', 'daily'), {'end_date': '2017-02-15'})
def test_loan(self):
branch = "Test Employee Branch"
applicant = make_employee("test_employee@loan.com", company="_Test Company")
company = "_Test Company"
@ -116,6 +164,7 @@ def make_payroll_entry(**args):
payroll_entry.posting_date = nowdate()
payroll_entry.payroll_frequency = "Monthly"
payroll_entry.branch = args.branch or None
payroll_entry.department = args.department or None
if args.cost_center:
payroll_entry.cost_center = args.cost_center
@ -123,6 +172,7 @@ def make_payroll_entry(**args):
if args.payment_account:
payroll_entry.payment_account = args.payment_account
payroll_entry.fill_employee_details()
payroll_entry.save()
payroll_entry.create_salary_slips()
payroll_entry.submit_salary_slips()

View File

@ -12,6 +12,7 @@
"department",
"designation",
"branch",
"payroll_cost_center",
"column_break1",
"status",
"journal_entry",
@ -459,13 +460,22 @@
"options": "Salary Slip",
"print_hide": 1,
"read_only": 1
},
{
"fetch_from": "employee.payroll_cost_center",
"fetch_if_empty": 1,
"fieldname": "payroll_cost_center",
"fieldtype": "Link",
"label": "Payroll Cost Center",
"options": "Cost Center",
"read_only": 1
}
],
"icon": "fa fa-file-text",
"idx": 9,
"is_submittable": 1,
"links": [],
"modified": "2020-04-14 20:02:53.159827",
"modified": "2020-05-05 18:55:26.173629",
"modified_by": "Administrator",
"module": "HR",
"name": "Salary Slip",

View File

@ -422,22 +422,32 @@ def get_salary_component_account(sal_comp, company_list=None):
sal_comp = frappe.get_doc("Salary Component", sal_comp)
if not sal_comp.get("accounts"):
for d in company_list:
company_abbr = frappe.get_cached_value('Company', d, 'abbr')
if sal_comp.type == "Earning":
account_name = "Salary"
parent_account = "Indirect Expenses - " + company_abbr
else:
account_name = "Salary Deductions"
parent_account = "Current Liabilities - " + company_abbr
sal_comp.append("accounts", {
"company": d,
"default_account": create_account(d)
"default_account": create_account(account_name, d, parent_account)
})
sal_comp.save()
def create_account(company):
salary_account = frappe.db.get_value("Account", "Salary - " + frappe.get_cached_value('Company', company, 'abbr'))
if not salary_account:
def create_account(account_name, company, parent_account):
company_abbr = frappe.get_cached_value('Company', company, 'abbr')
account = frappe.db.get_value("Account", account_name + " - " + company_abbr)
if not account:
frappe.get_doc({
"doctype": "Account",
"account_name": "Salary",
"parent_account": "Indirect Expenses - " + frappe.get_cached_value('Company', company, 'abbr'),
"account_name": account_name,
"parent_account": parent_account,
"company": company
}).insert()
return salary_account
return account
def make_earning_salary_component(setup=False, test_tax=False, company_list=None):
data = [
@ -683,7 +693,7 @@ def setup_test():
make_earning_salary_component(setup=True, company_list=["_Test Company"])
make_deduction_salary_component(setup=True, company_list=["_Test Company"])
for dt in ["Leave Application", "Leave Allocation", "Salary Slip", "Attendance"]:
for dt in ["Leave Application", "Leave Allocation", "Salary Slip", "Attendance", "Additional Salary"]:
frappe.db.sql("delete from `tab%s`" % dt)
make_holiday_list()

View File

@ -153,12 +153,16 @@ def make_salary_slip(source_name, target_doc = None, employee = None, as_print =
def postprocess(source, target):
if employee:
employee_details = frappe.db.get_value("Employee", employee,
["employee_name", "branch", "designation", "department"], as_dict=1)
["employee_name", "branch", "designation", "department", "payroll_cost_center"], as_dict=1)
target.employee = employee
target.employee_name = employee_details.employee_name
target.branch = employee_details.branch
target.designation = employee_details.designation
target.department = employee_details.department
target.payroll_cost_center = employee_details.payroll_cost_center
if not target.payroll_cost_center and target.department:
target.payroll_cost_center = frappe.db.get_value("Department", target.department, "payroll_cost_center")
target.run_method('process_salary_structure', for_preview=for_preview)
doc = get_mapped_doc("Salary Structure", source_name, {

View File

@ -128,6 +128,7 @@ def make_salary_structure(salary_structure, payroll_frequency, employee=None, do
salary_structure_doc.insert()
if not dont_submit:
salary_structure_doc.submit()
else:
salary_structure_doc = frappe.get_doc("Salary Structure", salary_structure)

View File

@ -685,3 +685,4 @@ execute:frappe.rename_doc("Desk Page", "Getting Started", "Home", force=True)
erpnext.patches.v12_0.unset_customer_supplier_based_on_type_of_item_price
erpnext.patches.v12_0.set_valid_till_date_in_supplier_quotation
erpnext.patches.v13_0.update_old_loans
erpnext.patches.v12_0.set_serial_no_status

View File

@ -0,0 +1,17 @@
from __future__ import unicode_literals
import frappe
from frappe.utils import getdate, nowdate
def execute():
frappe.reload_doc('stock', 'doctype', 'serial_no')
for serial_no in frappe.db.sql("""select name, delivery_document_type, warranty_expiry_date from `tabSerial No`
where (status is NULL OR status='')""", as_dict = 1):
if serial_no.get("delivery_document_type"):
status = "Delivered"
elif serial_no.get("warranty_expiry_date") and getdate(serial_no.get("warranty_expiry_date")) <= getdate(nowdate()):
status = "Expired"
else:
status = "Active"
frappe.db.set_value("Serial No", serial_no.get("name"), "status", status)