From d96be3f9f14b74d649a7e8c65d7444aeedeee9fa Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 2 Mar 2021 17:02:47 +0530 Subject: [PATCH] Reposting patch and earned leave rounding (#24783) * fix: rounding of earned leave is optional (#24782) * fix: reposting patch fixes (#24775) --- .../leave_allocation/leave_allocation.py | 6 --- .../leave_policy_assignment.json | 6 ++- .../leave_policy_assignment.py | 48 ++++++++++++++++--- erpnext/hr/doctype/leave_type/leave_type.json | 5 +- erpnext/hr/utils.py | 23 ++++++--- .../item_reposting_for_incorrect_sl_and_gl.py | 17 +++++-- .../purchase_receipt/purchase_receipt.py | 4 +- 7 files changed, 81 insertions(+), 28 deletions(-) diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.py b/erpnext/hr/doctype/leave_allocation/leave_allocation.py index a09cd2ea11..a4f4f1ace9 100755 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation.py +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.py @@ -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 diff --git a/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment.json b/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment.json index a0327bdaa0..3373350e73 100644 --- a/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment.json +++ b/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment.json @@ -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", diff --git a/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment.py b/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment.py index a5068bc26d..4064c56e44 100644 --- a/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment.py +++ b/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment.py @@ -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 diff --git a/erpnext/hr/doctype/leave_type/leave_type.json b/erpnext/hr/doctype/leave_type/leave_type.json index a2092919f8..fc577ef1d3 100644 --- a/erpnext/hr/doctype/leave_type/leave_type.json +++ b/erpnext/hr/doctype/leave_type/leave_type.json @@ -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", diff --git a/erpnext/hr/utils.py b/erpnext/hr/utils.py index d700e7fccf..3f8d61d414 100644 --- a/erpnext/hr/utils.py +++ b/erpnext/hr/utils.py @@ -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 diff --git a/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py b/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py index 3200363e01..d968e1fb76 100644 --- a/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py +++ b/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py @@ -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 = 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,8 +52,6 @@ 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) diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index d72101412e..70687bdac2 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -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