Merge branch 'version-13-beta-pre-release' into version-13-beta

This commit is contained in:
Saurabh 2021-03-05 09:34:01 +05:30
commit 202ef92fa1
15 changed files with 232 additions and 63 deletions

View File

@ -5,7 +5,7 @@ import frappe
from erpnext.hooks import regional_overrides
from frappe.utils import getdate
__version__ = '13.0.0-beta.12'
__version__ = '13.0.0-beta.13'
def get_default_company(user=None):
'''Get default company for user'''

View File

@ -18,7 +18,6 @@ class ValueMultiplierError(frappe.ValidationError): pass
class LeaveAllocation(Document):
def validate(self):
self.validate_period()
self.validate_new_leaves_allocated_value()
self.validate_allocation_overlap()
self.validate_back_dated_allocation()
self.set_total_leaves_allocated()
@ -72,11 +71,6 @@ class LeaveAllocation(Document):
if frappe.db.get_value("Leave Type", self.leave_type, "is_lwp"):
frappe.throw(_("Leave Type {0} cannot be allocated since it is leave without pay").format(self.leave_type))
def validate_new_leaves_allocated_value(self):
"""validate that leave allocation is in multiples of 0.5"""
if flt(self.new_leaves_allocated) % 0.5:
frappe.throw(_("Leaves must be allocated in multiples of 0.5"), ValueMultiplierError)
def validate_allocation_overlap(self):
leave_allocation = frappe.db.sql("""
SELECT

View File

@ -106,12 +106,14 @@
"fieldname": "leaves_allocated",
"fieldtype": "Check",
"hidden": 1,
"label": "Leaves Allocated"
"label": "Leaves Allocated",
"no_copy": 1,
"print_hide": 1
}
],
"is_submittable": 1,
"links": [],
"modified": "2020-12-31 16:43:30.695206",
"modified": "2021-03-01 17:54:01.014509",
"modified_by": "Administrator",
"module": "HR",
"name": "Leave Policy Assignment",

View File

@ -6,7 +6,7 @@ from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
from frappe import _, bold
from frappe.utils import getdate, date_diff, comma_and, formatdate
from frappe.utils import getdate, date_diff, comma_and, formatdate, get_datetime, flt
from math import ceil
import json
from six import string_types
@ -84,17 +84,52 @@ class LeavePolicyAssignment(Document):
return allocation.name, new_leaves_allocated
def get_new_leaves(self, leave_type, new_leaves_allocated, leave_type_details, date_of_joining):
from frappe.model.meta import get_field_precision
precision = get_field_precision(frappe.get_meta("Leave Allocation").get_field("new_leaves_allocated"))
# Earned Leaves and Compensatory Leaves are allocated by scheduler, initially allocate 0
if leave_type_details.get(leave_type).is_compensatory == 1:
new_leaves_allocated = 0
elif leave_type_details.get(leave_type).is_earned_leave == 1:
if self.assignment_based_on == "Leave Period":
new_leaves_allocated = self.get_leaves_for_passed_months(leave_type, new_leaves_allocated, leave_type_details, date_of_joining)
else:
new_leaves_allocated = 0
# Calculate leaves at pro-rata basis for employees joining after the beginning of the given leave period
if getdate(date_of_joining) > getdate(self.effective_from):
elif getdate(date_of_joining) > getdate(self.effective_from):
remaining_period = ((date_diff(self.effective_to, date_of_joining) + 1) / (date_diff(self.effective_to, self.effective_from) + 1))
new_leaves_allocated = ceil(new_leaves_allocated * remaining_period)
# Earned Leaves and Compensatory Leaves are allocated by scheduler, initially allocate 0
if leave_type_details.get(leave_type).is_earned_leave == 1 or leave_type_details.get(leave_type).is_compensatory == 1:
new_leaves_allocated = 0
return flt(new_leaves_allocated, precision)
def get_leaves_for_passed_months(self, leave_type, new_leaves_allocated, leave_type_details, date_of_joining):
from erpnext.hr.utils import get_monthly_earned_leave
current_month = get_datetime().month
current_year = get_datetime().year
from_date = frappe.db.get_value("Leave Period", self.leave_period, "from_date")
if getdate(date_of_joining) > getdate(from_date):
from_date = date_of_joining
from_date_month = get_datetime(from_date).month
from_date_year = get_datetime(from_date).year
months_passed = 0
if current_year == from_date_year and current_month > from_date_month:
months_passed = current_month - from_date_month
elif current_year > from_date_year:
months_passed = (12 - from_date_month) + current_month
if months_passed > 0:
monthly_earned_leave = get_monthly_earned_leave(new_leaves_allocated,
leave_type_details.get(leave_type).earned_leave_frequency, leave_type_details.get(leave_type).rounding)
new_leaves_allocated = monthly_earned_leave * months_passed
return new_leaves_allocated
@frappe.whitelist()
def grant_leave_for_multiple_employees(leave_policy_assignments):
leave_policy_assignments = json.loads(leave_policy_assignments)
@ -156,7 +191,8 @@ def automatically_allocate_leaves_based_on_leave_policy():
def get_leave_type_details():
leave_type_details = frappe._dict()
leave_types = frappe.get_all("Leave Type",
fields=["name", "is_lwp", "is_earned_leave", "is_compensatory", "is_carry_forward", "expire_carry_forwarded_leaves_after_days"])
fields=["name", "is_lwp", "is_earned_leave", "is_compensatory",
"is_carry_forward", "expire_carry_forwarded_leaves_after_days", "earned_leave_frequency", "rounding"])
for d in leave_types:
leave_type_details.setdefault(d.name, d)
return leave_type_details

View File

@ -172,7 +172,7 @@
"fieldname": "rounding",
"fieldtype": "Select",
"label": "Rounding",
"options": "0.5\n1.0"
"options": "\n0.25\n0.5\n1.0"
},
{
"depends_on": "is_carry_forward",
@ -197,6 +197,7 @@
"label": "Based On Date Of Joining"
},
{
"default": "0",
"depends_on": "eval:doc.is_lwp == 0",
"fieldname": "is_ppl",
"fieldtype": "Check",
@ -213,7 +214,7 @@
"icon": "fa fa-flag",
"idx": 1,
"links": [],
"modified": "2020-10-15 15:49:47.555105",
"modified": "2021-03-02 11:22:33.776320",
"modified_by": "Administrator",
"module": "HR",
"name": "Leave Type",

View File

@ -316,13 +316,7 @@ def allocate_earned_leaves():
update_previous_leave_allocation(allocation, annual_allocation, e_leave_type)
def update_previous_leave_allocation(allocation, annual_allocation, e_leave_type):
divide_by_frequency = {"Yearly": 1, "Half-Yearly": 6, "Quarterly": 4, "Monthly": 12}
if annual_allocation:
earned_leaves = flt(annual_allocation) / divide_by_frequency[e_leave_type.earned_leave_frequency]
if e_leave_type.rounding == "0.5":
earned_leaves = round(earned_leaves * 2) / 2
else:
earned_leaves = round(earned_leaves)
earned_leaves = get_monthly_earned_leave(annual_allocation, e_leave_type.earned_leave_frequency, e_leave_type.rounding)
allocation = frappe.get_doc('Leave Allocation', allocation.name)
new_allocation = flt(allocation.total_leaves_allocated) + flt(earned_leaves)
@ -335,6 +329,21 @@ def update_previous_leave_allocation(allocation, annual_allocation, e_leave_type
today_date = today()
create_additional_leave_ledger_entry(allocation, earned_leaves, today_date)
def get_monthly_earned_leave(annual_leaves, frequency, rounding):
earned_leaves = 0.0
divide_by_frequency = {"Yearly": 1, "Half-Yearly": 6, "Quarterly": 4, "Monthly": 12}
if annual_leaves:
earned_leaves = flt(annual_leaves) / divide_by_frequency[frequency]
if rounding:
if rounding == "0.25":
earned_leaves = round(earned_leaves * 4) / 4
elif rounding == "0.5":
earned_leaves = round(earned_leaves * 2) / 2
else:
earned_leaves = round(earned_leaves)
return earned_leaves
def get_leave_allocations(date, leave_type):
return frappe.db.sql("""select name, employee, from_date, to_date, leave_policy_assignment, leave_policy

View File

@ -1,13 +1,24 @@
import frappe
from frappe import _
from frappe.utils import getdate, get_time
from frappe.utils import getdate, get_time, today
from erpnext.stock.stock_ledger import update_entries_after
from erpnext.accounts.utils import update_gl_entries_after
def execute():
frappe.reload_doc('stock', 'doctype', 'repost_item_valuation')
for doctype in ('repost_item_valuation', 'stock_entry_detail', 'purchase_receipt_item',
'purchase_invoice_item', 'delivery_note_item', 'sales_invoice_item', 'packed_item'):
frappe.reload_doc('stock', 'doctype', doctype)
frappe.reload_doc('buying', 'doctype', 'purchase_receipt_item_supplied')
reposting_project_deployed_on = frappe.db.get_value("DocType", "Repost Item Valuation", "creation")
reposting_project_deployed_on = get_creation_time()
posting_date = getdate(reposting_project_deployed_on)
posting_time = get_time(reposting_project_deployed_on)
if posting_date == today():
return
frappe.clear_cache()
frappe.flags.warehouse_account_map = {}
data = frappe.db.sql('''
SELECT
@ -41,10 +52,12 @@ def execute():
print("Reposting General Ledger Entries...")
posting_date = getdate(reposting_project_deployed_on)
posting_time = get_time(reposting_project_deployed_on)
for row in frappe.get_all('Company', filters= {'enable_perpetual_inventory': 1}):
update_gl_entries_after(posting_date, posting_time, company=row.name)
frappe.db.auto_commit_on_many_writes = 0
def get_creation_time():
return frappe.db.sql(''' SELECT create_time FROM
INFORMATION_SCHEMA.TABLES where TABLE_NAME = "tabRepost Item Valuation" ''', as_list=1)[0][0]

View File

@ -15,6 +15,7 @@
"daily_wages_fraction_for_half_day",
"email_salary_slip_to_employee",
"encrypt_salary_slips_in_emails",
"show_leave_balances_in_salary_slip",
"password_policy"
],
"fields": [
@ -23,58 +24,44 @@
"fieldname": "payroll_based_on",
"fieldtype": "Select",
"label": "Calculate Payroll Working Days Based On",
"options": "Leave\nAttendance",
"show_days": 1,
"show_seconds": 1
"options": "Leave\nAttendance"
},
{
"fieldname": "max_working_hours_against_timesheet",
"fieldtype": "Float",
"label": "Max working hours against Timesheet",
"show_days": 1,
"show_seconds": 1
"label": "Max working hours against Timesheet"
},
{
"default": "0",
"description": "If checked, Total no. of Working Days will include holidays, and this will reduce the value of Salary Per Day",
"fieldname": "include_holidays_in_total_working_days",
"fieldtype": "Check",
"label": "Include holidays in Total no. of Working Days",
"show_days": 1,
"show_seconds": 1
"label": "Include holidays in Total no. of Working Days"
},
{
"default": "0",
"description": "If checked, hides and disables Rounded Total field in Salary Slips",
"fieldname": "disable_rounded_total",
"fieldtype": "Check",
"label": "Disable Rounded Total",
"show_days": 1,
"show_seconds": 1
"label": "Disable Rounded Total"
},
{
"fieldname": "column_break_11",
"fieldtype": "Column Break",
"show_days": 1,
"show_seconds": 1
"fieldtype": "Column Break"
},
{
"default": "0.5",
"description": "The fraction of daily wages to be paid for half-day attendance",
"fieldname": "daily_wages_fraction_for_half_day",
"fieldtype": "Float",
"label": "Fraction of Daily Salary for Half Day",
"show_days": 1,
"show_seconds": 1
"label": "Fraction of Daily Salary for Half Day"
},
{
"default": "1",
"description": "Emails salary slip to employee based on preferred email selected in Employee",
"fieldname": "email_salary_slip_to_employee",
"fieldtype": "Check",
"label": "Email Salary Slip to Employee",
"show_days": 1,
"show_seconds": 1
"label": "Email Salary Slip to Employee"
},
{
"default": "0",
@ -82,9 +69,7 @@
"description": "The salary slip emailed to the employee will be password protected, the password will be generated based on the password policy.",
"fieldname": "encrypt_salary_slips_in_emails",
"fieldtype": "Check",
"label": "Encrypt Salary Slips in Emails",
"show_days": 1,
"show_seconds": 1
"label": "Encrypt Salary Slips in Emails"
},
{
"depends_on": "eval: doc.encrypt_salary_slips_in_emails == 1",
@ -92,24 +77,27 @@
"fieldname": "password_policy",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Password Policy",
"show_days": 1,
"show_seconds": 1
"label": "Password Policy"
},
{
"depends_on": "eval:doc.payroll_based_on == 'Attendance'",
"fieldname": "consider_unmarked_attendance_as",
"fieldtype": "Select",
"label": "Consider Unmarked Attendance As",
"options": "Present\nAbsent",
"show_days": 1,
"show_seconds": 1
"options": "Present\nAbsent"
},
{
"default": "0",
"fieldname": "show_leave_balances_in_salary_slip",
"fieldtype": "Check",
"label": "Show Leave Balances in Salary Slip"
}
],
"icon": "fa fa-cog",
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
"modified": "2020-06-22 17:00:58.408030",
"modified": "2021-02-19 11:07:55.873991",
"modified_by": "Administrator",
"module": "Payroll",
"name": "Payroll Settings",

View File

@ -80,6 +80,8 @@
"total_in_words",
"column_break_69",
"base_total_in_words",
"leave_details_section",
"leave_details",
"section_break_75",
"amended_from"
],
@ -612,13 +614,25 @@
"label": "Month To Date(Company Currency)",
"options": "Company:company:default_currency",
"read_only": 1
},
{
"fieldname": "leave_details_section",
"fieldtype": "Section Break",
"label": "Leave Details"
},
{
"fieldname": "leave_details",
"fieldtype": "Table",
"label": "Leave Details",
"options": "Salary Slip Leave",
"read_only": 1
}
],
"icon": "fa fa-file-text",
"idx": 9,
"is_submittable": 1,
"links": [],
"modified": "2021-01-14 13:37:38.180920",
"modified": "2021-02-19 11:48:05.383945",
"modified_by": "Administrator",
"module": "Payroll",
"name": "Salary Slip",

View File

@ -19,6 +19,7 @@ from erpnext.payroll.doctype.employee_benefit_application.employee_benefit_appli
from erpnext.payroll.doctype.employee_benefit_claim.employee_benefit_claim import get_benefit_claim_amount, get_last_payroll_period_benefits
from erpnext.loan_management.doctype.loan_repayment.loan_repayment import calculate_amounts, create_repayment_entry
from erpnext.accounts.utils import get_fiscal_year
from six import iteritems
class SalarySlip(TransactionBase):
def __init__(self, *args, **kwargs):
@ -53,6 +54,7 @@ class SalarySlip(TransactionBase):
self.compute_year_to_date()
self.compute_month_to_date()
self.compute_component_wise_year_to_date()
self.add_leave_balances()
if frappe.db.get_single_value("Payroll Settings", "max_working_hours_against_timesheet"):
max_working_hours = frappe.db.get_single_value("Payroll Settings", "max_working_hours_against_timesheet")
@ -1212,6 +1214,22 @@ class SalarySlip(TransactionBase):
return period_start_date, period_end_date
def add_leave_balances(self):
self.set('leave_details', [])
if frappe.db.get_single_value('Payroll Settings', 'show_leave_balances_in_salary_slip'):
from erpnext.hr.doctype.leave_application.leave_application import get_leave_details
leave_details = get_leave_details(self.employee, self.end_date)
for leave_type, leave_values in iteritems(leave_details['leave_allocation']):
self.append('leave_details', {
'leave_type': leave_type,
'total_allocated_leaves': flt(leave_values.get('total_leaves')),
'expired_leaves': flt(leave_values.get('expired_leaves')),
'used_leaves': flt(leave_values.get('leaves_taken')),
'pending_leaves': flt(leave_values.get('pending_leaves')),
'available_leaves': flt(leave_values.get('remaining_leaves'))
})
def unlink_ref_doc_from_salary_slip(ref_no):
linked_ss = frappe.db.sql_list("""select name from `tabSalary Slip`

View File

@ -0,0 +1,78 @@
{
"actions": [],
"creation": "2021-02-19 11:45:18.173417",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"leave_type",
"total_allocated_leaves",
"expired_leaves",
"used_leaves",
"pending_leaves",
"available_leaves"
],
"fields": [
{
"fieldname": "leave_type",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Leave Type",
"no_copy": 1,
"options": "Leave Type",
"read_only": 1
},
{
"fieldname": "total_allocated_leaves",
"fieldtype": "Float",
"in_list_view": 1,
"label": "Total Allocated Leave",
"no_copy": 1,
"read_only": 1
},
{
"fieldname": "expired_leaves",
"fieldtype": "Float",
"in_list_view": 1,
"label": "Expired Leave",
"no_copy": 1,
"read_only": 1
},
{
"fieldname": "used_leaves",
"fieldtype": "Float",
"in_list_view": 1,
"label": "Used Leave",
"no_copy": 1,
"read_only": 1
},
{
"fieldname": "pending_leaves",
"fieldtype": "Float",
"in_list_view": 1,
"label": "Pending Leave",
"no_copy": 1,
"read_only": 1
},
{
"fieldname": "available_leaves",
"fieldtype": "Float",
"in_list_view": 1,
"label": "Available Leave",
"no_copy": 1,
"read_only": 1
}
],
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2021-02-19 10:47:48.546724",
"modified_by": "Administrator",
"module": "Payroll",
"name": "Salary Slip Leave",
"owner": "Administrator",
"permissions": [],
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

View File

@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
# import frappe
from frappe.model.document import Document
class SalarySlipLeave(Document):
pass

View File

@ -38,7 +38,7 @@ def get_warehouse_account_map(company=None):
frappe.flags.warehouse_account_map[company] = warehouse_account
else:
frappe.flags.warehouse_account_map = warehouse_account
return frappe.flags.warehouse_account_map.get(company) or frappe.flags.warehouse_account_map
def get_warehouse_account(warehouse, warehouse_account=None):
@ -64,6 +64,10 @@ def get_warehouse_account(warehouse, warehouse_account=None):
if not account and warehouse.company:
account = get_company_default_inventory_account(warehouse.company)
if not account and warehouse.company:
account = frappe.db.get_value('Account',
{'account_type': 'Stock', 'is_group': 0, 'company': warehouse.company}, 'name')
if not account and warehouse.company and not warehouse.is_group:
frappe.throw(_("Please set Account in Warehouse {0} or Default Inventory Account in Company {1}")
.format(warehouse.name, warehouse.company))

View File

@ -324,10 +324,12 @@ class PurchaseReceipt(BuyingController):
else:
loss_account = self.get_company_default("default_expense_account")
cost_center = d.cost_center or frappe.get_cached_value("Company", self.company, "cost_center")
gl_entries.append(self.get_gl_dict({
"account": loss_account,
"against": warehouse_account[d.warehouse]["account"],
"cost_center": d.cost_center,
"cost_center": cost_center,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"debit": divisional_loss,
"project": d.project