Merge branch 'develop' into clarkejj-patch-1

This commit is contained in:
John Clarke 2020-06-12 07:55:49 -06:00 committed by GitHub
commit 15e4ea0472
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 131 additions and 39 deletions

View File

@ -14,6 +14,9 @@ frappe.treeview_settings["Account"] = {
on_change: function() {
var me = frappe.treeview_settings['Account'].treeview;
var company = me.page.fields_dict.company.get_value();
if (!company) {
frappe.throw(__("Please set a Company"));
}
frappe.call({
method: "erpnext.accounts.doctype.account.account.get_root_company",
args: {

View File

@ -19,11 +19,15 @@
"attendance_date",
"company",
"department",
"shift",
"attendance_request",
"amended_from",
"details_section",
"shift",
"in_time",
"out_time",
"column_break_18",
"late_entry",
"early_exit"
"early_exit",
"amended_from"
],
"fields": [
{
@ -172,13 +176,36 @@
"fieldname": "early_exit",
"fieldtype": "Check",
"label": "Early Exit"
},
{
"fieldname": "details_section",
"fieldtype": "Section Break",
"label": "Details"
},
{
"depends_on": "shift",
"fieldname": "in_time",
"fieldtype": "Datetime",
"label": "In Time",
"read_only": 1
},
{
"depends_on": "shift",
"fieldname": "out_time",
"fieldtype": "Datetime",
"label": "Out Time",
"read_only": 1
},
{
"fieldname": "column_break_18",
"fieldtype": "Column Break"
}
],
"icon": "fa fa-ok",
"idx": 1,
"is_submittable": 1,
"links": [],
"modified": "2020-04-11 11:40:14.319496",
"modified": "2020-05-29 13:51:37.177231",
"modified_by": "Administrator",
"module": "HR",
"name": "Attendance",

View File

@ -139,13 +139,13 @@ frappe.ui.form.on('Employee Advance', {
employee: function (frm) {
if (frm.doc.employee) {
return frappe.call({
method: "erpnext.hr.doctype.employee_advance.employee_advance.get_due_advance_amount",
method: "erpnext.hr.doctype.employee_advance.employee_advance.get_pending_amount",
args: {
"employee": frm.doc.employee,
"posting_date": frm.doc.posting_date
},
callback: function(r) {
frm.set_value("due_advance_amount",r.message);
frm.set_value("pending_amount",r.message);
}
});
}

View File

@ -19,7 +19,7 @@
"column_break_11",
"advance_amount",
"paid_amount",
"due_advance_amount",
"pending_amount",
"claimed_amount",
"return_amount",
"section_break_7",
@ -102,14 +102,6 @@
"options": "Company:company:default_currency",
"read_only": 1
},
{
"depends_on": "eval:cur_frm.doc.employee",
"fieldname": "due_advance_amount",
"fieldtype": "Currency",
"label": "Due Advance Amount",
"options": "Company:company:default_currency",
"read_only": 1
},
{
"fieldname": "claimed_amount",
"fieldtype": "Currency",
@ -177,11 +169,19 @@
"fieldname": "repay_unclaimed_amount_from_salary",
"fieldtype": "Check",
"label": "Repay unclaimed amount from salary"
},
{
"depends_on": "eval:cur_frm.doc.employee",
"fieldname": "pending_amount",
"fieldtype": "Currency",
"label": "Pending Amount",
"options": "Company:company:default_currency",
"read_only": 1
}
],
"is_submittable": 1,
"links": [],
"modified": "2020-03-06 15:11:33.747535",
"modified": "2020-06-12 12:42:39.833818",
"modified_by": "Administrator",
"module": "HR",
"name": "Employee Advance",

View File

@ -95,7 +95,7 @@ class EmployeeAdvance(Document):
frappe.db.set_value("Employee Advance", self.name, "status", self.status)
@frappe.whitelist()
def get_due_advance_amount(employee, posting_date):
def get_pending_amount(employee, posting_date):
employee_due_amount = frappe.get_all("Employee Advance", \
filters = {"employee":employee, "docstatus":1, "posting_date":("<=", posting_date)}, \
fields = ["advance_amount", "paid_amount"])

View File

@ -72,7 +72,7 @@ def add_log_based_on_employee_field(employee_field_value, timestamp, device_id=N
return doc
def mark_attendance_and_link_log(logs, attendance_status, attendance_date, working_hours=None, late_entry=False, early_exit=False, shift=None):
def mark_attendance_and_link_log(logs, attendance_status, attendance_date, working_hours=None, late_entry=False, early_exit=False, in_time=None, out_time=None, shift=None):
"""Creates an attendance and links the attendance to the Employee Checkin.
Note: If attendance is already present for the given date, the logs are marked as skipped and no exception is thrown.
@ -100,7 +100,9 @@ def mark_attendance_and_link_log(logs, attendance_status, attendance_date, worki
'company': employee_doc.company,
'shift': shift,
'late_entry': late_entry,
'early_exit': early_exit
'early_exit': early_exit,
'in_time': in_time,
'out_time': out_time
}
attendance = frappe.get_doc(doc_dict).insert()
attendance.submit()

View File

@ -46,6 +46,7 @@ frappe.ui.form.on("Leave Application", {
make_dashboard: function(frm) {
var leave_details;
let lwps;
if (frm.doc.employee) {
frappe.call({
method: "erpnext.hr.doctype.leave_application.leave_application.get_leave_details",
@ -61,6 +62,7 @@ frappe.ui.form.on("Leave Application", {
if (!r.exc && r.message['leave_approver']) {
frm.set_value('leave_approver', r.message['leave_approver']);
}
lwps = r.message["lwps"];
}
});
$("div").remove(".form-dashboard-section.custom");
@ -70,12 +72,17 @@ frappe.ui.form.on("Leave Application", {
})
);
frm.dashboard.show();
let allowed_leave_types = Object.keys(leave_details);
// lwps should be allowed, lwps don't have any allocation
allowed_leave_types = allowed_leave_types.concat(lwps);
frm.set_query('leave_type', function(){
return {
filters : [
['leave_type_name', 'in', Object.keys(leave_details)]
['leave_type_name', 'in', allowed_leave_types]
]
}
};
});
}
},

View File

@ -19,7 +19,6 @@ class NotAnOptionalHoliday(frappe.ValidationError): pass
from frappe.model.document import Document
class LeaveApplication(Document):
def get_feed(self):
return _("{0}: From {0} of type {1}").format(self.employee_name, self.leave_type)
@ -463,9 +462,14 @@ def get_leave_details(employee, date):
"pending_leaves": leaves_pending,
"remaining_leaves": remaining_leaves}
#is used in set query
lwps = frappe.get_list("Leave Type", filters = {"is_lwp": 1})
lwps = [lwp.name for lwp in lwps]
ret = {
'leave_allocation': leave_allocation,
'leave_approver': get_leave_approver(employee)
'leave_approver': get_leave_approver(employee),
'lwps': lwps
}
return ret

View File

@ -28,13 +28,14 @@ class ShiftType(Document):
logs = frappe.db.get_list('Employee Checkin', fields="*", filters=filters, order_by="employee,time")
for key, group in itertools.groupby(logs, key=lambda x: (x['employee'], x['shift_actual_start'])):
single_shift_logs = list(group)
attendance_status, working_hours, late_entry, early_exit = self.get_attendance(single_shift_logs)
mark_attendance_and_link_log(single_shift_logs, attendance_status, key[1].date(), working_hours, late_entry, early_exit, self.name)
attendance_status, working_hours, late_entry, early_exit, in_time, out_time = self.get_attendance(single_shift_logs)
mark_attendance_and_link_log(single_shift_logs, attendance_status, key[1].date(), working_hours, late_entry, early_exit, in_time, out_time, self.name)
for employee in self.get_assigned_employee(self.process_attendance_after, True):
self.mark_absent_for_dates_with_no_attendance(employee)
def get_attendance(self, logs):
"""Return attendance_status, working_hours for a set of logs belonging to a single shift.
"""Return attendance_status, working_hours, late_entry, early_exit, in_time, out_time
for a set of logs belonging to a single shift.
Assumtion:
1. These logs belongs to an single shift, single employee and is not in a holiday date.
2. Logs are in chronological order
@ -48,10 +49,10 @@ class ShiftType(Document):
early_exit = True
if self.working_hours_threshold_for_absent and total_working_hours < self.working_hours_threshold_for_absent:
return 'Absent', total_working_hours, late_entry, early_exit
return 'Absent', total_working_hours, late_entry, early_exit, in_time, out_time
if self.working_hours_threshold_for_half_day and total_working_hours < self.working_hours_threshold_for_half_day:
return 'Half Day', total_working_hours, late_entry, early_exit
return 'Present', total_working_hours, late_entry, early_exit
return 'Half Day', total_working_hours, late_entry, early_exit, in_time, out_time
return 'Present', total_working_hours, late_entry, early_exit, in_time, out_time
def mark_absent_for_dates_with_no_attendance(self, employee):
"""Marks Absents for the given employee on working days in this shift which have no attendance marked.

View File

@ -680,6 +680,7 @@ erpnext.patches.v12_0.update_appointment_reminder_scheduler_entry
erpnext.patches.v12_0.retain_permission_rules_for_video_doctype
erpnext.patches.v12_0.remove_duplicate_leave_ledger_entries #2020-05-22
erpnext.patches.v13_0.patch_to_fix_reverse_linking_in_additional_salary_encashment_and_incentive
erpnext.patches.v12_0.move_due_advance_amount_to_pending_amount
execute:frappe.delete_doc_if_exists("Page", "appointment-analytic")
execute:frappe.rename_doc("Desk Page", "Getting Started", "Home", force=True)
erpnext.patches.v12_0.unset_customer_supplier_based_on_type_of_item_price
@ -694,4 +695,4 @@ execute:frappe.delete_doc("Report", "Department Analytics")
execute:frappe.rename_doc("Desk Page", "Loan Management", "Loan", force=True)
erpnext.patches.v12_0.update_uom_conversion_factor
erpnext.patches.v13_0.delete_old_purchase_reports
erpnext.patches.v12_0.set_italian_import_supplier_invoice_permissions
erpnext.patches.v12_0.set_italian_import_supplier_invoice_permissions

View File

@ -0,0 +1,9 @@
# Copyright (c) 2019, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
def execute():
''' Move from due_advance_amount to pending_amount '''
frappe.db.sql(''' UPDATE `tabEmployee Advance` SET pending_amount=due_advance_amount ''')

View File

@ -7,4 +7,4 @@ def execute():
for entry in doctypes:
if frappe.db.exists('DocType', entry):
frappe.reload_doc('Healthcare', 'doctype', entry)
frappe.db.sql("update `tab{dt}` set company = '{company}' where ifnull(company, '') = ''".format(dt=entry, company=company))
frappe.db.sql("update `tab{dt}` set company = {company} where ifnull(company, '') = ''".format(dt=entry, company=frappe.db.escape(company)))

View File

@ -3,16 +3,19 @@
from __future__ import unicode_literals
import frappe
import json
from frappe.model.naming import set_name_by_naming_series
from frappe import _, msgprint, throw
from frappe import _, msgprint
import frappe.defaults
from frappe.utils import flt, cint, cstr, today
from frappe.utils import flt, cint, cstr, today, get_formatted_email
from frappe.desk.reportview import build_match_conditions, get_filters_cond
from erpnext.utilities.transaction_base import TransactionBase
from erpnext.accounts.party import validate_party_accounts, get_dashboard_info, get_timeline_data # keep this
from frappe.contacts.address_and_contact import load_address_and_contact, delete_contact_and_address
from frappe.model.rename_doc import update_linked_doctypes
from frappe.model.mapper import get_mapped_doc
from frappe.utils.user import get_users_with_role
class Customer(TransactionBase):
def get_feed(self):
@ -378,10 +381,45 @@ def check_credit_limit(customer, company, ignore_outstanding_sales_order=False,
.format(customer, customer_outstanding, credit_limit))
# If not authorized person raise exception
credit_controller = frappe.db.get_value('Accounts Settings', None, 'credit_controller')
if not credit_controller or credit_controller not in frappe.get_roles():
throw(_("Please contact to the user who have Sales Master Manager {0} role")
.format(" / " + credit_controller if credit_controller else ""))
credit_controller_role = frappe.db.get_single_value('Accounts Settings', 'credit_controller')
if not credit_controller_role or credit_controller_role not in frappe.get_roles():
# form a list of emails for the credit controller users
credit_controller_users = get_users_with_role(credit_controller_role or "Sales Master Manager")
# form a list of emails and names to show to the user
credit_controller_users_list = [user for user in credit_controller_users if frappe.db.exists("Employee", {"prefered_email": user})]
credit_controller_users = [get_formatted_email(user).replace("<", "(").replace(">", ")") for user in credit_controller_users_list]
if not credit_controller_users:
frappe.throw(_("Please contact your administrator to extend the credit limits for {0}.".format(customer)))
message = """Please contact any of the following users to extend the credit limits for {0}:
<br><br><ul><li>{1}</li></ul>""".format(customer, '<li>'.join(credit_controller_users))
# if the current user does not have permissions to override credit limit,
# prompt them to send out an email to the controller users
frappe.msgprint(message,
title="Notify",
raise_exception=1,
primary_action={
'label': 'Send Email',
'server_action': 'erpnext.selling.doctype.customer.customer.send_emails',
'args': {
'customer': customer,
'customer_outstanding': customer_outstanding,
'credit_limit': credit_limit,
'credit_controller_users_list': credit_controller_users_list
}
}
)
@frappe.whitelist()
def send_emails(args):
args = json.loads(args)
subject = (_("Credit limit reached for customer {0}").format(args.get('customer')))
message = (_("Credit limit has been crossed for customer {0} ({1}/{2})")
.format(args.get('customer'), args.get('customer_outstanding'), args.get('credit_limit')))
frappe.sendmail(recipients=[args.get('credit_controller_users_list')], subject=subject, message=message)
def get_customer_outstanding(customer, company, ignore_outstanding_sales_order=False, cost_center=None):
# Outstanding based on GL Entries

View File

@ -8,7 +8,7 @@ from frappe.utils import getdate, flt
def execute(filters=None):
if not filters: filters = {}
float_preceision = frappe.db.get_default("float_preceision")
float_precision = frappe.db.get_default("float_precision")
condition = get_condition(filters)
@ -25,7 +25,7 @@ def execute(filters=None):
data = []
for item in items:
total_outgoing = flt(consumed_item_map.get(item.name, 0)) + flt(delivered_item_map.get(item.name,0))
avg_daily_outgoing = flt(total_outgoing / diff, float_preceision)
avg_daily_outgoing = flt(total_outgoing / diff, float_precision)
reorder_level = (avg_daily_outgoing * flt(item.lead_time_days)) + flt(item.safety_stock)
data.append([item.name, item.item_name, item.item_group, item.brand, item.description,