diff --git a/.github/try-on-f-cloud-button.svg b/.github/try-on-f-cloud-button.svg index fe0bb2c52df..270092722e5 100644 --- a/.github/try-on-f-cloud-button.svg +++ b/.github/try-on-f-cloud-button.svg @@ -1,4 +1,4 @@ - + diff --git a/README.md b/README.md index f8f5f80b341..cea3472447f 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ ERPNext is built on the [Frappe Framework](https://github.com/frappe/frappe), a ## Installation
- + diff --git a/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py b/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py index 9d4d76b8f63..2d9ae9377fb 100644 --- a/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py +++ b/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py @@ -114,10 +114,13 @@ class OpeningInvoiceCreationTool(Document): ) or {} ) + + default_currency = frappe.db.get_value(row.party_type, row.party, "default_currency") + if company_details: invoice.update( { - "currency": company_details.get("default_currency"), + "currency": default_currency or company_details.get("default_currency"), "letter_head": company_details.get("default_letter_head"), } ) diff --git a/erpnext/hr/doctype/employee_checkin/employee_checkin.py b/erpnext/hr/doctype/employee_checkin/employee_checkin.py index 64eb019b003..e07b5e5db52 100644 --- a/erpnext/hr/doctype/employee_checkin/employee_checkin.py +++ b/erpnext/hr/doctype/employee_checkin/employee_checkin.py @@ -5,7 +5,7 @@ import frappe from frappe import _ from frappe.model.document import Document -from frappe.utils import cint, get_datetime +from frappe.utils import cint, get_datetime, get_link_to_form from erpnext.hr.doctype.attendance.attendance import ( get_duplicate_attendance_record, @@ -130,14 +130,11 @@ def mark_attendance_and_link_log( """ log_names = [x.name for x in logs] employee = logs[0].employee + if attendance_status == "Skip": - frappe.db.sql( - """update `tabEmployee Checkin` - set skip_auto_attendance = %s - where name in %s""", - ("1", log_names), - ) + skip_attendance_in_checkins(log_names) return None + elif attendance_status in ("Present", "Absent", "Half Day"): employee_doc = frappe.get_doc("Employee", employee) duplicate = get_duplicate_attendance_record(employee, attendance_date, shift) @@ -159,6 +156,12 @@ def mark_attendance_and_link_log( } attendance = frappe.get_doc(doc_dict).insert() attendance.submit() + + if attendance_status == "Absent": + attendance.add_comment( + text=_("Employee was marked Absent for not meeting the working hours threshold.") + ) + frappe.db.sql( """update `tabEmployee Checkin` set attendance = %s @@ -167,13 +170,10 @@ def mark_attendance_and_link_log( ) return attendance else: - frappe.db.sql( - """update `tabEmployee Checkin` - set skip_auto_attendance = %s - where name in %s""", - ("1", log_names), - ) + skip_attendance_in_checkins(log_names) + add_comment_in_checkins(log_names, duplicate, overlapping) return None + else: frappe.throw(_("{} is an invalid Attendance Status.").format(attendance_status)) @@ -241,3 +241,34 @@ def time_diff_in_hours(start, end): def find_index_in_dict(dict_list, key, value): return next((index for (index, d) in enumerate(dict_list) if d[key] == value), None) + + +def add_comment_in_checkins(log_names, duplicate, overlapping): + if duplicate: + text = _("Auto Attendance skipped due to duplicate attendance record: {}").format( + get_link_to_form("Attendance", duplicate[0].name) + ) + else: + text = _("Auto Attendance skipped due to overlapping attendance record: {}").format( + get_link_to_form("Attendance", overlapping.name) + ) + + for name in log_names: + frappe.get_doc( + { + "doctype": "Comment", + "comment_type": "Comment", + "reference_doctype": "Employee Checkin", + "reference_name": name, + "content": text, + } + ).insert(ignore_permissions=True) + + +def skip_attendance_in_checkins(log_names): + EmployeeCheckin = frappe.qb.DocType("Employee Checkin") + ( + frappe.qb.update(EmployeeCheckin) + .set("skip_auto_attendance", 1) + .where(EmployeeCheckin.name.isin(log_names)) + ).run() diff --git a/erpnext/hr/doctype/shift_type/shift_type.py b/erpnext/hr/doctype/shift_type/shift_type.py index 5e214cf7b7a..a61bb9ee5f1 100644 --- a/erpnext/hr/doctype/shift_type/shift_type.py +++ b/erpnext/hr/doctype/shift_type/shift_type.py @@ -134,7 +134,17 @@ class ShiftType(Document): shift_details = get_employee_shift(employee, timestamp, True) if shift_details and shift_details.shift_type.name == self.name: - mark_attendance(employee, date, "Absent", self.name) + attendance = mark_attendance(employee, date, "Absent", self.name) + if attendance: + frappe.get_doc( + { + "doctype": "Comment", + "comment_type": "Comment", + "reference_doctype": "Attendance", + "reference_name": attendance, + "content": frappe._("Employee was marked Absent due to missing Employee Checkins."), + } + ).insert(ignore_permissions=True) def get_start_and_end_dates(self, employee): """Returns start and end dates for checking attendance and marking absent diff --git a/erpnext/selling/workspace/selling/selling.json b/erpnext/selling/workspace/selling/selling.json index a700ad89a32..45e160d143e 100644 --- a/erpnext/selling/workspace/selling/selling.json +++ b/erpnext/selling/workspace/selling/selling.json @@ -5,7 +5,7 @@ "label": "Sales Order Trends" } ], - "content": "[{\"type\":\"onboarding\",\"data\":{\"onboarding_name\":\"Selling\",\"col\":12}},{\"type\":\"chart\",\"data\":{\"chart_name\":\"Sales Order Trends\",\"col\":12}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"Quick Access\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Item\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Sales Order\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Sales Analytics\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Sales Order Analysis\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"Reports & Masters\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Selling\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Items and Pricing\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Key Reports\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Other Reports\",\"col\":4}}]", + "content": "[{\"type\":\"onboarding\",\"data\":{\"onboarding_name\":\"Selling\",\"col\":12}},{\"type\":\"chart\",\"data\":{\"chart_name\":\"Sales Order Trends\",\"col\":12}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"Quick Access\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Item\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Sales Order\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Sales Analytics\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Sales Order Analysis\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"Reports & Masters\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Selling\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Items and Pricing\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Key Reports\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Other Reports\",\"col\":4}}]", "creation": "2020-01-28 11:49:12.092882", "docstatus": 0, "doctype": "Workspace", @@ -314,118 +314,11 @@ "onboard": 0, "type": "Link" }, - { - "hidden": 0, - "is_query_report": 0, - "label": "Key Reports", - "link_count": 0, - "onboard": 0, - "type": "Card Break" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 1, - "label": "Sales Analytics", - "link_count": 0, - "link_to": "Sales Analytics", - "link_type": "Report", - "onboard": 1, - "type": "Link" - }, - { - "dependencies": "Sales Order", - "hidden": 0, - "is_query_report": 1, - "label": "Sales Order Analysis", - "link_count": 0, - "link_to": "Sales Order Analysis", - "link_type": "Report", - "onboard": 1, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Sales Funnel", - "link_count": 0, - "link_to": "sales-funnel", - "link_type": "Page", - "onboard": 1, - "type": "Link" - }, - { - "dependencies": "Sales Order", - "hidden": 0, - "is_query_report": 1, - "label": "Sales Order Trends", - "link_count": 0, - "link_to": "Sales Order Trends", - "link_type": "Report", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Quotation", - "hidden": 0, - "is_query_report": 1, - "label": "Quotation Trends", - "link_count": 0, - "link_to": "Quotation Trends", - "link_type": "Report", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Customer", - "hidden": 0, - "is_query_report": 1, - "label": "Customer Acquisition and Loyalty", - "link_count": 0, - "link_to": "Customer Acquisition and Loyalty", - "link_type": "Report", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Sales Order", - "hidden": 0, - "is_query_report": 1, - "label": "Inactive Customers", - "link_count": 0, - "link_to": "Inactive Customers", - "link_type": "Report", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Sales Order", - "hidden": 0, - "is_query_report": 1, - "label": "Sales Person-wise Transaction Summary", - "link_count": 0, - "link_to": "Sales Person-wise Transaction Summary", - "link_type": "Report", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Item", - "hidden": 0, - "is_query_report": 1, - "label": "Item-wise Sales History", - "link_count": 0, - "link_to": "Item-wise Sales History", - "link_type": "Report", - "onboard": 0, - "type": "Link" - }, { "hidden": 0, "is_query_report": 0, "label": "Other Reports", - "link_count": 0, + "link_count": 12, "onboard": 0, "type": "Card Break" }, @@ -560,9 +453,258 @@ "link_type": "Report", "onboard": 0, "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Key Reports", + "link_count": 22, + "onboard": 0, + "type": "Card Break" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 1, + "label": "Sales Analytics", + "link_count": 0, + "link_to": "Sales Analytics", + "link_type": "Report", + "onboard": 1, + "type": "Link" + }, + { + "dependencies": "Sales Order", + "hidden": 0, + "is_query_report": 1, + "label": "Sales Order Analysis", + "link_count": 0, + "link_to": "Sales Order Analysis", + "link_type": "Report", + "onboard": 1, + "type": "Link" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Sales Funnel", + "link_count": 0, + "link_to": "sales-funnel", + "link_type": "Page", + "onboard": 1, + "type": "Link" + }, + { + "dependencies": "Sales Order", + "hidden": 0, + "is_query_report": 1, + "label": "Sales Order Trends", + "link_count": 0, + "link_to": "Sales Order Trends", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "Quotation", + "hidden": 0, + "is_query_report": 1, + "label": "Quotation Trends", + "link_count": 0, + "link_to": "Quotation Trends", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "Customer", + "hidden": 0, + "is_query_report": 1, + "label": "Customer Acquisition and Loyalty", + "link_count": 0, + "link_to": "Customer Acquisition and Loyalty", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "Sales Order", + "hidden": 0, + "is_query_report": 1, + "label": "Inactive Customers", + "link_count": 0, + "link_to": "Inactive Customers", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "Sales Order", + "hidden": 0, + "is_query_report": 1, + "label": "Sales Person-wise Transaction Summary", + "link_count": 0, + "link_to": "Sales Person-wise Transaction Summary", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "Item", + "hidden": 0, + "is_query_report": 1, + "label": "Item-wise Sales History", + "link_count": 0, + "link_to": "Item-wise Sales History", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "Lead", + "hidden": 0, + "is_query_report": 1, + "label": "Lead Details", + "link_count": 0, + "link_to": "Lead Details", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "Address", + "hidden": 0, + "is_query_report": 1, + "label": "Customer Addresses And Contacts", + "link_count": 0, + "link_to": "Address And Contacts", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "Item", + "hidden": 0, + "is_query_report": 1, + "label": "Available Stock for Packing Items", + "link_count": 0, + "link_to": "Available Stock for Packing Items", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "Sales Order", + "hidden": 0, + "is_query_report": 1, + "label": "Pending SO Items For Purchase Request", + "link_count": 0, + "link_to": "Pending SO Items For Purchase Request", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "Delivery Note", + "hidden": 0, + "is_query_report": 1, + "label": "Delivery Note Trends", + "link_count": 0, + "link_to": "Delivery Note Trends", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "Sales Invoice", + "hidden": 0, + "is_query_report": 1, + "label": "Sales Invoice Trends", + "link_count": 0, + "link_to": "Sales Invoice Trends", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "Customer", + "hidden": 0, + "is_query_report": 1, + "label": "Customer Credit Balance", + "link_count": 0, + "link_to": "Customer Credit Balance", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "Customer", + "hidden": 0, + "is_query_report": 1, + "label": "Customers Without Any Sales Transactions", + "link_count": 0, + "link_to": "Customers Without Any Sales Transactions", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "Customer", + "hidden": 0, + "is_query_report": 1, + "label": "Sales Partners Commission", + "link_count": 0, + "link_to": "Sales Partners Commission", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "Sales Order", + "hidden": 0, + "is_query_report": 1, + "label": "Territory Target Variance Based On Item Group", + "link_count": 0, + "link_to": "Territory Target Variance Based On Item Group", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "Sales Order", + "hidden": 0, + "is_query_report": 1, + "label": "Sales Person Target Variance Based On Item Group", + "link_count": 0, + "link_to": "Sales Person Target Variance Based On Item Group", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "Sales Order", + "hidden": 0, + "is_query_report": 1, + "label": "Sales Partner Target Variance Based On Item Group", + "link_count": 0, + "link_to": "Sales Partner Target Variance based on Item Group", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 1, + "label": "Payment Terms Status for Sales Order", + "link_count": 0, + "link_to": "Payment Terms Status for Sales Order", + "link_type": "Report", + "onboard": 0, + "type": "Link" } ], - "modified": "2022-01-13 17:43:02.778627", + "modified": "2022-04-26 13:29:55.087240", "modified_by": "Administrator", "module": "Selling", "name": "Selling",