From 53e4fee4db69b5a1d7ee90bd37d807737fc53607 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Sun, 8 May 2022 16:04:14 +0530 Subject: [PATCH 01/99] refactor: Remove exotel Move it to separate app --- .../doctype/exotel_settings/__init__.py | 0 .../exotel_settings/exotel_settings.json | 61 -------- .../exotel_settings/exotel_settings.py | 22 --- .../exotel_integration.py | 133 ------------------ erpnext/tests/exotel_test_data.py | 122 ---------------- erpnext/tests/test_exotel.py | 69 --------- 6 files changed, 407 deletions(-) delete mode 100644 erpnext/erpnext_integrations/doctype/exotel_settings/__init__.py delete mode 100644 erpnext/erpnext_integrations/doctype/exotel_settings/exotel_settings.json delete mode 100644 erpnext/erpnext_integrations/doctype/exotel_settings/exotel_settings.py delete mode 100644 erpnext/erpnext_integrations/exotel_integration.py delete mode 100644 erpnext/tests/exotel_test_data.py delete mode 100644 erpnext/tests/test_exotel.py diff --git a/erpnext/erpnext_integrations/doctype/exotel_settings/__init__.py b/erpnext/erpnext_integrations/doctype/exotel_settings/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/erpnext_integrations/doctype/exotel_settings/exotel_settings.json b/erpnext/erpnext_integrations/doctype/exotel_settings/exotel_settings.json deleted file mode 100644 index 72f47b53ec..0000000000 --- a/erpnext/erpnext_integrations/doctype/exotel_settings/exotel_settings.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "creation": "2019-05-21 07:41:53.536536", - "doctype": "DocType", - "engine": "InnoDB", - "field_order": [ - "enabled", - "section_break_2", - "account_sid", - "api_key", - "api_token" - ], - "fields": [ - { - "fieldname": "enabled", - "fieldtype": "Check", - "label": "Enabled" - }, - { - "depends_on": "enabled", - "fieldname": "section_break_2", - "fieldtype": "Section Break" - }, - { - "fieldname": "account_sid", - "fieldtype": "Data", - "label": "Account SID" - }, - { - "fieldname": "api_token", - "fieldtype": "Data", - "label": "API Token" - }, - { - "fieldname": "api_key", - "fieldtype": "Data", - "label": "API Key" - } - ], - "issingle": 1, - "modified": "2019-05-22 06:25:18.026997", - "modified_by": "Administrator", - "module": "ERPNext Integrations", - "name": "Exotel Settings", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "delete": 1, - "email": 1, - "print": 1, - "read": 1, - "role": "System Manager", - "share": 1, - "write": 1 - } - ], - "quick_entry": 1, - "sort_field": "modified", - "sort_order": "ASC", - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/erpnext_integrations/doctype/exotel_settings/exotel_settings.py b/erpnext/erpnext_integrations/doctype/exotel_settings/exotel_settings.py deleted file mode 100644 index 4879cb5623..0000000000 --- a/erpnext/erpnext_integrations/doctype/exotel_settings/exotel_settings.py +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import frappe -import requests -from frappe import _ -from frappe.model.document import Document - - -class ExotelSettings(Document): - def validate(self): - self.verify_credentials() - - def verify_credentials(self): - if self.enabled: - response = requests.get( - "https://api.exotel.com/v1/Accounts/{sid}".format(sid=self.account_sid), - auth=(self.api_key, self.api_token), - ) - if response.status_code != 200: - frappe.throw(_("Invalid credentials")) diff --git a/erpnext/erpnext_integrations/exotel_integration.py b/erpnext/erpnext_integrations/exotel_integration.py deleted file mode 100644 index 522de9ead8..0000000000 --- a/erpnext/erpnext_integrations/exotel_integration.py +++ /dev/null @@ -1,133 +0,0 @@ -import frappe -import requests -from frappe import _ - -# api/method/erpnext.erpnext_integrations.exotel_integration.handle_incoming_call -# api/method/erpnext.erpnext_integrations.exotel_integration.handle_end_call -# api/method/erpnext.erpnext_integrations.exotel_integration.handle_missed_call - - -@frappe.whitelist(allow_guest=True) -def handle_incoming_call(**kwargs): - try: - exotel_settings = get_exotel_settings() - if not exotel_settings.enabled: - return - - call_payload = kwargs - status = call_payload.get("Status") - if status == "free": - return - - call_log = get_call_log(call_payload) - if not call_log: - create_call_log(call_payload) - else: - update_call_log(call_payload, call_log=call_log) - except Exception as e: - frappe.db.rollback() - frappe.log_error(title=_("Error in Exotel incoming call")) - frappe.db.commit() - - -@frappe.whitelist(allow_guest=True) -def handle_end_call(**kwargs): - update_call_log(kwargs, "Completed") - - -@frappe.whitelist(allow_guest=True) -def handle_missed_call(**kwargs): - status = "" - call_type = kwargs.get("CallType") - dial_call_status = kwargs.get("DialCallStatus") - - if call_type == "incomplete" and dial_call_status == "no-answer": - status = "No Answer" - elif call_type == "client-hangup" and dial_call_status == "canceled": - status = "Canceled" - elif call_type == "incomplete" and dial_call_status == "failed": - status = "Failed" - - update_call_log(kwargs, status) - - -def update_call_log(call_payload, status="Ringing", call_log=None): - call_log = call_log or get_call_log(call_payload) - - # for a new sid, call_log and get_call_log will be empty so create a new log - if not call_log: - call_log = create_call_log(call_payload) - if call_log: - call_log.status = status - call_log.to = call_payload.get("DialWhomNumber") - call_log.duration = call_payload.get("DialCallDuration") or 0 - call_log.recording_url = call_payload.get("RecordingUrl") - call_log.save(ignore_permissions=True) - frappe.db.commit() - return call_log - - -def get_call_log(call_payload): - call_log_id = call_payload.get("CallSid") - if frappe.db.exists("Call Log", call_log_id): - return frappe.get_doc("Call Log", call_log_id) - - -def create_call_log(call_payload): - call_log = frappe.new_doc("Call Log") - call_log.id = call_payload.get("CallSid") - call_log.to = call_payload.get("DialWhomNumber") - call_log.medium = call_payload.get("To") - call_log.status = "Ringing" - setattr(call_log, "from", call_payload.get("CallFrom")) - call_log.save(ignore_permissions=True) - frappe.db.commit() - return call_log - - -@frappe.whitelist() -def get_call_status(call_id): - endpoint = get_exotel_endpoint("Calls/{call_id}.json".format(call_id=call_id)) - response = requests.get(endpoint) - status = response.json().get("Call", {}).get("Status") - return status - - -@frappe.whitelist() -def make_a_call(from_number, to_number, caller_id): - endpoint = get_exotel_endpoint("Calls/connect.json?details=true") - response = requests.post( - endpoint, data={"From": from_number, "To": to_number, "CallerId": caller_id} - ) - - return response.json() - - -def get_exotel_settings(): - return frappe.get_single("Exotel Settings") - - -def whitelist_numbers(numbers, caller_id): - endpoint = get_exotel_endpoint("CustomerWhitelist") - response = requests.post( - endpoint, - data={ - "VirtualNumber": caller_id, - "Number": numbers, - }, - ) - - return response - - -def get_all_exophones(): - endpoint = get_exotel_endpoint("IncomingPhoneNumbers") - response = requests.post(endpoint) - return response - - -def get_exotel_endpoint(action): - settings = get_exotel_settings() - return "https://{api_key}:{api_token}@api.exotel.com/v1/Accounts/{sid}/{action}".format( - api_key=settings.api_key, api_token=settings.api_token, sid=settings.account_sid, action=action - ) diff --git a/erpnext/tests/exotel_test_data.py b/erpnext/tests/exotel_test_data.py deleted file mode 100644 index 3ad2575c23..0000000000 --- a/erpnext/tests/exotel_test_data.py +++ /dev/null @@ -1,122 +0,0 @@ -import frappe - -call_initiation_data = frappe._dict( - { - "CallSid": "23c162077629863c1a2d7f29263a162m", - "CallFrom": "09999999991", - "CallTo": "09999999980", - "Direction": "incoming", - "Created": "Wed, 23 Feb 2022 12:31:59", - "From": "09999999991", - "To": "09999999988", - "CurrentTime": "2022-02-23 12:32:02", - "DialWhomNumber": "09999999999", - "Status": "busy", - "EventType": "Dial", - "AgentEmail": "test_employee_exotel@company.com", - } -) - -call_end_data = frappe._dict( - { - "CallSid": "23c162077629863c1a2d7f29263a162m", - "CallFrom": "09999999991", - "CallTo": "09999999980", - "Direction": "incoming", - "ForwardedFrom": "null", - "Created": "Wed, 23 Feb 2022 12:31:59", - "DialCallDuration": "17", - "RecordingUrl": "https://s3-ap-southeast-1.amazonaws.com/random.mp3", - "StartTime": "2022-02-23 12:31:58", - "EndTime": "1970-01-01 05:30:00", - "DialCallStatus": "completed", - "CallType": "completed", - "DialWhomNumber": "09999999999", - "ProcessStatus": "null", - "flow_id": "228040", - "tenant_id": "67291", - "From": "09999999991", - "To": "09999999988", - "RecordingAvailableBy": "Wed, 23 Feb 2022 12:37:25", - "CurrentTime": "2022-02-23 12:32:25", - "OutgoingPhoneNumber": "09999999988", - "Legs": [ - { - "Number": "09999999999", - "Type": "single", - "OnCallDuration": "10", - "CallerId": "09999999980", - "CauseCode": "NORMAL_CLEARING", - "Cause": "16", - } - ], - } -) - -call_disconnected_data = frappe._dict( - { - "CallSid": "d96421addce69e24bdc7ce5880d1162l", - "CallFrom": "09999999991", - "CallTo": "09999999980", - "Direction": "incoming", - "ForwardedFrom": "null", - "Created": "Mon, 21 Feb 2022 15:58:12", - "DialCallDuration": "0", - "StartTime": "2022-02-21 15:58:12", - "EndTime": "1970-01-01 05:30:00", - "DialCallStatus": "canceled", - "CallType": "client-hangup", - "DialWhomNumber": "09999999999", - "ProcessStatus": "null", - "flow_id": "228040", - "tenant_id": "67291", - "From": "09999999991", - "To": "09999999988", - "CurrentTime": "2022-02-21 15:58:47", - "OutgoingPhoneNumber": "09999999988", - "Legs": [ - { - "Number": "09999999999", - "Type": "single", - "OnCallDuration": "0", - "CallerId": "09999999980", - "CauseCode": "RING_TIMEOUT", - "Cause": "1003", - } - ], - } -) - -call_not_answered_data = frappe._dict( - { - "CallSid": "fdb67a2b4b2d057b610a52ef43f81622", - "CallFrom": "09999999991", - "CallTo": "09999999980", - "Direction": "incoming", - "ForwardedFrom": "null", - "Created": "Mon, 21 Feb 2022 15:47:02", - "DialCallDuration": "0", - "StartTime": "2022-02-21 15:47:02", - "EndTime": "1970-01-01 05:30:00", - "DialCallStatus": "no-answer", - "CallType": "incomplete", - "DialWhomNumber": "09999999999", - "ProcessStatus": "null", - "flow_id": "228040", - "tenant_id": "67291", - "From": "09999999991", - "To": "09999999988", - "CurrentTime": "2022-02-21 15:47:40", - "OutgoingPhoneNumber": "09999999988", - "Legs": [ - { - "Number": "09999999999", - "Type": "single", - "OnCallDuration": "0", - "CallerId": "09999999980", - "CauseCode": "RING_TIMEOUT", - "Cause": "1003", - } - ], - } -) diff --git a/erpnext/tests/test_exotel.py b/erpnext/tests/test_exotel.py deleted file mode 100644 index 76bbb3e05a..0000000000 --- a/erpnext/tests/test_exotel.py +++ /dev/null @@ -1,69 +0,0 @@ -import frappe -from frappe.contacts.doctype.contact.test_contact import create_contact -from frappe.tests.test_api import FrappeAPITestCase - -from erpnext.hr.doctype.employee.test_employee import make_employee - - -class TestExotel(FrappeAPITestCase): - @classmethod - def setUpClass(cls): - cls.CURRENT_DB_CONNECTION = frappe.db - cls.test_employee_name = make_employee( - user="test_employee_exotel@company.com", cell_number="9999999999" - ) - frappe.db.set_value("Exotel Settings", "Exotel Settings", "enabled", 1) - phones = [{"phone": "+91 9999999991", "is_primary_phone": 0, "is_primary_mobile_no": 1}] - create_contact(name="Test Contact", salutation="Mr", phones=phones) - frappe.db.commit() - - def test_for_successful_call(self): - from .exotel_test_data import call_end_data, call_initiation_data - - api_method = "handle_incoming_call" - end_call_api_method = "handle_end_call" - - self.emulate_api_call_from_exotel(api_method, call_initiation_data) - self.emulate_api_call_from_exotel(end_call_api_method, call_end_data) - call_log = frappe.get_doc("Call Log", call_initiation_data.CallSid) - - self.assertEqual(call_log.get("from"), call_initiation_data.CallFrom) - self.assertEqual(call_log.get("to"), call_initiation_data.DialWhomNumber) - self.assertEqual(call_log.get("call_received_by"), self.test_employee_name) - self.assertEqual(call_log.get("status"), "Completed") - - def test_for_disconnected_call(self): - from .exotel_test_data import call_disconnected_data - - api_method = "handle_missed_call" - self.emulate_api_call_from_exotel(api_method, call_disconnected_data) - call_log = frappe.get_doc("Call Log", call_disconnected_data.CallSid) - self.assertEqual(call_log.get("from"), call_disconnected_data.CallFrom) - self.assertEqual(call_log.get("to"), call_disconnected_data.DialWhomNumber) - self.assertEqual(call_log.get("call_received_by"), self.test_employee_name) - self.assertEqual(call_log.get("status"), "Canceled") - - def test_for_call_not_answered(self): - from .exotel_test_data import call_not_answered_data - - api_method = "handle_missed_call" - self.emulate_api_call_from_exotel(api_method, call_not_answered_data) - call_log = frappe.get_doc("Call Log", call_not_answered_data.CallSid) - self.assertEqual(call_log.get("from"), call_not_answered_data.CallFrom) - self.assertEqual(call_log.get("to"), call_not_answered_data.DialWhomNumber) - self.assertEqual(call_log.get("call_received_by"), self.test_employee_name) - self.assertEqual(call_log.get("status"), "No Answer") - - def emulate_api_call_from_exotel(self, api_method, data): - self.post( - f"/api/method/erpnext.erpnext_integrations.exotel_integration.{api_method}", - data=frappe.as_json(data), - content_type="application/json", - as_tuple=True, - ) - # restart db connection to get latest data - frappe.connect() - - @classmethod - def tearDownClass(cls): - frappe.db = cls.CURRENT_DB_CONNECTION From e0bc437ddbb1bd490f9483d797d6221f709eafbd Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Sun, 8 May 2022 16:05:04 +0530 Subject: [PATCH 02/99] refactor: Simplify call log code --- .../telephony/doctype/call_log/call_log.py | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/erpnext/telephony/doctype/call_log/call_log.py b/erpnext/telephony/doctype/call_log/call_log.py index 7725e71f19..1d6839c1e6 100644 --- a/erpnext/telephony/doctype/call_log/call_log.py +++ b/erpnext/telephony/doctype/call_log/call_log.py @@ -24,12 +24,10 @@ class CallLog(Document): lead_number = self.get("from") if self.is_incoming_call() else self.get("to") lead_number = strip_number(lead_number) - contact = get_contact_with_phone_number(strip_number(lead_number)) - if contact: + if contact := get_contact_with_phone_number(strip_number(lead_number)): self.add_link(link_type="Contact", link_name=contact) - lead = get_lead_with_phone_number(lead_number) - if lead: + if lead := get_lead_with_phone_number(lead_number): self.add_link(link_type="Lead", link_name=lead) # Add Employee Name @@ -70,28 +68,30 @@ class CallLog(Document): self.append("links", {"link_doctype": link_type, "link_name": link_name}) def trigger_call_popup(self): - if self.is_incoming_call(): - scheduled_employees = get_scheduled_employees_for_popup(self.medium) - employees = get_employees_with_number(self.to) - employee_emails = [employee.get("user_id") for employee in employees] + if not self.is_incoming_call(): + return - # check if employees with matched number are scheduled to receive popup - emails = set(scheduled_employees).intersection(employee_emails) + scheduled_employees = get_scheduled_employees_for_popup(self.medium) + employees = get_employees_with_number(self.to) + employee_emails = [employee.get("user_id") for employee in employees] - if frappe.conf.developer_mode: - self.add_comment( - text=f""" + # check if employees with matched number are scheduled to receive popup + emails = set(scheduled_employees).intersection(employee_emails) + + if frappe.conf.developer_mode: + self.add_comment( + text=f""" Scheduled Employees: {scheduled_employees} Matching Employee: {employee_emails} Show Popup To: {emails} """ - ) + ) - if employee_emails and not emails: - self.add_comment(text=_("No employee was scheduled for call popup")) + if employee_emails and not emails: + self.add_comment(text=_("No employee was scheduled for call popup")) - for email in emails: - frappe.publish_realtime("show_call_popup", self, user=email) + for email in emails: + frappe.publish_realtime("show_call_popup", self, user=email) def update_received_by(self): if employees := get_employees_with_number(self.get("to")): @@ -154,8 +154,8 @@ def link_existing_conversations(doc, state): ELSE 0 END )=0 - """, - dict(phone_number="%{}".format(number), docname=doc.name, doctype=doc.doctype), + """, + dict(phone_number=f"%{number}", docname=doc.name, doctype=doc.doctype), ) for log in logs: @@ -175,7 +175,7 @@ def get_linked_call_logs(doctype, docname): filters={"parenttype": "Call Log", "link_doctype": doctype, "link_name": docname}, ) - logs = set([log.parent for log in logs]) + logs = {log.parent for log in logs} logs = frappe.get_all("Call Log", fields=["*"], filters={"name": ["in", logs]}) From cf9c065cf88ba706c036b4f199ae9c39b5bea836 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Fri, 22 Jul 2022 12:22:57 +0530 Subject: [PATCH 03/99] refactor: Add exotel deprecation warning --- .../v13_0/exotel_integration_deprecation_warning.py | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 erpnext/patches/v13_0/exotel_integration_deprecation_warning.py diff --git a/erpnext/patches/v13_0/exotel_integration_deprecation_warning.py b/erpnext/patches/v13_0/exotel_integration_deprecation_warning.py new file mode 100644 index 0000000000..6e84ba9176 --- /dev/null +++ b/erpnext/patches/v13_0/exotel_integration_deprecation_warning.py @@ -0,0 +1,10 @@ +import click + + +def execute(): + + click.secho( + "Exotel integration is moved to a separate app and will be removed from ERPNext in version-14.\n" + "Please install the app to continue using the integration: https://github.com/frappe/exotel_integration", + fg="yellow", + ) From 6349f29aedc2eec817786dffbe245db54eff0731 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Sat, 30 Jul 2022 14:26:37 +0530 Subject: [PATCH 04/99] fix: Remove option from Communication Medium --- .../communication_medium/communication_medium.json | 2 +- .../erpnext_integrations/erpnext_integrations.json | 11 ----------- erpnext/www/lms/__init__.py | 0 3 files changed, 1 insertion(+), 12 deletions(-) create mode 100644 erpnext/www/lms/__init__.py diff --git a/erpnext/communication/doctype/communication_medium/communication_medium.json b/erpnext/communication/doctype/communication_medium/communication_medium.json index 1e1fe3bf49..b6b9c7e434 100644 --- a/erpnext/communication/doctype/communication_medium/communication_medium.json +++ b/erpnext/communication/doctype/communication_medium/communication_medium.json @@ -61,7 +61,7 @@ "fieldname": "communication_channel", "fieldtype": "Select", "label": "Communication Channel", - "options": "\nExotel" + "options": "" } ], "links": [], diff --git a/erpnext/erpnext_integrations/workspace/erpnext_integrations/erpnext_integrations.json b/erpnext/erpnext_integrations/workspace/erpnext_integrations/erpnext_integrations.json index 1f2619b9a6..c5faa2d59e 100644 --- a/erpnext/erpnext_integrations/workspace/erpnext_integrations/erpnext_integrations.json +++ b/erpnext/erpnext_integrations/workspace/erpnext_integrations/erpnext_integrations.json @@ -77,17 +77,6 @@ "link_type": "DocType", "onboard": 0, "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Exotel Settings", - "link_count": 0, - "link_to": "Exotel Settings", - "link_type": "DocType", - "onboard": 0, - "type": "Link" } ], "modified": "2022-01-13 17:35:35.508718", diff --git a/erpnext/www/lms/__init__.py b/erpnext/www/lms/__init__.py new file mode 100644 index 0000000000..e69de29bb2 From 74619269f0fa5d380c820bc12ab59cb7a06669c8 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Fri, 2 Jun 2023 17:13:51 +0530 Subject: [PATCH 05/99] feat: Record Advance Payments as Liability Ability to let user record advance payments as liability instead of a negative asset. Issue #34282 --- .../doctype/party_account/party_account.json | 20 +- .../doctype/payment_entry/payment_entry.js | 53 +++- .../doctype/payment_entry/payment_entry.py | 5 +- .../payment_reconciliation.js | 53 +++- .../payment_reconciliation.json | 23 +- .../payment_reconciliation.py | 27 +- .../purchase_invoice/purchase_invoice.py | 7 + .../purchase_invoice_advance.json | 15 +- .../doctype/sales_invoice/sales_invoice.py | 6 + .../sales_invoice_advance.json | 15 +- erpnext/accounts/party.py | 32 ++- erpnext/accounts/utils.py | 77 ++--- erpnext/buying/doctype/supplier/supplier.js | 23 ++ erpnext/buying/doctype/supplier/supplier.json | 9 +- erpnext/controllers/accounts_controller.py | 264 ++++++++++++------ erpnext/selling/doctype/customer/customer.js | 22 ++ .../selling/doctype/customer/customer.json | 7 +- erpnext/setup/doctype/company/company.js | 4 +- erpnext/setup/doctype/company/company.json | 38 ++- .../doctype/customer_group/customer_group.js | 26 +- .../customer_group/customer_group.json | 5 +- 21 files changed, 559 insertions(+), 172 deletions(-) diff --git a/erpnext/accounts/doctype/party_account/party_account.json b/erpnext/accounts/doctype/party_account/party_account.json index 69330577ab..719b474f1f 100644 --- a/erpnext/accounts/doctype/party_account/party_account.json +++ b/erpnext/accounts/doctype/party_account/party_account.json @@ -6,7 +6,9 @@ "engine": "InnoDB", "field_order": [ "company", - "account" + "account", + "advances_received_account", + "advances_paid_account" ], "fields": [ { @@ -22,14 +24,26 @@ "fieldname": "account", "fieldtype": "Link", "in_list_view": 1, - "label": "Account", + "label": "Default Account", + "options": "Account" + }, + { + "fieldname": "advances_received_account", + "fieldtype": "Link", + "label": "Advances Received Account", + "options": "Account" + }, + { + "fieldname": "advances_paid_account", + "fieldtype": "Link", + "label": "Advances Paid Account", "options": "Account" } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2022-04-04 12:31:02.994197", + "modified": "2023-06-02 13:00:06.885744", "modified_by": "Administrator", "module": "Accounts", "name": "Party Account", diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index 2843824934..7bd3bb8710 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -18,14 +18,20 @@ frappe.ui.form.on('Payment Entry', { }, setup: function(frm) { + advance_payments_as_liability = frappe.db.get_value("Company", {"company_name": frm.doc.company}, "book_advance_payments_as_liability"); + + if(advance_payments_as_liability && frm.doc.payment_type == 'Receive'){ + account_type = "Payable"; + } + else{ + account_type = "Receivable"; + } + frm.set_query("paid_from", function() { frm.events.validate_company(frm); - - var account_types = in_list(["Pay", "Internal Transfer"], frm.doc.payment_type) ? - ["Bank", "Cash"] : [frappe.boot.party_account_types[frm.doc.party_type]]; return { filters: { - "account_type": ["in", account_types], + "account_type": account_type, "is_group": 0, "company": frm.doc.company } @@ -74,12 +80,15 @@ frappe.ui.form.on('Payment Entry', { frm.set_query("paid_to", function() { frm.events.validate_company(frm); - - var account_types = in_list(["Receive", "Internal Transfer"], frm.doc.payment_type) ? - ["Bank", "Cash"] : [frappe.boot.party_account_types[frm.doc.party_type]]; + if(advance_payments_as_liability && in_list(['Receive', 'Internal Transfer'], cur_frm.doc.payment_type)){ + account_type = ["Bank", "Cash"]; + } + else{ + account_type = "Receivable"; + } return { filters: { - "account_type": ["in", account_types], + "account_type": ["in", account_type], "is_group": 0, "company": frm.doc.company } @@ -270,6 +279,25 @@ frappe.ui.form.on('Payment Entry', { }, payment_type: function(frm) { + advance_payments_as_liability = frappe.db.get_value("Company", {"company_name": frm.doc.company}, "book_advance_payments_as_liability"); + + if(advance_payments_as_liability && frm.doc.payment_type == 'Receive'){ + account_type = ["Payable"]; + } + else{ + account_type = ["Bank", "Cash"]; + } + + frm.set_query("paid_from", function() { + frm.events.validate_company(frm); + return { + filters: { + "account_type": ["in", account_type], + "is_group": 0, + "company": frm.doc.company + } + } + }); if(frm.doc.payment_type == "Internal Transfer") { $.each(["party", "party_balance", "paid_from", "paid_to", "references", "total_allocated_amount"], function(i, field) { @@ -318,6 +346,10 @@ frappe.ui.form.on('Payment Entry', { } }, + company: function(frm){ + frm.trigger('party'); + }, + party: function(frm) { if (frm.doc.contact_email || frm.doc.contact_person) { frm.set_value("contact_email", ""); @@ -332,7 +364,7 @@ frappe.ui.form.on('Payment Entry', { frm.set_party_account_based_on_party = true; let company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency; - + return frappe.call({ method: "erpnext.accounts.doctype.payment_entry.payment_entry.get_party_details", args: { @@ -340,7 +372,8 @@ frappe.ui.form.on('Payment Entry', { party_type: frm.doc.party_type, party: frm.doc.party, date: frm.doc.posting_date, - cost_center: frm.doc.cost_center + cost_center: frm.doc.cost_center, + is_advance: !(frm.doc.references) }, callback: function(r, rt) { if(r.message) { diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 3df48e22ad..ed2158a649 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -1563,13 +1563,12 @@ def get_negative_outstanding_invoices( @frappe.whitelist() -def get_party_details(company, party_type, party, date, cost_center=None): +def get_party_details(company, party_type, party, date, cost_center=None, is_advance=False): bank_account = "" if not frappe.db.exists(party_type, party): frappe.throw(_("Invalid {0}: {1}").format(party_type, party)) - party_account = get_party_account(party_type, party, company) - + party_account = get_party_account(party_type, party, company, is_advance) account_currency = get_account_currency(party_account) account_balance = get_balance_on(party_account, date, cost_center=cost_center) _party_name = "title" if party_type == "Shareholder" else party_type.lower() + "_name" diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js index 08d38dde47..2e9628c9f0 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js @@ -29,6 +29,26 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo }; }); + this.frm.set_query('default_advances_received_account', () => { + return { + filters: { + "company": this.frm.doc.company, + "is_group": 0, + "root_type": "Liability" + } + }; + }); + + this.frm.set_query('default_advances_paid_account', () => { + return { + filters: { + "company": this.frm.doc.company, + "is_group": 0, + "root_type": "Asset" + } + }; + }); + this.frm.set_query('bank_cash_account', () => { return { filters:[ @@ -124,7 +144,7 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo this.frm.trigger("clear_child_tables"); if (!this.frm.doc.receivable_payable_account && this.frm.doc.party_type && this.frm.doc.party) { - return frappe.call({ + frappe.call({ method: "erpnext.accounts.party.get_party_account", args: { company: this.frm.doc.company, @@ -136,7 +156,38 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo this.frm.set_value("receivable_payable_account", r.message); } this.frm.refresh(); + } + }); + frappe.call({ + method: "erpnext.accounts.party.get_party_account", + args: { + company: this.frm.doc.company, + party_type: this.frm.doc.party_type, + party: this.frm.doc.party, + is_advance: 1 + }, + callback: (r) => { + if (!r.exc && r.message) { + this.frm.set_value("default_advances_received_account", r.message); + } + this.frm.refresh(); + } + }); + + frappe.call({ + method: "erpnext.accounts.party.get_party_account", + args: { + company: this.frm.doc.company, + party_type: (this.frm.doc.party_type == 'Customer')?'Supplier':'Customer', + party: this.frm.doc.party, + is_advance: 1 + }, + callback: (r) => { + if (!r.exc && r.message) { + this.frm.set_value("default_advances_paid_account", r.message); + } + this.frm.refresh(); } }); } diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json index 18d3485085..21b8392d60 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json @@ -7,9 +7,11 @@ "field_order": [ "company", "party_type", - "column_break_4", "party", + "column_break_4", "receivable_payable_account", + "default_advances_received_account", + "default_advances_paid_account", "col_break1", "from_invoice_date", "from_payment_date", @@ -185,13 +187,30 @@ "fieldtype": "Link", "label": "Cost Center", "options": "Cost Center" + }, + { + "depends_on": "eval:doc.party_type", + "fieldname": "default_advances_received_account", + "fieldtype": "Link", + "label": "Default Advances Received Account", + "mandatory_depends_on": "doc.party_type", + "options": "Account", + "reqd": 1 + }, + { + "depends_on": "eval:doc.party_type", + "fieldname": "default_advances_paid_account", + "fieldtype": "Link", + "label": "Default Advances Paid Account", + "mandatory_depends_on": "doc.party_type", + "options": "Account" } ], "hide_toolbar": 1, "icon": "icon-resize-horizontal", "issingle": 1, "links": [], - "modified": "2022-04-29 15:37:10.246831", + "modified": "2023-06-02 14:32:27.276083", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Reconciliation", diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index cc2b9420cc..9b03d36a8e 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -19,7 +19,8 @@ from erpnext.accounts.utils import ( reconcile_against_document, ) from erpnext.controllers.accounts_controller import get_advance_payment_entries - +from erpnext.controllers.accounts_controller import make_advance_liability_entry +from erpnext.accounts.general_ledger import make_gl_entries class PaymentReconciliation(Document): def __init__(self, *args, **kwargs): @@ -56,12 +57,26 @@ class PaymentReconciliation(Document): self.add_payment_entries(non_reconciled_payments) def get_payment_entries(self): + receivable_payable_account = self.receivable_payable_account + default_advances_account = self.default_advances_received_account + party_account = [receivable_payable_account, default_advances_account] order_doctype = "Sales Order" if self.party_type == "Customer" else "Purchase Order" - condition = self.get_conditions(get_payments=True) + condition = frappe._dict( + { + "company": self.get("company"), + "get_payments": True, + "cost_center": self.get("cost_center"), + "from_payment_date": self.get("from_payment_date"), + "to_payment_date": self.get("to_payment_date"), + "maximum_payment_amount": self.get("maximum_payment_amount"), + "minimum_payment_amount": self.get("minimum_payment_amount") + } + ) + payment_entries = get_advance_payment_entries( - self.party_type, + self.party_type, self.party, - self.receivable_payable_account, + party_account, order_doctype, against_all_orders=True, limit=self.payment_limit, @@ -319,6 +334,10 @@ class PaymentReconciliation(Document): for row in self.get("allocation"): reconciled_entry = [] if row.invoice_number and row.allocated_amount: + if row.invoice_type in ["Sales Invoice", "Purchase Invoice"]: + gl_entries = [] + make_advance_liability_entry(gl_entries, row.reference_name, row.allocated_amount, row.invoice_number, self.party_type) + make_gl_entries(gl_entries) if row.reference_type in ["Sales Invoice", "Purchase Invoice"]: reconciled_entry = dr_or_cr_notes else: diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 868a150edf..dfea8e9049 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -34,6 +34,7 @@ from erpnext.assets.doctype.asset.asset import get_asset_account, is_cwip_accoun from erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account from erpnext.buying.utils import check_on_hold_or_closed_status from erpnext.controllers.accounts_controller import validate_account_head +from erpnext.controllers.accounts_controller import make_advance_liability_entry from erpnext.controllers.buying_controller import BuyingController from erpnext.stock import get_warehouse_account_map from erpnext.stock.doctype.purchase_receipt.purchase_receipt import ( @@ -580,6 +581,12 @@ class PurchaseInvoice(BuyingController): gl_entries = [] self.make_supplier_gl_entry(gl_entries) + + advance_payments_as_liability = frappe.db.get_value("Company", {"company_name": self.company}, "book_advance_payments_as_liability") + if advance_payments_as_liability: + for advance_entry in self.advances: + make_advance_liability_entry(gl_entries, advance_entry.reference_name, advance_entry.allocated_amount, invoice=self.name, party_type="Supplier") + self.make_item_gl_entries(gl_entries) self.make_precision_loss_gl_entry(gl_entries) diff --git a/erpnext/accounts/doctype/purchase_invoice_advance/purchase_invoice_advance.json b/erpnext/accounts/doctype/purchase_invoice_advance/purchase_invoice_advance.json index 9fcbf5c633..9082115f23 100644 --- a/erpnext/accounts/doctype/purchase_invoice_advance/purchase_invoice_advance.json +++ b/erpnext/accounts/doctype/purchase_invoice_advance/purchase_invoice_advance.json @@ -14,7 +14,8 @@ "advance_amount", "allocated_amount", "exchange_gain_loss", - "ref_exchange_rate" + "ref_exchange_rate", + "account" ], "fields": [ { @@ -111,13 +112,20 @@ "label": "Reference Exchange Rate", "non_negative": 1, "read_only": 1 + }, + { + "fieldname": "account", + "fieldtype": "Link", + "label": "Account", + "options": "Account", + "read_only": 1 } ], "idx": 1, "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2021-09-26 15:47:28.167371", + "modified": "2023-06-01 16:56:48.530169", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice Advance", @@ -125,5 +133,6 @@ "permissions": [], "quick_entry": 1, "sort_field": "modified", - "sort_order": "DESC" + "sort_order": "DESC", + "states": [] } \ No newline at end of file diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 7454332cd3..419628eaff 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -33,6 +33,7 @@ from erpnext.assets.doctype.asset.depreciation import ( reverse_depreciation_entry_made_after_disposal, ) from erpnext.controllers.accounts_controller import validate_account_head +from erpnext.controllers.accounts_controller import make_advance_liability_entry from erpnext.controllers.selling_controller import SellingController from erpnext.projects.doctype.timesheet.timesheet import get_projectwise_timesheet_data from erpnext.setup.doctype.company.company import update_company_current_month_sales @@ -1064,6 +1065,11 @@ class SalesInvoice(SellingController): gl_entries = [] self.make_customer_gl_entry(gl_entries) + + advance_payments_as_liability = frappe.db.get_value("Company", {"company_name": self.company}, "book_advance_payments_as_liability") + if advance_payments_as_liability: + for advance_entry in self.advances: + make_advance_liability_entry(gl_entries, advance_entry.reference_name, advance_entry.allocated_amount, invoice=self.name, party_type="Customer") self.make_tax_gl_entries(gl_entries) self.make_exchange_gain_loss_gl_entries(gl_entries) diff --git a/erpnext/accounts/doctype/sales_invoice_advance/sales_invoice_advance.json b/erpnext/accounts/doctype/sales_invoice_advance/sales_invoice_advance.json index f92b57a45e..aa52b1cac2 100644 --- a/erpnext/accounts/doctype/sales_invoice_advance/sales_invoice_advance.json +++ b/erpnext/accounts/doctype/sales_invoice_advance/sales_invoice_advance.json @@ -14,7 +14,8 @@ "advance_amount", "allocated_amount", "exchange_gain_loss", - "ref_exchange_rate" + "ref_exchange_rate", + "account" ], "fields": [ { @@ -112,13 +113,20 @@ "label": "Reference Exchange Rate", "non_negative": 1, "read_only": 1 + }, + { + "fieldname": "account", + "fieldtype": "Link", + "label": "Account", + "options": "Account", + "read_only": 1 } ], "idx": 1, "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2021-09-26 15:47:46.911595", + "modified": "2023-05-31 11:47:00.191681", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice Advance", @@ -126,5 +134,6 @@ "permissions": [], "quick_entry": 1, "sort_field": "modified", - "sort_order": "DESC" + "sort_order": "DESC", + "states": [] } \ No newline at end of file diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index f86dd8f57e..3be4275888 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -365,7 +365,7 @@ def set_account_and_due_date( @frappe.whitelist() -def get_party_account(party_type, party=None, company=None): +def get_party_account(party_type, party=None, company=None, is_advance=False): """Returns the account for the given `party`. Will first search in party (Customer / Supplier) record, if not found, will search in group (Customer Group / Supplier Group), @@ -380,6 +380,11 @@ def get_party_account(party_type, party=None, company=None): return frappe.get_cached_value("Company", company, default_account_name) + advance_payments_as_liability = frappe.db.get_value("Company", {"company_name": company}, "book_advance_payments_as_liability") + + if is_advance and advance_payments_as_liability and party_type in ["Customer", "Supplier"]: + return get_party_advance_account(party_type, party, company) + account = frappe.db.get_value( "Party Account", {"parenttype": party_type, "parent": party, "company": company}, "account" ) @@ -409,6 +414,26 @@ def get_party_account(party_type, party=None, company=None): return account +def get_party_advance_account(party_type, party, company): + account_name = 'advances_received_account' if party_type == 'Customer' else 'advances_paid_account' + account = frappe.db.get_value( + "Party Account", {"parenttype": party_type, "parent": party, "company": company}, account_name + ) + + if not account: + party_group_doctype = "Customer Group" if party_type == "Customer" else "Supplier Group" + group = frappe.get_cached_value(party_type, party, scrub(party_group_doctype)) + account = frappe.db.get_value( + "Party Account", + {"parenttype": party_group_doctype, "parent": group, "company": company}, + account_name, + ) + + if not account: + account = frappe.get_cached_value("Company", company, "default_" + account_name) + + return account + @frappe.whitelist() def get_party_bank_account(party_type, party): return frappe.db.get_value( @@ -515,7 +540,10 @@ def validate_party_accounts(doc): ) # validate if account is mapped for same company - validate_account_head(account.idx, account.account, account.company) + if account.account: + validate_account_head(account.idx, account.account, account.company) + if account.advance_account: + validate_account_head(account.idx, account.advance_account, account.company) @frappe.whitelist() diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 0ee06e8239..5abc64315c 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -495,50 +495,51 @@ def check_if_advance_entry_modified(args): ret = None if args.voucher_type == "Journal Entry": - ret = frappe.db.sql( - """ - select t2.{dr_or_cr} from `tabJournal Entry` t1, `tabJournal Entry Account` t2 - where t1.name = t2.parent and t2.account = %(account)s - and t2.party_type = %(party_type)s and t2.party = %(party)s - and (t2.reference_type is null or t2.reference_type in ('', 'Sales Order', 'Purchase Order')) - and t1.name = %(voucher_no)s and t2.name = %(voucher_detail_no)s - and t1.docstatus=1 """.format( - dr_or_cr=args.get("dr_or_cr") - ), - args, + journal_entry = frappe.qb.DocType("Journal Entry") + journal_acc = frappe.qb.DocType("Journal Entry Account") + + q = (frappe.qb.from_(journal_entry) + .innerjoin(journal_acc) + .on(journal_entry.name == journal_acc.parent) ) + + if args.get("dr_or_cr") == 'debit_in_account_currency': + q = q.select(journal_acc.debit_in_account_currency) + else: + q = q.select(journal_acc.credit_in_account_currency) + + q = q.where((journal_acc.account == args.get("account")) + &((journal_acc.party_type == args.get("party_type"))) + &((journal_acc.party == args.get("party"))) + &((journal_acc.reference_type == None) | (journal_acc.reference_type.isin(['', 'Sales Order', 'Purchase Order']))) + &((journal_entry.name == args.get("voucher_no"))) + &((journal_acc.name == args.get("voucher_detail_no"))) + &((journal_entry.docstatus == 1)) + ) + else: - party_account_field = ( - "paid_from" if erpnext.get_party_account_type(args.party_type) == "Receivable" else "paid_to" + payment_entry = frappe.qb.DocType("Payment Entry") + payment_ref = frappe.qb.DocType("Payment Entry Reference") + + q = (frappe.qb.from_(payment_entry) + .select(payment_entry.name) + .where(payment_entry.name == args.get("voucher_no")) + .where(payment_entry.docstatus == 1) + .where(payment_entry.party_type == args.get("party_type")) + .where(payment_entry.party == args.get("party")) ) if args.voucher_detail_no: - ret = frappe.db.sql( - """select t1.name - from `tabPayment Entry` t1, `tabPayment Entry Reference` t2 - where - t1.name = t2.parent and t1.docstatus = 1 - and t1.name = %(voucher_no)s and t2.name = %(voucher_detail_no)s - and t1.party_type = %(party_type)s and t1.party = %(party)s and t1.{0} = %(account)s - and t2.reference_doctype in ('', 'Sales Order', 'Purchase Order') - and t2.allocated_amount = %(unreconciled_amount)s - """.format( - party_account_field - ), - args, - ) + q = ( q.inner_join(payment_ref) + .on(payment_entry.name == payment_ref.parent) + .where(payment_ref.name == args.get("voucher_detail_no")) + .where(payment_ref.reference_doctype.isin(('', 'Sales Order', 'Purchase Order'))) + .where(payment_ref.allocated_amount == args.get("unreconciled_amount")) + ) else: - ret = frappe.db.sql( - """select name from `tabPayment Entry` - where - name = %(voucher_no)s and docstatus = 1 - and party_type = %(party_type)s and party = %(party)s and {0} = %(account)s - and unallocated_amount = %(unreconciled_amount)s - """.format( - party_account_field - ), - args, - ) + q = q.where(payment_entry.unallocated_amount == args.get("unreconciled_amount")) + + ret = q.run(as_dict=True) if not ret: throw(_("""Payment Entry has been modified after you pulled it. Please pull it again.""")) diff --git a/erpnext/buying/doctype/supplier/supplier.js b/erpnext/buying/doctype/supplier/supplier.js index 1ae6f03647..9e217b5113 100644 --- a/erpnext/buying/doctype/supplier/supplier.js +++ b/erpnext/buying/doctype/supplier/supplier.js @@ -17,6 +17,29 @@ frappe.ui.form.on("Supplier", { } } }); + + frm.set_query('advances_received_account', 'accounts', function (doc, cdt, cdn) { + var d = locals[cdt][cdn]; + return { + filters: { + "root_type": 'Asset', + "company": d.company, + "is_group": 0 + } + } + }); + + frm.set_query('advances_paid_account', 'accounts', function (doc, cdt, cdn) { + var d = locals[cdt][cdn]; + return { + filters: { + "root_type": 'Liability', + "company": d.company, + "is_group": 0 + } + } + }); + frm.set_query("default_bank_account", function() { return { filters: { diff --git a/erpnext/buying/doctype/supplier/supplier.json b/erpnext/buying/doctype/supplier/supplier.json index 1bf7f589e2..86e5eeecbf 100644 --- a/erpnext/buying/doctype/supplier/supplier.json +++ b/erpnext/buying/doctype/supplier/supplier.json @@ -53,6 +53,7 @@ "primary_address", "accounting_tab", "payment_terms", + "default_accounts_section", "accounts", "settings_tab", "allow_purchase_invoice_creation_without_purchase_order", @@ -445,6 +446,11 @@ { "fieldname": "column_break_59", "fieldtype": "Column Break" + }, + { + "fieldname": "default_accounts_section", + "fieldtype": "Section Break", + "label": "Default Accounts" } ], "icon": "fa fa-user", @@ -457,7 +463,7 @@ "link_fieldname": "party" } ], - "modified": "2023-02-18 11:05:50.592270", + "modified": "2023-05-29 15:23:11.709415", "modified_by": "Administrator", "module": "Buying", "name": "Supplier", @@ -489,7 +495,6 @@ "read": 1, "report": 1, "role": "Purchase Master Manager", - "set_user_permissions": 1, "share": 1, "write": 1 }, diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 20b332e782..ac32fd352c 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -8,6 +8,8 @@ import frappe from frappe import _, bold, throw from frappe.model.workflow import get_workflow_name, is_transition_condition_satisfied from frappe.query_builder.functions import Abs, Sum +from frappe.query_builder.custom import ConstantColumn + from frappe.utils import ( add_days, add_months, @@ -871,24 +873,30 @@ class AccountsController(TransactionBase): "allocated_amount": allocated_amount, "ref_exchange_rate": flt(d.exchange_rate), # exchange_rate of advance entry } + if d.get("paid_from"): + advance_row["account"] = d.paid_from + if d.get("paid_to"): + advance_row["account"] = d.paid_to self.append("advances", advance_row) def get_advance_entries(self, include_unallocated=True): if self.doctype == "Sales Invoice": - party_account = self.debit_to party_type = "Customer" party = self.customer amount_field = "credit_in_account_currency" order_field = "sales_order" order_doctype = "Sales Order" + party_account = frappe.db.get_values("Company", {"company_name": self.company}, ["default_receivable_account", "default_advances_received_account"]) else: - party_account = self.credit_to party_type = "Supplier" party = self.supplier amount_field = "debit_in_account_currency" order_field = "purchase_order" order_doctype = "Purchase Order" + party_account = frappe.db.get_values("Company", {"company_name": self.company}, ["default_receivable_account", "default_advances_paid_account"]) + + party_account = list(party_account[0]) order_list = list(set(d.get(order_field) for d in self.get("items") if d.get(order_field))) @@ -2136,45 +2144,41 @@ def get_advance_journal_entries( order_list, include_unallocated=True, ): - dr_or_cr = ( - "credit_in_account_currency" if party_type == "Customer" else "debit_in_account_currency" - ) - - conditions = [] - if include_unallocated: - conditions.append("ifnull(t2.reference_name, '')=''") + journal_entry = frappe.qb.DocType('Journal Entry') + journal_acc = frappe.qb.DocType('Journal Entry Account') + q = (frappe.qb.from_(journal_entry) + .inner_join(journal_acc) + .on(journal_entry.name == journal_acc.parent) + .select( + ConstantColumn('Journal Entry').as_('reference_type'), + (journal_entry.name).as_('reference_name'), + (journal_entry.remark).as_('remarks'), + (journal_acc.debit_in_account_currency if party_type == 'Supplier' else journal_acc.credit_in_account_currency).as_('amount'), + (journal_acc.name).as_('reference_row'), + (journal_acc.reference_name).as_('against_order'), + (journal_acc.exchange_rate) + ) + .where(journal_acc.account.isin(party_account) + & (journal_acc.party_type == party_type) + & (journal_acc.party == party) + & (journal_acc.is_advance == 'Yes') + & (journal_entry.docstatus == 1) + ) + ) + if party_type == "Customer": + q = q.where(journal_acc.credit_in_account_currency > 0) if order_list: - order_condition = ", ".join(["%s"] * len(order_list)) - conditions.append( - " (t2.reference_type = '{0}' and ifnull(t2.reference_name, '') in ({1}))".format( - order_doctype, order_condition - ) - ) + q = q.where(journal_acc.reference_type == order_doctype) + if include_unallocated: + q = q.where(journal_acc.reference_name.isin(order_list) + |(journal_acc.reference_name == '')) + else: + q = q.where(journal_acc.reference_name.isin(order_list)) - reference_condition = " and (" + " or ".join(conditions) + ")" if conditions else "" - - # nosemgrep - journal_entries = frappe.db.sql( - """ - select - 'Journal Entry' as reference_type, t1.name as reference_name, - t1.remark as remarks, t2.{0} as amount, t2.name as reference_row, - t2.reference_name as against_order, t2.exchange_rate - from - `tabJournal Entry` t1, `tabJournal Entry Account` t2 - where - t1.name = t2.parent and t2.account = %s - and t2.party_type = %s and t2.party = %s - and t2.is_advance = 'Yes' and t1.docstatus = 1 - and {1} > 0 {2} - order by t1.posting_date""".format( - amount_field, dr_or_cr, reference_condition - ), - [party_account, party_type, party] + order_list, - as_dict=1, - ) + q = q.orderby(journal_entry.posting_date) + journal_entries = q.run(as_dict=True) return list(journal_entries) @@ -2189,65 +2193,76 @@ def get_advance_payment_entries( limit=None, condition=None, ): - party_account_field = "paid_from" if party_type == "Customer" else "paid_to" - currency_field = ( - "paid_from_account_currency" if party_type == "Customer" else "paid_to_account_currency" - ) + + q = build_query(party_type, party, party_account, order_doctype, order_list, include_unallocated, against_all_orders, limit, condition) + + payment_entries = q.run(as_dict=True) + + return list(payment_entries) + +def build_query(party_type, party, party_account, order_doctype, order_list, include_unallocated, against_all_orders, limit, condition): payment_type = "Receive" if party_type == "Customer" else "Pay" - exchange_rate_field = ( - "source_exchange_rate" if payment_type == "Receive" else "target_exchange_rate" - ) + payment_entry = frappe.qb.DocType('Payment Entry') + payment_ref = frappe.qb.DocType('Payment Entry Reference') - payment_entries_against_order, unallocated_payment_entries = [], [] - limit_cond = "limit %s" % limit if limit else "" - - if order_list or against_all_orders: - if order_list: - reference_condition = " and t2.reference_name in ({0})".format( - ", ".join(["%s"] * len(order_list)) - ) - else: - reference_condition = "" - order_list = [] - - payment_entries_against_order = frappe.db.sql( - """ - select - 'Payment Entry' as reference_type, t1.name as reference_name, - t1.remarks, t2.allocated_amount as amount, t2.name as reference_row, - t2.reference_name as against_order, t1.posting_date, - t1.{0} as currency, t1.{4} as exchange_rate - from `tabPayment Entry` t1, `tabPayment Entry Reference` t2 - where - t1.name = t2.parent and t1.{1} = %s and t1.payment_type = %s - and t1.party_type = %s and t1.party = %s and t1.docstatus = 1 - and t2.reference_doctype = %s {2} - order by t1.posting_date {3} - """.format( - currency_field, party_account_field, reference_condition, limit_cond, exchange_rate_field - ), - [party_account, payment_type, party_type, party, order_doctype] + order_list, - as_dict=1, + q = (frappe.qb.from_(payment_entry) + .select( + ConstantColumn('Payment Entry').as_('reference_type'), + (payment_entry.name).as_('reference_name'), + payment_entry.posting_date, + (payment_entry.remarks).as_('remarks') ) + .where(payment_entry.payment_type == payment_type) + .where(payment_entry.party_type == party_type) + .where(payment_entry.party == party) + .where(payment_entry.docstatus == 1) + ) + + if party_type == "Customer": + q = q.select(payment_entry.paid_from_account_currency) + q = q.select(payment_entry.paid_from) + q = q.where(payment_entry.paid_from.isin(party_account)) + else: + q = q.select(payment_entry.paid_to_account_currency) + q = q.select(payment_entry.paid_to) + q = q.where(payment_entry.paid_to.isin(party_account)) + + if payment_type == "Receive": + q = q.select(payment_entry.source_exchange_rate) + else: + q.select(payment_entry.target_exchange_rate) if include_unallocated: - unallocated_payment_entries = frappe.db.sql( - """ - select 'Payment Entry' as reference_type, name as reference_name, posting_date, - remarks, unallocated_amount as amount, {2} as exchange_rate, {3} as currency - from `tabPayment Entry` - where - {0} = %s and party_type = %s and party = %s and payment_type = %s - and docstatus = 1 and unallocated_amount > 0 {condition} - order by posting_date {1} - """.format( - party_account_field, limit_cond, exchange_rate_field, currency_field, condition=condition or "" - ), - (party_account, party_type, party, payment_type), - as_dict=1, - ) + q = q.select((payment_entry.unallocated_amount).as_('amount')) + q = q.where(payment_entry.unallocated_amount>0) - return list(payment_entries_against_order) + list(unallocated_payment_entries) + if condition: + q = q.where(payment_entry.company == condition["company"]) + q = q.where(payment_entry.posting_date >= condition["from_payment_date"]) if condition.get("from_payment_date") else q + q = q.where(payment_entry.posting_date <= condition["to_payment_date"]) if condition.get("to_payment_date") else q + if condition.get("get_payments") == True: + q = q.where(payment_entry.cost_center == condition["cost_center"]) if condition.get("cost_center") else q + q = q.where(payment_entry.unallocated_amount >= condition["minimum_payment_amount"]) if condition.get("minimum_payment_amount") else q + q = q.where(payment_entry.unallocated_amount <= condition["maximum_payment_amount"]) if condition.get("maximum_payment_amount") else q + else: + q = q.where(payment_entry.total_debit >= condition["minimum_payment_amount"]) if condition.get("minimum_payment_amount") else q + q = q.where(payment_entry.total_debit <= condition["maximum_payment_amount"]) if condition.get("maximum_payment_amount") else q + + elif order_list or against_all_orders: + q = q.inner_join(payment_ref).on(payment_entry.name == payment_ref.parent) + q = q.select( + (payment_ref.allocated_amount).as_('amount'), + (payment_ref.name).as_('reference_row'), + (payment_ref.reference_name).as_('against_order'), + payment_ref.reference_doctype == order_doctype + ) + + if order_list: + q = q.where(payment_ref.reference_name.isin(order_list)) + + q = q.orderby(payment_entry.posting_date) + q = q.limit(limit) if limit else q + return q def update_invoice_status(): @@ -2846,3 +2861,72 @@ def validate_regional(doc): @erpnext.allow_regional def validate_einvoice_fields(doc): pass + +def make_advance_liability_entry(gl_entries, pe, allocated_amount, invoice, party_type): + pe = frappe.get_doc("Payment Entry", pe) + if party_type=="Customer": + invoice = frappe.get_doc("Sales Invoice", invoice) + account = pe.paid_from + dr_or_cr = "debit" + rev = "credit" + against = invoice.debit_to + party = invoice.customer + voucher_type = "Sales Invoice" + else: + invoice = frappe.get_doc("Purchase Invoice", invoice) + account = pe.paid_to + dr_or_cr = "credit" + rev = "debit" + against = invoice.credit_to + party = invoice.supplier + voucher_type = "Purchase Invoice" + gl_entries.append(invoice.get_gl_dict( + { + "account": account, + "party_type": party_type, + "party": party, + "due_date": invoice.due_date, + "against": against, + dr_or_cr: allocated_amount, + dr_or_cr + "_in_account_currency": allocated_amount, + rev: 0, + rev + "_in_account_currency": 0, + "against_voucher": invoice.return_against + if cint(invoice.is_return) and invoice.return_against + else invoice.name, + "against_voucher_type": invoice.doctype, + "cost_center": invoice.cost_center, + "project": invoice.project, + "voucher_type": voucher_type, + "voucher_no": invoice.name + }, + invoice.party_account_currency, + item=invoice, + )) + + (dr_or_cr, rev) = ("credit", "debit") if party_type=="Customer" else ("debit", "credit") + gl_entries.append(invoice.get_gl_dict( + { + "account": against, + "party_type": party_type, + "party": party, + "due_date": invoice.due_date, + "against": account, + dr_or_cr: allocated_amount, + dr_or_cr + "_in_account_currency": allocated_amount, + rev: 0, + rev + "_in_account_currency": 0, + "against_voucher": invoice.return_against + if cint(invoice.is_return) and invoice.return_against + else invoice.name, + "against_voucher_type": invoice.doctype, + "cost_center": invoice.cost_center, + "project": invoice.project, + "voucher_type": voucher_type, + "voucher_no": invoice.name + }, + invoice.party_account_currency, + item=invoice, + )) + + diff --git a/erpnext/selling/doctype/customer/customer.js b/erpnext/selling/doctype/customer/customer.js index b53f339229..6dac692747 100644 --- a/erpnext/selling/doctype/customer/customer.js +++ b/erpnext/selling/doctype/customer/customer.js @@ -34,6 +34,28 @@ frappe.ui.form.on("Customer", { filters: filters } }); + + frm.set_query('advances_received_account', 'accounts', function (doc, cdt, cdn) { + var d = locals[cdt][cdn]; + return { + filters: { + "root_type": 'Liability', + "company": d.company, + "is_group": 0 + } + } + }); + + frm.set_query('advances_paid_account', 'accounts', function (doc, cdt, cdn) { + var d = locals[cdt][cdn]; + return { + filters: { + "root_type": 'Asset', + "company": d.company, + "is_group": 0 + } + } + }); if (frm.doc.__islocal == 1) { frm.set_value("represents_company", ""); diff --git a/erpnext/selling/doctype/customer/customer.json b/erpnext/selling/doctype/customer/customer.json index c133cd3152..0050279a4b 100644 --- a/erpnext/selling/doctype/customer/customer.json +++ b/erpnext/selling/doctype/customer/customer.json @@ -334,13 +334,13 @@ { "fieldname": "default_receivable_accounts", "fieldtype": "Section Break", - "label": "Default Receivable Accounts" + "label": "Default Accounts" }, { "description": "Mention if a non-standard receivable account", "fieldname": "accounts", "fieldtype": "Table", - "label": "Receivable Accounts", + "label": "Accounts", "options": "Party Account" }, { @@ -568,7 +568,7 @@ "link_fieldname": "party" } ], - "modified": "2023-02-18 11:04:46.343527", + "modified": "2023-05-29 14:29:17.789578", "modified_by": "Administrator", "module": "Selling", "name": "Customer", @@ -607,7 +607,6 @@ "read": 1, "report": 1, "role": "Sales Master Manager", - "set_user_permissions": 1, "share": 1, "write": 1 }, diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js index e50ce449e4..81919af4e1 100644 --- a/erpnext/setup/doctype/company/company.js +++ b/erpnext/setup/doctype/company/company.js @@ -226,7 +226,9 @@ erpnext.company.setup_queries = function(frm) { ["capital_work_in_progress_account", {"account_type": "Capital Work in Progress"}], ["asset_received_but_not_billed", {"account_type": "Asset Received But Not Billed"}], ["unrealized_profit_loss_account", {"root_type": ["in", ["Liability", "Asset"]]}], - ["default_provisional_account", {"root_type": ["in", ["Liability", "Asset"]]}] + ["default_provisional_account", {"root_type": ["in", ["Liability", "Asset"]]}], + ["default_advances_received_account", {"root_type": "Liability"}], + ["default_advances_paid_account", {"root_type": "Asset"}], ], function(i, v) { erpnext.company.set_custom_query(frm, v); }); diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json index f087d996ff..369e139eef 100644 --- a/erpnext/setup/doctype/company/company.json +++ b/erpnext/setup/doctype/company/company.json @@ -70,6 +70,11 @@ "payment_terms", "cost_center", "default_finance_book", + "advance_payments_section", + "book_advance_payments_as_liability", + "default_advances_received_account", + "column_break_cui0", + "default_advances_paid_account", "auto_accounting_for_stock_settings", "enable_perpetual_inventory", "enable_provisional_accounting_for_non_stock_items", @@ -694,6 +699,37 @@ "label": "Default Provisional Account", "no_copy": 1, "options": "Account" + }, + { + "default": "0", + "fieldname": "book_advance_payments_as_liability", + "fieldtype": "Check", + "label": "Book Advance Payments as Liability" + }, + { + "fieldname": "advance_payments_section", + "fieldtype": "Section Break", + "label": "Advance Payments" + }, + { + "depends_on": "eval:doc.book_advance_payments_as_liability", + "fieldname": "default_advances_received_account", + "fieldtype": "Link", + "label": "Default Advances Received Account", + "mandatory_depends_on": "book_advance_payments_as_liability", + "options": "Account" + }, + { + "depends_on": "eval:doc.book_advance_payments_as_liability", + "fieldname": "default_advances_paid_account", + "fieldtype": "Link", + "label": "Default Advances Paid Account", + "mandatory_depends_on": "book_advance_payments_as_liability", + "options": "Account" + }, + { + "fieldname": "column_break_cui0", + "fieldtype": "Column Break" } ], "icon": "fa fa-building", @@ -701,7 +737,7 @@ "image_field": "company_logo", "is_tree": 1, "links": [], - "modified": "2022-08-16 16:09:02.327724", + "modified": "2023-06-02 13:11:41.939016", "modified_by": "Administrator", "module": "Setup", "name": "Company", diff --git a/erpnext/setup/doctype/customer_group/customer_group.js b/erpnext/setup/doctype/customer_group/customer_group.js index 44a5019120..ef556c774d 100644 --- a/erpnext/setup/doctype/customer_group/customer_group.js +++ b/erpnext/setup/doctype/customer_group/customer_group.js @@ -30,9 +30,31 @@ cur_frm.fields_dict['accounts'].grid.get_field('account').get_query = function(d var d = locals[cdt][cdn]; return { filters: { - 'account_type': 'Receivable', - 'company': d.company, + "account_type": 'Receivable', + "company": d.company, "is_group": 0 } } } + +cur_frm.fields_dict['accounts'].grid.get_field('advances_received_account').get_query = function(doc, cdt, cdn) { + var d = locals[cdt][cdn]; + return { + filters: { + "root_type": 'Liability', + "company": d.company, + "is_group": 0 + } + } +} + +cur_frm.fields_dict['accounts'].grid.get_field('advances_paid_account').get_query = function(doc, cdt, cdn) { + var d = locals[cdt][cdn]; + return { + filters: { + "root_type": 'Asset', + "company": d.company, + "is_group": 0 + } + } +} \ No newline at end of file diff --git a/erpnext/setup/doctype/customer_group/customer_group.json b/erpnext/setup/doctype/customer_group/customer_group.json index d6a431ea61..4c36bc77ab 100644 --- a/erpnext/setup/doctype/customer_group/customer_group.json +++ b/erpnext/setup/doctype/customer_group/customer_group.json @@ -113,7 +113,7 @@ { "fieldname": "default_receivable_account", "fieldtype": "Section Break", - "label": "Default Receivable Account" + "label": "Default Accounts" }, { "depends_on": "eval:!doc.__islocal", @@ -139,7 +139,7 @@ "idx": 1, "is_tree": 1, "links": [], - "modified": "2022-12-24 11:15:17.142746", + "modified": "2023-06-02 13:40:34.435822", "modified_by": "Administrator", "module": "Setup", "name": "Customer Group", @@ -171,7 +171,6 @@ "read": 1, "report": 1, "role": "Sales Master Manager", - "set_user_permissions": 1, "share": 1, "write": 1 }, From 4ee163742a705083bd9b00e36aac9099efadaabd Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Thu, 8 Jun 2023 13:15:23 +0530 Subject: [PATCH 06/99] fix: Using one field for both advance liability accounts --- .../doctype/party_account/party_account.json | 15 +- .../doctype/payment_entry/payment_entry.js | 54 +--- .../doctype/payment_entry/payment_entry.json | 2 +- .../doctype/payment_entry/payment_entry.py | 116 ++++++-- .../payment_entry_reference.json | 11 +- .../payment_reconciliation.js | 32 +- .../payment_reconciliation.json | 17 +- .../payment_reconciliation.py | 31 +- .../purchase_invoice/purchase_invoice.json | 5 +- .../sales_invoice/test_sales_invoice.py | 4 +- erpnext/accounts/party.py | 18 +- erpnext/accounts/utils.py | 38 ++- erpnext/buying/doctype/supplier/supplier.js | 13 +- erpnext/controllers/accounts_controller.py | 275 +++++++++++------- erpnext/selling/doctype/customer/customer.js | 14 +- .../selling/doctype/customer/customer.json | 14 +- erpnext/setup/doctype/company/company.js | 3 +- erpnext/setup/doctype/company/company.json | 29 +- .../doctype/customer_group/customer_group.js | 13 +- .../doctype/supplier_group/supplier_group.js | 11 + 20 files changed, 385 insertions(+), 330 deletions(-) diff --git a/erpnext/accounts/doctype/party_account/party_account.json b/erpnext/accounts/doctype/party_account/party_account.json index 719b474f1f..6ac6e56086 100644 --- a/erpnext/accounts/doctype/party_account/party_account.json +++ b/erpnext/accounts/doctype/party_account/party_account.json @@ -7,8 +7,7 @@ "field_order": [ "company", "account", - "advances_received_account", - "advances_paid_account" + "advance_account" ], "fields": [ { @@ -28,22 +27,16 @@ "options": "Account" }, { - "fieldname": "advances_received_account", + "fieldname": "advance_account", "fieldtype": "Link", - "label": "Advances Received Account", - "options": "Account" - }, - { - "fieldname": "advances_paid_account", - "fieldtype": "Link", - "label": "Advances Paid Account", + "label": "Advance Account", "options": "Account" } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2023-06-02 13:00:06.885744", + "modified": "2023-06-05 14:15:42.053150", "modified_by": "Administrator", "module": "Accounts", "name": "Party Account", diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index 7bd3bb8710..1f0e45fc11 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -18,20 +18,14 @@ frappe.ui.form.on('Payment Entry', { }, setup: function(frm) { - advance_payments_as_liability = frappe.db.get_value("Company", {"company_name": frm.doc.company}, "book_advance_payments_as_liability"); - - if(advance_payments_as_liability && frm.doc.payment_type == 'Receive'){ - account_type = "Payable"; - } - else{ - account_type = "Receivable"; - } - frm.set_query("paid_from", function() { frm.events.validate_company(frm); + + var account_types = in_list(["Pay", "Internal Transfer"], frm.doc.payment_type) ? + ["Bank", "Cash"] : [frappe.boot.party_account_types[frm.doc.party_type]]; return { filters: { - "account_type": account_type, + "account_type": ["in", account_types], "is_group": 0, "company": frm.doc.company } @@ -80,15 +74,12 @@ frappe.ui.form.on('Payment Entry', { frm.set_query("paid_to", function() { frm.events.validate_company(frm); - if(advance_payments_as_liability && in_list(['Receive', 'Internal Transfer'], cur_frm.doc.payment_type)){ - account_type = ["Bank", "Cash"]; - } - else{ - account_type = "Receivable"; - } + + var account_types = in_list(["Receive", "Internal Transfer"], frm.doc.payment_type) ? + ["Bank", "Cash"] : [frappe.boot.party_account_types[frm.doc.party_type]]; return { filters: { - "account_type": ["in", account_type], + "account_type": ["in", account_types], "is_group": 0, "company": frm.doc.company } @@ -279,25 +270,6 @@ frappe.ui.form.on('Payment Entry', { }, payment_type: function(frm) { - advance_payments_as_liability = frappe.db.get_value("Company", {"company_name": frm.doc.company}, "book_advance_payments_as_liability"); - - if(advance_payments_as_liability && frm.doc.payment_type == 'Receive'){ - account_type = ["Payable"]; - } - else{ - account_type = ["Bank", "Cash"]; - } - - frm.set_query("paid_from", function() { - frm.events.validate_company(frm); - return { - filters: { - "account_type": ["in", account_type], - "is_group": 0, - "company": frm.doc.company - } - } - }); if(frm.doc.payment_type == "Internal Transfer") { $.each(["party", "party_balance", "paid_from", "paid_to", "references", "total_allocated_amount"], function(i, field) { @@ -364,7 +336,7 @@ frappe.ui.form.on('Payment Entry', { frm.set_party_account_based_on_party = true; let company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency; - + return frappe.call({ method: "erpnext.accounts.doctype.payment_entry.payment_entry.get_party_details", args: { @@ -372,8 +344,7 @@ frappe.ui.form.on('Payment Entry', { party_type: frm.doc.party_type, party: frm.doc.party, date: frm.doc.posting_date, - cost_center: frm.doc.cost_center, - is_advance: !(frm.doc.references) + cost_center: frm.doc.cost_center }, callback: function(r, rt) { if(r.message) { @@ -741,7 +712,7 @@ frappe.ui.form.on('Payment Entry', { if(r.message) { var total_positive_outstanding = 0; var total_negative_outstanding = 0; - + console.log(r.message); $.each(r.message, function(i, d) { var c = frm.add_child("references"); c.reference_doctype = d.voucher_type; @@ -752,6 +723,7 @@ frappe.ui.form.on('Payment Entry', { c.bill_no = d.bill_no; c.payment_term = d.payment_term; c.allocated_amount = d.allocated_amount; + c.account = d.account; if(!in_list(frm.events.get_order_doctypes(frm), d.voucher_type)) { if(flt(d.outstanding_amount) > 0) @@ -1467,4 +1439,4 @@ frappe.ui.form.on('Payment Entry', { }); } }, -}) +}) \ No newline at end of file diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.json b/erpnext/accounts/doctype/payment_entry/payment_entry.json index 3927ecae43..1c330b841e 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.json +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.json @@ -733,7 +733,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2023-02-14 04:52:30.478523", + "modified": "2023-06-07 14:36:50.521884", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Entry", diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index ed2158a649..291f8e4411 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -86,12 +86,36 @@ class PaymentEntry(AccountsController): def on_submit(self): if self.difference_amount: frappe.throw(_("Difference Amount must be zero")) + book_advance_payments_as_liability = frappe.get_value( + "Company", {"company_name": self.company}, "book_advance_payments_as_liability" + ) + if book_advance_payments_as_liability: + self.get_liability_account() self.make_gl_entries() self.update_outstanding_amounts() self.update_advance_paid() self.update_payment_schedule() self.set_status() + def get_liability_account(self): + liability_account = get_party_account(self.party_type, self.party, self.company, is_advance=True) + if self.party_type == "Customer": + msg = "Book Advance Payments as Liability option is chosen. Paid From account changed from {0} to {1}.".format( + frappe.bold(self.paid_from), + frappe.bold(liability_account), + ) + frappe.db.set_value("Payment Entry", self.name, "paid_from", liability_account) + self.paid_from = liability_account + else: + msg = "Book Advance Payments as Liability option is chosen. Paid To account changed from {0} to {1}.".format( + frappe.bold(self.paid_to), + frappe.bold(liability_account), + ) + frappe.db.set_value("Payment Entry", self.name, "paid_to", liability_account) + self.paid_to = liability_account + frappe.msgprint(_(msg), title="Warning", indicator="orange") + return liability_account + def on_cancel(self): self.ignore_linked_doctypes = ( "GL Entry", @@ -869,10 +893,14 @@ class PaymentEntry(AccountsController): if self.party_account: if self.payment_type == "Receive": against_account = self.paid_to + self.party_account = self.paid_from + dr_or_cr = "credit" else: against_account = self.paid_from + self.party_account = self.paid_to + dr_or_cr = "debit" - party_gl_dict = self.get_gl_dict( + party_dict = self.get_gl_dict( { "account": self.party_account, "party_type": self.party_type, @@ -883,30 +911,24 @@ class PaymentEntry(AccountsController): }, item=self, ) - - dr_or_cr = ( - "credit" if erpnext.get_party_account_type(self.party_type) == "Receivable" else "debit" - ) - for d in self.get("references"): - cost_center = self.cost_center - if d.reference_doctype == "Sales Invoice" and not cost_center: - cost_center = frappe.db.get_value(d.reference_doctype, d.reference_name, "cost_center") - gle = party_gl_dict.copy() - gle.update( - { - "against_voucher_type": d.reference_doctype, - "against_voucher": d.reference_name, - "cost_center": cost_center, - } + book_advance_payments_as_liability = frappe.get_value( + "Company", {"company_name": self.company}, "book_advance_payments_as_liability" ) + if ( + d.reference_doctype in ["Sales Invoice", "Purchase Invoice"] + and book_advance_payments_as_liability + ): + self.make_invoice_liability_entry(gl_entries, d) allocated_amount_in_company_currency = self.calculate_base_allocated_amount_for_reference(d) - + gle = party_dict.copy() gle.update( { - dr_or_cr + "_in_account_currency": d.allocated_amount, dr_or_cr: allocated_amount_in_company_currency, + dr_or_cr + "_in_account_currency": d.allocated_amount, + "against_voucher_type": d.reference_doctype, + "against_voucher": d.reference_name, } ) @@ -916,8 +938,7 @@ class PaymentEntry(AccountsController): exchange_rate = self.get_exchange_rate() base_unallocated_amount = self.unallocated_amount * exchange_rate - gle = party_gl_dict.copy() - + gle = party_dict.copy() gle.update( { dr_or_cr + "_in_account_currency": self.unallocated_amount, @@ -927,6 +948,40 @@ class PaymentEntry(AccountsController): gl_entries.append(gle) + def make_invoice_liability_entry(self, gl_entries, invoice): + args_dict = { + "party_type": self.party_type, + "party": self.party, + "account_currency": self.party_account_currency, + "cost_center": self.cost_center, + "voucher_type": invoice.reference_doctype, + "voucher_no": invoice.reference_name, + "against_voucher_type": invoice.reference_doctype, + "against_voucher": invoice.reference_name, + } + + dr_or_cr = "credit" if invoice.reference_doctype == "Sales Invoice" else "debit" + args_dict["account"] = invoice.account + args_dict[dr_or_cr] = invoice.allocated_amount + args_dict[dr_or_cr + "_in_account_currency"] = invoice.allocated_amount + gle = self.get_gl_dict( + args_dict, + item=self, + ) + gl_entries.append(gle) + + args_dict[dr_or_cr] = 0 + args_dict[dr_or_cr + "_in_account_currency"] = 0 + dr_or_cr = "debit" if dr_or_cr == "credit" else "credit" + args_dict["account"] = self.get_liability_account() + args_dict[dr_or_cr] = invoice.allocated_amount + args_dict[dr_or_cr + "_in_account_currency"] = invoice.allocated_amount + gle = self.get_gl_dict( + args_dict, + item=self, + ) + gl_entries.append(gle) + def add_bank_gl_entries(self, gl_entries): if self.payment_type in ("Pay", "Internal Transfer"): gl_entries.append( @@ -1401,6 +1456,7 @@ def split_invoices_based_on_payment_terms(outstanding_invoices): "outstanding_amount": flt(d.outstanding_amount), "payment_amount": payment_term.payment_amount, "payment_term": payment_term.payment_term, + "account": d.account, } ) ) @@ -1449,7 +1505,7 @@ def get_orders_to_be_billed( if voucher_type: doc = frappe.get_doc({"doctype": voucher_type}) condition = "" - if doc and hasattr(doc, "cost_center"): + if cost_center and doc and hasattr(doc, "cost_center"): condition = " and cost_center='%s'" % cost_center orders = [] @@ -1495,9 +1551,13 @@ def get_orders_to_be_billed( order_list = [] for d in orders: - if not ( - flt(d.outstanding_amount) >= flt(filters.get("outstanding_amt_greater_than")) - and flt(d.outstanding_amount) <= flt(filters.get("outstanding_amt_less_than")) + if filters.get("oustanding_amt_greater_than") and flt(d.outstanding_amount) < flt( + filters.get("outstanding_amt_greater_than") + ): + continue + + if filters.get("oustanding_amt_less_than") and flt(d.outstanding_amount) > flt( + filters.get("outstanding_amt_less_than") ): continue @@ -1519,6 +1579,7 @@ def get_negative_outstanding_invoices( condition=None, ): voucher_type = "Sales Invoice" if party_type == "Customer" else "Purchase Invoice" + account = "debit_to" if voucher_type == "Sales Invoice" else "credit_to" supplier_condition = "" if voucher_type == "Purchase Invoice": supplier_condition = "and (release_date is null or release_date <= CURRENT_DATE)" @@ -1532,7 +1593,7 @@ def get_negative_outstanding_invoices( return frappe.db.sql( """ select - "{voucher_type}" as voucher_type, name as voucher_no, + "{voucher_type}" as voucher_type, name as voucher_no, {account} as account, if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) as invoice_amount, outstanding_amount, posting_date, due_date, conversion_rate as exchange_rate @@ -1555,6 +1616,7 @@ def get_negative_outstanding_invoices( "party_type": scrub(party_type), "party_account": "debit_to" if party_type == "Customer" else "credit_to", "cost_center": cost_center, + "account": account, } ), (party, party_account), @@ -1563,12 +1625,12 @@ def get_negative_outstanding_invoices( @frappe.whitelist() -def get_party_details(company, party_type, party, date, cost_center=None, is_advance=False): +def get_party_details(company, party_type, party, date, cost_center=None): bank_account = "" if not frappe.db.exists(party_type, party): frappe.throw(_("Invalid {0}: {1}").format(party_type, party)) - party_account = get_party_account(party_type, party, company, is_advance) + party_account = get_party_account(party_type, party, company) account_currency = get_account_currency(party_account) account_balance = get_balance_on(party_account, date, cost_center=cost_center) _party_name = "title" if party_type == "Shareholder" else party_type.lower() + "_name" diff --git a/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.json b/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.json index 3003c68196..c318ea53bd 100644 --- a/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.json +++ b/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.json @@ -15,7 +15,8 @@ "outstanding_amount", "allocated_amount", "exchange_rate", - "exchange_gain_loss" + "exchange_gain_loss", + "account" ], "fields": [ { @@ -101,12 +102,18 @@ "label": "Exchange Gain/Loss", "options": "Company:company:default_currency", "read_only": 1 + }, + { + "fieldname": "account", + "fieldtype": "Link", + "label": "Account", + "options": "Account" } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2022-12-12 12:31:44.919895", + "modified": "2023-06-07 14:35:06.166907", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Entry Reference", diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js index 2e9628c9f0..d8743bb69e 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js @@ -29,22 +29,12 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo }; }); - this.frm.set_query('default_advances_received_account', () => { + this.frm.set_query('default_advance_account', () => { return { filters: { "company": this.frm.doc.company, "is_group": 0, - "root_type": "Liability" - } - }; - }); - - this.frm.set_query('default_advances_paid_account', () => { - return { - filters: { - "company": this.frm.doc.company, - "is_group": 0, - "root_type": "Asset" + "root_type": (this.frm.party_type == 'Customer') ? "Liability": "Asset" } }; }); @@ -169,23 +159,7 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo }, callback: (r) => { if (!r.exc && r.message) { - this.frm.set_value("default_advances_received_account", r.message); - } - this.frm.refresh(); - } - }); - - frappe.call({ - method: "erpnext.accounts.party.get_party_account", - args: { - company: this.frm.doc.company, - party_type: (this.frm.doc.party_type == 'Customer')?'Supplier':'Customer', - party: this.frm.doc.party, - is_advance: 1 - }, - callback: (r) => { - if (!r.exc && r.message) { - this.frm.set_value("default_advances_paid_account", r.message); + this.frm.set_value("default_advance_account", r.message); } this.frm.refresh(); } diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json index 21b8392d60..0e166ffd5d 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json @@ -10,8 +10,7 @@ "party", "column_break_4", "receivable_payable_account", - "default_advances_received_account", - "default_advances_paid_account", + "default_advance_account", "col_break1", "from_invoice_date", "from_payment_date", @@ -190,27 +189,19 @@ }, { "depends_on": "eval:doc.party_type", - "fieldname": "default_advances_received_account", + "fieldname": "default_advance_account", "fieldtype": "Link", - "label": "Default Advances Received Account", + "label": "Default Advance Account", "mandatory_depends_on": "doc.party_type", "options": "Account", "reqd": 1 - }, - { - "depends_on": "eval:doc.party_type", - "fieldname": "default_advances_paid_account", - "fieldtype": "Link", - "label": "Default Advances Paid Account", - "mandatory_depends_on": "doc.party_type", - "options": "Account" } ], "hide_toolbar": 1, "icon": "icon-resize-horizontal", "issingle": 1, "links": [], - "modified": "2023-06-02 14:32:27.276083", + "modified": "2023-06-05 20:09:58.925427", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Reconciliation", diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index 9b03d36a8e..e7d7f2c1e4 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -13,14 +13,17 @@ import erpnext from erpnext.accounts.doctype.process_payment_reconciliation.process_payment_reconciliation import ( is_any_doc_running, ) +from erpnext.accounts.general_ledger import make_gl_entries from erpnext.accounts.utils import ( QueryPaymentLedger, get_outstanding_invoices, reconcile_against_document, ) -from erpnext.controllers.accounts_controller import get_advance_payment_entries -from erpnext.controllers.accounts_controller import make_advance_liability_entry -from erpnext.accounts.general_ledger import make_gl_entries +from erpnext.controllers.accounts_controller import ( + get_advance_payment_entries, + make_advance_liability_entry, +) + class PaymentReconciliation(Document): def __init__(self, *args, **kwargs): @@ -57,9 +60,17 @@ class PaymentReconciliation(Document): self.add_payment_entries(non_reconciled_payments) def get_payment_entries(self): - receivable_payable_account = self.receivable_payable_account - default_advances_account = self.default_advances_received_account - party_account = [receivable_payable_account, default_advances_account] + advance_accounts = [] + if self.party_type == "Customer": + advance_accounts = frappe.db.get_list( + "Account", filters={"root_type": "Liability", "company": self.company}, pluck="name" + ) + elif self.party_type == "Supplier": + advance_accounts = frappe.db.get_list( + "Account", filters={"root_type": "Asset", "company": self.company}, pluck="name" + ) + party_account = [self.receivable_payable_account] + advance_accounts + order_doctype = "Sales Order" if self.party_type == "Customer" else "Purchase Order" condition = frappe._dict( { @@ -69,12 +80,12 @@ class PaymentReconciliation(Document): "from_payment_date": self.get("from_payment_date"), "to_payment_date": self.get("to_payment_date"), "maximum_payment_amount": self.get("maximum_payment_amount"), - "minimum_payment_amount": self.get("minimum_payment_amount") + "minimum_payment_amount": self.get("minimum_payment_amount"), } ) payment_entries = get_advance_payment_entries( - self.party_type, + self.party_type, self.party, party_account, order_doctype, @@ -336,7 +347,9 @@ class PaymentReconciliation(Document): if row.invoice_number and row.allocated_amount: if row.invoice_type in ["Sales Invoice", "Purchase Invoice"]: gl_entries = [] - make_advance_liability_entry(gl_entries, row.reference_name, row.allocated_amount, row.invoice_number, self.party_type) + make_advance_liability_entry( + gl_entries, row.reference_name, row.allocated_amount, row.invoice_number, self.party_type + ) make_gl_entries(gl_entries) if row.reference_type in ["Sales Invoice", "Purchase Invoice"]: reconciled_entry = dr_or_cr_notes diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index 60f9d62bf2..f0f1684b4d 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -1086,6 +1086,7 @@ "fieldtype": "Button", "label": "Get Advances Paid", "oldfieldtype": "Button", + "options": "set_advances", "print_hide": 1 }, { @@ -1364,12 +1365,12 @@ "depends_on": "eval:doc.update_stock && doc.is_internal_supplier", "fieldname": "set_from_warehouse", "fieldtype": "Link", + "ignore_user_permissions": 1, "label": "Set From Warehouse", "no_copy": 1, "options": "Warehouse", "print_hide": 1, "print_width": "50px", - "ignore_user_permissions": 1, "width": "50px" }, { @@ -1573,7 +1574,7 @@ "idx": 204, "is_submittable": 1, "links": [], - "modified": "2023-04-29 12:57:50.832598", + "modified": "2023-06-05 17:40:35.320635", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice", diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 6051c9915d..dbc277044a 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -3353,14 +3353,16 @@ def check_gl_entries(doc, voucher_no, expected_gle, posting_date): gl_entries = frappe.db.sql( """select account, debit, credit, posting_date from `tabGL Entry` - where voucher_type='Sales Invoice' and voucher_no=%s and posting_date > %s + where voucher_type='Sales Invoice' and voucher_no=%s and posting_date >= %s and is_cancelled = 0 order by posting_date asc, account asc""", (voucher_no, posting_date), as_dict=1, + debug=True, ) for i, gle in enumerate(gl_entries): + print(i, gle) doc.assertEqual(expected_gle[i][0], gle.account) doc.assertEqual(expected_gle[i][1], gle.debit) doc.assertEqual(expected_gle[i][2], gle.credit) diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index 3be4275888..782c41e529 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -380,9 +380,7 @@ def get_party_account(party_type, party=None, company=None, is_advance=False): return frappe.get_cached_value("Company", company, default_account_name) - advance_payments_as_liability = frappe.db.get_value("Company", {"company_name": company}, "book_advance_payments_as_liability") - - if is_advance and advance_payments_as_liability and party_type in ["Customer", "Supplier"]: + if is_advance and party_type in ["Customer", "Supplier"]: return get_party_advance_account(party_type, party, company) account = frappe.db.get_value( @@ -415,9 +413,10 @@ def get_party_account(party_type, party=None, company=None, is_advance=False): def get_party_advance_account(party_type, party, company): - account_name = 'advances_received_account' if party_type == 'Customer' else 'advances_paid_account' account = frappe.db.get_value( - "Party Account", {"parenttype": party_type, "parent": party, "company": company}, account_name + "Party Account", + {"parenttype": party_type, "parent": party, "company": company}, + "advance_account", ) if not account: @@ -426,14 +425,15 @@ def get_party_advance_account(party_type, party, company): account = frappe.db.get_value( "Party Account", {"parenttype": party_group_doctype, "parent": group, "company": company}, - account_name, + "advance_account", ) - + if not account: - account = frappe.get_cached_value("Company", company, "default_" + account_name) + account = frappe.get_cached_value("Company", company, "default_advance_account") return account - + + @frappe.whitelist() def get_party_bank_account(party_type, party): return frappe.db.get_value( diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 5abc64315c..506279c1e5 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -498,30 +498,36 @@ def check_if_advance_entry_modified(args): journal_entry = frappe.qb.DocType("Journal Entry") journal_acc = frappe.qb.DocType("Journal Entry Account") - q = (frappe.qb.from_(journal_entry) + q = ( + frappe.qb.from_(journal_entry) .innerjoin(journal_acc) .on(journal_entry.name == journal_acc.parent) ) - if args.get("dr_or_cr") == 'debit_in_account_currency': + if args.get("dr_or_cr") == "debit_in_account_currency": q = q.select(journal_acc.debit_in_account_currency) else: q = q.select(journal_acc.credit_in_account_currency) - - q = q.where((journal_acc.account == args.get("account")) - &((journal_acc.party_type == args.get("party_type"))) - &((journal_acc.party == args.get("party"))) - &((journal_acc.reference_type == None) | (journal_acc.reference_type.isin(['', 'Sales Order', 'Purchase Order']))) - &((journal_entry.name == args.get("voucher_no"))) - &((journal_acc.name == args.get("voucher_detail_no"))) - &((journal_entry.docstatus == 1)) + + q = q.where( + (journal_acc.account == args.get("account")) + & ((journal_acc.party_type == args.get("party_type"))) + & ((journal_acc.party == args.get("party"))) + & ( + (journal_acc.reference_type == None) + | (journal_acc.reference_type.isin(["", "Sales Order", "Purchase Order"])) + ) + & ((journal_entry.name == args.get("voucher_no"))) + & ((journal_acc.name == args.get("voucher_detail_no"))) + & ((journal_entry.docstatus == 1)) ) else: payment_entry = frappe.qb.DocType("Payment Entry") payment_ref = frappe.qb.DocType("Payment Entry Reference") - q = (frappe.qb.from_(payment_entry) + q = ( + frappe.qb.from_(payment_entry) .select(payment_entry.name) .where(payment_entry.name == args.get("voucher_no")) .where(payment_entry.docstatus == 1) @@ -530,15 +536,16 @@ def check_if_advance_entry_modified(args): ) if args.voucher_detail_no: - q = ( q.inner_join(payment_ref) + q = ( + q.inner_join(payment_ref) .on(payment_entry.name == payment_ref.parent) .where(payment_ref.name == args.get("voucher_detail_no")) - .where(payment_ref.reference_doctype.isin(('', 'Sales Order', 'Purchase Order'))) + .where(payment_ref.reference_doctype.isin(("", "Sales Order", "Purchase Order"))) .where(payment_ref.allocated_amount == args.get("unreconciled_amount")) - ) + ) else: q = q.where(payment_entry.unallocated_amount == args.get("unreconciled_amount")) - + ret = q.run(as_dict=True) if not ret: @@ -921,6 +928,7 @@ def get_outstanding_invoices( "outstanding_amount": outstanding_amount, "due_date": d.due_date, "currency": d.currency, + "account": d.account, } ) ) diff --git a/erpnext/buying/doctype/supplier/supplier.js b/erpnext/buying/doctype/supplier/supplier.js index 9e217b5113..6da47c5c69 100644 --- a/erpnext/buying/doctype/supplier/supplier.js +++ b/erpnext/buying/doctype/supplier/supplier.js @@ -18,7 +18,7 @@ frappe.ui.form.on("Supplier", { } }); - frm.set_query('advances_received_account', 'accounts', function (doc, cdt, cdn) { + frm.set_query('advance_account', 'accounts', function (doc, cdt, cdn) { var d = locals[cdt][cdn]; return { filters: { @@ -29,17 +29,6 @@ frappe.ui.form.on("Supplier", { } }); - frm.set_query('advances_paid_account', 'accounts', function (doc, cdt, cdn) { - var d = locals[cdt][cdn]; - return { - filters: { - "root_type": 'Liability', - "company": d.company, - "is_group": 0 - } - } - }); - frm.set_query("default_bank_account", function() { return { filters: { diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index ac32fd352c..0589f4a89a 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -7,9 +7,8 @@ import json import frappe from frappe import _, bold, throw from frappe.model.workflow import get_workflow_name, is_transition_condition_satisfied -from frappe.query_builder.functions import Abs, Sum from frappe.query_builder.custom import ConstantColumn - +from frappe.query_builder.functions import Abs, Sum from frappe.utils import ( add_days, add_months, @@ -859,7 +858,6 @@ class AccountsController(TransactionBase): amount = self.get("base_rounded_total") or self.base_grand_total else: amount = self.get("rounded_total") or self.grand_total - allocated_amount = min(amount - advance_allocated, d.amount) advance_allocated += flt(allocated_amount) @@ -887,16 +885,18 @@ class AccountsController(TransactionBase): amount_field = "credit_in_account_currency" order_field = "sales_order" order_doctype = "Sales Order" - party_account = frappe.db.get_values("Company", {"company_name": self.company}, ["default_receivable_account", "default_advances_received_account"]) + party_account = [ + get_party_account(party_type, party=party, company=self.company, is_advance=True) + ] else: party_type = "Supplier" party = self.supplier amount_field = "debit_in_account_currency" order_field = "purchase_order" order_doctype = "Purchase Order" - party_account = frappe.db.get_values("Company", {"company_name": self.company}, ["default_receivable_account", "default_advances_paid_account"]) - - party_account = list(party_account[0]) + party_account = [ + get_party_account(party_type, party=party, company=self.company, is_advance=True) + ] order_list = list(set(d.get(order_field) for d in self.get("items") if d.get(order_field))) @@ -2144,35 +2144,43 @@ def get_advance_journal_entries( order_list, include_unallocated=True, ): - journal_entry = frappe.qb.DocType('Journal Entry') - journal_acc = frappe.qb.DocType('Journal Entry Account') - q = (frappe.qb.from_(journal_entry) + journal_entry = frappe.qb.DocType("Journal Entry") + journal_acc = frappe.qb.DocType("Journal Entry Account") + q = ( + frappe.qb.from_(journal_entry) .inner_join(journal_acc) .on(journal_entry.name == journal_acc.parent) .select( - ConstantColumn('Journal Entry').as_('reference_type'), - (journal_entry.name).as_('reference_name'), - (journal_entry.remark).as_('remarks'), - (journal_acc.debit_in_account_currency if party_type == 'Supplier' else journal_acc.credit_in_account_currency).as_('amount'), - (journal_acc.name).as_('reference_row'), - (journal_acc.reference_name).as_('against_order'), - (journal_acc.exchange_rate) + ConstantColumn("Journal Entry").as_("reference_type"), + (journal_entry.name).as_("reference_name"), + (journal_entry.remark).as_("remarks"), + ( + journal_acc.debit_in_account_currency + if party_type == "Supplier" + else journal_acc.credit_in_account_currency + ).as_("amount"), + (journal_acc.name).as_("reference_row"), + (journal_acc.reference_name).as_("against_order"), + (journal_acc.exchange_rate), ) - .where(journal_acc.account.isin(party_account) - & (journal_acc.party_type == party_type) - & (journal_acc.party == party) - & (journal_acc.is_advance == 'Yes') - & (journal_entry.docstatus == 1) - ) - ) + .where( + journal_acc.account.isin(party_account) + & (journal_acc.party_type == party_type) + & (journal_acc.party == party) + & (journal_acc.is_advance == "Yes") + & (journal_entry.docstatus == 1) + ) + ) if party_type == "Customer": q = q.where(journal_acc.credit_in_account_currency > 0) + else: + q = q.where(journal_acc.debit_in_account_currency > 0) + if order_list: q = q.where(journal_acc.reference_type == order_doctype) if include_unallocated: - q = q.where(journal_acc.reference_name.isin(order_list) - |(journal_acc.reference_name == '')) + q = q.where(journal_acc.reference_name.isin(order_list) | (journal_acc.reference_name == "")) else: q = q.where(journal_acc.reference_name.isin(order_list)) @@ -2194,69 +2202,119 @@ def get_advance_payment_entries( condition=None, ): - q = build_query(party_type, party, party_account, order_doctype, order_list, include_unallocated, against_all_orders, limit, condition) + q = build_query( + party_type, + party, + party_account, + order_doctype, + order_list, + include_unallocated, + against_all_orders, + limit, + condition, + ) payment_entries = q.run(as_dict=True) return list(payment_entries) -def build_query(party_type, party, party_account, order_doctype, order_list, include_unallocated, against_all_orders, limit, condition): - payment_type = "Receive" if party_type == "Customer" else "Pay" - payment_entry = frappe.qb.DocType('Payment Entry') - payment_ref = frappe.qb.DocType('Payment Entry Reference') - q = (frappe.qb.from_(payment_entry) +def build_query( + party_type, + party, + party_account, + order_doctype, + order_list, + include_unallocated, + against_all_orders, + limit, + condition, +): + payment_type = "Receive" if party_type == "Customer" else "Pay" + payment_entry = frappe.qb.DocType("Payment Entry") + payment_ref = frappe.qb.DocType("Payment Entry Reference") + + q = ( + frappe.qb.from_(payment_entry) .select( - ConstantColumn('Payment Entry').as_('reference_type'), - (payment_entry.name).as_('reference_name'), + ConstantColumn("Payment Entry").as_("reference_type"), + (payment_entry.name).as_("reference_name"), payment_entry.posting_date, - (payment_entry.remarks).as_('remarks') + (payment_entry.remarks).as_("remarks"), ) .where(payment_entry.payment_type == payment_type) .where(payment_entry.party_type == party_type) .where(payment_entry.party == party) .where(payment_entry.docstatus == 1) ) - + if party_type == "Customer": - q = q.select(payment_entry.paid_from_account_currency) - q = q.select(payment_entry.paid_from) - q = q.where(payment_entry.paid_from.isin(party_account)) + q = q.select(payment_entry.paid_from_account_currency) + q = q.select(payment_entry.paid_from) + q = q.where(payment_entry.paid_from.isin(party_account)) else: q = q.select(payment_entry.paid_to_account_currency) q = q.select(payment_entry.paid_to) - q = q.where(payment_entry.paid_to.isin(party_account)) + q = q.where(payment_entry.paid_to.isin(party_account)) if payment_type == "Receive": q = q.select(payment_entry.source_exchange_rate) - else: + else: q.select(payment_entry.target_exchange_rate) if include_unallocated: - q = q.select((payment_entry.unallocated_amount).as_('amount')) - q = q.where(payment_entry.unallocated_amount>0) + q = q.select((payment_entry.unallocated_amount).as_("amount")) + q = q.where(payment_entry.unallocated_amount > 0) if condition: q = q.where(payment_entry.company == condition["company"]) - q = q.where(payment_entry.posting_date >= condition["from_payment_date"]) if condition.get("from_payment_date") else q - q = q.where(payment_entry.posting_date <= condition["to_payment_date"]) if condition.get("to_payment_date") else q + q = ( + q.where(payment_entry.posting_date >= condition["from_payment_date"]) + if condition.get("from_payment_date") + else q + ) + q = ( + q.where(payment_entry.posting_date <= condition["to_payment_date"]) + if condition.get("to_payment_date") + else q + ) if condition.get("get_payments") == True: - q = q.where(payment_entry.cost_center == condition["cost_center"]) if condition.get("cost_center") else q - q = q.where(payment_entry.unallocated_amount >= condition["minimum_payment_amount"]) if condition.get("minimum_payment_amount") else q - q = q.where(payment_entry.unallocated_amount <= condition["maximum_payment_amount"]) if condition.get("maximum_payment_amount") else q + q = ( + q.where(payment_entry.cost_center == condition["cost_center"]) + if condition.get("cost_center") + else q + ) + q = ( + q.where(payment_entry.unallocated_amount >= condition["minimum_payment_amount"]) + if condition.get("minimum_payment_amount") + else q + ) + q = ( + q.where(payment_entry.unallocated_amount <= condition["maximum_payment_amount"]) + if condition.get("maximum_payment_amount") + else q + ) else: - q = q.where(payment_entry.total_debit >= condition["minimum_payment_amount"]) if condition.get("minimum_payment_amount") else q - q = q.where(payment_entry.total_debit <= condition["maximum_payment_amount"]) if condition.get("maximum_payment_amount") else q + q = ( + q.where(payment_entry.total_debit >= condition["minimum_payment_amount"]) + if condition.get("minimum_payment_amount") + else q + ) + q = ( + q.where(payment_entry.total_debit <= condition["maximum_payment_amount"]) + if condition.get("maximum_payment_amount") + else q + ) elif order_list or against_all_orders: q = q.inner_join(payment_ref).on(payment_entry.name == payment_ref.parent) q = q.select( - (payment_ref.allocated_amount).as_('amount'), - (payment_ref.name).as_('reference_row'), - (payment_ref.reference_name).as_('against_order'), - payment_ref.reference_doctype == order_doctype + (payment_ref.allocated_amount).as_("amount"), + (payment_ref.name).as_("reference_row"), + (payment_ref.reference_name).as_("against_order"), + payment_ref.reference_doctype == order_doctype, ) - + if order_list: q = q.where(payment_ref.reference_name.isin(order_list)) @@ -2862,9 +2920,12 @@ def validate_regional(doc): def validate_einvoice_fields(doc): pass -def make_advance_liability_entry(gl_entries, pe, allocated_amount, invoice, party_type): + +def make_advance_liability_entry( + gl_entries, pe, allocated_amount, invoice, party_type, references=False +): pe = frappe.get_doc("Payment Entry", pe) - if party_type=="Customer": + if party_type == "Customer": invoice = frappe.get_doc("Sales Invoice", invoice) account = pe.paid_from dr_or_cr = "debit" @@ -2880,53 +2941,55 @@ def make_advance_liability_entry(gl_entries, pe, allocated_amount, invoice, part against = invoice.credit_to party = invoice.supplier voucher_type = "Purchase Invoice" - gl_entries.append(invoice.get_gl_dict( - { - "account": account, - "party_type": party_type, - "party": party, - "due_date": invoice.due_date, - "against": against, - dr_or_cr: allocated_amount, - dr_or_cr + "_in_account_currency": allocated_amount, - rev: 0, - rev + "_in_account_currency": 0, - "against_voucher": invoice.return_against - if cint(invoice.is_return) and invoice.return_against - else invoice.name, - "against_voucher_type": invoice.doctype, - "cost_center": invoice.cost_center, - "project": invoice.project, - "voucher_type": voucher_type, - "voucher_no": invoice.name - }, - invoice.party_account_currency, - item=invoice, - )) - - (dr_or_cr, rev) = ("credit", "debit") if party_type=="Customer" else ("debit", "credit") - gl_entries.append(invoice.get_gl_dict( - { - "account": against, - "party_type": party_type, - "party": party, - "due_date": invoice.due_date, - "against": account, - dr_or_cr: allocated_amount, - dr_or_cr + "_in_account_currency": allocated_amount, - rev: 0, - rev + "_in_account_currency": 0, - "against_voucher": invoice.return_against - if cint(invoice.is_return) and invoice.return_against - else invoice.name, - "against_voucher_type": invoice.doctype, - "cost_center": invoice.cost_center, - "project": invoice.project, - "voucher_type": voucher_type, - "voucher_no": invoice.name - }, - invoice.party_account_currency, - item=invoice, - )) - + gl_entries.append( + invoice.get_gl_dict( + { + "account": account, + "party_type": party_type, + "party": party, + "due_date": invoice.due_date, + "against": against, + dr_or_cr: allocated_amount, + dr_or_cr + "_in_account_currency": allocated_amount, + rev: 0, + rev + "_in_account_currency": 0, + "against_voucher": invoice.return_against + if cint(invoice.is_return) and invoice.return_against + else invoice.name, + "against_voucher_type": invoice.doctype, + "cost_center": invoice.cost_center, + "project": invoice.project, + "voucher_type": voucher_type, + "voucher_no": invoice.name, + }, + invoice.party_account_currency, + item=invoice, + ) + ) + (dr_or_cr, rev) = ("credit", "debit") if party_type == "Customer" else ("debit", "credit") + gl_entries.append( + invoice.get_gl_dict( + { + "account": against, + "party_type": party_type, + "party": party, + "due_date": invoice.due_date, + "against": account, + dr_or_cr: allocated_amount, + dr_or_cr + "_in_account_currency": allocated_amount, + rev: 0, + rev + "_in_account_currency": 0, + "against_voucher": invoice.return_against + if cint(invoice.is_return) and invoice.return_against + else invoice.name, + "against_voucher_type": invoice.doctype, + "cost_center": invoice.cost_center, + "project": invoice.project, + "voucher_type": "Payment Entry" if references else voucher_type, + "voucher_no": pe.name if references else invoice.name, + }, + invoice.party_account_currency, + item=invoice, + ) + ) diff --git a/erpnext/selling/doctype/customer/customer.js b/erpnext/selling/doctype/customer/customer.js index 6dac692747..35cf8c53ee 100644 --- a/erpnext/selling/doctype/customer/customer.js +++ b/erpnext/selling/doctype/customer/customer.js @@ -34,8 +34,8 @@ frappe.ui.form.on("Customer", { filters: filters } }); - - frm.set_query('advances_received_account', 'accounts', function (doc, cdt, cdn) { + + frm.set_query('advance_account', 'accounts', function (doc, cdt, cdn) { var d = locals[cdt][cdn]; return { filters: { @@ -46,16 +46,6 @@ frappe.ui.form.on("Customer", { } }); - frm.set_query('advances_paid_account', 'accounts', function (doc, cdt, cdn) { - var d = locals[cdt][cdn]; - return { - filters: { - "root_type": 'Asset', - "company": d.company, - "is_group": 0 - } - } - }); if (frm.doc.__islocal == 1) { frm.set_value("represents_company", ""); diff --git a/erpnext/selling/doctype/customer/customer.json b/erpnext/selling/doctype/customer/customer.json index 0050279a4b..46b10351fd 100644 --- a/erpnext/selling/doctype/customer/customer.json +++ b/erpnext/selling/doctype/customer/customer.json @@ -337,12 +337,12 @@ "label": "Default Accounts" }, { - "description": "Mention if a non-standard receivable account", - "fieldname": "accounts", - "fieldtype": "Table", - "label": "Accounts", - "options": "Party Account" - }, + "description": "Mention if non-standard Receivable account", + "fieldname": "accounts", + "fieldtype": "Table", + "label": "Accounts", + "options": "Party Account" + }, { "fieldname": "credit_limit_section", "fieldtype": "Section Break", @@ -568,7 +568,7 @@ "link_fieldname": "party" } ], - "modified": "2023-05-29 14:29:17.789578", + "modified": "2023-06-05 13:48:46.152659", "modified_by": "Administrator", "module": "Selling", "name": "Customer", diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js index 81919af4e1..fb0ee7f07a 100644 --- a/erpnext/setup/doctype/company/company.js +++ b/erpnext/setup/doctype/company/company.js @@ -227,8 +227,7 @@ erpnext.company.setup_queries = function(frm) { ["asset_received_but_not_billed", {"account_type": "Asset Received But Not Billed"}], ["unrealized_profit_loss_account", {"root_type": ["in", ["Liability", "Asset"]]}], ["default_provisional_account", {"root_type": ["in", ["Liability", "Asset"]]}], - ["default_advances_received_account", {"root_type": "Liability"}], - ["default_advances_paid_account", {"root_type": "Asset"}], + ["default_advance_account", {"root_type": ["in", ["Liability", "Asset"]]}], ], function(i, v) { erpnext.company.set_custom_query(frm, v); }); diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json index 369e139eef..5b3d3bb086 100644 --- a/erpnext/setup/doctype/company/company.json +++ b/erpnext/setup/doctype/company/company.json @@ -72,9 +72,8 @@ "default_finance_book", "advance_payments_section", "book_advance_payments_as_liability", - "default_advances_received_account", + "default_advance_account", "column_break_cui0", - "default_advances_paid_account", "auto_accounting_for_stock_settings", "enable_perpetual_inventory", "enable_provisional_accounting_for_non_stock_items", @@ -711,25 +710,17 @@ "fieldtype": "Section Break", "label": "Advance Payments" }, - { - "depends_on": "eval:doc.book_advance_payments_as_liability", - "fieldname": "default_advances_received_account", - "fieldtype": "Link", - "label": "Default Advances Received Account", - "mandatory_depends_on": "book_advance_payments_as_liability", - "options": "Account" - }, - { - "depends_on": "eval:doc.book_advance_payments_as_liability", - "fieldname": "default_advances_paid_account", - "fieldtype": "Link", - "label": "Default Advances Paid Account", - "mandatory_depends_on": "book_advance_payments_as_liability", - "options": "Account" - }, { "fieldname": "column_break_cui0", "fieldtype": "Column Break" + }, + { + "depends_on": "eval:doc.book_advance_payments_as_liability", + "fieldname": "default_advance_account", + "fieldtype": "Link", + "label": "Default Account", + "mandatory_depends_on": "book_advance_payments_as_liability", + "options": "Account" } ], "icon": "fa fa-building", @@ -737,7 +728,7 @@ "image_field": "company_logo", "is_tree": 1, "links": [], - "modified": "2023-06-02 13:11:41.939016", + "modified": "2023-06-05 14:12:37.946451", "modified_by": "Administrator", "module": "Setup", "name": "Company", diff --git a/erpnext/setup/doctype/customer_group/customer_group.js b/erpnext/setup/doctype/customer_group/customer_group.js index ef556c774d..3a71b43ee8 100644 --- a/erpnext/setup/doctype/customer_group/customer_group.js +++ b/erpnext/setup/doctype/customer_group/customer_group.js @@ -37,7 +37,7 @@ cur_frm.fields_dict['accounts'].grid.get_field('account').get_query = function(d } } -cur_frm.fields_dict['accounts'].grid.get_field('advances_received_account').get_query = function(doc, cdt, cdn) { +cur_frm.fields_dict['accounts'].grid.get_field('advance_account').get_query = function(doc, cdt, cdn) { var d = locals[cdt][cdn]; return { filters: { @@ -46,15 +46,4 @@ cur_frm.fields_dict['accounts'].grid.get_field('advances_received_account').get_ "is_group": 0 } } -} - -cur_frm.fields_dict['accounts'].grid.get_field('advances_paid_account').get_query = function(doc, cdt, cdn) { - var d = locals[cdt][cdn]; - return { - filters: { - "root_type": 'Asset', - "company": d.company, - "is_group": 0 - } - } } \ No newline at end of file diff --git a/erpnext/setup/doctype/supplier_group/supplier_group.js b/erpnext/setup/doctype/supplier_group/supplier_group.js index e75030d441..58ab7fa928 100644 --- a/erpnext/setup/doctype/supplier_group/supplier_group.js +++ b/erpnext/setup/doctype/supplier_group/supplier_group.js @@ -36,3 +36,14 @@ cur_frm.fields_dict['accounts'].grid.get_field('account').get_query = function(d } }; }; + +cur_frm.fields_dict['accounts'].grid.get_field('advance_account').get_query = function(doc, cdt, cdn) { + var d = locals[cdt][cdn]; + return { + filters: { + "root_type": 'Asset', + "company": d.company, + "is_group": 0 + } + } +}; \ No newline at end of file From b65e58c1ae215215a73ad92e24791e9c6090cfa5 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Thu, 8 Jun 2023 18:15:37 +0530 Subject: [PATCH 07/99] test: add tests for advance liability entries Add Sales and Purchase Invoice Tests to check if GL entries and Outstanding Amount are generated correctly when advance entries are recorded as liability. Few changes to return value of added column in Payment Entry References. --- .../doctype/payment_entry/payment_entry.py | 8 ++- .../payment_entry_reference.json | 2 +- .../purchase_invoice/test_purchase_invoice.py | 60 +++++++++++++++--- .../sales_invoice/test_sales_invoice.py | 63 +++++++++++++++---- erpnext/accounts/utils.py | 1 + 5 files changed, 113 insertions(+), 21 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 291f8e4411..236a78a370 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -1741,6 +1741,9 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre if reference_doctype in ("Sales Invoice", "Purchase Invoice"): outstanding_amount = ref_doc.get("outstanding_amount") + account = ( + ref_doc.get("debit_to") if reference_doctype == "Sales Invoice" else ref_doc.get("credit_to") + ) else: outstanding_amount = flt(total_amount) - flt(ref_doc.get("advance_paid")) @@ -1748,7 +1751,7 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre # Get the exchange rate based on the posting date of the ref doc. exchange_rate = get_exchange_rate(party_account_currency, company_currency, ref_doc.posting_date) - return frappe._dict( + res = frappe._dict( { "due_date": ref_doc.get("due_date"), "total_amount": flt(total_amount), @@ -1757,6 +1760,9 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre "bill_no": ref_doc.get("bill_no"), } ) + if account: + res.update({"account": account}) + return res @frappe.whitelist() diff --git a/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.json b/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.json index c318ea53bd..12aa0b520e 100644 --- a/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.json +++ b/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.json @@ -113,7 +113,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2023-06-07 14:35:06.166907", + "modified": "2023-06-08 07:40:38.487874", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Entry Reference", diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index a6d7df6971..ebb4970b3d 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -1662,22 +1662,66 @@ class TestPurchaseInvoice(unittest.TestCase, StockTestMixin): self.assertTrue(return_pi.docstatus == 1) + def test_advance_entries_as_liability(self): + from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_entry + from erpnext.accounts.party import get_party_account + + frappe.db.set_value( + "Company", + "_Test Company", + {"book_advance_payments_as_liability": 1, "default_advance_account": "Debtors - _TC"}, + ) + pe = create_payment_entry( + company="_Test Company", + payment_type="Pay", + party_type="Supplier", + party="_Test Supplier", + paid_from="Cash - _TC", + paid_to=get_party_account("Supplier", "_Test Supplier", "_Test Company", is_advance=True), + paid_amount=1000, + ) + pe.submit() + + pi = make_purchase_invoice( + company="_Test Company", customer="_Test Supplier", do_not_save=True, do_not_submit=True + ) + pi.base_grand_total = 100 + pi.grand_total = 100 + pi.set_advances() + self.assertEqual(pi.advances[0].allocated_amount, 100) + pi.advances[0].allocated_amount = 50 + pi.advances = [pi.advances[0]] + pi.save() + pi.submit() + expected_gle = [ + ["Creditors - _TC", 50, 100], + ["Debtors - _TC", 0.0, 50], + ["Stock Received But Not Billed - _TC", 100, 0.0], + ] + + check_gl_entries(self, pi.name, expected_gle, nowdate()) + self.assertEqual(pi.outstanding_amount, 200) + def check_gl_entries(doc, voucher_no, expected_gle, posting_date): - gl_entries = frappe.db.sql( - """select account, debit, credit, posting_date - from `tabGL Entry` - where voucher_type='Purchase Invoice' and voucher_no=%s and posting_date >= %s - order by posting_date asc, account asc""", - (voucher_no, posting_date), - as_dict=1, + gl = frappe.qb.DocType("GL Entry") + q = ( + frappe.qb.from_(gl) + .select(gl.account, gl.debit, gl.credit, gl.posting_date) + .where( + (gl.voucher_type == "Sales Invoice") + & (gl.voucher_no == voucher_no) + & (gl.posting_date >= posting_date) + & (gl.is_cancelled == 0) + ) + .orderby(gl.posting_date, gl.account) ) + gl_entries = q.run(as_dict=True) for i, gle in enumerate(gl_entries): doc.assertEqual(expected_gle[i][0], gle.account) doc.assertEqual(expected_gle[i][1], gle.debit) doc.assertEqual(expected_gle[i][2], gle.credit) - doc.assertEqual(getdate(expected_gle[i][3]), gle.posting_date) def create_tax_witholding_category(category_name, company, account): diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index dbc277044a..d10fa05a19 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -3313,6 +3313,46 @@ class TestSalesInvoice(unittest.TestCase): ) self.assertRaises(frappe.ValidationError, si.submit) + def test_advance_entries_as_liability(self): + from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_entry + from erpnext.accounts.party import get_party_account + + frappe.db.set_value( + "Company", + "_Test Company", + {"book_advance_payments_as_liability": 1, "default_advance_account": "Creditors - _TC"}, + ) + pe = create_payment_entry( + company="_Test Company", + payment_type="Receive", + party_type="Customer", + party="_Test Customer", + paid_from=get_party_account("Customer", "_Test Customer", "_Test Company", is_advance=True), + paid_to="Cash - _TC", + paid_amount=1000, + ) + pe.submit() + + si = create_sales_invoice( + company="_Test Company", customer="_Test Customer", do_not_save=True, do_not_submit=True + ) + si.base_grand_total = 100 + si.grand_total = 100 + si.set_advances() + self.assertEqual(si.advances[0].allocated_amount, 100) + si.advances[0].allocated_amount = 50 + si.advances = [si.advances[0]] + si.save() + si.submit() + expected_gle = [ + ["Creditors - _TC", 50, 0.0], + ["Debtors - _TC", 100, 50], + ["Sales - _TC", 0.0, 100], + ] + + check_gl_entries(self, si.name, expected_gle, nowdate()) + self.assertEqual(si.outstanding_amount, 50) + def get_sales_invoice_for_e_invoice(): si = make_sales_invoice_for_ewaybill() @@ -3350,23 +3390,24 @@ def get_sales_invoice_for_e_invoice(): def check_gl_entries(doc, voucher_no, expected_gle, posting_date): - gl_entries = frappe.db.sql( - """select account, debit, credit, posting_date - from `tabGL Entry` - where voucher_type='Sales Invoice' and voucher_no=%s and posting_date >= %s - and is_cancelled = 0 - order by posting_date asc, account asc""", - (voucher_no, posting_date), - as_dict=1, - debug=True, + gl = frappe.qb.DocType("GL Entry") + q = ( + frappe.qb.from_(gl) + .select(gl.account, gl.debit, gl.credit, gl.posting_date) + .where( + (gl.voucher_type == "Sales Invoice") + & (gl.voucher_no == voucher_no) + & (gl.posting_date >= posting_date) + & (gl.is_cancelled == 0) + ) + .orderby(gl.posting_date, gl.account) ) + gl_entries = q.run(as_dict=True) for i, gle in enumerate(gl_entries): - print(i, gle) doc.assertEqual(expected_gle[i][0], gle.account) doc.assertEqual(expected_gle[i][1], gle.debit) doc.assertEqual(expected_gle[i][2], gle.credit) - doc.assertEqual(getdate(expected_gle[i][3]), gle.posting_date) def create_sales_invoice(**args): diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 506279c1e5..c1d365304f 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -625,6 +625,7 @@ def update_reference_in_payment_entry( if not d.exchange_gain_loss else payment_entry.get_exchange_rate(), "exchange_gain_loss": d.exchange_gain_loss, # only populated from invoice in case of advance allocation + "account": d.account, } if d.voucher_detail_no: From 7591f1010b761adcb647f92988b3886bc9c6c40c Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Mon, 12 Jun 2023 11:06:03 +0530 Subject: [PATCH 08/99] fix: Make get party account method return a list instead of a single default account. --- .../doctype/party_account/party_account.json | 2 +- .../doctype/payment_entry/payment_entry.js | 1 - .../doctype/payment_entry/payment_entry.py | 61 ++++++++----------- .../payment_reconciliation.js | 20 +----- .../payment_reconciliation.json | 9 ++- .../purchase_invoice/test_purchase_invoice.py | 2 +- .../sales_invoice/test_sales_invoice.py | 2 +- erpnext/accounts/party.py | 8 +-- erpnext/buying/doctype/supplier/supplier.js | 4 +- erpnext/controllers/accounts_controller.py | 9 +-- erpnext/selling/doctype/customer/customer.js | 6 +- .../doctype/customer_group/customer_group.js | 57 ++++++++--------- .../doctype/supplier_group/supplier_group.js | 57 ++++++++--------- 13 files changed, 106 insertions(+), 132 deletions(-) diff --git a/erpnext/accounts/doctype/party_account/party_account.json b/erpnext/accounts/doctype/party_account/party_account.json index 6ac6e56086..7e345d84ea 100644 --- a/erpnext/accounts/doctype/party_account/party_account.json +++ b/erpnext/accounts/doctype/party_account/party_account.json @@ -36,7 +36,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2023-06-05 14:15:42.053150", + "modified": "2023-06-06 14:15:42.053150", "modified_by": "Administrator", "module": "Accounts", "name": "Party Account", diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index 1f0e45fc11..4cebb7b2fc 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -712,7 +712,6 @@ frappe.ui.form.on('Payment Entry', { if(r.message) { var total_positive_outstanding = 0; var total_negative_outstanding = 0; - console.log(r.message); $.each(r.message, function(i, d) { var c = frm.add_child("references"); c.reference_doctype = d.voucher_type; diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 236a78a370..5249f171fb 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -60,6 +60,7 @@ class PaymentEntry(AccountsController): def validate(self): self.setup_party_account_field() self.set_missing_values() + self.set_liability_account() self.set_missing_ref_details() self.validate_payment_type() self.validate_party_details() @@ -86,35 +87,34 @@ class PaymentEntry(AccountsController): def on_submit(self): if self.difference_amount: frappe.throw(_("Difference Amount must be zero")) - book_advance_payments_as_liability = frappe.get_value( - "Company", {"company_name": self.company}, "book_advance_payments_as_liability" - ) - if book_advance_payments_as_liability: - self.get_liability_account() self.make_gl_entries() self.update_outstanding_amounts() self.update_advance_paid() self.update_payment_schedule() self.set_status() - def get_liability_account(self): - liability_account = get_party_account(self.party_type, self.party, self.company, is_advance=True) - if self.party_type == "Customer": - msg = "Book Advance Payments as Liability option is chosen. Paid From account changed from {0} to {1}.".format( - frappe.bold(self.paid_from), - frappe.bold(liability_account), - ) - frappe.db.set_value("Payment Entry", self.name, "paid_from", liability_account) - self.paid_from = liability_account - else: - msg = "Book Advance Payments as Liability option is chosen. Paid To account changed from {0} to {1}.".format( - frappe.bold(self.paid_to), - frappe.bold(liability_account), - ) - frappe.db.set_value("Payment Entry", self.name, "paid_to", liability_account) - self.paid_to = liability_account - frappe.msgprint(_(msg), title="Warning", indicator="orange") - return liability_account + def set_liability_account(self): + book_advance_payments_as_liability = frappe.get_value( + "Company", {"company_name": self.company}, "book_advance_payments_as_liability" + ) + if not book_advance_payments_as_liability: + return + root_type = frappe.get_value( + "Account", {"name": self.party_account, "company": self.company}, "root_type" + ) + if (root_type == "Liability" and self.party_type == "Customer") or ( + root_type == "Asset" and self.party_type == "Supplier" + ): + return + liability_account = get_party_account( + self.party_type, self.party, self.company, include_advance=True + )[1] + self.set(self.party_account_field, liability_account) + msg = "Book Advance Payments as Liability option is chosen. Paid From account changed from {0} to {1}.".format( + frappe.bold(self.party_account), + frappe.bold(liability_account), + ) + frappe.msgprint(_(msg), alert=True) def on_cancel(self): self.ignore_linked_doctypes = ( @@ -354,13 +354,6 @@ class PaymentEntry(AccountsController): elif self.party_type == "Employee": ref_party_account = ref_doc.payable_account - if ref_party_account != self.party_account: - frappe.throw( - _("{0} {1} is associated with {2}, but Party Account is {3}").format( - d.reference_doctype, d.reference_name, ref_party_account, self.party_account - ) - ) - if ref_doc.doctype == "Purchase Invoice" and ref_doc.get("on_hold"): frappe.throw( _("{0} {1} is on hold").format(d.reference_doctype, d.reference_name), @@ -893,11 +886,9 @@ class PaymentEntry(AccountsController): if self.party_account: if self.payment_type == "Receive": against_account = self.paid_to - self.party_account = self.paid_from dr_or_cr = "credit" else: against_account = self.paid_from - self.party_account = self.paid_to dr_or_cr = "debit" party_dict = self.get_gl_dict( @@ -927,8 +918,8 @@ class PaymentEntry(AccountsController): { dr_or_cr: allocated_amount_in_company_currency, dr_or_cr + "_in_account_currency": d.allocated_amount, - "against_voucher_type": d.reference_doctype, - "against_voucher": d.reference_name, + "against_voucher_type": "Payment Entry", + "against_voucher": self.name, } ) @@ -973,7 +964,7 @@ class PaymentEntry(AccountsController): args_dict[dr_or_cr] = 0 args_dict[dr_or_cr + "_in_account_currency"] = 0 dr_or_cr = "debit" if dr_or_cr == "credit" else "credit" - args_dict["account"] = self.get_liability_account() + args_dict["account"] = self.party_account args_dict[dr_or_cr] = invoice.allocated_amount args_dict[dr_or_cr + "_in_account_currency"] = invoice.allocated_amount gle = self.get_gl_dict( diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js index d8743bb69e..a40e6f274f 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js @@ -134,32 +134,18 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo this.frm.trigger("clear_child_tables"); if (!this.frm.doc.receivable_payable_account && this.frm.doc.party_type && this.frm.doc.party) { - frappe.call({ - method: "erpnext.accounts.party.get_party_account", - args: { - company: this.frm.doc.company, - party_type: this.frm.doc.party_type, - party: this.frm.doc.party - }, - callback: (r) => { - if (!r.exc && r.message) { - this.frm.set_value("receivable_payable_account", r.message); - } - this.frm.refresh(); - } - }); - frappe.call({ method: "erpnext.accounts.party.get_party_account", args: { company: this.frm.doc.company, party_type: this.frm.doc.party_type, party: this.frm.doc.party, - is_advance: 1 + include_advance: 1 }, callback: (r) => { if (!r.exc && r.message) { - this.frm.set_value("default_advance_account", r.message); + this.frm.set_value("receivable_payable_account", r.message[0]); + this.frm.set_value("default_advance_account", r.message[1]); } this.frm.refresh(); } diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json index 0e166ffd5d..5f6c7034ed 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json @@ -7,8 +7,8 @@ "field_order": [ "company", "party_type", - "party", "column_break_4", + "party", "receivable_payable_account", "default_advance_account", "col_break1", @@ -188,20 +188,19 @@ "options": "Cost Center" }, { - "depends_on": "eval:doc.party_type", + "depends_on": "eval:doc.party", "fieldname": "default_advance_account", "fieldtype": "Link", "label": "Default Advance Account", "mandatory_depends_on": "doc.party_type", - "options": "Account", - "reqd": 1 + "options": "Account" } ], "hide_toolbar": 1, "icon": "icon-resize-horizontal", "issingle": 1, "links": [], - "modified": "2023-06-05 20:09:58.925427", + "modified": "2023-06-09 13:02:48.718362", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Reconciliation", diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index ebb4970b3d..9ade46e045 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -1677,7 +1677,7 @@ class TestPurchaseInvoice(unittest.TestCase, StockTestMixin): party_type="Supplier", party="_Test Supplier", paid_from="Cash - _TC", - paid_to=get_party_account("Supplier", "_Test Supplier", "_Test Company", is_advance=True), + paid_to="Creditors - _TC", paid_amount=1000, ) pe.submit() diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index d10fa05a19..364bf6898a 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -3327,7 +3327,7 @@ class TestSalesInvoice(unittest.TestCase): payment_type="Receive", party_type="Customer", party="_Test Customer", - paid_from=get_party_account("Customer", "_Test Customer", "_Test Company", is_advance=True), + paid_from="Debtors - _TC", paid_to="Cash - _TC", paid_amount=1000, ) diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index 782c41e529..ccd54b344f 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -365,7 +365,7 @@ def set_account_and_due_date( @frappe.whitelist() -def get_party_account(party_type, party=None, company=None, is_advance=False): +def get_party_account(party_type, party=None, company=None, include_advance=False): """Returns the account for the given `party`. Will first search in party (Customer / Supplier) record, if not found, will search in group (Customer Group / Supplier Group), @@ -380,9 +380,6 @@ def get_party_account(party_type, party=None, company=None, is_advance=False): return frappe.get_cached_value("Company", company, default_account_name) - if is_advance and party_type in ["Customer", "Supplier"]: - return get_party_advance_account(party_type, party, company) - account = frappe.db.get_value( "Party Account", {"parenttype": party_type, "parent": party, "company": company}, "account" ) @@ -409,6 +406,9 @@ def get_party_account(party_type, party=None, company=None, is_advance=False): if (account and account_currency != existing_gle_currency) or not account: account = get_party_gle_account(party_type, party, company) + if include_advance and party_type in ["Customer", "Supplier"]: + advance_account = get_party_advance_account(party_type, party, company) + return [account, advance_account] return account diff --git a/erpnext/buying/doctype/supplier/supplier.js b/erpnext/buying/doctype/supplier/supplier.js index 6da47c5c69..50f9bb6cb9 100644 --- a/erpnext/buying/doctype/supplier/supplier.js +++ b/erpnext/buying/doctype/supplier/supplier.js @@ -8,7 +8,7 @@ frappe.ui.form.on("Supplier", { frm.set_value("represents_company", ""); } frm.set_query('account', 'accounts', function (doc, cdt, cdn) { - var d = locals[cdt][cdn]; + let d = locals[cdt][cdn]; return { filters: { 'account_type': 'Payable', @@ -19,7 +19,7 @@ frappe.ui.form.on("Supplier", { }); frm.set_query('advance_account', 'accounts', function (doc, cdt, cdn) { - var d = locals[cdt][cdn]; + let d = locals[cdt][cdn]; return { filters: { "root_type": 'Asset', diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 0589f4a89a..d8c4135292 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -885,18 +885,15 @@ class AccountsController(TransactionBase): amount_field = "credit_in_account_currency" order_field = "sales_order" order_doctype = "Sales Order" - party_account = [ - get_party_account(party_type, party=party, company=self.company, is_advance=True) - ] else: party_type = "Supplier" party = self.supplier amount_field = "debit_in_account_currency" order_field = "purchase_order" order_doctype = "Purchase Order" - party_account = [ - get_party_account(party_type, party=party, company=self.company, is_advance=True) - ] + party_account = [ + get_party_account(party_type, party=party, company=self.company, include_advance=True)[1] + ] order_list = list(set(d.get(order_field) for d in self.get("items") if d.get(order_field))) diff --git a/erpnext/selling/doctype/customer/customer.js b/erpnext/selling/doctype/customer/customer.js index 35cf8c53ee..8f828807a5 100644 --- a/erpnext/selling/doctype/customer/customer.js +++ b/erpnext/selling/doctype/customer/customer.js @@ -20,8 +20,8 @@ frappe.ui.form.on("Customer", { frm.set_query('customer_group', {'is_group': 0}); frm.set_query('default_price_list', { 'selling': 1}); frm.set_query('account', 'accounts', function(doc, cdt, cdn) { - var d = locals[cdt][cdn]; - var filters = { + let d = locals[cdt][cdn]; + let filters = { 'account_type': 'Receivable', 'company': d.company, "is_group": 0 @@ -36,7 +36,7 @@ frappe.ui.form.on("Customer", { }); frm.set_query('advance_account', 'accounts', function (doc, cdt, cdn) { - var d = locals[cdt][cdn]; + let d = locals[cdt][cdn]; return { filters: { "root_type": 'Liability', diff --git a/erpnext/setup/doctype/customer_group/customer_group.js b/erpnext/setup/doctype/customer_group/customer_group.js index 3a71b43ee8..ed9893346c 100644 --- a/erpnext/setup/doctype/customer_group/customer_group.js +++ b/erpnext/setup/doctype/customer_group/customer_group.js @@ -16,34 +16,35 @@ cur_frm.cscript.set_root_readonly = function(doc) { } } -//get query select Customer Group -cur_frm.fields_dict['parent_customer_group'].get_query = function(doc,cdt,cdn) { - return { - filters: { - 'is_group': 1, - 'name': ['!=', cur_frm.doc.customer_group_name] - } - } -} +frappe.ui.form.on("Customer Group", { + setup: function(frm){ + frm.set_query('parent_customer_group', function (doc) { + return { + filters: { + 'is_group': 1, + 'name': ['!=', cur_frm.doc.customer_group_name] + } + } + }); -cur_frm.fields_dict['accounts'].grid.get_field('account').get_query = function(doc, cdt, cdn) { - var d = locals[cdt][cdn]; - return { - filters: { - "account_type": 'Receivable', - "company": d.company, - "is_group": 0 - } - } -} + frm.set_query('account', 'accounts', function (doc, cdt, cdn) { + return { + filters: { + "account_type": 'Receivable', + "company": locals[cdt][cdn].company, + "is_group": 0 + } + } + }); -cur_frm.fields_dict['accounts'].grid.get_field('advance_account').get_query = function(doc, cdt, cdn) { - var d = locals[cdt][cdn]; - return { - filters: { - "root_type": 'Liability', - "company": d.company, - "is_group": 0 - } + frm.set_query('advance_account', 'accounts', function (doc, cdt, cdn) { + return { + filters: { + "root_type": 'Liability', + "company": locals[cdt][cdn].company, + "is_group": 0 + } + } + }); } -} \ No newline at end of file +}); diff --git a/erpnext/setup/doctype/supplier_group/supplier_group.js b/erpnext/setup/doctype/supplier_group/supplier_group.js index 58ab7fa928..ac5904f4d2 100644 --- a/erpnext/setup/doctype/supplier_group/supplier_group.js +++ b/erpnext/setup/doctype/supplier_group/supplier_group.js @@ -16,34 +16,35 @@ cur_frm.cscript.set_root_readonly = function(doc) { } }; -// get query select Customer Group -cur_frm.fields_dict['parent_supplier_group'].get_query = function() { - return { - filters: { - 'is_group': 1, - 'name': ['!=', cur_frm.doc.supplier_group_name] - } - }; -}; +frappe.ui.form.on("Supplier Group", { + setup: function(frm){ + frm.set_query('parent_supplier_group', function (doc) { + return { + filters: { + 'is_group': 1, + 'name': ['!=', cur_frm.doc.supplier_group_name] + } + } + }); -cur_frm.fields_dict['accounts'].grid.get_field('account').get_query = function(doc, cdt, cdn) { - var d = locals[cdt][cdn]; - return { - filters: { - 'account_type': 'Payable', - 'company': d.company, - "is_group": 0 - } - }; -}; + frm.set_query('account', 'accounts', function (doc, cdt, cdn) { + return { + filters: { + 'account_type': 'Payable', + 'company': locals[cdt][cdn].company, + "is_group": 0 + } + } + }); -cur_frm.fields_dict['accounts'].grid.get_field('advance_account').get_query = function(doc, cdt, cdn) { - var d = locals[cdt][cdn]; - return { - filters: { - "root_type": 'Asset', - "company": d.company, - "is_group": 0 - } + frm.set_query('advance_account', 'accounts', function (doc, cdt, cdn) { + return { + filters: { + "root_type": 'Asset', + "company": locals[cdt][cdn].company, + "is_group": 0 + } + } + }); } -}; \ No newline at end of file +}); From a06017c2c3b0db520c86229619cd1c173d0a234c Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Mon, 12 Jun 2023 15:24:53 +0530 Subject: [PATCH 09/99] fix: Use advance account from Reconciliation document for fetching Payment Entries --- .../doctype/payment_entry/payment_entry.py | 8 +++- .../payment_reconciliation.js | 2 +- .../payment_reconciliation.py | 11 +---- .../purchase_invoice/purchase_invoice.py | 19 +++++---- .../purchase_invoice/test_purchase_invoice.py | 3 +- .../doctype/sales_invoice/sales_invoice.py | 19 +++++---- .../sales_invoice/test_sales_invoice.py | 6 ++- erpnext/accounts/party.py | 7 +++- erpnext/controllers/accounts_controller.py | 40 ++++++++++++++----- erpnext/setup/doctype/company/company.js | 3 +- erpnext/setup/doctype/company/company.json | 17 ++++++-- 11 files changed, 91 insertions(+), 44 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 5249f171fb..7012eacb61 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -106,6 +106,12 @@ class PaymentEntry(AccountsController): root_type == "Asset" and self.party_type == "Supplier" ): return + if self.unallocated_amount == 0: + for d in self.references: + if d.reference_doctype in ["Sales Order", "Purchase Order"]: + break + else: + return liability_account = get_party_account( self.party_type, self.party, self.company, include_advance=True )[1] @@ -1694,7 +1700,7 @@ def get_outstanding_on_journal_entry(name): @frappe.whitelist() def get_reference_details(reference_doctype, reference_name, party_account_currency): - total_amount = outstanding_amount = exchange_rate = None + total_amount = outstanding_amount = exchange_rate = account = None ref_doc = frappe.get_doc(reference_doctype, reference_name) company_currency = ref_doc.get("company_currency") or erpnext.get_company_currency( diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js index a40e6f274f..e46c81b9ed 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js @@ -34,7 +34,7 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo filters: { "company": this.frm.doc.company, "is_group": 0, - "root_type": (this.frm.party_type == 'Customer') ? "Liability": "Asset" + "root_type": this.frm.doc.party_type == 'Customer' ? "Liability": "Asset" } }; }); diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index e7d7f2c1e4..e5fb3df1a4 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -60,16 +60,7 @@ class PaymentReconciliation(Document): self.add_payment_entries(non_reconciled_payments) def get_payment_entries(self): - advance_accounts = [] - if self.party_type == "Customer": - advance_accounts = frappe.db.get_list( - "Account", filters={"root_type": "Liability", "company": self.company}, pluck="name" - ) - elif self.party_type == "Supplier": - advance_accounts = frappe.db.get_list( - "Account", filters={"root_type": "Asset", "company": self.company}, pluck="name" - ) - party_account = [self.receivable_payable_account] + advance_accounts + party_account = [self.receivable_payable_account, self.default_advance_account] order_doctype = "Sales Order" if self.party_type == "Customer" else "Purchase Order" condition = frappe._dict( diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index dfea8e9049..0f3b12b3ef 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -33,8 +33,10 @@ from erpnext.accounts.utils import get_account_currency, get_fiscal_year from erpnext.assets.doctype.asset.asset import get_asset_account, is_cwip_accounting_enabled from erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account from erpnext.buying.utils import check_on_hold_or_closed_status -from erpnext.controllers.accounts_controller import validate_account_head -from erpnext.controllers.accounts_controller import make_advance_liability_entry +from erpnext.controllers.accounts_controller import ( + check_advance_liability_entry, + validate_account_head, +) from erpnext.controllers.buying_controller import BuyingController from erpnext.stock import get_warehouse_account_map from erpnext.stock.doctype.purchase_receipt.purchase_receipt import ( @@ -581,11 +583,14 @@ class PurchaseInvoice(BuyingController): gl_entries = [] self.make_supplier_gl_entry(gl_entries) - - advance_payments_as_liability = frappe.db.get_value("Company", {"company_name": self.company}, "book_advance_payments_as_liability") - if advance_payments_as_liability: - for advance_entry in self.advances: - make_advance_liability_entry(gl_entries, advance_entry.reference_name, advance_entry.allocated_amount, invoice=self.name, party_type="Supplier") + + check_advance_liability_entry( + gl_entries, + company=self.company, + advances=self.advances, + invoice=self.name, + party_type="Supplier", + ) self.make_item_gl_entries(gl_entries) self.make_precision_loss_gl_entry(gl_entries) diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index 9ade46e045..c15692b751 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -1669,7 +1669,7 @@ class TestPurchaseInvoice(unittest.TestCase, StockTestMixin): frappe.db.set_value( "Company", "_Test Company", - {"book_advance_payments_as_liability": 1, "default_advance_account": "Debtors - _TC"}, + {"book_advance_payments_as_liability": 1, "default_advance_paid_account": "Debtors - _TC"}, ) pe = create_payment_entry( company="_Test Company", @@ -1722,6 +1722,7 @@ def check_gl_entries(doc, voucher_no, expected_gle, posting_date): doc.assertEqual(expected_gle[i][0], gle.account) doc.assertEqual(expected_gle[i][1], gle.debit) doc.assertEqual(expected_gle[i][2], gle.credit) + doc.assertEqual(getdate(expected_gle[i][3]), gle.posting_date) def create_tax_witholding_category(category_name, company, account): diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 419628eaff..d43e6e8c59 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -32,8 +32,10 @@ from erpnext.assets.doctype.asset.depreciation import ( reset_depreciation_schedule, reverse_depreciation_entry_made_after_disposal, ) -from erpnext.controllers.accounts_controller import validate_account_head -from erpnext.controllers.accounts_controller import make_advance_liability_entry +from erpnext.controllers.accounts_controller import ( + check_advance_liability_entry, + validate_account_head, +) from erpnext.controllers.selling_controller import SellingController from erpnext.projects.doctype.timesheet.timesheet import get_projectwise_timesheet_data from erpnext.setup.doctype.company.company import update_company_current_month_sales @@ -1065,11 +1067,14 @@ class SalesInvoice(SellingController): gl_entries = [] self.make_customer_gl_entry(gl_entries) - - advance_payments_as_liability = frappe.db.get_value("Company", {"company_name": self.company}, "book_advance_payments_as_liability") - if advance_payments_as_liability: - for advance_entry in self.advances: - make_advance_liability_entry(gl_entries, advance_entry.reference_name, advance_entry.allocated_amount, invoice=self.name, party_type="Customer") + + check_advance_liability_entry( + gl_entries, + company=self.company, + advances=self.advances, + invoice=self.name, + party_type="Customer", + ) self.make_tax_gl_entries(gl_entries) self.make_exchange_gain_loss_gl_entries(gl_entries) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 364bf6898a..54c8b8472e 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -3320,7 +3320,10 @@ class TestSalesInvoice(unittest.TestCase): frappe.db.set_value( "Company", "_Test Company", - {"book_advance_payments_as_liability": 1, "default_advance_account": "Creditors - _TC"}, + { + "book_advance_payments_as_liability": 1, + "default_advance_received_account": "Creditors - _TC", + }, ) pe = create_payment_entry( company="_Test Company", @@ -3408,6 +3411,7 @@ def check_gl_entries(doc, voucher_no, expected_gle, posting_date): doc.assertEqual(expected_gle[i][0], gle.account) doc.assertEqual(expected_gle[i][1], gle.debit) doc.assertEqual(expected_gle[i][2], gle.credit) + doc.assertEqual(getdate(expected_gle[i][3]), gle.posting_date) def create_sales_invoice(**args): diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index ccd54b344f..1e38217a16 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -429,7 +429,12 @@ def get_party_advance_account(party_type, party, company): ) if not account: - account = frappe.get_cached_value("Company", company, "default_advance_account") + account_name = ( + "default_advance_received_account" + if party_type == "Customer" + else "default_advance_paid_account" + ) + account = frappe.get_cached_value("Company", company, account_name) return account diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index d8c4135292..3fac56cfa2 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -2908,16 +2908,6 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil parent.create_stock_reservation_entries() -@erpnext.allow_regional -def validate_regional(doc): - pass - - -@erpnext.allow_regional -def validate_einvoice_fields(doc): - pass - - def make_advance_liability_entry( gl_entries, pe, allocated_amount, invoice, party_type, references=False ): @@ -2990,3 +2980,33 @@ def make_advance_liability_entry( item=invoice, ) ) + + +def check_advance_liability_entry(gl_entries, company, advances, invoice, party_type): + advance_payments_as_liability = frappe.db.get_value( + "Company", {"company_name": company}, "book_advance_payments_as_liability" + ) + if advance_payments_as_liability: + for advance_entry in advances: + make_advance_liability_entry( + gl_entries, + advance_entry.reference_name, + advance_entry.allocated_amount, + invoice=invoice, + party_type=party_type, + ) + + +@erpnext.allow_regional +def validate_regional(doc): + pass + + +@erpnext.allow_regional +def validate_einvoice_fields(doc): + pass + + +@erpnext.allow_regional +def update_gl_dict_with_regional_fields(doc, gl_dict): + pass diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js index fb0ee7f07a..436fe0595a 100644 --- a/erpnext/setup/doctype/company/company.js +++ b/erpnext/setup/doctype/company/company.js @@ -227,7 +227,8 @@ erpnext.company.setup_queries = function(frm) { ["asset_received_but_not_billed", {"account_type": "Asset Received But Not Billed"}], ["unrealized_profit_loss_account", {"root_type": ["in", ["Liability", "Asset"]]}], ["default_provisional_account", {"root_type": ["in", ["Liability", "Asset"]]}], - ["default_advance_account", {"root_type": ["in", ["Liability", "Asset"]]}], + ["default_advance_received_account", {"root_type": "Liability"}], + ["default_advance_paid_account", {"root_type": "Asset"}], ], function(i, v) { erpnext.company.set_custom_query(frm, v); }); diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json index 5b3d3bb086..3523af1988 100644 --- a/erpnext/setup/doctype/company/company.json +++ b/erpnext/setup/doctype/company/company.json @@ -72,7 +72,8 @@ "default_finance_book", "advance_payments_section", "book_advance_payments_as_liability", - "default_advance_account", + "default_advance_received_account", + "default_advance_paid_account", "column_break_cui0", "auto_accounting_for_stock_settings", "enable_perpetual_inventory", @@ -716,9 +717,17 @@ }, { "depends_on": "eval:doc.book_advance_payments_as_liability", - "fieldname": "default_advance_account", + "fieldname": "default_advance_received_account", "fieldtype": "Link", - "label": "Default Account", + "label": "Default Advance Received Account", + "mandatory_depends_on": "book_advance_payments_as_liability", + "options": "Account" + }, + { + "depends_on": "eval:doc.book_advance_payments_as_liability", + "fieldname": "default_advance_paid_account", + "fieldtype": "Link", + "label": "Default Advance Paid Account", "mandatory_depends_on": "book_advance_payments_as_liability", "options": "Account" } @@ -728,7 +737,7 @@ "image_field": "company_logo", "is_tree": 1, "links": [], - "modified": "2023-06-05 14:12:37.946451", + "modified": "2023-06-12 12:51:12.007410", "modified_by": "Administrator", "module": "Setup", "name": "Company", From 5e9821dce24fdbca59a11f9763b8b96df2b0d3bd Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Mon, 12 Jun 2023 18:00:15 +0530 Subject: [PATCH 10/99] test: modify test to check posting date --- .../doctype/payment_entry/payment_entry.py | 19 ++++++++++++++++--- .../purchase_invoice/test_purchase_invoice.py | 11 ++++++++--- .../sales_invoice/test_sales_invoice.py | 14 +++++++++++--- 3 files changed, 35 insertions(+), 9 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 7012eacb61..7a31dc5885 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -909,6 +909,7 @@ class PaymentEntry(AccountsController): item=self, ) for d in self.get("references"): + gle = party_dict.copy() book_advance_payments_as_liability = frappe.get_value( "Company", {"company_name": self.company}, "book_advance_payments_as_liability" ) @@ -917,17 +918,27 @@ class PaymentEntry(AccountsController): and book_advance_payments_as_liability ): self.make_invoice_liability_entry(gl_entries, d) + gle.update( + { + "against_voucher_type": "Payment Entry", + "against_voucher": self.name, + } + ) allocated_amount_in_company_currency = self.calculate_base_allocated_amount_for_reference(d) - gle = party_dict.copy() gle.update( { dr_or_cr: allocated_amount_in_company_currency, dr_or_cr + "_in_account_currency": d.allocated_amount, - "against_voucher_type": "Payment Entry", - "against_voucher": self.name, } ) + if not gle.get("against_voucher_type"): + gle.update( + { + "against_voucher_type": d.reference_doctype, + "against_voucher": d.reference_name, + } + ) gl_entries.append(gle) @@ -940,6 +951,8 @@ class PaymentEntry(AccountsController): { dr_or_cr + "_in_account_currency": self.unallocated_amount, dr_or_cr: base_unallocated_amount, + "against_voucher_type": "Payment Entry", + "against_voucher": self.name, } ) diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index c15692b751..1ac231bc19 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -1694,13 +1694,18 @@ class TestPurchaseInvoice(unittest.TestCase, StockTestMixin): pi.save() pi.submit() expected_gle = [ - ["Creditors - _TC", 50, 100], - ["Debtors - _TC", 0.0, 50], - ["Stock Received But Not Billed - _TC", 100, 0.0], + ["Creditors - _TC", 50, 100, nowdate()], + ["Debtors - _TC", 0.0, 50, nowdate()], + ["Stock Received But Not Billed - _TC", 100, 0.0, nowdate()], ] check_gl_entries(self, pi.name, expected_gle, nowdate()) self.assertEqual(pi.outstanding_amount, 200) + frappe.db.set_value( + "Company", + "_Test Company", + {"book_advance_payments_as_liability": 0, "default_advance_paid_account": ""}, + ) def check_gl_entries(doc, voucher_no, expected_gle, posting_date): diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 54c8b8472e..8ab7fd7cd0 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -3348,13 +3348,21 @@ class TestSalesInvoice(unittest.TestCase): si.save() si.submit() expected_gle = [ - ["Creditors - _TC", 50, 0.0], - ["Debtors - _TC", 100, 50], - ["Sales - _TC", 0.0, 100], + ["Creditors - _TC", 50, 0.0, nowdate()], + ["Debtors - _TC", 100, 50, nowdate()], + ["Sales - _TC", 0.0, 100, nowdate()], ] check_gl_entries(self, si.name, expected_gle, nowdate()) self.assertEqual(si.outstanding_amount, 50) + frappe.db.set_value( + "Company", + "_Test Company", + { + "book_advance_payments_as_liability": 0, + "default_advance_received_account": "", + }, + ) def get_sales_invoice_for_e_invoice(): From 17341adf1cb385e3326552d95dc1bcd00e4b938a Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Tue, 13 Jun 2023 15:00:46 +0530 Subject: [PATCH 11/99] fix: calculate outstanding amount on reconcile correctly --- .../doctype/payment_entry/payment_entry.py | 21 ++++--- .../purchase_invoice/test_purchase_invoice.py | 58 ++++++++++------- .../sales_invoice/test_sales_invoice.py | 62 ++++++++++--------- erpnext/accounts/utils.py | 2 +- erpnext/buying/doctype/supplier/supplier.js | 2 +- erpnext/controllers/accounts_controller.py | 6 +- erpnext/selling/doctype/customer/customer.js | 2 +- erpnext/setup/doctype/company/company.js | 4 +- 8 files changed, 85 insertions(+), 72 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 7a31dc5885..dd3490936f 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -872,12 +872,12 @@ class PaymentEntry(AccountsController): self.set("remarks", "\n".join(remarks)) - def build_gl_map(self): + def build_gl_map(self, is_reconcile=False): if self.payment_type in ("Receive", "Pay") and not self.get("party_account_field"): self.setup_party_account_field() gl_entries = [] - self.add_party_gl_entries(gl_entries) + self.add_party_gl_entries(gl_entries, is_reconcile) self.add_bank_gl_entries(gl_entries) self.add_deductions_gl_entries(gl_entries) self.add_tax_gl_entries(gl_entries) @@ -888,7 +888,7 @@ class PaymentEntry(AccountsController): gl_entries = process_gl_map(gl_entries) make_gl_entries(gl_entries, cancel=cancel, adv_adj=adv_adj) - def add_party_gl_entries(self, gl_entries): + def add_party_gl_entries(self, gl_entries, is_reconcile): if self.party_account: if self.payment_type == "Receive": against_account = self.paid_to @@ -917,13 +917,14 @@ class PaymentEntry(AccountsController): d.reference_doctype in ["Sales Invoice", "Purchase Invoice"] and book_advance_payments_as_liability ): - self.make_invoice_liability_entry(gl_entries, d) - gle.update( - { - "against_voucher_type": "Payment Entry", - "against_voucher": self.name, - } - ) + if not is_reconcile: + self.make_invoice_liability_entry(gl_entries, d) + gle.update( + { + "against_voucher_type": "Payment Entry", + "against_voucher": self.name, + } + ) allocated_amount_in_company_currency = self.calculate_base_allocated_amount_for_reference(d) gle.update( diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index 1ac231bc19..6ebb6afe6e 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -1664,13 +1664,9 @@ class TestPurchaseInvoice(unittest.TestCase, StockTestMixin): def test_advance_entries_as_liability(self): from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_entry - from erpnext.accounts.party import get_party_account - frappe.db.set_value( - "Company", - "_Test Company", - {"book_advance_payments_as_liability": 1, "default_advance_paid_account": "Debtors - _TC"}, - ) + set_advance_flag(company="_Test Company", flag=1, default_account="Debtors - _TC") + pe = create_payment_entry( company="_Test Company", payment_type="Pay", @@ -1678,34 +1674,48 @@ class TestPurchaseInvoice(unittest.TestCase, StockTestMixin): party="_Test Supplier", paid_from="Cash - _TC", paid_to="Creditors - _TC", - paid_amount=1000, + paid_amount=500, ) pe.submit() pi = make_purchase_invoice( - company="_Test Company", customer="_Test Supplier", do_not_save=True, do_not_submit=True + company="_Test Company", + customer="_Test Supplier", + do_not_save=True, + do_not_submit=True, + rate=1000, + price_list_rate=1000, + qty=1, ) - pi.base_grand_total = 100 - pi.grand_total = 100 + pi.base_grand_total = 1000 + pi.grand_total = 1000 pi.set_advances() - self.assertEqual(pi.advances[0].allocated_amount, 100) - pi.advances[0].allocated_amount = 50 - pi.advances = [pi.advances[0]] + for advance in pi.advances: + advance.allocated_amount = 500 if advance.reference_name == pe.name else 0 pi.save() pi.submit() - expected_gle = [ - ["Creditors - _TC", 50, 100, nowdate()], - ["Debtors - _TC", 0.0, 50, nowdate()], - ["Stock Received But Not Billed - _TC", 100, 0.0, nowdate()], - ] + self.assertEqual(pi.advances[0].allocated_amount, 500) + expected_gle = [ + ["Creditors - _TC", 500, 1000, nowdate()], + ["Debtors - _TC", 0.0, 500, nowdate()], + ["Stock Received But Not Billed - _TC", 1000, 0.0, nowdate()], + ] check_gl_entries(self, pi.name, expected_gle, nowdate()) - self.assertEqual(pi.outstanding_amount, 200) - frappe.db.set_value( - "Company", - "_Test Company", - {"book_advance_payments_as_liability": 0, "default_advance_paid_account": ""}, - ) + self.assertEqual(pi.outstanding_amount, 500) + + set_advance_flag(company="_Test Company", flag=0, default_account="") + + +def set_advance_flag(company, flag, default_account): + frappe.db.set_value( + "Company", + company, + { + "book_advance_payments_as_liability": flag, + "default_advance_paid_account": default_account, + }, + ) def check_gl_entries(doc, voucher_no, expected_gle, posting_date): diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 8ab7fd7cd0..067808d9a1 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -3315,16 +3315,10 @@ class TestSalesInvoice(unittest.TestCase): def test_advance_entries_as_liability(self): from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_entry - from erpnext.accounts.party import get_party_account + from erpnext.accounts.report.accounts_receivable.accounts_receivable import execute + + set_advance_flag(company="_Test Company", flag=1, default_account="Creditors - _TC") - frappe.db.set_value( - "Company", - "_Test Company", - { - "book_advance_payments_as_liability": 1, - "default_advance_received_account": "Creditors - _TC", - }, - ) pe = create_payment_entry( company="_Test Company", payment_type="Receive", @@ -3337,32 +3331,42 @@ class TestSalesInvoice(unittest.TestCase): pe.submit() si = create_sales_invoice( - company="_Test Company", customer="_Test Customer", do_not_save=True, do_not_submit=True + company="_Test Company", + customer="_Test Customer", + do_not_save=True, + do_not_submit=True, + rate=500, + price_list_rate=500, ) - si.base_grand_total = 100 - si.grand_total = 100 + si.base_grand_total = 500 + si.grand_total = 500 si.set_advances() - self.assertEqual(si.advances[0].allocated_amount, 100) - si.advances[0].allocated_amount = 50 - si.advances = [si.advances[0]] + for advance in si.advances: + advance.allocated_amount = 500 if advance.reference_name == pe.name else 0 si.save() si.submit() - expected_gle = [ - ["Creditors - _TC", 50, 0.0, nowdate()], - ["Debtors - _TC", 100, 50, nowdate()], - ["Sales - _TC", 0.0, 100, nowdate()], - ] + self.assertEqual(si.advances[0].allocated_amount, 500) + expected_gle = [ + ["Creditors - _TC", 500, 0.0, nowdate()], + ["Debtors - _TC", 500, 500, nowdate()], + ["Sales - _TC", 0.0, 500, nowdate()], + ] check_gl_entries(self, si.name, expected_gle, nowdate()) - self.assertEqual(si.outstanding_amount, 50) - frappe.db.set_value( - "Company", - "_Test Company", - { - "book_advance_payments_as_liability": 0, - "default_advance_received_account": "", - }, - ) + self.assertEqual(si.outstanding_amount, 0) + + set_advance_flag(company="_Test Company", flag=0, default_account="") + + +def set_advance_flag(company, flag, default_account): + frappe.db.set_value( + "Company", + company, + { + "book_advance_payments_as_liability": flag, + "default_advance_received_account": default_account, + }, + ) def get_sales_invoice_for_e_invoice(): diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index c1d365304f..728a6d7a96 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -472,7 +472,7 @@ def reconcile_against_document(args, skip_ref_details_update_for_pe=False): # n doc.save(ignore_permissions=True) # re-submit advance entry doc = frappe.get_doc(entry.voucher_type, entry.voucher_no) - gl_map = doc.build_gl_map() + gl_map = doc.build_gl_map(is_reconcile=True) create_payment_ledger_entry(gl_map, update_outstanding="No", cancel=0, adv_adj=1) # Only update outstanding for newly linked vouchers diff --git a/erpnext/buying/doctype/supplier/supplier.js b/erpnext/buying/doctype/supplier/supplier.js index 50f9bb6cb9..a5bf4b246b 100644 --- a/erpnext/buying/doctype/supplier/supplier.js +++ b/erpnext/buying/doctype/supplier/supplier.js @@ -22,7 +22,7 @@ frappe.ui.form.on("Supplier", { let d = locals[cdt][cdn]; return { filters: { - "root_type": 'Asset', + "account_type": "Receivable", "company": d.company, "is_group": 0 } diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 3fac56cfa2..7b2039d193 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -2940,10 +2940,8 @@ def make_advance_liability_entry( dr_or_cr + "_in_account_currency": allocated_amount, rev: 0, rev + "_in_account_currency": 0, - "against_voucher": invoice.return_against - if cint(invoice.is_return) and invoice.return_against - else invoice.name, - "against_voucher_type": invoice.doctype, + "against_voucher": pe.name, + "against_voucher_type": "Payment Entry", "cost_center": invoice.cost_center, "project": invoice.project, "voucher_type": voucher_type, diff --git a/erpnext/selling/doctype/customer/customer.js b/erpnext/selling/doctype/customer/customer.js index 8f828807a5..408e89b0ef 100644 --- a/erpnext/selling/doctype/customer/customer.js +++ b/erpnext/selling/doctype/customer/customer.js @@ -39,7 +39,7 @@ frappe.ui.form.on("Customer", { let d = locals[cdt][cdn]; return { filters: { - "root_type": 'Liability', + "account_type": 'Payable', "company": d.company, "is_group": 0 } diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js index 436fe0595a..089e20d442 100644 --- a/erpnext/setup/doctype/company/company.js +++ b/erpnext/setup/doctype/company/company.js @@ -227,8 +227,8 @@ erpnext.company.setup_queries = function(frm) { ["asset_received_but_not_billed", {"account_type": "Asset Received But Not Billed"}], ["unrealized_profit_loss_account", {"root_type": ["in", ["Liability", "Asset"]]}], ["default_provisional_account", {"root_type": ["in", ["Liability", "Asset"]]}], - ["default_advance_received_account", {"root_type": "Liability"}], - ["default_advance_paid_account", {"root_type": "Asset"}], + ["default_advance_received_account", {"account_type": "Payable"}], + ["default_advance_paid_account", {"account_type": "Receivable"}], ], function(i, v) { erpnext.company.set_custom_query(frm, v); }); From ba4ab06ae3024d387570ef6779ab218f72518ed1 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Wed, 14 Jun 2023 12:39:16 +0530 Subject: [PATCH 12/99] fix: changed account types in controller method --- .../doctype/payment_entry/payment_entry.py | 34 +++++++++++++------ erpnext/accounts/utils.py | 6 ++-- erpnext/controllers/accounts_controller.py | 2 +- 3 files changed, 28 insertions(+), 14 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index dd3490936f..7c813eb329 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -99,11 +99,11 @@ class PaymentEntry(AccountsController): ) if not book_advance_payments_as_liability: return - root_type = frappe.get_value( - "Account", {"name": self.party_account, "company": self.company}, "root_type" + account_type = frappe.get_value( + "Account", {"name": self.party_account, "company": self.company}, "account_type" ) - if (root_type == "Liability" and self.party_type == "Customer") or ( - root_type == "Asset" and self.party_type == "Supplier" + if (account_type == "Payable" and self.party_type == "Customer") or ( + account_type == "Receivable" and self.party_type == "Supplier" ): return if self.unallocated_amount == 0: @@ -908,6 +908,8 @@ class PaymentEntry(AccountsController): }, item=self, ) + is_advance = self.get_advance_flag() + for d in self.get("references"): gle = party_dict.copy() book_advance_payments_as_liability = frappe.get_value( @@ -916,13 +918,14 @@ class PaymentEntry(AccountsController): if ( d.reference_doctype in ["Sales Invoice", "Purchase Invoice"] and book_advance_payments_as_liability + and is_advance ): if not is_reconcile: self.make_invoice_liability_entry(gl_entries, d) gle.update( { - "against_voucher_type": "Payment Entry", - "against_voucher": self.name, + "voucher_type": "Payment Entry", + "voucher_no": self.name, } ) @@ -940,7 +943,6 @@ class PaymentEntry(AccountsController): "against_voucher": d.reference_name, } ) - gl_entries.append(gle) if self.unallocated_amount: @@ -952,13 +954,19 @@ class PaymentEntry(AccountsController): { dr_or_cr + "_in_account_currency": self.unallocated_amount, dr_or_cr: base_unallocated_amount, - "against_voucher_type": "Payment Entry", - "against_voucher": self.name, } ) gl_entries.append(gle) + def get_advance_flag(self): + for d in self.get("references"): + if d.reference_doctype == "Sales Order": + return True + if self.unallocated_amount > 0: + return True + return False + def make_invoice_liability_entry(self, gl_entries, invoice): args_dict = { "party_type": self.party_type, @@ -967,8 +975,6 @@ class PaymentEntry(AccountsController): "cost_center": self.cost_center, "voucher_type": invoice.reference_doctype, "voucher_no": invoice.reference_name, - "against_voucher_type": invoice.reference_doctype, - "against_voucher": invoice.reference_name, } dr_or_cr = "credit" if invoice.reference_doctype == "Sales Invoice" else "debit" @@ -987,6 +993,12 @@ class PaymentEntry(AccountsController): args_dict["account"] = self.party_account args_dict[dr_or_cr] = invoice.allocated_amount args_dict[dr_or_cr + "_in_account_currency"] = invoice.allocated_amount + args_dict.update( + { + "against_voucher_type": "Payment Entry", + "against_voucher": self.name, + } + ) gle = self.get_gl_dict( args_dict, item=self, diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 728a6d7a96..abf9b5e9b2 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -436,7 +436,9 @@ def add_cc(args=None): return cc.name -def reconcile_against_document(args, skip_ref_details_update_for_pe=False): # nosemgrep +def reconcile_against_document( + args, skip_ref_details_update_for_pe=False, is_reconcile=False +): # nosemgrep """ Cancel PE or JV, Update against document, split if required and resubmit """ @@ -472,7 +474,7 @@ def reconcile_against_document(args, skip_ref_details_update_for_pe=False): # n doc.save(ignore_permissions=True) # re-submit advance entry doc = frappe.get_doc(entry.voucher_type, entry.voucher_no) - gl_map = doc.build_gl_map(is_reconcile=True) + gl_map = doc.build_gl_map(is_reconcile) create_payment_ledger_entry(gl_map, update_outstanding="No", cancel=0, adv_adj=1) # Only update outstanding for newly linked vouchers diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 7b2039d193..578b26a2cd 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1078,7 +1078,7 @@ class AccountsController(TransactionBase): if lst: from erpnext.accounts.utils import reconcile_against_document - reconcile_against_document(lst) + reconcile_against_document(lst, is_reconcile=True) def on_cancel(self): from erpnext.accounts.utils import unlink_ref_doc_from_payment_entries From 033e4e84f5c91ef1e6a36ffb9ff66cd41a1fd108 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Wed, 14 Jun 2023 14:20:42 +0530 Subject: [PATCH 13/99] fix: modify voucher details for liability entries --- .../doctype/payment_entry/payment_entry.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 7c813eb329..40dcc8cc68 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -924,8 +924,8 @@ class PaymentEntry(AccountsController): self.make_invoice_liability_entry(gl_entries, d) gle.update( { - "voucher_type": "Payment Entry", - "voucher_no": self.name, + "against_voucher_type": "Payment Entry", + "against_voucher": self.name, } ) @@ -973,14 +973,20 @@ class PaymentEntry(AccountsController): "party": self.party, "account_currency": self.party_account_currency, "cost_center": self.cost_center, - "voucher_type": invoice.reference_doctype, - "voucher_no": invoice.reference_name, + "voucher_type": "Payment Entry", + "voucher_no": self.name, } dr_or_cr = "credit" if invoice.reference_doctype == "Sales Invoice" else "debit" args_dict["account"] = invoice.account args_dict[dr_or_cr] = invoice.allocated_amount args_dict[dr_or_cr + "_in_account_currency"] = invoice.allocated_amount + args_dict.update( + { + "against_voucher_type": invoice.reference_doctype, + "against_voucher": invoice.reference_name, + } + ) gle = self.get_gl_dict( args_dict, item=self, From 442e3f2aa271454ff6b6001bc773df86196b0880 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Fri, 16 Jun 2023 13:38:47 +0530 Subject: [PATCH 14/99] fix: update outstanding amount and unpaid status on cancellation of payment entry --- .../doctype/payment_entry/payment_entry.py | 25 ++++++++-------- erpnext/accounts/general_ledger.py | 1 + erpnext/accounts/utils.py | 2 +- erpnext/controllers/accounts_controller.py | 29 +++++++------------ erpnext/setup/doctype/company/company.json | 15 +++++----- 5 files changed, 33 insertions(+), 39 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 40dcc8cc68..d901faaed3 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -872,7 +872,7 @@ class PaymentEntry(AccountsController): self.set("remarks", "\n".join(remarks)) - def build_gl_map(self, is_reconcile=False): + def build_gl_map(self, is_reconcile=True): if self.payment_type in ("Receive", "Pay") and not self.get("party_account_field"): self.setup_party_account_field() @@ -918,16 +918,15 @@ class PaymentEntry(AccountsController): if ( d.reference_doctype in ["Sales Invoice", "Purchase Invoice"] and book_advance_payments_as_liability - and is_advance + and (is_advance or is_reconcile) ): - if not is_reconcile: - self.make_invoice_liability_entry(gl_entries, d) - gle.update( - { - "against_voucher_type": "Payment Entry", - "against_voucher": self.name, - } - ) + self.make_invoice_liability_entry(gl_entries, d) + gle.update( + { + "against_voucher_type": "Payment Entry", + "against_voucher": self.name, + } + ) allocated_amount_in_company_currency = self.calculate_base_allocated_amount_for_reference(d) gle.update( @@ -939,8 +938,8 @@ class PaymentEntry(AccountsController): if not gle.get("against_voucher_type"): gle.update( { - "against_voucher_type": d.reference_doctype, - "against_voucher": d.reference_name, + "against_voucher_type": d.reference_doctype if is_advance else "Payment Entry", + "against_voucher": d.reference_name if is_advance else self.name, } ) gl_entries.append(gle) @@ -954,6 +953,8 @@ class PaymentEntry(AccountsController): { dr_or_cr + "_in_account_currency": self.unallocated_amount, dr_or_cr: base_unallocated_amount, + "against_voucher_type": "Payment Entry", + "against_voucher": self.name, } ) diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py index a929ff17b0..a0954a955a 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -223,6 +223,7 @@ def check_if_in_list(gle, gl_map, dimensions=None): "party_type", "project", "finance_book", + "voucher_no", ] if dimensions: diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index abf9b5e9b2..e69dcd4199 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -437,7 +437,7 @@ def add_cc(args=None): def reconcile_against_document( - args, skip_ref_details_update_for_pe=False, is_reconcile=False + args, skip_ref_details_update_for_pe=False, is_reconcile=True ): # nosemgrep """ Cancel PE or JV, Update against document, split if required and resubmit diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 578b26a2cd..f564840251 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1021,7 +1021,7 @@ class AccountsController(TransactionBase): ) ) - def update_against_document_in_jv(self): + def update_against_document_in_jv(self, is_reconcile=True): """ Links invoice and advance voucher: 1. cancel advance voucher @@ -1078,7 +1078,7 @@ class AccountsController(TransactionBase): if lst: from erpnext.accounts.utils import reconcile_against_document - reconcile_against_document(lst, is_reconcile=True) + reconcile_against_document(lst, is_reconcile) def on_cancel(self): from erpnext.accounts.utils import unlink_ref_doc_from_payment_entries @@ -2919,7 +2919,6 @@ def make_advance_liability_entry( rev = "credit" against = invoice.debit_to party = invoice.customer - voucher_type = "Sales Invoice" else: invoice = frappe.get_doc("Purchase Invoice", invoice) account = pe.paid_to @@ -2927,9 +2926,8 @@ def make_advance_liability_entry( rev = "debit" against = invoice.credit_to party = invoice.supplier - voucher_type = "Purchase Invoice" gl_entries.append( - invoice.get_gl_dict( + pe.get_gl_dict( { "account": account, "party_type": party_type, @@ -2940,42 +2938,35 @@ def make_advance_liability_entry( dr_or_cr + "_in_account_currency": allocated_amount, rev: 0, rev + "_in_account_currency": 0, - "against_voucher": pe.name, - "against_voucher_type": "Payment Entry", "cost_center": invoice.cost_center, "project": invoice.project, - "voucher_type": voucher_type, - "voucher_no": invoice.name, + "against_voucher_type": "Payment Entry", + "against_voucher": pe.name, }, invoice.party_account_currency, - item=invoice, + item=pe, ) ) (dr_or_cr, rev) = ("credit", "debit") if party_type == "Customer" else ("debit", "credit") gl_entries.append( - invoice.get_gl_dict( + pe.get_gl_dict( { "account": against, "party_type": party_type, "party": party, "due_date": invoice.due_date, - "against": account, dr_or_cr: allocated_amount, dr_or_cr + "_in_account_currency": allocated_amount, rev: 0, rev + "_in_account_currency": 0, - "against_voucher": invoice.return_against - if cint(invoice.is_return) and invoice.return_against - else invoice.name, - "against_voucher_type": invoice.doctype, "cost_center": invoice.cost_center, "project": invoice.project, - "voucher_type": "Payment Entry" if references else voucher_type, - "voucher_no": pe.name if references else invoice.name, + "against_voucher_type": invoice.doctype, + "against_voucher": invoice.name, }, invoice.party_account_currency, - item=invoice, + item=pe, ) ) diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json index 3523af1988..611e2ab221 100644 --- a/erpnext/setup/doctype/company/company.json +++ b/erpnext/setup/doctype/company/company.json @@ -72,9 +72,9 @@ "default_finance_book", "advance_payments_section", "book_advance_payments_as_liability", + "column_break_fwcf", "default_advance_received_account", "default_advance_paid_account", - "column_break_cui0", "auto_accounting_for_stock_settings", "enable_perpetual_inventory", "enable_provisional_accounting_for_non_stock_items", @@ -702,19 +702,16 @@ }, { "default": "0", + "description": "Enabling this option will allow you to record -

1. Advances Received in a Liability Account instead of the Receivable Account

2. Advances Paid in an Asset Account instead of the Payable Account", "fieldname": "book_advance_payments_as_liability", "fieldtype": "Check", - "label": "Book Advance Payments as Liability" + "label": "Book Advance Payments in Separate Party Account" }, { "fieldname": "advance_payments_section", "fieldtype": "Section Break", "label": "Advance Payments" }, - { - "fieldname": "column_break_cui0", - "fieldtype": "Column Break" - }, { "depends_on": "eval:doc.book_advance_payments_as_liability", "fieldname": "default_advance_received_account", @@ -730,6 +727,10 @@ "label": "Default Advance Paid Account", "mandatory_depends_on": "book_advance_payments_as_liability", "options": "Account" + }, + { + "fieldname": "column_break_fwcf", + "fieldtype": "Column Break" } ], "icon": "fa fa-building", @@ -737,7 +738,7 @@ "image_field": "company_logo", "is_tree": 1, "links": [], - "modified": "2023-06-12 12:51:12.007410", + "modified": "2023-06-16 13:32:48.790947", "modified_by": "Administrator", "module": "Setup", "name": "Company", From 016ed951da02ca87e735f035f5c6663cc113ad76 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 20 Jun 2023 13:22:32 +0530 Subject: [PATCH 15/99] test: Update tests --- .../doctype/payment_entry/payment_entry.py | 42 ++++++++++--------- .../purchase_invoice/test_purchase_invoice.py | 16 ++++--- .../sales_invoice/test_sales_invoice.py | 17 ++++---- erpnext/accounts/utils.py | 6 +-- erpnext/controllers/accounts_controller.py | 4 +- 5 files changed, 46 insertions(+), 39 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index d23afb17d9..752d22a353 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -97,29 +97,37 @@ class PaymentEntry(AccountsController): book_advance_payments_as_liability = frappe.get_value( "Company", {"company_name": self.company}, "book_advance_payments_as_liability" ) + if not book_advance_payments_as_liability: return + account_type = frappe.get_value( "Account", {"name": self.party_account, "company": self.company}, "account_type" ) + if (account_type == "Payable" and self.party_type == "Customer") or ( account_type == "Receivable" and self.party_type == "Supplier" ): return + if self.unallocated_amount == 0: for d in self.references: if d.reference_doctype in ["Sales Order", "Purchase Order"]: break else: return + liability_account = get_party_account( self.party_type, self.party, self.company, include_advance=True )[1] + self.set(self.party_account_field, liability_account) + msg = "Book Advance Payments as Liability option is chosen. Paid From account changed from {0} to {1}.".format( frappe.bold(self.party_account), frappe.bold(liability_account), ) + frappe.msgprint(_(msg), alert=True) def on_cancel(self): @@ -921,12 +929,12 @@ class PaymentEntry(AccountsController): self.set("remarks", "\n".join(remarks)) - def build_gl_map(self, is_reconcile=True): + def build_gl_map(self): if self.payment_type in ("Receive", "Pay") and not self.get("party_account_field"): self.setup_party_account_field() gl_entries = [] - self.add_party_gl_entries(gl_entries, is_reconcile) + self.add_party_gl_entries(gl_entries) self.add_bank_gl_entries(gl_entries) self.add_deductions_gl_entries(gl_entries) self.add_tax_gl_entries(gl_entries) @@ -937,7 +945,7 @@ class PaymentEntry(AccountsController): gl_entries = process_gl_map(gl_entries) make_gl_entries(gl_entries, cancel=cancel, adv_adj=adv_adj) - def add_party_gl_entries(self, gl_entries, is_reconcile): + def add_party_gl_entries(self, gl_entries): if self.party_account: if self.payment_type == "Receive": against_account = self.paid_to @@ -957,7 +965,7 @@ class PaymentEntry(AccountsController): }, item=self, ) - is_advance = self.get_advance_flag() + is_advance = self.is_advance_entry() for d in self.get("references"): gle = party_dict.copy() @@ -967,30 +975,24 @@ class PaymentEntry(AccountsController): if ( d.reference_doctype in ["Sales Invoice", "Purchase Invoice"] and book_advance_payments_as_liability - and (is_advance or is_reconcile) + and is_advance ): self.make_invoice_liability_entry(gl_entries, d) - gle.update( - { - "against_voucher_type": "Payment Entry", - "against_voucher": self.name, - } - ) + against_voucher_type = "Payment Entry" + against_voucher = self.name + else: + against_voucher_type = d.reference_doctype + against_voucher = d.reference_name allocated_amount_in_company_currency = self.calculate_base_allocated_amount_for_reference(d) gle.update( { dr_or_cr: allocated_amount_in_company_currency, dr_or_cr + "_in_account_currency": d.allocated_amount, + "against_voucher_type": against_voucher_type, + "against_voucher": against_voucher, } ) - if not gle.get("against_voucher_type"): - gle.update( - { - "against_voucher_type": d.reference_doctype if is_advance else "Payment Entry", - "against_voucher": d.reference_name if is_advance else self.name, - } - ) gl_entries.append(gle) if self.unallocated_amount: @@ -1009,9 +1011,9 @@ class PaymentEntry(AccountsController): gl_entries.append(gle) - def get_advance_flag(self): + def is_advance_entry(self): for d in self.get("references"): - if d.reference_doctype == "Sales Order": + if d.reference_doctype in ("Sales Order", "Purchase Order"): return True if self.unallocated_amount > 0: return True diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index 02d60dccab..569a48acad 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -1698,12 +1698,16 @@ class TestPurchaseInvoice(unittest.TestCase, StockTestMixin): pi.submit() self.assertEqual(pi.advances[0].allocated_amount, 500) + + # Check GL Entry against payment doctype expected_gle = [ - ["Creditors - _TC", 500, 1000, nowdate()], + ["Cash - _TC", 0.0, 500, nowdate()], + ["Creditors - _TC", 500, 0.0, nowdate()], + ["Creditors - _TC", 500, 0.0, nowdate()], ["Debtors - _TC", 0.0, 500, nowdate()], - ["Stock Received But Not Billed - _TC", 1000, 0.0, nowdate()], ] - check_gl_entries(self, pi.name, expected_gle, nowdate()) + + check_gl_entries(self, pe.name, expected_gle, nowdate(), voucher_type="Payment Entry") self.assertEqual(pi.outstanding_amount, 500) set_advance_flag(company="_Test Company", flag=0, default_account="") @@ -1735,18 +1739,18 @@ def set_advance_flag(company, flag, default_account): ) -def check_gl_entries(doc, voucher_no, expected_gle, posting_date): +def check_gl_entries(doc, voucher_no, expected_gle, posting_date, voucher_type="Purchase Invoice"): gl = frappe.qb.DocType("GL Entry") q = ( frappe.qb.from_(gl) .select(gl.account, gl.debit, gl.credit, gl.posting_date) .where( - (gl.voucher_type == "Sales Invoice") + (gl.voucher_type == voucher_type) & (gl.voucher_no == voucher_no) & (gl.posting_date >= posting_date) & (gl.is_cancelled == 0) ) - .orderby(gl.posting_date, gl.account) + .orderby(gl.posting_date, gl.account, gl.creation) ) gl_entries = q.run(as_dict=True) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index c23ef34759..4cf3abaad1 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -3312,7 +3312,6 @@ class TestSalesInvoice(unittest.TestCase): def test_advance_entries_as_liability(self): from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_entry - from erpnext.accounts.report.accounts_receivable.accounts_receivable import execute set_advance_flag(company="_Test Company", flag=1, default_account="Creditors - _TC") @@ -3344,12 +3343,16 @@ class TestSalesInvoice(unittest.TestCase): si.submit() self.assertEqual(si.advances[0].allocated_amount, 500) + + # Check GL Entry against payment doctype expected_gle = [ + ["Cash - _TC", 1000, 0.0, nowdate()], ["Creditors - _TC", 500, 0.0, nowdate()], - ["Debtors - _TC", 500, 500, nowdate()], - ["Sales - _TC", 0.0, 500, nowdate()], + ["Debtors - _TC", 0.0, 1000, nowdate()], + ["Debtors - _TC", 0.0, 500, nowdate()], ] - check_gl_entries(self, si.name, expected_gle, nowdate()) + + check_gl_entries(self, pe.name, expected_gle, nowdate(), voucher_type="Payment Entry") self.assertEqual(si.outstanding_amount, 0) set_advance_flag(company="_Test Company", flag=0, default_account="") @@ -3401,18 +3404,18 @@ def get_sales_invoice_for_e_invoice(): return si -def check_gl_entries(doc, voucher_no, expected_gle, posting_date): +def check_gl_entries(doc, voucher_no, expected_gle, posting_date, voucher_type="Sales Invoice"): gl = frappe.qb.DocType("GL Entry") q = ( frappe.qb.from_(gl) .select(gl.account, gl.debit, gl.credit, gl.posting_date) .where( - (gl.voucher_type == "Sales Invoice") + (gl.voucher_type == voucher_type) & (gl.voucher_no == voucher_no) & (gl.posting_date >= posting_date) & (gl.is_cancelled == 0) ) - .orderby(gl.posting_date, gl.account) + .orderby(gl.posting_date, gl.account, gl.creation) ) gl_entries = q.run(as_dict=True) diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index e69dcd4199..c1d365304f 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -436,9 +436,7 @@ def add_cc(args=None): return cc.name -def reconcile_against_document( - args, skip_ref_details_update_for_pe=False, is_reconcile=True -): # nosemgrep +def reconcile_against_document(args, skip_ref_details_update_for_pe=False): # nosemgrep """ Cancel PE or JV, Update against document, split if required and resubmit """ @@ -474,7 +472,7 @@ def reconcile_against_document( doc.save(ignore_permissions=True) # re-submit advance entry doc = frappe.get_doc(entry.voucher_type, entry.voucher_no) - gl_map = doc.build_gl_map(is_reconcile) + gl_map = doc.build_gl_map() create_payment_ledger_entry(gl_map, update_outstanding="No", cancel=0, adv_adj=1) # Only update outstanding for newly linked vouchers diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index dc499b9155..80f8430724 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1025,7 +1025,7 @@ class AccountsController(TransactionBase): ) ) - def update_against_document_in_jv(self, is_reconcile=True): + def update_against_document_in_jv(self): """ Links invoice and advance voucher: 1. cancel advance voucher @@ -1082,7 +1082,7 @@ class AccountsController(TransactionBase): if lst: from erpnext.accounts.utils import reconcile_against_document - reconcile_against_document(lst, is_reconcile) + reconcile_against_document(lst) def on_cancel(self): from erpnext.accounts.utils import unlink_ref_doc_from_payment_entries From 92f845c0e1093ec1eeeb6e3bcd66d27ca4f5ca29 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 21 Jun 2023 12:21:19 +0530 Subject: [PATCH 16/99] chore: Advance fetching order --- erpnext/accounts/party.py | 6 +- erpnext/controllers/accounts_controller.py | 164 +++++++++++---------- 2 files changed, 91 insertions(+), 79 deletions(-) diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index d6aa7d8402..03cf82a2b0 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -410,7 +410,11 @@ def get_party_account(party_type, party=None, company=None, include_advance=Fals if include_advance and party_type in ["Customer", "Supplier"]: advance_account = get_party_advance_account(party_type, party, company) - return [account, advance_account] + if advance_account: + return [account, advance_account] + else: + return [account] + return account diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 80f8430724..eec58e853d 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -892,9 +892,10 @@ class AccountsController(TransactionBase): amount_field = "debit_in_account_currency" order_field = "purchase_order" order_doctype = "Purchase Order" - party_account = [ - get_party_account(party_type, party=party, company=self.company, include_advance=True)[1] - ] + + party_account = get_party_account( + party_type, party=party, company=self.company, include_advance=True + ) order_list = list(set(d.get(order_field) for d in self.get("items") if d.get(order_field))) @@ -2203,37 +2204,59 @@ def get_advance_payment_entries( condition=None, ): - q = build_query( - party_type, - party, - party_account, - order_doctype, - order_list, - include_unallocated, - against_all_orders, - limit, - condition, - ) + payment_entries = [] + payment_entry = frappe.qb.DocType("Payment Entry") - payment_entries = q.run(as_dict=True) + if order_list or against_all_orders: + q = get_common_query( + party_type, + party, + party_account, + limit, + condition, + ) + payment_ref = frappe.qb.DocType("Payment Entry Reference") - return list(payment_entries) + q = q.inner_join(payment_ref).on(payment_entry.name == payment_ref.parent) + q = q.select( + (payment_ref.allocated_amount).as_("amount"), + (payment_ref.name).as_("reference_row"), + (payment_ref.reference_name).as_("against_order"), + payment_ref.reference_doctype == order_doctype, + ) + + if order_list: + q = q.where(payment_ref.reference_name.isin(order_list)) + + allocated = list(q.run(as_dict=True)) + payment_entries += allocated + + if include_unallocated: + q = get_common_query( + party_type, + party, + party_account, + limit, + condition, + ) + q = q.select((payment_entry.unallocated_amount).as_("amount")) + q = q.where(payment_entry.unallocated_amount > 0) + + unallocated = list(q.run(as_dict=True)) + payment_entries += unallocated + + return payment_entries -def build_query( +def get_common_query( party_type, party, party_account, - order_doctype, - order_list, - include_unallocated, - against_all_orders, limit, condition, ): payment_type = "Receive" if party_type == "Customer" else "Pay" payment_entry = frappe.qb.DocType("Payment Entry") - payment_ref = frappe.qb.DocType("Payment Entry Reference") q = ( frappe.qb.from_(payment_entry) @@ -2259,68 +2282,53 @@ def build_query( q = q.where(payment_entry.paid_to.isin(party_account)) if payment_type == "Receive": - q = q.select(payment_entry.source_exchange_rate) + q = q.select((payment_entry.source_exchange_rate).as_("exchange_rate")) else: - q.select(payment_entry.target_exchange_rate) + q = q.select((payment_entry.target_exchange_rate).as_("exchange_rate")) - if include_unallocated: - q = q.select((payment_entry.unallocated_amount).as_("amount")) - q = q.where(payment_entry.unallocated_amount > 0) - - if condition: - q = q.where(payment_entry.company == condition["company"]) - q = ( - q.where(payment_entry.posting_date >= condition["from_payment_date"]) - if condition.get("from_payment_date") - else q - ) - q = ( - q.where(payment_entry.posting_date <= condition["to_payment_date"]) - if condition.get("to_payment_date") - else q - ) - if condition.get("get_payments") == True: - q = ( - q.where(payment_entry.cost_center == condition["cost_center"]) - if condition.get("cost_center") - else q - ) - q = ( - q.where(payment_entry.unallocated_amount >= condition["minimum_payment_amount"]) - if condition.get("minimum_payment_amount") - else q - ) - q = ( - q.where(payment_entry.unallocated_amount <= condition["maximum_payment_amount"]) - if condition.get("maximum_payment_amount") - else q - ) - else: - q = ( - q.where(payment_entry.total_debit >= condition["minimum_payment_amount"]) - if condition.get("minimum_payment_amount") - else q - ) - q = ( - q.where(payment_entry.total_debit <= condition["maximum_payment_amount"]) - if condition.get("maximum_payment_amount") - else q - ) - - elif order_list or against_all_orders: - q = q.inner_join(payment_ref).on(payment_entry.name == payment_ref.parent) - q = q.select( - (payment_ref.allocated_amount).as_("amount"), - (payment_ref.name).as_("reference_row"), - (payment_ref.reference_name).as_("against_order"), - payment_ref.reference_doctype == order_doctype, + if condition: + q = q.where(payment_entry.company == condition["company"]) + q = ( + q.where(payment_entry.posting_date >= condition["from_payment_date"]) + if condition.get("from_payment_date") + else q ) - - if order_list: - q = q.where(payment_ref.reference_name.isin(order_list)) + q = ( + q.where(payment_entry.posting_date <= condition["to_payment_date"]) + if condition.get("to_payment_date") + else q + ) + if condition.get("get_payments") == True: + q = ( + q.where(payment_entry.cost_center == condition["cost_center"]) + if condition.get("cost_center") + else q + ) + q = ( + q.where(payment_entry.unallocated_amount >= condition["minimum_payment_amount"]) + if condition.get("minimum_payment_amount") + else q + ) + q = ( + q.where(payment_entry.unallocated_amount <= condition["maximum_payment_amount"]) + if condition.get("maximum_payment_amount") + else q + ) + else: + q = ( + q.where(payment_entry.total_debit >= condition["minimum_payment_amount"]) + if condition.get("minimum_payment_amount") + else q + ) + q = ( + q.where(payment_entry.total_debit <= condition["maximum_payment_amount"]) + if condition.get("maximum_payment_amount") + else q + ) q = q.orderby(payment_entry.posting_date) q = q.limit(limit) if limit else q + return q From b64ebc6fcc4130dfeb8d83f44072a4dd01eca42a Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 21 Jun 2023 17:49:45 +0530 Subject: [PATCH 17/99] test: fix payment reco tests --- .../test_process_deferred_accounting.py | 4 ++-- erpnext/controllers/accounts_controller.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/process_deferred_accounting/test_process_deferred_accounting.py b/erpnext/accounts/doctype/process_deferred_accounting/test_process_deferred_accounting.py index 5a0aeb7284..83646c90ba 100644 --- a/erpnext/accounts/doctype/process_deferred_accounting/test_process_deferred_accounting.py +++ b/erpnext/accounts/doctype/process_deferred_accounting/test_process_deferred_accounting.py @@ -38,7 +38,7 @@ class TestProcessDeferredAccounting(unittest.TestCase): si.save() si.submit() - process_deferred_accounting = doc = frappe.get_doc( + process_deferred_accounting = frappe.get_doc( dict( doctype="Process Deferred Accounting", posting_date="2019-01-01", @@ -56,7 +56,7 @@ class TestProcessDeferredAccounting(unittest.TestCase): ["Sales - _TC", 0.0, 33.85, "2019-01-31"], ] - check_gl_entries(self, si.name, expected_gle, "2019-01-10") + check_gl_entries(self, si.name, expected_gle, "2019-01-31") def test_pda_submission_and_cancellation(self): pda = frappe.get_doc( diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index eec58e853d..3dadd46ee8 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -2273,11 +2273,11 @@ def get_common_query( ) if party_type == "Customer": - q = q.select(payment_entry.paid_from_account_currency) + q = q.select((payment_entry.paid_from_account_currency).as_("currency")) q = q.select(payment_entry.paid_from) q = q.where(payment_entry.paid_from.isin(party_account)) else: - q = q.select(payment_entry.paid_to_account_currency) + q = q.select((payment_entry.paid_to_account_currency).as_("currency")) q = q.select(payment_entry.paid_to) q = q.where(payment_entry.paid_to.isin(party_account)) From 3aead05f42ec62ee0edd2d1e9ca205e8ca6b0f2f Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 22 Jun 2023 11:41:43 +0530 Subject: [PATCH 18/99] fix: Test related errors --- .../doctype/payment_entry/payment_entry.py | 7 +++++-- .../payment_reconciliation.py | 10 +++++++-- erpnext/accounts/utils.py | 2 +- erpnext/controllers/accounts_controller.py | 21 +++++++------------ 4 files changed, 22 insertions(+), 18 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 752d22a353..f6c6bce5bc 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -949,10 +949,8 @@ class PaymentEntry(AccountsController): if self.party_account: if self.payment_type == "Receive": against_account = self.paid_to - dr_or_cr = "credit" else: against_account = self.paid_from - dr_or_cr = "debit" party_dict = self.get_gl_dict( { @@ -965,6 +963,11 @@ class PaymentEntry(AccountsController): }, item=self, ) + + dr_or_cr = ( + "credit" if erpnext.get_party_account_type(self.party_type) == "Receivable" else "debit" + ) + is_advance = self.is_advance_entry() for d in self.get("references"): diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index 9d869f21ee..e9cc3902d3 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -59,7 +59,10 @@ class PaymentReconciliation(Document): self.add_payment_entries(non_reconciled_payments) def get_payment_entries(self): - party_account = [self.receivable_payable_account, self.default_advance_account] + if self.default_advance_account: + party_account = [self.receivable_payable_account, self.default_advance_account] + else: + party_account = [self.receivable_payable_account] order_doctype = "Sales Order" if self.party_type == "Customer" else "Purchase Order" condition = frappe._dict( @@ -352,7 +355,10 @@ class PaymentReconciliation(Document): for row in self.get("allocation"): reconciled_entry = [] if row.invoice_number and row.allocated_amount: - if row.invoice_type in ["Sales Invoice", "Purchase Invoice"]: + if ( + row.invoice_type in ["Sales Invoice", "Purchase Invoice"] + and row.reference_type == "Payment Entry" + ): gl_entries = [] make_advance_liability_entry( gl_entries, row.reference_name, row.allocated_amount, row.invoice_number, self.party_type diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index c1d365304f..d9561ad468 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -500,7 +500,7 @@ def check_if_advance_entry_modified(args): q = ( frappe.qb.from_(journal_entry) - .innerjoin(journal_acc) + .inner_join(journal_acc) .on(journal_entry.name == journal_acc.parent) ) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 3dadd46ee8..72c93261aa 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -2156,11 +2156,7 @@ def get_advance_journal_entries( ConstantColumn("Journal Entry").as_("reference_type"), (journal_entry.name).as_("reference_name"), (journal_entry.remark).as_("remarks"), - ( - journal_acc.debit_in_account_currency - if party_type == "Supplier" - else journal_acc.credit_in_account_currency - ).as_("amount"), + (journal_acc[amount_field]).as_("amount"), (journal_acc.name).as_("reference_row"), (journal_acc.reference_name).as_("against_order"), (journal_acc.exchange_rate), @@ -2179,12 +2175,13 @@ def get_advance_journal_entries( else: q = q.where(journal_acc.debit_in_account_currency > 0) + if include_unallocated: + q = q.where((journal_acc.reference_name.isnull()) | (journal_acc.reference_name == "")) + if order_list: - q = q.where(journal_acc.reference_type == order_doctype) - if include_unallocated: - q = q.where(journal_acc.reference_name.isin(order_list) | (journal_acc.reference_name == "")) - else: - q = q.where(journal_acc.reference_name.isin(order_list)) + q = q.where( + (journal_acc.reference_type == order_doctype) & ((journal_acc.reference).isin(order_list)) + ) q = q.orderby(journal_entry.posting_date) @@ -2222,15 +2219,14 @@ def get_advance_payment_entries( (payment_ref.allocated_amount).as_("amount"), (payment_ref.name).as_("reference_row"), (payment_ref.reference_name).as_("against_order"), - payment_ref.reference_doctype == order_doctype, ) + q = q.where(payment_ref.reference_doctype == order_doctype) if order_list: q = q.where(payment_ref.reference_name.isin(order_list)) allocated = list(q.run(as_dict=True)) payment_entries += allocated - if include_unallocated: q = get_common_query( party_type, @@ -2244,7 +2240,6 @@ def get_advance_payment_entries( unallocated = list(q.run(as_dict=True)) payment_entries += unallocated - return payment_entries From 6d121ae6e42b678a2879e6278e58e3441b2787d7 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 22 Jun 2023 13:03:09 +0530 Subject: [PATCH 19/99] chore: fix typo --- erpnext/controllers/accounts_controller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 72c93261aa..02fef4d31b 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -2180,7 +2180,7 @@ def get_advance_journal_entries( if order_list: q = q.where( - (journal_acc.reference_type == order_doctype) & ((journal_acc.reference).isin(order_list)) + (journal_acc.reference_type == order_doctype) & ((journal_acc.reference_type).isin(order_list)) ) q = q.orderby(journal_entry.posting_date) From d81d6069fb8688223fd5e777c781347990100aa7 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 22 Jun 2023 18:28:16 +0530 Subject: [PATCH 20/99] fix: JV query --- erpnext/accounts/utils.py | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index d9561ad468..f92e2c7cba 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -502,24 +502,19 @@ def check_if_advance_entry_modified(args): frappe.qb.from_(journal_entry) .inner_join(journal_acc) .on(journal_entry.name == journal_acc.parent) - ) - - if args.get("dr_or_cr") == "debit_in_account_currency": - q = q.select(journal_acc.debit_in_account_currency) - else: - q = q.select(journal_acc.credit_in_account_currency) - - q = q.where( - (journal_acc.account == args.get("account")) - & ((journal_acc.party_type == args.get("party_type"))) - & ((journal_acc.party == args.get("party"))) - & ( - (journal_acc.reference_type == None) - | (journal_acc.reference_type.isin(["", "Sales Order", "Purchase Order"])) + .select(journal_acc[args.get("dr_or_cr")]) + .where( + (journal_acc.account == args.get("account")) + & ((journal_acc.party_type == args.get("party_type"))) + & ((journal_acc.party == args.get("party"))) + & ( + (journal_acc.reference_type.isnull()) + | (journal_acc.reference_type.isin(["", "Sales Order", "Purchase Order"])) + ) + & ((journal_entry.name == args.get("voucher_no"))) + & ((journal_acc.name == args.get("voucher_detail_no"))) + & ((journal_entry.docstatus == 1)) ) - & ((journal_entry.name == args.get("voucher_no"))) - & ((journal_acc.name == args.get("voucher_detail_no"))) - & ((journal_entry.docstatus == 1)) ) else: From b101dceb2a047731b8bd9931639d3ece6b66087e Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 22 Jun 2023 19:38:33 +0530 Subject: [PATCH 21/99] test: GL Entry order --- erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 4cf3abaad1..6599e3960a 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -3252,9 +3252,9 @@ class TestSalesInvoice(unittest.TestCase): si.submit() expected_gle = [ - ["_Test Receivable USD - _TC", 7500.0, 500], ["Exchange Gain/Loss - _TC", 500.0, 0.0], ["Sales - _TC", 0.0, 7500.0], + ["_Test Receivable USD - _TC", 7500.0, 500], ] check_gl_entries(self, si.name, expected_gle, nowdate()) From 11a9bd523dd33430e18916e527abdbbc7a89d09f Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 22 Jun 2023 20:26:12 +0530 Subject: [PATCH 22/99] test: Add posting date parameter --- .../accounts/doctype/sales_invoice/test_sales_invoice.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 6599e3960a..a5280bcbf4 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -3252,9 +3252,10 @@ class TestSalesInvoice(unittest.TestCase): si.submit() expected_gle = [ - ["Exchange Gain/Loss - _TC", 500.0, 0.0], - ["Sales - _TC", 0.0, 7500.0], - ["_Test Receivable USD - _TC", 7500.0, 500], + ["_Test Receivable USD - _TC", 7500.0, 0.0, nowdate()], + ["_Test Receivable USD - _TC", 0.0, 500.0, nowdate()], + ["Exchange Gain/Loss - _TC", 500.0, 0.0, nowdate()], + ["Sales - _TC", 0.0, 7500.0, nowdate()], ] check_gl_entries(self, si.name, expected_gle, nowdate()) From 05c2198569f88785432c4ca6fee49fa04e268b03 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 22 Jun 2023 21:08:58 +0530 Subject: [PATCH 23/99] test: Update order --- erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index a5280bcbf4..e8e81fd82f 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -3252,9 +3252,9 @@ class TestSalesInvoice(unittest.TestCase): si.submit() expected_gle = [ + ["_Test Exchange Gain/Loss - _TC", 500.0, 0.0, nowdate()], ["_Test Receivable USD - _TC", 7500.0, 0.0, nowdate()], ["_Test Receivable USD - _TC", 0.0, 500.0, nowdate()], - ["Exchange Gain/Loss - _TC", 500.0, 0.0, nowdate()], ["Sales - _TC", 0.0, 7500.0, nowdate()], ] From da6bc1a13eb0effeb3f5e307d91ed6380e6dd823 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 23 Jun 2023 20:57:51 +0530 Subject: [PATCH 24/99] refactor: Redo workflows --- .../doctype/payment_entry/payment_entry.json | 12 +- .../doctype/payment_entry/payment_entry.py | 108 +++++++------ .../payment_reconciliation.py | 15 +- .../purchase_invoice/purchase_invoice.py | 14 +- .../purchase_invoice/test_purchase_invoice.py | 17 +- .../doctype/sales_invoice/sales_invoice.py | 16 +- .../sales_invoice/test_sales_invoice.py | 17 +- erpnext/accounts/general_ledger.py | 10 +- erpnext/accounts/utils.py | 4 +- erpnext/buying/doctype/supplier/supplier.js | 3 +- erpnext/controllers/accounts_controller.py | 147 +++++++++--------- erpnext/selling/doctype/customer/customer.js | 3 +- erpnext/setup/doctype/company/company.js | 4 +- erpnext/setup/doctype/company/company.json | 22 +-- .../doctype/customer_group/customer_group.js | 1 + .../doctype/supplier_group/supplier_group.js | 1 + 16 files changed, 208 insertions(+), 186 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.json b/erpnext/accounts/doctype/payment_entry/payment_entry.json index 6224d4038d..d7b6a198df 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.json +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.json @@ -19,6 +19,7 @@ "party_type", "party", "party_name", + "book_advance_payments_in_separate_party_account", "column_break_11", "bank_account", "party_bank_account", @@ -735,12 +736,21 @@ "fieldname": "get_outstanding_orders", "fieldtype": "Button", "label": "Get Outstanding Orders" + }, + { + "default": "0", + "fetch_from": "company.book_advance_payments_in_separate_party_account", + "fieldname": "book_advance_payments_in_separate_party_account", + "fieldtype": "Check", + "hidden": 1, + "label": "Book Advance Payments in Separate Party Account", + "read_only": 1 } ], "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2023-06-19 11:38:04.387219", + "modified": "2023-06-23 18:07:38.023010", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Entry", diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index f6c6bce5bc..8141d0519e 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -21,7 +21,11 @@ from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_ban from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import ( get_party_tax_withholding_details, ) -from erpnext.accounts.general_ledger import make_gl_entries, process_gl_map +from erpnext.accounts.general_ledger import ( + make_gl_entries, + make_reverse_gl_entries, + process_gl_map, +) from erpnext.accounts.party import get_party_account from erpnext.accounts.utils import get_account_currency, get_balance_on, get_outstanding_invoices from erpnext.controllers.accounts_controller import ( @@ -88,17 +92,14 @@ class PaymentEntry(AccountsController): if self.difference_amount: frappe.throw(_("Difference Amount must be zero")) self.make_gl_entries() + self.make_advance_gl_entries() self.update_outstanding_amounts() self.update_advance_paid() self.update_payment_schedule() self.set_status() def set_liability_account(self): - book_advance_payments_as_liability = frappe.get_value( - "Company", {"company_name": self.company}, "book_advance_payments_as_liability" - ) - - if not book_advance_payments_as_liability: + if not self.book_advance_payments_in_separate_party_account: return account_type = frappe.get_value( @@ -139,6 +140,7 @@ class PaymentEntry(AccountsController): "Repost Payment Ledger Items", ) self.make_gl_entries(cancel=1) + self.make_advance_gl_entries(cancel=1) self.update_outstanding_amounts() self.update_advance_paid() self.delink_advance_entry_references() @@ -212,7 +214,8 @@ class PaymentEntry(AccountsController): "party_account": self.paid_from if self.payment_type == "Receive" else self.paid_to, "get_outstanding_invoices": True, "get_orders_to_be_billed": True, - } + }, + validate=True, ) # Group latest_references by (voucher_type, voucher_no) @@ -417,6 +420,13 @@ class PaymentEntry(AccountsController): elif self.party_type == "Employee": ref_party_account = ref_doc.payable_account + if ref_party_account != self.party_account: + frappe.throw( + _("{0} {1} is associated with {2}, but Party Account is {3}").format( + d.reference_doctype, d.reference_name, ref_party_account, self.party_account + ) + ) + if ref_doc.doctype == "Purchase Invoice" and ref_doc.get("on_hold"): frappe.throw( _("{0} {1} is on hold").format(d.reference_doctype, d.reference_name), @@ -952,7 +962,7 @@ class PaymentEntry(AccountsController): else: against_account = self.paid_from - party_dict = self.get_gl_dict( + party_gl_dict = self.get_gl_dict( { "account": self.party_account, "party_type": self.party_type, @@ -968,32 +978,21 @@ class PaymentEntry(AccountsController): "credit" if erpnext.get_party_account_type(self.party_type) == "Receivable" else "debit" ) - is_advance = self.is_advance_entry() - for d in self.get("references"): - gle = party_dict.copy() - book_advance_payments_as_liability = frappe.get_value( - "Company", {"company_name": self.company}, "book_advance_payments_as_liability" - ) - if ( - d.reference_doctype in ["Sales Invoice", "Purchase Invoice"] - and book_advance_payments_as_liability - and is_advance - ): - self.make_invoice_liability_entry(gl_entries, d) - against_voucher_type = "Payment Entry" - against_voucher = self.name - else: - against_voucher_type = d.reference_doctype - against_voucher = d.reference_name + cost_center = self.cost_center + if d.reference_doctype == "Sales Invoice" and not cost_center: + cost_center = frappe.db.get_value(d.reference_doctype, d.reference_name, "cost_center") + + gle = party_gl_dict.copy() allocated_amount_in_company_currency = self.calculate_base_allocated_amount_for_reference(d) gle.update( { dr_or_cr: allocated_amount_in_company_currency, dr_or_cr + "_in_account_currency": d.allocated_amount, - "against_voucher_type": against_voucher_type, - "against_voucher": against_voucher, + "against_voucher_type": d.reference_doctype, + "against_voucher": d.reference_name, + "cost_center": cost_center, } ) gl_entries.append(gle) @@ -1002,25 +1001,44 @@ class PaymentEntry(AccountsController): exchange_rate = self.get_exchange_rate() base_unallocated_amount = self.unallocated_amount * exchange_rate - gle = party_dict.copy() + gle = party_gl_dict.copy() gle.update( { dr_or_cr + "_in_account_currency": self.unallocated_amount, dr_or_cr: base_unallocated_amount, - "against_voucher_type": "Payment Entry", - "against_voucher": self.name, } ) gl_entries.append(gle) - def is_advance_entry(self): - for d in self.get("references"): - if d.reference_doctype in ("Sales Order", "Purchase Order"): - return True - if self.unallocated_amount > 0: - return True - return False + def make_advance_gl_entries(self, against_voucher_type=None, against_voucher=None, cancel=0): + if self.book_advance_payments_in_separate_party_account: + gl_entries = [] + for d in self.get("references"): + if d.reference_doctype in ("Sales Invoice", "Purchase Invoice"): + if not (against_voucher_type and against_voucher) or ( + d.reference_doctype == against_voucher_type and d.reference_name == against_voucher + ): + self.make_invoice_liability_entry(gl_entries, d) + + if cancel: + for entry in gl_entries: + frappe.db.set_value( + "GL Entry", + { + "voucher_no": self.name, + "voucher_type": self.doctype, + "voucher_detail_no": entry.voucher_detail_no, + "against_voucher_type": entry.against_voucher_type, + "against_voucher": entry.against_voucher, + }, + "is_cancelled", + 1, + ) + + make_reverse_gl_entries(gl_entries=gl_entries, partial_cancel=True) + else: + make_gl_entries(gl_entries) def make_invoice_liability_entry(self, gl_entries, invoice): args_dict = { @@ -1030,6 +1048,7 @@ class PaymentEntry(AccountsController): "cost_center": self.cost_center, "voucher_type": "Payment Entry", "voucher_no": self.name, + "voucher_detail_no": invoice.name, } dr_or_cr = "credit" if invoice.reference_doctype == "Sales Invoice" else "debit" @@ -1391,7 +1410,7 @@ def validate_inclusive_tax(tax, doc): @frappe.whitelist() -def get_outstanding_reference_documents(args): +def get_outstanding_reference_documents(args, validate=False): if isinstance(args, str): args = json.loads(args) @@ -1511,13 +1530,14 @@ def get_outstanding_reference_documents(args): elif args.get("get_orders_to_be_billed"): ref_document_type = "orders" - frappe.msgprint( - _( - "No outstanding {0} found for the {1} {2} which qualify the filters you have specified." - ).format( - ref_document_type, _(args.get("party_type")).lower(), frappe.bold(args.get("party")) + if not validate: + frappe.msgprint( + _( + "No outstanding {0} found for the {1} {2} which qualify the filters you have specified." + ).format( + ref_document_type, _(args.get("party_type")).lower(), frappe.bold(args.get("party")) + ) ) - ) return data diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index e9cc3902d3..8e2f0e5232 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -12,16 +12,12 @@ import erpnext from erpnext.accounts.doctype.process_payment_reconciliation.process_payment_reconciliation import ( is_any_doc_running, ) -from erpnext.accounts.general_ledger import make_gl_entries from erpnext.accounts.utils import ( QueryPaymentLedger, get_outstanding_invoices, reconcile_against_document, ) -from erpnext.controllers.accounts_controller import ( - get_advance_payment_entries, - make_advance_liability_entry, -) +from erpnext.controllers.accounts_controller import get_advance_payment_entries class PaymentReconciliation(Document): @@ -355,15 +351,6 @@ class PaymentReconciliation(Document): for row in self.get("allocation"): reconciled_entry = [] if row.invoice_number and row.allocated_amount: - if ( - row.invoice_type in ["Sales Invoice", "Purchase Invoice"] - and row.reference_type == "Payment Entry" - ): - gl_entries = [] - make_advance_liability_entry( - gl_entries, row.reference_name, row.allocated_amount, row.invoice_number, self.party_type - ) - make_gl_entries(gl_entries) if row.reference_type in ["Sales Invoice", "Purchase Invoice"]: reconciled_entry = dr_or_cr_notes else: diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 68fa7bf1c9..230a8b3c58 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -33,10 +33,7 @@ from erpnext.accounts.utils import get_account_currency, get_fiscal_year from erpnext.assets.doctype.asset.asset import get_asset_account, is_cwip_accounting_enabled from erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account from erpnext.buying.utils import check_on_hold_or_closed_status -from erpnext.controllers.accounts_controller import ( - check_advance_liability_entry, - validate_account_head, -) +from erpnext.controllers.accounts_controller import validate_account_head from erpnext.controllers.buying_controller import BuyingController from erpnext.stock import get_warehouse_account_map from erpnext.stock.doctype.purchase_receipt.purchase_receipt import ( @@ -576,15 +573,6 @@ class PurchaseInvoice(BuyingController): gl_entries = [] self.make_supplier_gl_entry(gl_entries) - - check_advance_liability_entry( - gl_entries, - company=self.company, - advances=self.advances, - invoice=self.name, - party_type="Supplier", - ) - self.make_item_gl_entries(gl_entries) self.make_precision_loss_gl_entry(gl_entries) diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index 569a48acad..8c96480478 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -1664,10 +1664,17 @@ class TestPurchaseInvoice(unittest.TestCase, StockTestMixin): self.assertTrue(return_pi.docstatus == 1) - def test_advance_entries_as_liability(self): + def test_advance_entries_as_asset(self): from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_entry - set_advance_flag(company="_Test Company", flag=1, default_account="Debtors - _TC") + account = create_account( + parent_account="Current Assets - _TC", + account_name="Advances Paid", + company="_Test Company", + account_type="Receivable", + ) + + set_advance_flag(company="_Test Company", flag=1, default_account=account) pe = create_payment_entry( company="_Test Company", @@ -1701,13 +1708,15 @@ class TestPurchaseInvoice(unittest.TestCase, StockTestMixin): # Check GL Entry against payment doctype expected_gle = [ + ["Advances Paid - _TC", 0.0, 500, nowdate()], ["Cash - _TC", 0.0, 500, nowdate()], ["Creditors - _TC", 500, 0.0, nowdate()], ["Creditors - _TC", 500, 0.0, nowdate()], - ["Debtors - _TC", 0.0, 500, nowdate()], ] check_gl_entries(self, pe.name, expected_gle, nowdate(), voucher_type="Payment Entry") + + pi.load_from_db() self.assertEqual(pi.outstanding_amount, 500) set_advance_flag(company="_Test Company", flag=0, default_account="") @@ -1733,7 +1742,7 @@ def set_advance_flag(company, flag, default_account): "Company", company, { - "book_advance_payments_as_liability": flag, + "book_advance_payments_in_separate_party_account": flag, "default_advance_paid_account": default_account, }, ) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index d2cd95fe9e..25553cff0c 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -32,10 +32,7 @@ from erpnext.assets.doctype.asset.depreciation import ( reset_depreciation_schedule, reverse_depreciation_entry_made_after_disposal, ) -from erpnext.controllers.accounts_controller import ( - check_advance_liability_entry, - validate_account_head, -) +from erpnext.controllers.accounts_controller import validate_account_head from erpnext.controllers.selling_controller import SellingController from erpnext.projects.doctype.timesheet.timesheet import get_projectwise_timesheet_data from erpnext.setup.doctype.company.company import update_company_current_month_sales @@ -1055,21 +1052,12 @@ class SalesInvoice(SellingController): elif self.docstatus == 2 and cint(self.update_stock) and cint(auto_accounting_for_stock): make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name) - def get_gl_entries(self, warehouse_account=None): + def get_gl_entries(self): from erpnext.accounts.general_ledger import merge_similar_entries gl_entries = [] self.make_customer_gl_entry(gl_entries) - - check_advance_liability_entry( - gl_entries, - company=self.company, - advances=self.advances, - invoice=self.name, - party_type="Customer", - ) - self.make_tax_gl_entries(gl_entries) self.make_exchange_gain_loss_gl_entries(gl_entries) self.make_internal_transfer_gl_entries(gl_entries) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index e8e81fd82f..69ddfaac8d 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -6,7 +6,6 @@ import unittest import frappe from frappe.model.dynamic_links import get_dynamic_link_map -from frappe.model.naming import make_autoname from frappe.tests.utils import change_settings from frappe.utils import add_days, flt, getdate, nowdate, today @@ -35,7 +34,6 @@ from erpnext.stock.doctype.serial_and_batch_bundle.test_serial_and_batch_bundle get_serial_nos_from_bundle, make_serial_batch_bundle, ) -from erpnext.stock.doctype.serial_no.serial_no import SerialNoWarehouseError from erpnext.stock.doctype.stock_entry.test_stock_entry import ( get_qty_after_transaction, make_stock_entry, @@ -3314,7 +3312,14 @@ class TestSalesInvoice(unittest.TestCase): def test_advance_entries_as_liability(self): from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_entry - set_advance_flag(company="_Test Company", flag=1, default_account="Creditors - _TC") + account = create_account( + parent_account="Current Liabilities - _TC", + account_name="Advances Received", + company="_Test Company", + account_type="Receivable", + ) + + set_advance_flag(company="_Test Company", flag=1, default_account=account) pe = create_payment_entry( company="_Test Company", @@ -3347,13 +3352,15 @@ class TestSalesInvoice(unittest.TestCase): # Check GL Entry against payment doctype expected_gle = [ + ["Advances Received - _TC", 500, 0.0, nowdate()], ["Cash - _TC", 1000, 0.0, nowdate()], - ["Creditors - _TC", 500, 0.0, nowdate()], ["Debtors - _TC", 0.0, 1000, nowdate()], ["Debtors - _TC", 0.0, 500, nowdate()], ] check_gl_entries(self, pe.name, expected_gle, nowdate(), voucher_type="Payment Entry") + + si.load_from_db() self.assertEqual(si.outstanding_amount, 0) set_advance_flag(company="_Test Company", flag=0, default_account="") @@ -3364,7 +3371,7 @@ def set_advance_flag(company, flag, default_account): "Company", company, { - "book_advance_payments_as_liability": flag, + "book_advance_payments_in_separate_party_account": flag, "default_advance_received_account": default_account, }, ) diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py index a0954a955a..22f39596e6 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -501,7 +501,12 @@ def get_round_off_account_and_cost_center( def make_reverse_gl_entries( - gl_entries=None, voucher_type=None, voucher_no=None, adv_adj=False, update_outstanding="Yes" + gl_entries=None, + voucher_type=None, + voucher_no=None, + adv_adj=False, + update_outstanding="Yes", + partial_cancel=False, ): """ Get original gl entries of the voucher @@ -528,7 +533,8 @@ def make_reverse_gl_entries( is_opening = any(d.get("is_opening") == "Yes" for d in gl_entries) validate_against_pcv(is_opening, gl_entries[0]["posting_date"], gl_entries[0]["company"]) - set_as_cancel(gl_entries[0]["voucher_type"], gl_entries[0]["voucher_no"]) + if not partial_cancel: + set_as_cancel(gl_entries[0]["voucher_type"], gl_entries[0]["voucher_no"]) for entry in gl_entries: new_gle = copy.deepcopy(entry) diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index f92e2c7cba..5c256b730f 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -474,6 +474,7 @@ def reconcile_against_document(args, skip_ref_details_update_for_pe=False): # n doc = frappe.get_doc(entry.voucher_type, entry.voucher_no) gl_map = doc.build_gl_map() create_payment_ledger_entry(gl_map, update_outstanding="No", cancel=0, adv_adj=1) + doc.make_advance_gl_entries(entry.against_voucher_type, entry.against_voucher) # Only update outstanding for newly linked vouchers for entry in entries: @@ -731,8 +732,9 @@ def remove_ref_doc_link_from_pe(ref_type, ref_no): for pe in linked_pe: try: - pe_doc = frappe.get_doc("Payment Entry", pe) + pe_doc = frappe.get_doc("Payment Entry", pe, cache=True) pe_doc.set_amounts() + pe_doc.make_advance_gl_entries(against_voucher_type=ref_type, against_voucher=ref_no, cancel=1) pe_doc.clear_unallocated_reference_document_rows() pe_doc.validate_payment_type_with_outstanding() except Exception as e: diff --git a/erpnext/buying/doctype/supplier/supplier.js b/erpnext/buying/doctype/supplier/supplier.js index a5bf4b246b..c9ac279722 100644 --- a/erpnext/buying/doctype/supplier/supplier.js +++ b/erpnext/buying/doctype/supplier/supplier.js @@ -22,7 +22,8 @@ frappe.ui.form.on("Supplier", { let d = locals[cdt][cdn]; return { filters: { - "account_type": "Receivable", + "account_type": "Payable", + "root_type": "Asset", "company": d.company, "is_group": 0 } diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 02fef4d31b..0d9ab6498d 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -756,6 +756,7 @@ class AccountsController(TransactionBase): "party": None, "project": self.get("project"), "post_net_value": args.get("post_net_value"), + "voucher_detail_no": args.get("voucher_detail_no"), } ) @@ -2915,82 +2916,82 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil parent.create_stock_reservation_entries() -def make_advance_liability_entry( - gl_entries, pe, allocated_amount, invoice, party_type, references=False -): - pe = frappe.get_doc("Payment Entry", pe) - if party_type == "Customer": - invoice = frappe.get_doc("Sales Invoice", invoice) - account = pe.paid_from - dr_or_cr = "debit" - rev = "credit" - against = invoice.debit_to - party = invoice.customer - else: - invoice = frappe.get_doc("Purchase Invoice", invoice) - account = pe.paid_to - dr_or_cr = "credit" - rev = "debit" - against = invoice.credit_to - party = invoice.supplier - gl_entries.append( - pe.get_gl_dict( - { - "account": account, - "party_type": party_type, - "party": party, - "due_date": invoice.due_date, - "against": against, - dr_or_cr: allocated_amount, - dr_or_cr + "_in_account_currency": allocated_amount, - rev: 0, - rev + "_in_account_currency": 0, - "cost_center": invoice.cost_center, - "project": invoice.project, - "against_voucher_type": "Payment Entry", - "against_voucher": pe.name, - }, - invoice.party_account_currency, - item=pe, - ) - ) +# def make_advance_liability_entry( +# gl_entries, pe, allocated_amount, invoice, party_type +# ): +# pe = frappe.get_doc("Payment Entry", pe) +# if party_type == "Customer": +# invoice = frappe.get_doc("Sales Invoice", invoice) +# account = pe.paid_from +# dr_or_cr = "debit" +# rev = "credit" +# against = invoice.debit_to +# party = invoice.customer +# else: +# invoice = frappe.get_doc("Purchase Invoice", invoice) +# account = pe.paid_to +# dr_or_cr = "credit" +# rev = "debit" +# against = invoice.credit_to +# party = invoice.supplier +# gl_entries.append( +# pe.get_gl_dict( +# { +# "account": account, +# "party_type": party_type, +# "party": party, +# "due_date": invoice.due_date, +# "against": against, +# dr_or_cr: allocated_amount, +# dr_or_cr + "_in_account_currency": allocated_amount, +# rev: 0, +# rev + "_in_account_currency": 0, +# "cost_center": invoice.cost_center, +# "project": invoice.project, +# "against_voucher_type": "Payment Entry", +# "against_voucher": pe.name, +# }, +# invoice.party_account_currency, +# item=pe, +# ) +# ) - (dr_or_cr, rev) = ("credit", "debit") if party_type == "Customer" else ("debit", "credit") - gl_entries.append( - pe.get_gl_dict( - { - "account": against, - "party_type": party_type, - "party": party, - "due_date": invoice.due_date, - dr_or_cr: allocated_amount, - dr_or_cr + "_in_account_currency": allocated_amount, - rev: 0, - rev + "_in_account_currency": 0, - "cost_center": invoice.cost_center, - "project": invoice.project, - "against_voucher_type": invoice.doctype, - "against_voucher": invoice.name, - }, - invoice.party_account_currency, - item=pe, - ) - ) +# (dr_or_cr, rev) = ("credit", "debit") if party_type == "Customer" else ("debit", "credit") +# gl_entries.append( +# pe.get_gl_dict( +# { +# "account": against, +# "party_type": party_type, +# "party": party, +# "due_date": invoice.due_date, +# dr_or_cr: allocated_amount, +# dr_or_cr + "_in_account_currency": allocated_amount, +# rev: 0, +# rev + "_in_account_currency": 0, +# "cost_center": invoice.cost_center, +# "project": invoice.project, +# "against_voucher_type": invoice.doctype, +# "against_voucher": invoice.name, +# }, +# invoice.party_account_currency, +# item=pe, +# ) +# ) -def check_advance_liability_entry(gl_entries, company, advances, invoice, party_type): - advance_payments_as_liability = frappe.db.get_value( - "Company", {"company_name": company}, "book_advance_payments_as_liability" - ) - if advance_payments_as_liability: - for advance_entry in advances: - make_advance_liability_entry( - gl_entries, - advance_entry.reference_name, - advance_entry.allocated_amount, - invoice=invoice, - party_type=party_type, - ) +# def check_advance_liability_entry(gl_entries, company, advances, invoice, party_type): +# advance_payments_as_liability = frappe.db.get_value( +# "Company", {"company_name": company}, "book_advance_payments_as_liability" +# ) +# if advance_payments_as_liability: +# for advance_entry in advances: +# make_advance_liability_entry( +# gl_entries, +# advance_entry.reference_name, +# advance_entry.allocated_amount, +# invoice=invoice, +# party_type=party_type, +# ) @erpnext.allow_regional diff --git a/erpnext/selling/doctype/customer/customer.js b/erpnext/selling/doctype/customer/customer.js index 408e89b0ef..c3bd753f1b 100644 --- a/erpnext/selling/doctype/customer/customer.js +++ b/erpnext/selling/doctype/customer/customer.js @@ -39,7 +39,8 @@ frappe.ui.form.on("Customer", { let d = locals[cdt][cdn]; return { filters: { - "account_type": 'Payable', + "account_type": 'Receivable', + "root_type": "Liability", "company": d.company, "is_group": 0 } diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js index 089e20d442..333538722e 100644 --- a/erpnext/setup/doctype/company/company.js +++ b/erpnext/setup/doctype/company/company.js @@ -227,8 +227,8 @@ erpnext.company.setup_queries = function(frm) { ["asset_received_but_not_billed", {"account_type": "Asset Received But Not Billed"}], ["unrealized_profit_loss_account", {"root_type": ["in", ["Liability", "Asset"]]}], ["default_provisional_account", {"root_type": ["in", ["Liability", "Asset"]]}], - ["default_advance_received_account", {"account_type": "Payable"}], - ["default_advance_paid_account", {"account_type": "Receivable"}], + ["default_advance_received_account", {"root_type": "Liability", "account_type": "Receivable"}], + ["default_advance_paid_account", {"root_type": "Asset", "account_type": "Payable"}], ], function(i, v) { erpnext.company.set_custom_query(frm, v); }); diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json index 611e2ab221..6292ad7349 100644 --- a/erpnext/setup/doctype/company/company.json +++ b/erpnext/setup/doctype/company/company.json @@ -71,7 +71,7 @@ "cost_center", "default_finance_book", "advance_payments_section", - "book_advance_payments_as_liability", + "book_advance_payments_in_separate_party_account", "column_break_fwcf", "default_advance_received_account", "default_advance_paid_account", @@ -700,20 +700,13 @@ "no_copy": 1, "options": "Account" }, - { - "default": "0", - "description": "Enabling this option will allow you to record -

1. Advances Received in a Liability Account instead of the Receivable Account

2. Advances Paid in an Asset Account instead of the Payable Account", - "fieldname": "book_advance_payments_as_liability", - "fieldtype": "Check", - "label": "Book Advance Payments in Separate Party Account" - }, { "fieldname": "advance_payments_section", "fieldtype": "Section Break", "label": "Advance Payments" }, { - "depends_on": "eval:doc.book_advance_payments_as_liability", + "depends_on": "eval:doc.book_advance_payments_in_separate_party_account", "fieldname": "default_advance_received_account", "fieldtype": "Link", "label": "Default Advance Received Account", @@ -721,7 +714,7 @@ "options": "Account" }, { - "depends_on": "eval:doc.book_advance_payments_as_liability", + "depends_on": "eval:doc.book_advance_payments_in_separate_party_account", "fieldname": "default_advance_paid_account", "fieldtype": "Link", "label": "Default Advance Paid Account", @@ -731,6 +724,13 @@ { "fieldname": "column_break_fwcf", "fieldtype": "Column Break" + }, + { + "default": "0", + "description": "Enabling this option will allow you to record -

1. Advances Received in a Liability Account instead of the Asset Account

2. Advances Paid in an Asset Account instead of the Liability Account", + "fieldname": "book_advance_payments_in_separate_party_account", + "fieldtype": "Check", + "label": "Book Advance Payments in Separate Party Account" } ], "icon": "fa fa-building", @@ -738,7 +738,7 @@ "image_field": "company_logo", "is_tree": 1, "links": [], - "modified": "2023-06-16 13:32:48.790947", + "modified": "2023-06-23 18:22:27.219706", "modified_by": "Administrator", "module": "Setup", "name": "Company", diff --git a/erpnext/setup/doctype/customer_group/customer_group.js b/erpnext/setup/doctype/customer_group/customer_group.js index ed9893346c..49a90f959d 100644 --- a/erpnext/setup/doctype/customer_group/customer_group.js +++ b/erpnext/setup/doctype/customer_group/customer_group.js @@ -41,6 +41,7 @@ frappe.ui.form.on("Customer Group", { return { filters: { "root_type": 'Liability', + "account_type": "Receivable", "company": locals[cdt][cdn].company, "is_group": 0 } diff --git a/erpnext/setup/doctype/supplier_group/supplier_group.js b/erpnext/setup/doctype/supplier_group/supplier_group.js index ac5904f4d2..b2acfd7355 100644 --- a/erpnext/setup/doctype/supplier_group/supplier_group.js +++ b/erpnext/setup/doctype/supplier_group/supplier_group.js @@ -41,6 +41,7 @@ frappe.ui.form.on("Supplier Group", { return { filters: { "root_type": 'Asset', + "account_type": "Payable", "company": locals[cdt][cdn].company, "is_group": 0 } From 1894dc8197c568936d839ef50fa65879c7ce9ac7 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 23 Jun 2023 21:53:34 +0530 Subject: [PATCH 25/99] fix: Test case and code cleanup --- .../payment_reconciliation.js | 1 + .../purchase_invoice_advance.json | 12 +-- .../doctype/sales_invoice/sales_invoice.py | 3 +- .../sales_invoice_advance.json | 12 +-- erpnext/accounts/utils.py | 6 +- erpnext/controllers/accounts_controller.py | 78 ------------------- 6 files changed, 11 insertions(+), 101 deletions(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js index bd931f1a2b..16e3f95ee6 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js @@ -34,6 +34,7 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo filters: { "company": this.frm.doc.company, "is_group": 0, + "account_type": this.frm.doc.party_type == 'Customer' ? "Receivable": "Payable", "root_type": this.frm.doc.party_type == 'Customer' ? "Liability": "Asset" } }; diff --git a/erpnext/accounts/doctype/purchase_invoice_advance/purchase_invoice_advance.json b/erpnext/accounts/doctype/purchase_invoice_advance/purchase_invoice_advance.json index 9082115f23..4db531eac9 100644 --- a/erpnext/accounts/doctype/purchase_invoice_advance/purchase_invoice_advance.json +++ b/erpnext/accounts/doctype/purchase_invoice_advance/purchase_invoice_advance.json @@ -14,8 +14,7 @@ "advance_amount", "allocated_amount", "exchange_gain_loss", - "ref_exchange_rate", - "account" + "ref_exchange_rate" ], "fields": [ { @@ -112,20 +111,13 @@ "label": "Reference Exchange Rate", "non_negative": 1, "read_only": 1 - }, - { - "fieldname": "account", - "fieldtype": "Link", - "label": "Account", - "options": "Account", - "read_only": 1 } ], "idx": 1, "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2023-06-01 16:56:48.530169", + "modified": "2023-06-23 21:13:18.013816", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice Advance", diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 25553cff0c..7ab1c89397 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1052,12 +1052,13 @@ class SalesInvoice(SellingController): elif self.docstatus == 2 and cint(self.update_stock) and cint(auto_accounting_for_stock): make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name) - def get_gl_entries(self): + def get_gl_entries(self, warehouse_account=None): from erpnext.accounts.general_ledger import merge_similar_entries gl_entries = [] self.make_customer_gl_entry(gl_entries) + self.make_tax_gl_entries(gl_entries) self.make_exchange_gain_loss_gl_entries(gl_entries) self.make_internal_transfer_gl_entries(gl_entries) diff --git a/erpnext/accounts/doctype/sales_invoice_advance/sales_invoice_advance.json b/erpnext/accounts/doctype/sales_invoice_advance/sales_invoice_advance.json index aa52b1cac2..0ae85d9000 100644 --- a/erpnext/accounts/doctype/sales_invoice_advance/sales_invoice_advance.json +++ b/erpnext/accounts/doctype/sales_invoice_advance/sales_invoice_advance.json @@ -14,8 +14,7 @@ "advance_amount", "allocated_amount", "exchange_gain_loss", - "ref_exchange_rate", - "account" + "ref_exchange_rate" ], "fields": [ { @@ -113,20 +112,13 @@ "label": "Reference Exchange Rate", "non_negative": 1, "read_only": 1 - }, - { - "fieldname": "account", - "fieldtype": "Link", - "label": "Account", - "options": "Account", - "read_only": 1 } ], "idx": 1, "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2023-05-31 11:47:00.191681", + "modified": "2023-06-23 21:12:57.557731", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice Advance", diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 5c256b730f..046711532f 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -474,7 +474,9 @@ def reconcile_against_document(args, skip_ref_details_update_for_pe=False): # n doc = frappe.get_doc(entry.voucher_type, entry.voucher_no) gl_map = doc.build_gl_map() create_payment_ledger_entry(gl_map, update_outstanding="No", cancel=0, adv_adj=1) - doc.make_advance_gl_entries(entry.against_voucher_type, entry.against_voucher) + + if voucher_type == "Payment Entry": + doc.make_advance_gl_entries(entry.against_voucher_type, entry.against_voucher) # Only update outstanding for newly linked vouchers for entry in entries: @@ -732,7 +734,7 @@ def remove_ref_doc_link_from_pe(ref_type, ref_no): for pe in linked_pe: try: - pe_doc = frappe.get_doc("Payment Entry", pe, cache=True) + pe_doc = frappe.get_doc("Payment Entry", pe) pe_doc.set_amounts() pe_doc.make_advance_gl_entries(against_voucher_type=ref_type, against_voucher=ref_no, cancel=1) pe_doc.clear_unallocated_reference_document_rows() diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 0d9ab6498d..4193b5327d 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -2916,84 +2916,6 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil parent.create_stock_reservation_entries() -# def make_advance_liability_entry( -# gl_entries, pe, allocated_amount, invoice, party_type -# ): -# pe = frappe.get_doc("Payment Entry", pe) -# if party_type == "Customer": -# invoice = frappe.get_doc("Sales Invoice", invoice) -# account = pe.paid_from -# dr_or_cr = "debit" -# rev = "credit" -# against = invoice.debit_to -# party = invoice.customer -# else: -# invoice = frappe.get_doc("Purchase Invoice", invoice) -# account = pe.paid_to -# dr_or_cr = "credit" -# rev = "debit" -# against = invoice.credit_to -# party = invoice.supplier -# gl_entries.append( -# pe.get_gl_dict( -# { -# "account": account, -# "party_type": party_type, -# "party": party, -# "due_date": invoice.due_date, -# "against": against, -# dr_or_cr: allocated_amount, -# dr_or_cr + "_in_account_currency": allocated_amount, -# rev: 0, -# rev + "_in_account_currency": 0, -# "cost_center": invoice.cost_center, -# "project": invoice.project, -# "against_voucher_type": "Payment Entry", -# "against_voucher": pe.name, -# }, -# invoice.party_account_currency, -# item=pe, -# ) -# ) - -# (dr_or_cr, rev) = ("credit", "debit") if party_type == "Customer" else ("debit", "credit") -# gl_entries.append( -# pe.get_gl_dict( -# { -# "account": against, -# "party_type": party_type, -# "party": party, -# "due_date": invoice.due_date, -# dr_or_cr: allocated_amount, -# dr_or_cr + "_in_account_currency": allocated_amount, -# rev: 0, -# rev + "_in_account_currency": 0, -# "cost_center": invoice.cost_center, -# "project": invoice.project, -# "against_voucher_type": invoice.doctype, -# "against_voucher": invoice.name, -# }, -# invoice.party_account_currency, -# item=pe, -# ) -# ) - - -# def check_advance_liability_entry(gl_entries, company, advances, invoice, party_type): -# advance_payments_as_liability = frappe.db.get_value( -# "Company", {"company_name": company}, "book_advance_payments_as_liability" -# ) -# if advance_payments_as_liability: -# for advance_entry in advances: -# make_advance_liability_entry( -# gl_entries, -# advance_entry.reference_name, -# advance_entry.allocated_amount, -# invoice=invoice, -# party_type=party_type, -# ) - - @erpnext.allow_regional def validate_regional(doc): pass From 62c3ca8286e131d9d4072e9c8d3d298133c755d5 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 26 Jun 2023 23:53:55 +0530 Subject: [PATCH 26/99] fix: Paid invoice in AR report --- .../accounts/doctype/payment_entry/payment_entry.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 8141d0519e..cbf8c0865f 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -986,12 +986,20 @@ class PaymentEntry(AccountsController): gle = party_gl_dict.copy() allocated_amount_in_company_currency = self.calculate_base_allocated_amount_for_reference(d) + + if self.book_advance_payments_in_separate_party_account: + against_voucher_type = "Payment Entry" + against_voucher = self.name + else: + against_voucher_type = d.reference_doctype + against_voucher = d.reference_name + gle.update( { dr_or_cr: allocated_amount_in_company_currency, dr_or_cr + "_in_account_currency": d.allocated_amount, - "against_voucher_type": d.reference_doctype, - "against_voucher": d.reference_name, + "against_voucher_type": against_voucher_type, + "against_voucher": against_voucher, "cost_center": cost_center, } ) From f2edc91dc6ce60fae598f537634430a8da7dd453 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 27 Jun 2023 18:11:47 +0530 Subject: [PATCH 27/99] fix: Multi invoice reconciliation --- erpnext/accounts/doctype/payment_entry/payment_entry.py | 7 ++----- erpnext/accounts/utils.py | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index cbf8c0865f..a0f638246f 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -1019,15 +1019,12 @@ class PaymentEntry(AccountsController): gl_entries.append(gle) - def make_advance_gl_entries(self, against_voucher_type=None, against_voucher=None, cancel=0): + def make_advance_gl_entries(self, cancel=0): if self.book_advance_payments_in_separate_party_account: gl_entries = [] for d in self.get("references"): if d.reference_doctype in ("Sales Invoice", "Purchase Invoice"): - if not (against_voucher_type and against_voucher) or ( - d.reference_doctype == against_voucher_type and d.reference_name == against_voucher - ): - self.make_invoice_liability_entry(gl_entries, d) + self.make_invoice_liability_entry(gl_entries, d) if cancel: for entry in gl_entries: diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 046711532f..1fa0e86c26 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -476,7 +476,7 @@ def reconcile_against_document(args, skip_ref_details_update_for_pe=False): # n create_payment_ledger_entry(gl_map, update_outstanding="No", cancel=0, adv_adj=1) if voucher_type == "Payment Entry": - doc.make_advance_gl_entries(entry.against_voucher_type, entry.against_voucher) + doc.make_advance_gl_entries() # Only update outstanding for newly linked vouchers for entry in entries: From 7312827d4dd06aa89d338b6eb3d63ed4f8d64c8f Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 27 Jun 2023 18:25:10 +0530 Subject: [PATCH 28/99] fix: On cancel flow --- erpnext/accounts/doctype/payment_entry/payment_entry.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index a0f638246f..cbf8c0865f 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -1019,12 +1019,15 @@ class PaymentEntry(AccountsController): gl_entries.append(gle) - def make_advance_gl_entries(self, cancel=0): + def make_advance_gl_entries(self, against_voucher_type=None, against_voucher=None, cancel=0): if self.book_advance_payments_in_separate_party_account: gl_entries = [] for d in self.get("references"): if d.reference_doctype in ("Sales Invoice", "Purchase Invoice"): - self.make_invoice_liability_entry(gl_entries, d) + if not (against_voucher_type and against_voucher) or ( + d.reference_doctype == against_voucher_type and d.reference_name == against_voucher + ): + self.make_invoice_liability_entry(gl_entries, d) if cancel: for entry in gl_entries: From 1e078d03bb6c8b4abc74254b0daff4edfdcc2c2b Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 29 Jun 2023 12:18:25 +0530 Subject: [PATCH 29/99] fix: Partial PLE cancellation --- .../payment_ledger_entry/payment_ledger_entry.json | 8 +++++++- erpnext/accounts/general_ledger.py | 6 +++++- erpnext/accounts/utils.py | 11 ++++++++--- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.json b/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.json index 22842cec0f..9cf2ac6c2a 100644 --- a/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.json +++ b/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.json @@ -13,6 +13,7 @@ "party_type", "party", "due_date", + "voucher_detail_no", "cost_center", "finance_book", "voucher_type", @@ -142,12 +143,17 @@ "fieldname": "remarks", "fieldtype": "Text", "label": "Remarks" + }, + { + "fieldname": "voucher_detail_no", + "fieldtype": "Data", + "label": "Voucher Detail No" } ], "in_create": 1, "index_web_pages_for_search": 1, "links": [], - "modified": "2022-08-22 15:32:56.629430", + "modified": "2023-06-29 12:24:20.500632", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Ledger Entry", diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py index 22f39596e6..f1dad875fa 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -526,7 +526,11 @@ def make_reverse_gl_entries( if gl_entries: create_payment_ledger_entry( - gl_entries, cancel=1, adv_adj=adv_adj, update_outstanding=update_outstanding + gl_entries, + cancel=1, + adv_adj=adv_adj, + update_outstanding=update_outstanding, + partial_cancel=partial_cancel, ) validate_accounting_period(gl_entries) check_freezing_date(gl_entries[0]["posting_date"], adv_adj) diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 1fa0e86c26..2a40a3abaf 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -1467,6 +1467,7 @@ def get_payment_ledger_entries(gl_entries, cancel=0): due_date=gle.due_date, voucher_type=gle.voucher_type, voucher_no=gle.voucher_no, + voucher_detail_no=gle.voucher_detail_no, against_voucher_type=gle.against_voucher_type if gle.against_voucher_type else gle.voucher_type, @@ -1488,7 +1489,7 @@ def get_payment_ledger_entries(gl_entries, cancel=0): def create_payment_ledger_entry( - gl_entries, cancel=0, adv_adj=0, update_outstanding="Yes", from_repost=0 + gl_entries, cancel=0, adv_adj=0, update_outstanding="Yes", from_repost=0, partial_cancel=False ): if gl_entries: ple_map = get_payment_ledger_entries(gl_entries, cancel=cancel) @@ -1498,7 +1499,7 @@ def create_payment_ledger_entry( ple = frappe.get_doc(entry) if cancel: - delink_original_entry(ple) + delink_original_entry(ple, partial_cancel=partial_cancel) ple.flags.ignore_permissions = 1 ple.flags.adv_adj = adv_adj @@ -1545,7 +1546,7 @@ def update_voucher_outstanding(voucher_type, voucher_no, account, party_type, pa ref_doc.set_status(update=True) -def delink_original_entry(pl_entry): +def delink_original_entry(pl_entry, partial_cancel=False): if pl_entry: ple = qb.DocType("Payment Ledger Entry") query = ( @@ -1565,6 +1566,10 @@ def delink_original_entry(pl_entry): & (ple.against_voucher_no == pl_entry.against_voucher_no) ) ) + + if partial_cancel: + query = query.where(ple.voucher_detail_no == pl_entry.voucher_detail_no) + query.run() From 904ca746a69e3e53fd8469fadac56d18d5af9e27 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Fri, 30 Jun 2023 12:11:23 +0530 Subject: [PATCH 30/99] fix: project filtering in P&L Report --- .../profit_and_loss_statement/profit_and_loss_statement.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js index 1c461efbcd..298d83894c 100644 --- a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js +++ b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js @@ -14,8 +14,10 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { "label": __("Project"), "fieldtype": "MultiSelectList", get_data: function(txt) { - return frappe.db.get_link_options('Project', txt); - } + return frappe.db.get_link_options('Project', txt, { + company: frappe.query_report.get_filter_value("company") + }); + }, }, { "fieldname": "include_default_book_entries", From ce252a0d45918f9cb03626bad9521e92df2be260 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Fri, 30 Jun 2023 12:42:19 +0530 Subject: [PATCH 31/99] fix: show projects with no company value set --- .../profit_and_loss_statement/profit_and_loss_statement.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js index 298d83894c..6fda7b5220 100644 --- a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js +++ b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js @@ -15,7 +15,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { "fieldtype": "MultiSelectList", get_data: function(txt) { return frappe.db.get_link_options('Project', txt, { - company: frappe.query_report.get_filter_value("company") + company: ["in", [frappe.query_report.get_filter_value("company"), ""]], }); }, }, From 84d4888f5fcd8ff98e308b501b63f28d890dd861 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Fri, 30 Jun 2023 12:54:45 +0530 Subject: [PATCH 32/99] fix: make company field mandatory in project doctype --- .../profit_and_loss_statement/profit_and_loss_statement.js | 2 +- erpnext/projects/doctype/project/project.json | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js index 6fda7b5220..298d83894c 100644 --- a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js +++ b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js @@ -15,7 +15,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { "fieldtype": "MultiSelectList", get_data: function(txt) { return frappe.db.get_link_options('Project', txt, { - company: ["in", [frappe.query_report.get_filter_value("company"), ""]], + company: frappe.query_report.get_filter_value("company") }); }, }, diff --git a/erpnext/projects/doctype/project/project.json b/erpnext/projects/doctype/project/project.json index f007430ab3..502ee57415 100644 --- a/erpnext/projects/doctype/project/project.json +++ b/erpnext/projects/doctype/project/project.json @@ -289,7 +289,8 @@ "fieldtype": "Link", "label": "Company", "options": "Company", - "remember_last_selected_value": 1 + "remember_last_selected_value": 1, + "reqd": 1 }, { "fieldname": "column_break_28", From bbb6ebb84ec5ae2cd360c98ab279e1a3cd0aa202 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 30 Jun 2023 13:25:22 +0530 Subject: [PATCH 33/99] fix: Outstanding amount validation --- erpnext/accounts/doctype/payment_entry/payment_entry.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 37459e3cf0..c0fd63e9a2 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -420,7 +420,10 @@ class PaymentEntry(AccountsController): elif self.party_type == "Employee": ref_party_account = ref_doc.payable_account - if ref_party_account != self.party_account: + if ( + ref_party_account != self.party_account + and not self.book_advance_payments_in_separate_party_account + ): frappe.throw( _("{0} {1} is associated with {2}, but Party Account is {3}").format( d.reference_doctype, d.reference_name, ref_party_account, self.party_account @@ -1482,7 +1485,7 @@ def get_outstanding_reference_documents(args, validate=False): outstanding_invoices = get_outstanding_invoices( args.get("party_type"), args.get("party"), - args.get("party_account"), + get_party_account(args.get("party_type"), args.get("party"), args.get("company")), common_filter=common_filter, posting_date=posting_and_due_date, min_outstanding=args.get("outstanding_amt_greater_than"), From 0a49213338f6dac2418d5bc789b984293bb1cc84 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 30 Jun 2023 17:32:42 +0530 Subject: [PATCH 34/99] test: Update test records --- erpnext/accounts/doctype/sales_invoice/test_records.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/test_records.json b/erpnext/accounts/doctype/sales_invoice/test_records.json index 3781f8ccc9..61e5219c80 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_records.json +++ b/erpnext/accounts/doctype/sales_invoice/test_records.json @@ -6,7 +6,7 @@ "cost_center": "_Test Cost Center - _TC", "customer": "_Test Customer", "customer_name": "_Test Customer", - "debit_to": "_Test Receivable - _TC", + "debit_to": "Debtors - _TC", "doctype": "Sales Invoice", "items": [ { @@ -78,7 +78,7 @@ "currency": "INR", "customer": "_Test Customer", "customer_name": "_Test Customer", - "debit_to": "_Test Receivable - _TC", + "debit_to": "Debtors - _TC", "doctype": "Sales Invoice", "cost_center": "_Test Cost Center - _TC", "items": [ @@ -137,7 +137,7 @@ "currency": "INR", "customer": "_Test Customer", "customer_name": "_Test Customer", - "debit_to": "_Test Receivable - _TC", + "debit_to": "Debtors - _TC", "doctype": "Sales Invoice", "cost_center": "_Test Cost Center - _TC", "items": [ @@ -265,7 +265,7 @@ "currency": "INR", "customer": "_Test Customer", "customer_name": "_Test Customer", - "debit_to": "_Test Receivable - _TC", + "debit_to": "Debtors - _TC", "doctype": "Sales Invoice", "cost_center": "_Test Cost Center - _TC", "items": [ From 7e7737d6926ed94b891556b7df4e46b1e87673f6 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 30 Jun 2023 18:37:52 +0530 Subject: [PATCH 35/99] test: Update test account --- erpnext/accounts/doctype/finance_book/test_finance_book.py | 2 +- .../accounts/doctype/journal_entry/test_journal_entry.py | 4 ++-- erpnext/accounts/doctype/journal_entry/test_records.json | 4 ++-- .../accounts/doctype/sales_invoice/test_sales_invoice.py | 6 +++--- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/erpnext/accounts/doctype/finance_book/test_finance_book.py b/erpnext/accounts/doctype/finance_book/test_finance_book.py index 7b2575d2c3..42c0e51238 100644 --- a/erpnext/accounts/doctype/finance_book/test_finance_book.py +++ b/erpnext/accounts/doctype/finance_book/test_finance_book.py @@ -13,7 +13,7 @@ class TestFinanceBook(unittest.TestCase): finance_book = create_finance_book() # create jv entry - jv = make_journal_entry("_Test Bank - _TC", "_Test Receivable - _TC", 100, save=False) + jv = make_journal_entry("_Test Bank - _TC", "Debtors - _TC", 100, save=False) jv.accounts[1].update({"party_type": "Customer", "party": "_Test Customer"}) diff --git a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py index 73b1911543..e7aca79d08 100644 --- a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py @@ -43,7 +43,7 @@ class TestJournalEntry(unittest.TestCase): frappe.db.sql( """select name from `tabJournal Entry Account` where account = %s and docstatus = 1 and parent = %s""", - ("_Test Receivable - _TC", test_voucher.name), + ("Debtors - _TC", test_voucher.name), ) ) @@ -273,7 +273,7 @@ class TestJournalEntry(unittest.TestCase): jv.submit() # create jv in USD, but account currency in INR - jv = make_journal_entry("_Test Bank - _TC", "_Test Receivable - _TC", 100, save=False) + jv = make_journal_entry("_Test Bank - _TC", "Debtors - _TC", 100, save=False) jv.accounts[1].update({"party_type": "Customer", "party": "_Test Customer USD"}) diff --git a/erpnext/accounts/doctype/journal_entry/test_records.json b/erpnext/accounts/doctype/journal_entry/test_records.json index 5077305cf2..dafcf56abd 100644 --- a/erpnext/accounts/doctype/journal_entry/test_records.json +++ b/erpnext/accounts/doctype/journal_entry/test_records.json @@ -6,7 +6,7 @@ "doctype": "Journal Entry", "accounts": [ { - "account": "_Test Receivable - _TC", + "account": "Debtors - _TC", "party_type": "Customer", "party": "_Test Customer", "credit_in_account_currency": 400.0, @@ -70,7 +70,7 @@ "doctype": "Journal Entry", "accounts": [ { - "account": "_Test Receivable - _TC", + "account": "Debtors - _TC", "party_type": "Customer", "party": "_Test Customer", "credit_in_account_currency": 0.0, diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 69ddfaac8d..0280c3590c 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -1724,7 +1724,7 @@ class TestSalesInvoice(unittest.TestCase): # Party Account currency must be in USD, as there is existing GLE with USD si4 = create_sales_invoice( customer="_Test Customer USD", - debit_to="_Test Receivable - _TC", + debit_to="Debtors - _TC", currency="USD", conversion_rate=50, do_not_submit=True, @@ -1737,7 +1737,7 @@ class TestSalesInvoice(unittest.TestCase): si3.cancel() si5 = create_sales_invoice( customer="_Test Customer USD", - debit_to="_Test Receivable - _TC", + debit_to="Debtors - _TC", currency="USD", conversion_rate=50, do_not_submit=True, @@ -1816,7 +1816,7 @@ class TestSalesInvoice(unittest.TestCase): "reference_date": nowdate(), "received_amount": 300, "paid_amount": 300, - "paid_from": "_Test Receivable - _TC", + "paid_from": "Debtors - _TC", "paid_to": "_Test Cash - _TC", } ) From 80e6c90740d50919806e76183e061247e7c2d08a Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 30 Jun 2023 19:35:22 +0530 Subject: [PATCH 36/99] chore: precision in test --- erpnext/accounts/doctype/payment_entry/test_payment_entry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py index ae2625b653..70cc4b3d34 100644 --- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py @@ -932,7 +932,7 @@ class TestPaymentEntry(FrappeTestCase): self.assertEqual(pe.cost_center, si.cost_center) self.assertEqual(flt(expected_account_balance), account_balance) self.assertEqual(flt(expected_party_balance), party_balance) - self.assertEqual(flt(expected_party_account_balance), party_account_balance) + self.assertEqual(flt(expected_party_account_balance, 2), flt(party_account_balance, 2)) def test_multi_currency_payment_entry_with_taxes(self): payment_entry = create_payment_entry( From d54f52474af27d129fc1bcbb166933bf0fc94fbc Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 30 Jun 2023 20:02:46 +0530 Subject: [PATCH 37/99] fix: Expense Account filter in Sales Invoice (#35944) --- .../accounts/doctype/sales_invoice/sales_invoice.js | 13 ------------- erpnext/public/js/controllers/transaction.js | 4 +++- 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 68407e0221..8753ebc3ba 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -674,19 +674,6 @@ frappe.ui.form.on('Sales Invoice', { } } - // expense account - frm.fields_dict['items'].grid.get_field('expense_account').get_query = function(doc) { - if (erpnext.is_perpetual_inventory_enabled(doc.company)) { - return { - filters: { - 'report_type': 'Profit and Loss', - 'company': doc.company, - "is_group": 0 - } - } - } - } - // discount account frm.fields_dict['items'].grid.get_field('discount_account').get_query = function(doc) { return { diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 0d92683f21..543d0e9790 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -193,7 +193,9 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe this.frm.set_query("expense_account", "items", function(doc) { return { filters: { - "company": doc.company + "company": doc.company, + "report_type": "Profit and Loss", + "is_group": 0 } }; }); From b77a8089217a6b1e552622b0fed5b3fac23aca94 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Sat, 1 Jul 2023 11:30:46 +0530 Subject: [PATCH 38/99] fix: reposting has not changed valuation rate (cherry picked from commit c0c693d8b0faf42089314cf43f4fc117f45de4b4) # Conflicts: # erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py --- .../purchase_receipt/test_purchase_receipt.py | 117 ++++++++++++++++++ erpnext/stock/stock_ledger.py | 6 + 2 files changed, 123 insertions(+) diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index c6c84cadc8..76ba738a99 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -1834,6 +1834,7 @@ class TestPurchaseReceipt(FrappeTestCase): self.assertEqual(abs(data["stock_value_difference"]), 400.00) +<<<<<<< HEAD def test_return_from_rejected_warehouse(self): from erpnext.stock.doctype.purchase_receipt.purchase_receipt import ( make_purchase_return_against_rejected_warehouse, @@ -1860,6 +1861,122 @@ class TestPurchaseReceipt(FrappeTestCase): self.assertEqual(pr_return.items[0].qty, 2.0 * -1) self.assertEqual(pr_return.items[0].rejected_qty, 0.0) self.assertEqual(pr_return.items[0].rejected_warehouse, "") +======= + def test_purchase_receipt_with_backdated_landed_cost_voucher(self): + from erpnext.controllers.sales_and_purchase_return import make_return_doc + from erpnext.stock.doctype.landed_cost_voucher.test_landed_cost_voucher import ( + create_landed_cost_voucher, + ) + from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry + + item_code = "_Test Purchase Item With Landed Cost" + create_item(item_code) + + warehouse = create_warehouse("_Test Purchase Warehouse With Landed Cost") + warehouse1 = create_warehouse("_Test Purchase Warehouse With Landed Cost 1") + warehouse2 = create_warehouse("_Test Purchase Warehouse With Landed Cost 2") + warehouse3 = create_warehouse("_Test Purchase Warehouse With Landed Cost 3") + + pr = make_purchase_receipt( + item_code=item_code, + warehouse=warehouse, + posting_date=add_days(today(), -10), + posting_time="10:59:59", + qty=100, + rate=275.00, + ) + + pr_return = make_return_doc("Purchase Receipt", pr.name) + pr_return.posting_date = add_days(today(), -9) + pr_return.items[0].qty = 2 * -1 + pr_return.items[0].received_qty = 2 * -1 + pr_return.submit() + + ste1 = make_stock_entry( + purpose="Material Transfer", + posting_date=add_days(today(), -8), + source=warehouse, + target=warehouse1, + item_code=item_code, + qty=20, + company=pr.company, + ) + + ste1.reload() + self.assertEqual(ste1.items[0].valuation_rate, 275.00) + + ste2 = make_stock_entry( + purpose="Material Transfer", + posting_date=add_days(today(), -7), + source=warehouse, + target=warehouse2, + item_code=item_code, + qty=20, + company=pr.company, + ) + + ste2.reload() + self.assertEqual(ste2.items[0].valuation_rate, 275.00) + + ste3 = make_stock_entry( + purpose="Material Transfer", + posting_date=add_days(today(), -6), + source=warehouse, + target=warehouse3, + item_code=item_code, + qty=20, + company=pr.company, + ) + + ste3.reload() + self.assertEqual(ste3.items[0].valuation_rate, 275.00) + + ste4 = make_stock_entry( + purpose="Material Transfer", + posting_date=add_days(today(), -5), + source=warehouse1, + target=warehouse, + item_code=item_code, + qty=20, + company=pr.company, + ) + + ste4.reload() + self.assertEqual(ste4.items[0].valuation_rate, 275.00) + + ste5 = make_stock_entry( + purpose="Material Transfer", + posting_date=add_days(today(), -4), + source=warehouse, + target=warehouse1, + item_code=item_code, + qty=20, + company=pr.company, + ) + + ste5.reload() + self.assertEqual(ste5.items[0].valuation_rate, 275.00) + + create_landed_cost_voucher("Purchase Receipt", pr.name, pr.company, charges=2500 * -1) + + pr.reload() + valuation_rate = pr.items[0].valuation_rate + + ste1.reload() + self.assertEqual(ste1.items[0].valuation_rate, valuation_rate) + + ste2.reload() + self.assertEqual(ste2.items[0].valuation_rate, valuation_rate) + + ste3.reload() + self.assertEqual(ste3.items[0].valuation_rate, valuation_rate) + + ste4.reload() + self.assertEqual(ste4.items[0].valuation_rate, valuation_rate) + + ste5.reload() + self.assertEqual(ste5.items[0].valuation_rate, valuation_rate) +>>>>>>> c0c693d8b0 (fix: reposting has not changed valuation rate) def prepare_data_for_internal_transfer(): diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index b3ed220680..7b1eae5545 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -646,6 +646,7 @@ class update_entries_after(object): def update_distinct_item_warehouses(self, dependant_sle): key = (dependant_sle.item_code, dependant_sle.warehouse) val = frappe._dict({"sle": dependant_sle}) + if key not in self.distinct_item_warehouses: self.distinct_item_warehouses[key] = val self.new_items_found = True @@ -657,6 +658,9 @@ class update_entries_after(object): val.sle_changed = True self.distinct_item_warehouses[key] = val self.new_items_found = True + elif self.distinct_item_warehouses[key].get("reposting_status"): + self.distinct_item_warehouses[key] = val + self.new_items_found = True def process_sle(self, sle): # previous sle data for this warehouse @@ -1362,6 +1366,8 @@ def get_sle_by_voucher_detail_no(voucher_detail_no, excluded_sle=None): [ "item_code", "warehouse", + "actual_qty", + "qty_after_transaction", "posting_date", "posting_time", "timestamp(posting_date, posting_time) as timestamp", From 232dfad13a49d47f828a907ec8799979ebf97d21 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Sun, 2 Jul 2023 10:45:12 +0530 Subject: [PATCH 39/99] fix: conflicts --- .../stock/doctype/purchase_receipt/test_purchase_receipt.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index 76ba738a99..07d6e86795 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -1834,7 +1834,6 @@ class TestPurchaseReceipt(FrappeTestCase): self.assertEqual(abs(data["stock_value_difference"]), 400.00) -<<<<<<< HEAD def test_return_from_rejected_warehouse(self): from erpnext.stock.doctype.purchase_receipt.purchase_receipt import ( make_purchase_return_against_rejected_warehouse, @@ -1861,7 +1860,7 @@ class TestPurchaseReceipt(FrappeTestCase): self.assertEqual(pr_return.items[0].qty, 2.0 * -1) self.assertEqual(pr_return.items[0].rejected_qty, 0.0) self.assertEqual(pr_return.items[0].rejected_warehouse, "") -======= + def test_purchase_receipt_with_backdated_landed_cost_voucher(self): from erpnext.controllers.sales_and_purchase_return import make_return_doc from erpnext.stock.doctype.landed_cost_voucher.test_landed_cost_voucher import ( @@ -1976,7 +1975,6 @@ class TestPurchaseReceipt(FrappeTestCase): ste5.reload() self.assertEqual(ste5.items[0].valuation_rate, valuation_rate) ->>>>>>> c0c693d8b0 (fix: reposting has not changed valuation rate) def prepare_data_for_internal_transfer(): From e05b33a6c23cf9d21ec687a383cf937bc5767ad9 Mon Sep 17 00:00:00 2001 From: Vishnu VS Date: Mon, 3 Jul 2023 09:23:27 +0530 Subject: [PATCH 40/99] feat: add method for ordered quantity in supplier scorecard (#35930) fix: add method for getting ordered quantity in the supplier scorecard variable. Co-authored-by: vishnu --- .../supplier_scorecard/supplier_scorecard.py | 5 +++++ .../supplier_scorecard_variable.py | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.py b/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.py index 486bf23e90..58da851295 100644 --- a/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.py +++ b/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.py @@ -329,6 +329,11 @@ def make_default_records(): "variable_label": "Total Shipments", "path": "get_total_shipments", }, + { + "param_name": "total_ordered", + "variable_label": "Total Ordered", + "path": "get_ordered_qty", + }, ] install_standing_docs = [ { diff --git a/erpnext/buying/doctype/supplier_scorecard_variable/supplier_scorecard_variable.py b/erpnext/buying/doctype/supplier_scorecard_variable/supplier_scorecard_variable.py index fb8819eaf8..4080d1fde0 100644 --- a/erpnext/buying/doctype/supplier_scorecard_variable/supplier_scorecard_variable.py +++ b/erpnext/buying/doctype/supplier_scorecard_variable/supplier_scorecard_variable.py @@ -7,6 +7,7 @@ import sys import frappe from frappe import _ from frappe.model.document import Document +from frappe.query_builder.functions import Sum from frappe.utils import getdate @@ -422,6 +423,23 @@ def get_total_shipments(scorecard): return data +def get_ordered_qty(scorecard): + """Returns the total number of ordered quantity (based on Purchase Orders)""" + + po = frappe.qb.DocType("Purchase Order") + + return ( + frappe.qb.from_(po) + .select(Sum(po.total_qty)) + .where( + (po.supplier == scorecard.supplier) + & (po.docstatus == 1) + & (po.transaction_date >= scorecard.get("start_date")) + & (po.transaction_date <= scorecard.get("end_date")) + ) + ).run(as_list=True)[0][0] or 0 + + def get_rfq_total_number(scorecard): """Gets the total number of RFQs sent to supplier""" supplier = frappe.get_doc("Supplier", scorecard.supplier) From 544885925480bcaea382357600f22d7c134ab6f2 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 3 Jul 2023 13:03:52 +0530 Subject: [PATCH 41/99] fix: Update no copy for received_qty field (#35965) --- .../doctype/purchase_invoice_item/purchase_invoice_item.json | 3 ++- .../doctype/purchase_receipt_item/purchase_receipt_item.json | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json index deb202d145..c5187a2f46 100644 --- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json +++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json @@ -178,6 +178,7 @@ "fieldname": "received_qty", "fieldtype": "Float", "label": "Received Qty", + "no_copy": 1, "read_only": 1 }, { @@ -903,7 +904,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2023-04-01 20:08:54.545160", + "modified": "2023-07-02 18:39:41.495723", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice Item", diff --git a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json index e576ab789a..3929616f7c 100644 --- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json +++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json @@ -213,6 +213,7 @@ "fieldname": "received_qty", "fieldtype": "Float", "label": "Received Quantity", + "no_copy": 1, "oldfieldname": "received_qty", "oldfieldtype": "Currency", "print_hide": 1, @@ -1057,7 +1058,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2023-03-12 13:37:47.778021", + "modified": "2023-07-02 18:40:48.152637", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt Item", From dedb5e23f7b3e1de5389989fbf98f9a821860500 Mon Sep 17 00:00:00 2001 From: Anand Baburajan Date: Mon, 3 Jul 2023 13:06:46 +0530 Subject: [PATCH 42/99] fix: delete loan module workspace properly after separation (#35971) * fix: delete loan module workspace properly after separation * chore: run remove_loan_management_module patch again --- erpnext/patches.txt | 2 +- erpnext/patches/v15_0/remove_loan_management_module.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index dc056635ed..b3b9bc60b7 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -331,6 +331,6 @@ execute:frappe.delete_doc('DocType', 'Cash Flow Mapper', ignore_missing=True) execute:frappe.delete_doc('DocType', 'Cash Flow Mapping Template', ignore_missing=True) execute:frappe.delete_doc('DocType', 'Cash Flow Mapping Accounts', ignore_missing=True) erpnext.patches.v14_0.cleanup_workspaces -erpnext.patches.v15_0.remove_loan_management_module +erpnext.patches.v15_0.remove_loan_management_module #2023-07-03 erpnext.patches.v14_0.set_report_in_process_SOA erpnext.buying.doctype.supplier.patches.migrate_supplier_portal_users \ No newline at end of file diff --git a/erpnext/patches/v15_0/remove_loan_management_module.py b/erpnext/patches/v15_0/remove_loan_management_module.py index 6f08c361ba..8242f9cce5 100644 --- a/erpnext/patches/v15_0/remove_loan_management_module.py +++ b/erpnext/patches/v15_0/remove_loan_management_module.py @@ -7,7 +7,7 @@ def execute(): frappe.delete_doc("Module Def", "Loan Management", ignore_missing=True, force=True) - frappe.delete_doc("Workspace", "Loan Management", ignore_missing=True, force=True) + frappe.delete_doc("Workspace", "Loans", ignore_missing=True, force=True) print_formats = frappe.get_all( "Print Format", {"module": "Loan Management", "standard": "Yes"}, pluck="name" From 6644311c8b4c2398729e6b07ef5845f7fed6795e Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 3 Jul 2023 14:30:15 +0530 Subject: [PATCH 43/99] refactor: checkbox for enabling auto ERR creation --- erpnext/setup/doctype/company/company.json | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json index 6292ad7349..7e55ca4fc0 100644 --- a/erpnext/setup/doctype/company/company.json +++ b/erpnext/setup/doctype/company/company.json @@ -95,6 +95,9 @@ "depreciation_cost_center", "capital_work_in_progress_account", "asset_received_but_not_billed", + "exchange_rate_revaluation_settings_section", + "auto_exchange_rate_revaluation", + "auto_err_frequency", "budget_detail", "exception_budget_approver_role", "registration_info", @@ -731,6 +734,23 @@ "fieldname": "book_advance_payments_in_separate_party_account", "fieldtype": "Check", "label": "Book Advance Payments in Separate Party Account" + }, + { + "fieldname": "exchange_rate_revaluation_settings_section", + "fieldtype": "Section Break", + "label": "Exchange Rate Revaluation Settings" + }, + { + "default": "0", + "fieldname": "auto_exchange_rate_revaluation", + "fieldtype": "Check", + "label": "Auto Create Exchange Rate Revaluation" + }, + { + "fieldname": "auto_err_frequency", + "fieldtype": "Select", + "label": "Frequency", + "options": "Daily\nWeekly" } ], "icon": "fa fa-building", @@ -738,7 +758,7 @@ "image_field": "company_logo", "is_tree": 1, "links": [], - "modified": "2023-06-23 18:22:27.219706", + "modified": "2023-07-03 14:29:56.262188", "modified_by": "Administrator", "module": "Setup", "name": "Company", From 1e8f6c0840b0ea3f77df0c2e63e551c689b1b5a4 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 3 Jul 2023 17:28:55 +0530 Subject: [PATCH 44/99] fix: reserve the pos invoice batches --- .../doctype/pos_invoice/pos_invoice.js | 2 +- .../doctype/pos_invoice/pos_invoice.py | 2 +- .../doctype/pos_invoice/test_pos_invoice.py | 33 +++++ .../serial_and_batch_bundle.py | 134 +++++++++++++----- 4 files changed, 135 insertions(+), 36 deletions(-) diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.js b/erpnext/accounts/doctype/pos_invoice/pos_invoice.js index cced37589b..32e267f33c 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.js +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.js @@ -20,7 +20,7 @@ erpnext.selling.POSInvoiceController = class POSInvoiceController extends erpnex onload(doc) { super.onload(); - this.frm.ignore_doctypes_on_cancel_all = ['POS Invoice Merge Log', 'POS Closing Entry']; + this.frm.ignore_doctypes_on_cancel_all = ['POS Invoice Merge Log', 'POS Closing Entry', 'Serial and Batch Bundle']; if(doc.__islocal && doc.is_pos && frappe.get_route_str() !== 'point-of-sale') { this.frm.script_manager.trigger("is_pos"); diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py index bf393c0d29..4b2fcec757 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py @@ -93,7 +93,7 @@ class POSInvoice(SalesInvoice): ) def on_cancel(self): - self.ignore_linked_doctypes = "Payment Ledger Entry" + self.ignore_linked_doctypes = ["Payment Ledger Entry", "Serial and Batch Bundle"] # run on cancel method of selling controller super(SalesInvoice, self).on_cancel() if not self.is_return and self.loyalty_program: diff --git a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py index f842a16b74..0fce61f1e7 100644 --- a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py @@ -767,6 +767,39 @@ class TestPOSInvoice(unittest.TestCase): ) self.assertEqual(rounded_total, 400) + def test_pos_batch_reservation(self): + from erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle import ( + get_auto_batch_nos, + ) + from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import ( + create_batch_item_with_batch, + ) + + create_batch_item_with_batch("_BATCH ITEM Test For Reserve", "TestBatch-RS 02") + make_stock_entry( + target="_Test Warehouse - _TC", + item_code="_BATCH ITEM Test For Reserve", + qty=20, + basic_rate=100, + batch_no="TestBatch-RS 02", + ) + + pos_inv1 = create_pos_invoice( + item="_BATCH ITEM Test For Reserve", rate=300, qty=15, batch_no="TestBatch-RS 02" + ) + pos_inv1.save() + pos_inv1.submit() + + batches = get_auto_batch_nos( + frappe._dict( + {"item_code": "_BATCH ITEM Test For Reserve", "warehouse": "_Test Warehouse - _TC"} + ) + ) + + for batch in batches: + if batch.batch_no == "TestBatch-RS 02" and batch.warehouse == "_Test Warehouse - _TC": + self.assertEqual(batch.qty, 5) + def test_pos_batch_item_qty_validation(self): from erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle import ( BatchNegativeStockError, diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py index 57bb71ef1e..a5c7879b17 100644 --- a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py +++ b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py @@ -1241,59 +1241,125 @@ def get_reserved_serial_nos_for_pos(kwargs): return list(set(ignore_serial_nos) - set(returned_serial_nos)) +def get_reserved_batches_for_pos(kwargs): + pos_batches = frappe._dict() + pos_invoices = frappe.get_all( + "POS Invoice", + fields=[ + "`tabPOS Invoice Item`.batch_no", + "`tabPOS Invoice`.is_return", + "`tabPOS Invoice Item`.warehouse", + "`tabPOS Invoice Item`.name as child_docname", + "`tabPOS Invoice`.name as parent_docname", + "`tabPOS Invoice Item`.serial_and_batch_bundle", + ], + filters=[ + ["POS Invoice", "consolidated_invoice", "is", "not set"], + ["POS Invoice", "docstatus", "=", 1], + ["POS Invoice Item", "item_code", "=", kwargs.item_code], + ["POS Invoice", "name", "!=", kwargs.ignore_voucher_no], + ], + ) + + ids = [ + pos_invoice.serial_and_batch_bundle + for pos_invoice in pos_invoices + if pos_invoice.serial_and_batch_bundle + ] + + if not ids: + return [] + + if ids: + for d in get_serial_batch_ledgers(kwargs.item_code, docstatus=1, name=ids): + if d.batch_no not in pos_batches: + pos_batches[d.batch_no] = frappe._dict( + { + "qty": d.qty, + "warehouse": d.warehouse, + } + ) + else: + pos_batches[d.batch_no].qty += d.qty + + for row in pos_invoices: + if not row.batch_no: + continue + + if row.batch_no in pos_batches: + pos_batches[row.batch_no] -= row.qty * -1 if row.is_return else row.qty + else: + pos_batches[row.batch_no] = frappe._dict( + { + "qty": (row.qty * -1 if row.is_return else row.qty), + "warehouse": row.warehouse, + } + ) + + return pos_batches + + def get_auto_batch_nos(kwargs): available_batches = get_available_batches(kwargs) qty = flt(kwargs.qty) + pos_invoice_batches = get_reserved_batches_for_pos(kwargs) stock_ledgers_batches = get_stock_ledgers_batches(kwargs) - if stock_ledgers_batches: - update_available_batches(available_batches, stock_ledgers_batches) + if stock_ledgers_batches or pos_invoice_batches: + update_available_batches(available_batches, stock_ledgers_batches, pos_invoice_batches) available_batches = list(filter(lambda x: x.qty > 0, available_batches)) - if not qty: return available_batches + return get_qty_based_available_batches(available_batches, qty) + + +def get_qty_based_available_batches(available_batches, qty): batches = [] for batch in available_batches: - if qty > 0: - batch_qty = flt(batch.qty) - if qty > batch_qty: - batches.append( - frappe._dict( - { - "batch_no": batch.batch_no, - "qty": batch_qty, - "warehouse": batch.warehouse, - } - ) + if qty <= 0: + break + + batch_qty = flt(batch.qty) + if qty > batch_qty: + batches.append( + frappe._dict( + { + "batch_no": batch.batch_no, + "qty": batch_qty, + "warehouse": batch.warehouse, + } ) - qty -= batch_qty - else: - batches.append( - frappe._dict( - { - "batch_no": batch.batch_no, - "qty": qty, - "warehouse": batch.warehouse, - } - ) + ) + qty -= batch_qty + else: + batches.append( + frappe._dict( + { + "batch_no": batch.batch_no, + "qty": qty, + "warehouse": batch.warehouse, + } ) - qty = 0 + ) + qty = 0 return batches -def update_available_batches(available_batches, reserved_batches): - for batch_no, data in reserved_batches.items(): - batch_not_exists = True - for batch in available_batches: - if batch.batch_no == batch_no: - batch.qty += data.qty - batch_not_exists = False +def update_available_batches(available_batches, reserved_batches=None, pos_invoice_batches=None): + for batches in [reserved_batches, pos_invoice_batches]: + if batches: + for batch_no, data in batches.items(): + batch_not_exists = True + for batch in available_batches: + if batch.batch_no == batch_no and batch.warehouse == data.warehouse: + batch.qty += data.qty + batch_not_exists = False - if batch_not_exists: - available_batches.append(data) + if batch_not_exists: + available_batches.append(data) def get_available_batches(kwargs): From a449a4be29f53db4c7bc7babc93e89173485d7f6 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Tue, 4 Jul 2023 14:58:45 +0530 Subject: [PATCH 45/99] chore: Add frappe school links (#35995) Frappe School provides plenty of free tutorial for learning ERPNext. --- .../workspace/accounting/accounting.json | 11 +- erpnext/buying/workspace/buying/buying.json | 11 +- .../manufacturing/manufacturing.json | 11 +- .../projects/workspace/projects/projects.json | 11 +- .../selling/workspace/selling/selling.json | 11 +- erpnext/setup/install.py | 8 +- erpnext/stock/workspace/stock/stock.json | 300 +++++++++++------- 7 files changed, 234 insertions(+), 129 deletions(-) diff --git a/erpnext/accounts/workspace/accounting/accounting.json b/erpnext/accounts/workspace/accounting/accounting.json index 2260bcad76..c27ede29d1 100644 --- a/erpnext/accounts/workspace/accounting/accounting.json +++ b/erpnext/accounts/workspace/accounting/accounting.json @@ -5,7 +5,7 @@ "label": "Profit and Loss" } ], - "content": "[{\"id\":\"MmUf9abwxg\",\"type\":\"onboarding\",\"data\":{\"onboarding_name\":\"Accounts\",\"col\":12}},{\"id\":\"i0EtSjDAXq\",\"type\":\"chart\",\"data\":{\"chart_name\":\"Profit and Loss\",\"col\":12}},{\"id\":\"X78jcbq1u3\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"vikWSkNm6_\",\"type\":\"header\",\"data\":{\"text\":\"Your Shortcuts\",\"col\":12}},{\"id\":\"pMywM0nhlj\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Chart of Accounts\",\"col\":3}},{\"id\":\"_pRdD6kqUG\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Sales Invoice\",\"col\":3}},{\"id\":\"G984SgVRJN\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Purchase Invoice\",\"col\":3}},{\"id\":\"1ArNvt9qhz\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Journal Entry\",\"col\":3}},{\"id\":\"F9f4I1viNr\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Payment Entry\",\"col\":3}},{\"id\":\"4IBBOIxfqW\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Accounts Receivable\",\"col\":3}},{\"id\":\"El2anpPaFY\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"General Ledger\",\"col\":3}},{\"id\":\"1nwcM9upJo\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Trial Balance\",\"col\":3}},{\"id\":\"OF9WOi1Ppc\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"id\":\"B7-uxs8tkU\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"tHb3yxthkR\",\"type\":\"header\",\"data\":{\"text\":\"Reports & Masters\",\"col\":12}},{\"id\":\"DnNtsmxpty\",\"type\":\"card\",\"data\":{\"card_name\":\"Accounting Masters\",\"col\":4}},{\"id\":\"nKKr6fjgjb\",\"type\":\"card\",\"data\":{\"card_name\":\"General Ledger\",\"col\":4}},{\"id\":\"xOHTyD8b5l\",\"type\":\"card\",\"data\":{\"card_name\":\"Accounts Receivable\",\"col\":4}},{\"id\":\"_Cb7C8XdJJ\",\"type\":\"card\",\"data\":{\"card_name\":\"Accounts Payable\",\"col\":4}},{\"id\":\"p7NY6MHe2Y\",\"type\":\"card\",\"data\":{\"card_name\":\"Financial Statements\",\"col\":4}},{\"id\":\"KlqilF5R_V\",\"type\":\"card\",\"data\":{\"card_name\":\"Taxes\",\"col\":4}},{\"id\":\"jTUy8LB0uw\",\"type\":\"card\",\"data\":{\"card_name\":\"Cost Center and Budgeting\",\"col\":4}},{\"id\":\"Wn2lhs7WLn\",\"type\":\"card\",\"data\":{\"card_name\":\"Multi Currency\",\"col\":4}},{\"id\":\"PAQMqqNkBM\",\"type\":\"card\",\"data\":{\"card_name\":\"Bank Statement\",\"col\":4}},{\"id\":\"Q_hBCnSeJY\",\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}},{\"id\":\"3AK1Zf0oew\",\"type\":\"card\",\"data\":{\"card_name\":\"Profitability\",\"col\":4}},{\"id\":\"kxhoaiqdLq\",\"type\":\"card\",\"data\":{\"card_name\":\"Opening and Closing\",\"col\":4}},{\"id\":\"q0MAlU2j_Z\",\"type\":\"card\",\"data\":{\"card_name\":\"Subscription Management\",\"col\":4}},{\"id\":\"ptm7T6Hwu-\",\"type\":\"card\",\"data\":{\"card_name\":\"Share Management\",\"col\":4}},{\"id\":\"OX7lZHbiTr\",\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}}]", + "content": "[{\"id\":\"MmUf9abwxg\",\"type\":\"onboarding\",\"data\":{\"onboarding_name\":\"Accounts\",\"col\":12}},{\"id\":\"i0EtSjDAXq\",\"type\":\"chart\",\"data\":{\"chart_name\":\"Profit and Loss\",\"col\":12}},{\"id\":\"X78jcbq1u3\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"vikWSkNm6_\",\"type\":\"header\",\"data\":{\"text\":\"Your Shortcuts\",\"col\":12}},{\"id\":\"pMywM0nhlj\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Chart of Accounts\",\"col\":3}},{\"id\":\"_pRdD6kqUG\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Sales Invoice\",\"col\":3}},{\"id\":\"G984SgVRJN\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Purchase Invoice\",\"col\":3}},{\"id\":\"1ArNvt9qhz\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Journal Entry\",\"col\":3}},{\"id\":\"F9f4I1viNr\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Payment Entry\",\"col\":3}},{\"id\":\"4IBBOIxfqW\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Accounts Receivable\",\"col\":3}},{\"id\":\"El2anpPaFY\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"General Ledger\",\"col\":3}},{\"id\":\"1nwcM9upJo\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Trial Balance\",\"col\":3}},{\"id\":\"OF9WOi1Ppc\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"id\":\"iAwpe-Chra\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Learn Accounting\",\"col\":3}},{\"id\":\"B7-uxs8tkU\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"tHb3yxthkR\",\"type\":\"header\",\"data\":{\"text\":\"Reports & Masters\",\"col\":12}},{\"id\":\"DnNtsmxpty\",\"type\":\"card\",\"data\":{\"card_name\":\"Accounting Masters\",\"col\":4}},{\"id\":\"nKKr6fjgjb\",\"type\":\"card\",\"data\":{\"card_name\":\"General Ledger\",\"col\":4}},{\"id\":\"xOHTyD8b5l\",\"type\":\"card\",\"data\":{\"card_name\":\"Accounts Receivable\",\"col\":4}},{\"id\":\"_Cb7C8XdJJ\",\"type\":\"card\",\"data\":{\"card_name\":\"Accounts Payable\",\"col\":4}},{\"id\":\"p7NY6MHe2Y\",\"type\":\"card\",\"data\":{\"card_name\":\"Financial Statements\",\"col\":4}},{\"id\":\"KlqilF5R_V\",\"type\":\"card\",\"data\":{\"card_name\":\"Taxes\",\"col\":4}},{\"id\":\"jTUy8LB0uw\",\"type\":\"card\",\"data\":{\"card_name\":\"Cost Center and Budgeting\",\"col\":4}},{\"id\":\"Wn2lhs7WLn\",\"type\":\"card\",\"data\":{\"card_name\":\"Multi Currency\",\"col\":4}},{\"id\":\"PAQMqqNkBM\",\"type\":\"card\",\"data\":{\"card_name\":\"Bank Statement\",\"col\":4}},{\"id\":\"Q_hBCnSeJY\",\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}},{\"id\":\"3AK1Zf0oew\",\"type\":\"card\",\"data\":{\"card_name\":\"Profitability\",\"col\":4}},{\"id\":\"kxhoaiqdLq\",\"type\":\"card\",\"data\":{\"card_name\":\"Opening and Closing\",\"col\":4}},{\"id\":\"q0MAlU2j_Z\",\"type\":\"card\",\"data\":{\"card_name\":\"Subscription Management\",\"col\":4}},{\"id\":\"ptm7T6Hwu-\",\"type\":\"card\",\"data\":{\"card_name\":\"Share Management\",\"col\":4}},{\"id\":\"OX7lZHbiTr\",\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}}]", "creation": "2020-03-02 15:41:59.515192", "custom_blocks": [], "docstatus": 0, @@ -1061,7 +1061,7 @@ "type": "Link" } ], - "modified": "2023-05-30 13:23:29.316711", + "modified": "2023-07-04 14:32:15.842044", "modified_by": "Administrator", "module": "Accounts", "name": "Accounting", @@ -1074,6 +1074,13 @@ "roles": [], "sequence_id": 2.0, "shortcuts": [ + { + "color": "Grey", + "doc_view": "List", + "label": "Learn Accounting", + "type": "URL", + "url": "https://frappe.school/courses/erpnext-accounting?utm_source=in_app" + }, { "label": "Chart of Accounts", "link_to": "Account", diff --git a/erpnext/buying/workspace/buying/buying.json b/erpnext/buying/workspace/buying/buying.json index 58c8f74710..1394fc48d5 100644 --- a/erpnext/buying/workspace/buying/buying.json +++ b/erpnext/buying/workspace/buying/buying.json @@ -5,7 +5,7 @@ "label": "Purchase Order Trends" } ], - "content": "[{\"type\":\"onboarding\",\"data\":{\"onboarding_name\":\"Buying\",\"col\":12}},{\"type\":\"chart\",\"data\":{\"chart_name\":\"Purchase Order Trends\",\"col\":12}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"Your Shortcuts\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Item\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Material Request\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Purchase Order\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Purchase Analytics\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Purchase 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\":\"Buying\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Items & Pricing\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Supplier\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Supplier Scorecard\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Key Reports\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Other Reports\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Regional\",\"col\":4}}]", + "content": "[{\"id\":\"I3JijHOxil\",\"type\":\"onboarding\",\"data\":{\"onboarding_name\":\"Buying\",\"col\":12}},{\"id\":\"j3dJGo8Ok6\",\"type\":\"chart\",\"data\":{\"chart_name\":\"Purchase Order Trends\",\"col\":12}},{\"id\":\"oN7lXSwQji\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"Ivw1PI_wEJ\",\"type\":\"header\",\"data\":{\"text\":\"Your Shortcuts\",\"col\":12}},{\"id\":\"RrWFEi4kCf\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Item\",\"col\":3}},{\"id\":\"RFIakryyJP\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Material Request\",\"col\":3}},{\"id\":\"bM10abFmf6\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Purchase Order\",\"col\":3}},{\"id\":\"lR0Hw_37Pu\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Purchase Analytics\",\"col\":3}},{\"id\":\"_HN0Ljw1lX\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Purchase Order Analysis\",\"col\":3}},{\"id\":\"kuLuiMRdnX\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"id\":\"tQFeiKptW2\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Learn Procurement\",\"col\":3}},{\"id\":\"0NiuFE_EGS\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"Xe2GVLOq8J\",\"type\":\"header\",\"data\":{\"text\":\"Reports & Masters\",\"col\":12}},{\"id\":\"QwqyG6XuUt\",\"type\":\"card\",\"data\":{\"card_name\":\"Buying\",\"col\":4}},{\"id\":\"bTPjOxC_N_\",\"type\":\"card\",\"data\":{\"card_name\":\"Items & Pricing\",\"col\":4}},{\"id\":\"87ht0HIneb\",\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}},{\"id\":\"EDOsBOmwgw\",\"type\":\"card\",\"data\":{\"card_name\":\"Supplier\",\"col\":4}},{\"id\":\"oWNNIiNb2i\",\"type\":\"card\",\"data\":{\"card_name\":\"Supplier Scorecard\",\"col\":4}},{\"id\":\"7F_13-ihHB\",\"type\":\"card\",\"data\":{\"card_name\":\"Key Reports\",\"col\":4}},{\"id\":\"pfwiLvionl\",\"type\":\"card\",\"data\":{\"card_name\":\"Other Reports\",\"col\":4}},{\"id\":\"8ySDy6s4qn\",\"type\":\"card\",\"data\":{\"card_name\":\"Regional\",\"col\":4}}]", "creation": "2020-01-28 11:50:26.195467", "custom_blocks": [], "docstatus": 0, @@ -511,7 +511,7 @@ "type": "Link" } ], - "modified": "2023-05-24 14:47:20.535772", + "modified": "2023-07-04 14:43:30.387683", "modified_by": "Administrator", "module": "Buying", "name": "Buying", @@ -532,6 +532,13 @@ "stats_filter": "{\n \"disabled\": 0\n}", "type": "DocType" }, + { + "color": "Grey", + "doc_view": "List", + "label": "Learn Procurement", + "type": "URL", + "url": "https://frappe.school/courses/procurement?utm_source=in_app" + }, { "color": "Yellow", "format": "{} Pending", diff --git a/erpnext/manufacturing/workspace/manufacturing/manufacturing.json b/erpnext/manufacturing/workspace/manufacturing/manufacturing.json index d862c349e3..518ae14659 100644 --- a/erpnext/manufacturing/workspace/manufacturing/manufacturing.json +++ b/erpnext/manufacturing/workspace/manufacturing/manufacturing.json @@ -1,6 +1,6 @@ { "charts": [], - "content": "[{\"id\":\"csBCiDglCE\",\"type\":\"header\",\"data\":{\"text\":\"Your Shortcuts\",\"col\":12}},{\"id\":\"xit0dg7KvY\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"BOM\",\"col\":3}},{\"id\":\"LRhGV9GAov\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Production Plan\",\"col\":3}},{\"id\":\"69KKosI6Hg\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Work Order\",\"col\":3}},{\"id\":\"PwndxuIpB3\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Job Card\",\"col\":3}},{\"id\":\"OaiDqTT03Y\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Forecasting\",\"col\":3}},{\"id\":\"OtMcArFRa5\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"BOM Stock Report\",\"col\":3}},{\"id\":\"76yYsI5imF\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Production Planning Report\",\"col\":3}},{\"id\":\"bN_6tHS-Ct\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"yVEFZMqVwd\",\"type\":\"header\",\"data\":{\"text\":\"Reports & Masters\",\"col\":12}},{\"id\":\"rwrmsTI58-\",\"type\":\"card\",\"data\":{\"card_name\":\"Production\",\"col\":4}},{\"id\":\"6dnsyX-siZ\",\"type\":\"card\",\"data\":{\"card_name\":\"Bill of Materials\",\"col\":4}},{\"id\":\"CIq-v5f5KC\",\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}},{\"id\":\"8RRiQeYr0G\",\"type\":\"card\",\"data\":{\"card_name\":\"Tools\",\"col\":4}},{\"id\":\"Pu8z7-82rT\",\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}}]", + "content": "[{\"id\":\"csBCiDglCE\",\"type\":\"header\",\"data\":{\"text\":\"Your Shortcuts\",\"col\":12}},{\"id\":\"xit0dg7KvY\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"BOM\",\"col\":3}},{\"id\":\"LRhGV9GAov\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Production Plan\",\"col\":3}},{\"id\":\"69KKosI6Hg\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Work Order\",\"col\":3}},{\"id\":\"PwndxuIpB3\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Job Card\",\"col\":3}},{\"id\":\"OaiDqTT03Y\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Forecasting\",\"col\":3}},{\"id\":\"OtMcArFRa5\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"BOM Stock Report\",\"col\":3}},{\"id\":\"76yYsI5imF\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Production Planning Report\",\"col\":3}},{\"id\":\"PIQJYZOMnD\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Learn Manufacturing\",\"col\":3}},{\"id\":\"bN_6tHS-Ct\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"yVEFZMqVwd\",\"type\":\"header\",\"data\":{\"text\":\"Reports & Masters\",\"col\":12}},{\"id\":\"rwrmsTI58-\",\"type\":\"card\",\"data\":{\"card_name\":\"Production\",\"col\":4}},{\"id\":\"6dnsyX-siZ\",\"type\":\"card\",\"data\":{\"card_name\":\"Bill of Materials\",\"col\":4}},{\"id\":\"CIq-v5f5KC\",\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}},{\"id\":\"8RRiQeYr0G\",\"type\":\"card\",\"data\":{\"card_name\":\"Tools\",\"col\":4}},{\"id\":\"Pu8z7-82rT\",\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}}]", "creation": "2020-03-02 17:11:37.032604", "custom_blocks": [], "docstatus": 0, @@ -316,7 +316,7 @@ "type": "Link" } ], - "modified": "2023-05-27 16:41:04.776115", + "modified": "2023-07-04 14:40:47.281125", "modified_by": "Administrator", "module": "Manufacturing", "name": "Manufacturing", @@ -329,6 +329,13 @@ "roles": [], "sequence_id": 8.0, "shortcuts": [ + { + "color": "Grey", + "doc_view": "List", + "label": "Learn Manufacturing", + "type": "URL", + "url": "https://frappe.school/courses/manufacturing?utm_source=in_app" + }, { "color": "Grey", "doc_view": "List", diff --git a/erpnext/projects/workspace/projects/projects.json b/erpnext/projects/workspace/projects/projects.json index 50730bac0d..94ae9c04a4 100644 --- a/erpnext/projects/workspace/projects/projects.json +++ b/erpnext/projects/workspace/projects/projects.json @@ -5,7 +5,7 @@ "label": "Open Projects" } ], - "content": "[{\"type\":\"chart\",\"data\":{\"chart_name\":\"Open Projects\",\"col\":12}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"Your Shortcuts\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Task\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Project\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Timesheet\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Project Billing Summary\",\"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\":\"Projects\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Time Tracking\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}}]", + "content": "[{\"id\":\"VDMms0hapk\",\"type\":\"chart\",\"data\":{\"chart_name\":\"Open Projects\",\"col\":12}},{\"id\":\"7Mbx6I5JUf\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"nyuMo9byw7\",\"type\":\"header\",\"data\":{\"text\":\"Your Shortcuts\",\"col\":12}},{\"id\":\"dILbX_r0ve\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Task\",\"col\":3}},{\"id\":\"JT8ntrqRiJ\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Project\",\"col\":3}},{\"id\":\"RsafDhm1MS\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Timesheet\",\"col\":3}},{\"id\":\"cVJH-gD0CR\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Project Billing Summary\",\"col\":3}},{\"id\":\"DbctrdmAy1\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"id\":\"jx5aPK9aXN\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Learn Project Management\",\"col\":3}},{\"id\":\"ncIHWGQQvX\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"oGhjvYjfv-\",\"type\":\"header\",\"data\":{\"text\":\"Reports & Masters\",\"col\":12}},{\"id\":\"TdsgJyG3EI\",\"type\":\"card\",\"data\":{\"card_name\":\"Projects\",\"col\":4}},{\"id\":\"nIc0iyvf1T\",\"type\":\"card\",\"data\":{\"card_name\":\"Time Tracking\",\"col\":4}},{\"id\":\"8G1if4jsQ7\",\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}},{\"id\":\"o7qTNRXZI8\",\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}}]", "creation": "2020-03-02 15:46:04.874669", "custom_blocks": [], "docstatus": 0, @@ -192,7 +192,7 @@ "type": "Link" } ], - "modified": "2023-05-24 14:47:23.179860", + "modified": "2023-07-04 14:39:08.935853", "modified_by": "Administrator", "module": "Projects", "name": "Projects", @@ -205,6 +205,13 @@ "roles": [], "sequence_id": 11.0, "shortcuts": [ + { + "color": "Grey", + "doc_view": "List", + "label": "Learn Project Management", + "type": "URL", + "url": "https://frappe.school/courses/project-management?utm_source=in_app" + }, { "color": "Blue", "format": "{} Assigned", diff --git a/erpnext/selling/workspace/selling/selling.json b/erpnext/selling/workspace/selling/selling.json index f498223aa8..e13bdec11f 100644 --- a/erpnext/selling/workspace/selling/selling.json +++ b/erpnext/selling/workspace/selling/selling.json @@ -5,7 +5,7 @@ "label": "Sales Order Trends" } ], - "content": "[{\"id\":\"ow595dYDrI\",\"type\":\"onboarding\",\"data\":{\"onboarding_name\":\"Selling\",\"col\":12}},{\"id\":\"vBSf8Vi9U8\",\"type\":\"chart\",\"data\":{\"chart_name\":\"Sales Order Trends\",\"col\":12}},{\"id\":\"aW2i5R5GRP\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"1it3dCOnm6\",\"type\":\"header\",\"data\":{\"text\":\"Quick Access\",\"col\":12}},{\"id\":\"x7pLl-spS4\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Item\",\"col\":3}},{\"id\":\"SSGrXWmY-H\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Sales Order\",\"col\":3}},{\"id\":\"-5J_yLxDaS\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Sales Analytics\",\"col\":3}},{\"id\":\"6YEYpnIBKV\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Point of Sale\",\"col\":3}},{\"id\":\"c_GjZuZ2oN\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"id\":\"oNjjNbnUHp\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"0BcePLg0g1\",\"type\":\"header\",\"data\":{\"text\":\"Reports & Masters\",\"col\":12}},{\"id\":\"uze5dJ1ipL\",\"type\":\"card\",\"data\":{\"card_name\":\"Selling\",\"col\":4}},{\"id\":\"3j2fYwMAkq\",\"type\":\"card\",\"data\":{\"card_name\":\"Point of Sale\",\"col\":4}},{\"id\":\"xImm8NepFt\",\"type\":\"card\",\"data\":{\"card_name\":\"Items and Pricing\",\"col\":4}},{\"id\":\"6MjIe7KCQo\",\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}},{\"id\":\"lBu2EKgmJF\",\"type\":\"card\",\"data\":{\"card_name\":\"Key Reports\",\"col\":4}},{\"id\":\"1ARHrjg4kI\",\"type\":\"card\",\"data\":{\"card_name\":\"Other Reports\",\"col\":4}}]", + "content": "[{\"id\":\"ow595dYDrI\",\"type\":\"onboarding\",\"data\":{\"onboarding_name\":\"Selling\",\"col\":12}},{\"id\":\"vBSf8Vi9U8\",\"type\":\"chart\",\"data\":{\"chart_name\":\"Sales Order Trends\",\"col\":12}},{\"id\":\"aW2i5R5GRP\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"1it3dCOnm6\",\"type\":\"header\",\"data\":{\"text\":\"Quick Access\",\"col\":12}},{\"id\":\"x7pLl-spS4\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Item\",\"col\":3}},{\"id\":\"SSGrXWmY-H\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Sales Order\",\"col\":3}},{\"id\":\"-5J_yLxDaS\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Sales Analytics\",\"col\":3}},{\"id\":\"6YEYpnIBKV\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Point of Sale\",\"col\":3}},{\"id\":\"c_GjZuZ2oN\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"id\":\"mX-9DJSyT2\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Learn Sales Management\",\"col\":3}},{\"id\":\"oNjjNbnUHp\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"0BcePLg0g1\",\"type\":\"header\",\"data\":{\"text\":\"Reports & Masters\",\"col\":12}},{\"id\":\"uze5dJ1ipL\",\"type\":\"card\",\"data\":{\"card_name\":\"Selling\",\"col\":4}},{\"id\":\"3j2fYwMAkq\",\"type\":\"card\",\"data\":{\"card_name\":\"Point of Sale\",\"col\":4}},{\"id\":\"xImm8NepFt\",\"type\":\"card\",\"data\":{\"card_name\":\"Items and Pricing\",\"col\":4}},{\"id\":\"6MjIe7KCQo\",\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}},{\"id\":\"lBu2EKgmJF\",\"type\":\"card\",\"data\":{\"card_name\":\"Key Reports\",\"col\":4}},{\"id\":\"1ARHrjg4kI\",\"type\":\"card\",\"data\":{\"card_name\":\"Other Reports\",\"col\":4}}]", "creation": "2020-01-28 11:49:12.092882", "custom_blocks": [], "docstatus": 0, @@ -621,7 +621,7 @@ "type": "Link" } ], - "modified": "2023-05-26 16:31:53.634851", + "modified": "2023-07-04 14:35:58.204465", "modified_by": "Administrator", "module": "Selling", "name": "Selling", @@ -634,6 +634,13 @@ "roles": [], "sequence_id": 6.0, "shortcuts": [ + { + "color": "Grey", + "doc_view": "List", + "label": "Learn Sales Management", + "type": "URL", + "url": "https://frappe.school/courses/sales-management-course?utm_source=in_app" + }, { "label": "Point of Sale", "link_to": "point-of-sale", diff --git a/erpnext/setup/install.py b/erpnext/setup/install.py index 013d945c22..85eaf5fa92 100644 --- a/erpnext/setup/install.py +++ b/erpnext/setup/install.py @@ -169,7 +169,7 @@ def add_standard_navbar_items(): { "item_label": "Documentation", "item_type": "Route", - "route": "https://docs.erpnext.com/docs/v14/user/manual/en/introduction", + "route": "https://docs.erpnext.com/", "is_standard": 1, }, { @@ -178,6 +178,12 @@ def add_standard_navbar_items(): "route": "https://discuss.frappe.io", "is_standard": 1, }, + { + "item_label": "Frappe School", + "item_type": "Route", + "route": "https://frappe.school?utm_source=in_app", + "is_standard": 1, + }, { "item_label": "Report an Issue", "item_type": "Route", diff --git a/erpnext/stock/workspace/stock/stock.json b/erpnext/stock/workspace/stock/stock.json index 502afde2f4..1a683e2b3d 100644 --- a/erpnext/stock/workspace/stock/stock.json +++ b/erpnext/stock/workspace/stock/stock.json @@ -5,7 +5,7 @@ "label": "Warehouse wise Stock Value" } ], - "content": "[{\"type\":\"onboarding\",\"data\":{\"onboarding_name\":\"Stock\",\"col\":12}},{\"type\":\"chart\",\"data\":{\"chart_name\":\"Warehouse wise Stock Value\",\"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\":\"Material Request\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Stock Entry\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Purchase Receipt\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Delivery Note\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Stock Ledger\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Stock Balance\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"Masters & Reports\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Items and Pricing\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Stock Transactions\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Stock Reports\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Serial No and Batch\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Tools\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Key Reports\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Other Reports\",\"col\":4}}]", + "content": "[{\"id\":\"BJTnTemGjc\",\"type\":\"onboarding\",\"data\":{\"onboarding_name\":\"Stock\",\"col\":12}},{\"id\":\"WKeeHLcyXI\",\"type\":\"number_card\",\"data\":{\"number_card_name\":\"Total Stock Value\",\"col\":4}},{\"id\":\"6nVoOHuy5w\",\"type\":\"number_card\",\"data\":{\"number_card_name\":\"Total Warehouses\",\"col\":4}},{\"id\":\"OUex5VED7d\",\"type\":\"number_card\",\"data\":{\"number_card_name\":\"Total Active Items\",\"col\":4}},{\"id\":\"A3svBa974t\",\"type\":\"chart\",\"data\":{\"chart_name\":\"Warehouse wise Stock Value\",\"col\":12}},{\"id\":\"wwAoBx30p3\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"LkqrpJHM9X\",\"type\":\"header\",\"data\":{\"text\":\"Quick Access\",\"col\":12}},{\"id\":\"OR8PYiYspy\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Item\",\"col\":3}},{\"id\":\"KP1A22WjDl\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Material Request\",\"col\":3}},{\"id\":\"0EYKOrx6U1\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Stock Entry\",\"col\":3}},{\"id\":\"cqotiphmhZ\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Purchase Receipt\",\"col\":3}},{\"id\":\"Xhjqnm-JxZ\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Delivery Note\",\"col\":3}},{\"id\":\"yxCx6Tay4Z\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Stock Ledger\",\"col\":3}},{\"id\":\"o3sdEnNy34\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Stock Balance\",\"col\":3}},{\"id\":\"m9O0HUUDS5\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"id\":\"NwWcNC_xNj\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Learn Inventory Management\",\"col\":3}},{\"id\":\"9AmAh9LnPI\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"3SmmwBbOER\",\"type\":\"header\",\"data\":{\"text\":\"Masters & Reports\",\"col\":12}},{\"id\":\"OAGNH9njt7\",\"type\":\"card\",\"data\":{\"card_name\":\"Items Catalogue\",\"col\":4}},{\"id\":\"jF9eKz0qr0\",\"type\":\"card\",\"data\":{\"card_name\":\"Stock Transactions\",\"col\":4}},{\"id\":\"tyTnQo-MIS\",\"type\":\"card\",\"data\":{\"card_name\":\"Stock Reports\",\"col\":4}},{\"id\":\"dJaJw6YNPU\",\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}},{\"id\":\"rQf5vK4N_T\",\"type\":\"card\",\"data\":{\"card_name\":\"Serial No and Batch\",\"col\":4}},{\"id\":\"7oM7hFL4v8\",\"type\":\"card\",\"data\":{\"card_name\":\"Tools\",\"col\":4}},{\"id\":\"ve3L6ZifkB\",\"type\":\"card\",\"data\":{\"card_name\":\"Key Reports\",\"col\":4}},{\"id\":\"8Kfvu3umw7\",\"type\":\"card\",\"data\":{\"card_name\":\"Other Reports\",\"col\":4}}]", "creation": "2020-03-02 15:43:10.096528", "custom_blocks": [], "docstatus": 0, @@ -17,14 +17,6 @@ "is_hidden": 0, "label": "Stock", "links": [ - { - "hidden": 0, - "is_query_report": 0, - "label": "Items and Pricing", - "link_count": 0, - "onboard": 0, - "type": "Card Break" - }, { "dependencies": "", "hidden": 0, @@ -442,113 +434,6 @@ "onboard": 0, "type": "Link" }, - { - "hidden": 0, - "is_query_report": 0, - "label": "Key Reports", - "link_count": 0, - "onboard": 0, - "type": "Card Break" - }, - { - "dependencies": "Item Price", - "hidden": 0, - "is_query_report": 0, - "label": "Item-wise Price List Rate", - "link_count": 0, - "link_to": "Item-wise Price List Rate", - "link_type": "Report", - "onboard": 1, - "type": "Link" - }, - { - "dependencies": "Stock Entry", - "hidden": 0, - "is_query_report": 1, - "label": "Stock Analytics", - "link_count": 0, - "link_to": "Stock Analytics", - "link_type": "Report", - "onboard": 1, - "type": "Link" - }, - { - "dependencies": "Item", - "hidden": 0, - "is_query_report": 1, - "label": "Stock Qty vs Serial No Count", - "link_count": 0, - "link_to": "Stock Qty vs Serial No Count", - "link_type": "Report", - "onboard": 1, - "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": "Purchase Receipt", - "hidden": 0, - "is_query_report": 1, - "label": "Purchase Receipt Trends", - "link_count": 0, - "link_to": "Purchase Receipt Trends", - "link_type": "Report", - "onboard": 0, - "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": 0, - "type": "Link" - }, - { - "dependencies": "Purchase Order", - "hidden": 0, - "is_query_report": 1, - "label": "Purchase Order Analysis", - "link_count": 0, - "link_to": "Purchase Order Analysis", - "link_type": "Report", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Bin", - "hidden": 0, - "is_query_report": 1, - "label": "Item Shortage Report", - "link_count": 0, - "link_to": "Item Shortage Report", - "link_type": "Report", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Batch", - "hidden": 0, - "is_query_report": 1, - "label": "Batch-Wise Balance History", - "link_count": 0, - "link_to": "Batch-Wise Balance History", - "link_type": "Report", - "onboard": 0, - "type": "Link" - }, { "hidden": 0, "is_query_report": 0, @@ -717,13 +602,185 @@ "link_type": "Report", "onboard": 0, "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Items Catalogue", + "link_count": 6, + "onboard": 0, + "type": "Card Break" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Item", + "link_count": 0, + "link_to": "Item", + "link_type": "DocType", + "onboard": 1, + "type": "Link" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Item Group", + "link_count": 0, + "link_to": "Item Group", + "link_type": "DocType", + "onboard": 1, + "type": "Link" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Product Bundle", + "link_count": 0, + "link_to": "Product Bundle", + "link_type": "DocType", + "onboard": 1, + "type": "Link" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Shipping Rule", + "link_count": 0, + "link_to": "Shipping Rule", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Item Alternative", + "link_count": 0, + "link_to": "Item Alternative", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Item Manufacturer", + "link_count": 0, + "link_to": "Item Manufacturer", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Key Reports", + "link_count": 7, + "onboard": 0, + "type": "Card Break" + }, + { + "dependencies": "Stock Entry", + "hidden": 0, + "is_query_report": 1, + "label": "Stock Analytics", + "link_count": 0, + "link_to": "Stock Analytics", + "link_type": "Report", + "onboard": 1, + "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": "Purchase Receipt", + "hidden": 0, + "is_query_report": 1, + "label": "Purchase Receipt Trends", + "link_count": 0, + "link_to": "Purchase Receipt Trends", + "link_type": "Report", + "onboard": 0, + "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": 0, + "type": "Link" + }, + { + "dependencies": "Purchase Order", + "hidden": 0, + "is_query_report": 1, + "label": "Purchase Order Analysis", + "link_count": 0, + "link_to": "Purchase Order Analysis", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "Bin", + "hidden": 0, + "is_query_report": 1, + "label": "Item Shortage Report", + "link_count": 0, + "link_to": "Item Shortage Report", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "Batch", + "hidden": 0, + "is_query_report": 1, + "label": "Batch-Wise Balance History", + "link_count": 0, + "link_to": "Batch-Wise Balance History", + "link_type": "Report", + "onboard": 0, + "type": "Link" } ], - "modified": "2023-05-24 14:47:21.707580", + "modified": "2023-07-04 14:38:14.988756", "modified_by": "Administrator", "module": "Stock", "name": "Stock", - "number_cards": [], + "number_cards": [ + { + "label": "Total Warehouses", + "number_card_name": "Total Warehouses" + }, + { + "label": "Total Stock Value", + "number_card_name": "Total Stock Value" + }, + { + "label": "Total Active Items", + "number_card_name": "Total Active Items" + } + ], "owner": "Administrator", "parent_page": "", "public": 1, @@ -740,6 +797,13 @@ "stats_filter": "{\n \"disabled\" : 0\n}", "type": "DocType" }, + { + "color": "Grey", + "doc_view": "List", + "label": "Learn Inventory Management", + "type": "URL", + "url": "https://frappe.school/courses/inventory-management?utm_source=in_app" + }, { "color": "Yellow", "format": "{} Pending", From 2f169575e9603009a4473bf14c2d36a006bf829e Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 4 Jul 2023 15:30:27 +0530 Subject: [PATCH 46/99] fix: Netherlands - Grootboekschema COA structure (#35991) fix: Netherlands - Grootboekschema coa structure --- .../verified/nl_grootboekschema.json | 784 +++++++++--------- 1 file changed, 393 insertions(+), 391 deletions(-) diff --git a/erpnext/accounts/doctype/account/chart_of_accounts/verified/nl_grootboekschema.json b/erpnext/accounts/doctype/account/chart_of_accounts/verified/nl_grootboekschema.json index 58b91227f7..9fb47bb02d 100644 --- a/erpnext/accounts/doctype/account/chart_of_accounts/verified/nl_grootboekschema.json +++ b/erpnext/accounts/doctype/account/chart_of_accounts/verified/nl_grootboekschema.json @@ -2,75 +2,13 @@ "country_code": "nl", "name": "Netherlands - Grootboekschema", "tree": { - "FABRIKAGEREKENINGEN": { - "is_group": 1, - "root_type": "Expense" - }, "FINANCIELE REKENINGEN, KORTLOPENDE VORDERINGEN EN SCHULDEN": { "Bank": { "RABO Bank": { "account_type": "Bank" }, "account_type": "Bank" - }, - "KORTLOPENDE SCHULDEN": { - "Af te dragen Btw-verlegd": { - "account_type": "Tax" - }, - "Afdracht loonheffing": {}, - "Btw af te dragen hoog": { - "account_type": "Tax" - }, - "Btw af te dragen laag": { - "account_type": "Tax" - }, - "Btw af te dragen overig": { - "account_type": "Tax" - }, - "Btw oude jaren": { - "account_type": "Tax" - }, - "Btw te vorderen hoog": { - "account_type": "Tax" - }, - "Btw te vorderen laag": { - "account_type": "Tax" - }, - "Btw te vorderen overig": { - "account_type": "Tax" - }, - "Btw-afdracht": { - "account_type": "Tax" - }, - "Crediteuren": { - "account_type": "Payable" - }, - "Dividend": {}, - "Dividendbelasting": {}, - "Energiekosten 1": {}, - "Investeringsaftrek": {}, - "Loonheffing": {}, - "Overige te betalen posten": {}, - "Pensioenpremies 1": {}, - "Premie WIR": {}, - "Rekening-courant inkoopvereniging": {}, - "Rente": {}, - "Sociale lasten 1": {}, - "Stock Recieved niet gefactureerd": { - "account_type": "Stock Received But Not Billed" - }, - "Tanti\u00e8mes 1": {}, - "Te vorderen Btw-verlegd": { - "account_type": "Tax" - }, - "Telefoon/telefax 1": {}, - "Termijnen onderh. werk": {}, - "Vakantiedagen": {}, - "Vakantiegeld 1": {}, - "Vakantiezegels": {}, - "Vennootschapsbelasting": {}, - "Vooruit ontvangen bedr.": {} - }, + }, "LIQUIDE MIDDELEN": { "ABN-AMRO bank": {}, "Bankbetaalkaarten": {}, @@ -91,6 +29,110 @@ }, "account_type": "Cash" }, + "TUSSENREKENINGEN": { + "Betaalwijze cadeaubonnen": { + "account_type": "Cash" + }, + "Betaalwijze chipknip": { + "account_type": "Cash" + }, + "Betaalwijze contant": { + "account_type": "Cash" + }, + "Betaalwijze pin": { + "account_type": "Cash" + }, + "Inkopen Nederland hoog": { + "account_type": "Cash" + }, + "Inkopen Nederland laag": { + "account_type": "Cash" + }, + "Inkopen Nederland onbelast": { + "account_type": "Cash" + }, + "Inkopen Nederland overig": { + "account_type": "Cash" + }, + "Inkopen Nederland verlegd": { + "account_type": "Cash" + }, + "Inkopen binnen EU hoog": { + "account_type": "Cash" + }, + "Inkopen binnen EU laag": { + "account_type": "Cash" + }, + "Inkopen binnen EU overig": { + "account_type": "Cash" + }, + "Inkopen buiten EU hoog": { + "account_type": "Cash" + }, + "Inkopen buiten EU laag": { + "account_type": "Cash" + }, + "Inkopen buiten EU overig": { + "account_type": "Cash" + }, + "Kassa 1": { + "account_type": "Cash" + }, + "Kassa 2": { + "account_type": "Cash" + }, + "Netto lonen": { + "account_type": "Cash" + }, + "Tegenrekening Inkopen": { + "account_type": "Cash" + }, + "Tussenrek. autom. betalingen": { + "account_type": "Cash" + }, + "Tussenrek. autom. loonbetalingen": { + "account_type": "Cash" + }, + "Tussenrek. cadeaubonbetalingen": { + "account_type": "Cash" + }, + "Tussenrekening balans": { + "account_type": "Cash" + }, + "Tussenrekening chipknip": { + "account_type": "Cash" + }, + "Tussenrekening correcties": { + "account_type": "Cash" + }, + "Tussenrekening pin": { + "account_type": "Cash" + }, + "Vraagposten": { + "account_type": "Cash" + }, + "VOORRAAD GRONDSTOFFEN, HULPMATERIALEN EN HANDELSGOEDEREN": { + "Emballage": {}, + "Gereed product 1": {}, + "Gereed product 2": {}, + "Goederen 1": {}, + "Goederen 2": {}, + "Goederen in consignatie": {}, + "Goederen onderweg": {}, + "Grondstoffen 1": {}, + "Grondstoffen 2": {}, + "Halffabrikaten 1": {}, + "Halffabrikaten 2": {}, + "Hulpstoffen 1": {}, + "Hulpstoffen 2": {}, + "Kantoorbenodigdheden": {}, + "Onderhanden werk": {}, + "Verpakkingsmateriaal": {}, + "Zegels": {}, + "root_type": "Asset" + }, + "root_type": "Asset" + }, "VORDERINGEN": { "Debiteuren": { "account_type": "Receivable" @@ -104,278 +146,299 @@ "Voorziening dubieuze debiteuren": {} }, "root_type": "Asset" - }, - "INDIRECTE KOSTEN": { + }, + "KORTLOPENDE SCHULDEN": { + "Af te dragen Btw-verlegd": { + "account_type": "Tax" + }, + "Afdracht loonheffing": {}, + "Btw af te dragen hoog": { + "account_type": "Tax" + }, + "Btw af te dragen laag": { + "account_type": "Tax" + }, + "Btw af te dragen overig": { + "account_type": "Tax" + }, + "Btw oude jaren": { + "account_type": "Tax" + }, + "Btw te vorderen hoog": { + "account_type": "Tax" + }, + "Btw te vorderen laag": { + "account_type": "Tax" + }, + "Btw te vorderen overig": { + "account_type": "Tax" + }, + "Btw-afdracht": { + "account_type": "Tax" + }, + "Crediteuren": { + "account_type": "Payable" + }, + "Dividend": {}, + "Dividendbelasting": {}, + "Energiekosten 1": {}, + "Investeringsaftrek": {}, + "Loonheffing": {}, + "Overige te betalen posten": {}, + "Pensioenpremies 1": {}, + "Premie WIR": {}, + "Rekening-courant inkoopvereniging": {}, + "Rente": {}, + "Sociale lasten 1": {}, + "Stock Recieved niet gefactureerd": { + "account_type": "Stock Received But Not Billed" + }, + "Tanti\u00e8mes 1": {}, + "Te vorderen Btw-verlegd": { + "account_type": "Tax" + }, + "Telefoon/telefax 1": {}, + "Termijnen onderh. werk": {}, + "Vakantiedagen": {}, + "Vakantiegeld 1": {}, + "Vakantiezegels": {}, + "Vennootschapsbelasting": {}, + "Vooruit ontvangen bedr.": {}, + "is_group": 1, + "root_type": "Liability" + }, + "FABRIKAGEREKENINGEN": { "is_group": 1, - "root_type": "Expense" - }, - "KOSTENREKENINGEN": { - "AFSCHRIJVINGEN": { - "Aanhangwagens": {}, - "Aankoopkosten": {}, - "Aanloopkosten": {}, - "Auteursrechten": {}, - "Bedrijfsgebouwen": {}, - "Bedrijfsinventaris": { + "root_type": "Expense", + "INDIRECTE KOSTEN": { + "is_group": 1, + "root_type": "Expense" + }, + "KOSTENREKENINGEN": { + "AFSCHRIJVINGEN": { + "Aanhangwagens": {}, + "Aankoopkosten": {}, + "Aanloopkosten": {}, + "Auteursrechten": {}, + "Bedrijfsgebouwen": {}, + "Bedrijfsinventaris": { + "account_type": "Depreciation" + }, + "Drankvergunningen": {}, + "Fabrieksinventaris": { + "account_type": "Depreciation" + }, + "Gebouwen": {}, + "Gereedschappen": {}, + "Goodwill": {}, + "Grondverbetering": {}, + "Heftrucks": {}, + "Kantine-inventaris": {}, + "Kantoorinventaris": { + "account_type": "Depreciation" + }, + "Kantoormachines": {}, + "Licenties": {}, + "Machines 1": {}, + "Magazijninventaris": {}, + "Octrooien": {}, + "Ontwikkelingskosten": {}, + "Pachtersinvestering": {}, + "Parkeerplaats": {}, + "Personenauto's": { + "account_type": "Depreciation" + }, + "Rijwielen en bromfietsen": {}, + "Tonnagevergunningen": {}, + "Verbouwingen": {}, + "Vergunningen": {}, + "Voorraadverschillen": {}, + "Vrachtauto's": {}, + "Winkels": {}, + "Woon-winkelhuis": {}, "account_type": "Depreciation" }, - "Drankvergunningen": {}, - "Fabrieksinventaris": { - "account_type": "Depreciation" + "ALGEMENE KOSTEN": { + "Accountantskosten": {}, + "Advieskosten": {}, + "Assuranties 1": {}, + "Bankkosten": {}, + "Juridische kosten": {}, + "Overige algemene kosten": {}, + "Toev. Ass. eigen risico": {} }, - "Gebouwen": {}, - "Gereedschappen": {}, - "Goodwill": {}, - "Grondverbetering": {}, - "Heftrucks": {}, - "Kantine-inventaris": {}, - "Kantoorinventaris": { - "account_type": "Depreciation" + "BEDRIJFSKOSTEN": { + "Assuranties 2": {}, + "Energie (krachtstroom)": {}, + "Gereedschappen 1": {}, + "Hulpmaterialen 1": {}, + "Huur inventaris": {}, + "Huur machines": {}, + "Leasing invent.operational": {}, + "Leasing mach. operational": {}, + "Onderhoud inventaris": {}, + "Onderhoud machines": {}, + "Ophalen/vervoer afval": {}, + "Overige bedrijfskosten": {} }, - "Kantoormachines": {}, - "Licenties": {}, - "Machines 1": {}, - "Magazijninventaris": {}, - "Octrooien": {}, - "Ontwikkelingskosten": {}, - "Pachtersinvestering": {}, - "Parkeerplaats": {}, - "Personenauto's": { - "account_type": "Depreciation" + "FINANCIERINGSKOSTEN 1": { + "Overige rentebaten": {}, + "Overige rentelasten": {}, + "Rente bankkrediet": {}, + "Rente huurkoopcontracten": {}, + "Rente hypotheek": {}, + "Rente leasecontracten": {}, + "Rente lening o/g": {}, + "Rente lening u/g": {} }, - "Rijwielen en bromfietsen": {}, - "Tonnagevergunningen": {}, - "Verbouwingen": {}, - "Vergunningen": {}, - "Voorraadverschillen": {}, - "Vrachtauto's": {}, - "Winkels": {}, - "Woon-winkelhuis": {}, - "account_type": "Depreciation" - }, - "ALGEMENE KOSTEN": { - "Accountantskosten": {}, - "Advieskosten": {}, - "Assuranties 1": {}, - "Bankkosten": {}, - "Juridische kosten": {}, - "Overige algemene kosten": {}, - "Toev. Ass. eigen risico": {} - }, - "BEDRIJFSKOSTEN": { - "Assuranties 2": {}, - "Energie (krachtstroom)": {}, - "Gereedschappen 1": {}, - "Hulpmaterialen 1": {}, - "Huur inventaris": {}, - "Huur machines": {}, - "Leasing invent.operational": {}, - "Leasing mach. operational": {}, - "Onderhoud inventaris": {}, - "Onderhoud machines": {}, - "Ophalen/vervoer afval": {}, - "Overige bedrijfskosten": {} - }, - "FINANCIERINGSKOSTEN 1": { - "Overige rentebaten": {}, - "Overige rentelasten": {}, - "Rente bankkrediet": {}, - "Rente huurkoopcontracten": {}, - "Rente hypotheek": {}, - "Rente leasecontracten": {}, - "Rente lening o/g": {}, - "Rente lening u/g": {} - }, - "HUISVESTINGSKOSTEN": { - "Assurantie onroerend goed": {}, - "Belastingen onr. Goed": {}, - "Energiekosten": {}, - "Groot onderhoud onr. Goed": {}, - "Huur": {}, - "Huurwaarde woongedeelte": {}, - "Onderhoud onroerend goed": {}, - "Ontvangen huren": {}, - "Overige huisvestingskosten": {}, - "Pacht": {}, - "Schoonmaakkosten": {}, - "Toevoeging egalisatieres. Groot onderhoud": {} - }, - "KANTOORKOSTEN": { - "Administratiekosten": {}, - "Contributies/abonnementen": {}, - "Huur kantoorapparatuur": {}, - "Internetaansluiting": {}, - "Kantoorbenodigdh./drukw.": {}, - "Onderhoud kantoorinvent.": {}, - "Overige kantoorkosten": {}, - "Porti": {}, - "Telefoon/telefax": {} - }, - "OVERIGE BATEN EN LASTEN": { - "Betaalde schadevergoed.": {}, - "Boekverlies vaste activa": {}, - "Boekwinst van vaste activa": {}, - "K.O. regeling OB": {}, - "Kasverschillen": {}, - "Kosten loonbelasting": {}, - "Kosten omzetbelasting": {}, - "Nadelige koersverschillen": {}, - "Naheffing bedrijfsver.": {}, - "Ontvangen schadevergoed.": {}, - "Overige baten": {}, - "Overige lasten": {}, - "Voordelige koersverschil.": {} - }, - "PERSONEELSKOSTEN": { - "Autokostenvergoeding": {}, - "Bedrijfskleding": {}, - "Belastingvrije uitkeringen": {}, - "Bijzondere beloningen": {}, - "Congressen, seminars en symposia": {}, - "Gereedschapsgeld": {}, - "Geschenken personeel": {}, - "Gratificaties": {}, - "Inhouding pensioenpremies": {}, - "Inhouding sociale lasten": {}, - "Kantinekosten": {}, - "Lonen en salarissen": {}, - "Loonwerk": {}, - "Managementvergoedingen": {}, - "Opleidingskosten": {}, - "Oprenting stamrechtverpl.": {}, - "Overhevelingstoeslag": {}, - "Overige kostenverg.": {}, - "Overige personeelskosten": {}, - "Overige uitkeringen": {}, - "Pensioenpremies": {}, - "Provisie 1": {}, - "Reiskosten": {}, - "Rijwielvergoeding": {}, - "Sociale lasten": {}, - "Tanti\u00e8mes": {}, - "Thuiswerkers": {}, - "Toev. Backservice pens.verpl.": {}, - "Toevoeging pensioenverpl.": {}, - "Uitkering ziekengeld": {}, - "Uitzendkrachten": {}, - "Vakantiebonnen": {}, - "Vakantiegeld": {}, - "Vergoeding studiekosten": {}, - "Wervingskosten personeel": {} - }, - "VERKOOPKOSTEN": { - "Advertenties": {}, - "Afschrijving dubieuze deb.": {}, - "Beurskosten": {}, - "Etalagekosten": {}, - "Exportkosten": {}, - "Kascorrecties": {}, - "Overige verkoopkosten": {}, - "Provisie": {}, - "Reclame": {}, - "Reis en verblijfkosten": {}, - "Relatiegeschenken": {}, - "Representatiekosten": {}, - "Uitgaande vrachten": {}, - "Veilingkosten": {}, - "Verpakkingsmateriaal 1": {}, - "Websitekosten": {} - }, - "VERVOERSKOSTEN": { - "Assuranties auto's": {}, - "Brandstoffen": {}, - "Leasing auto's": {}, - "Onderhoud personenauto's": {}, - "Onderhoud vrachtauto's": {}, - "Overige vervoerskosten": {}, - "Priv\u00e9-gebruik auto's": {}, - "Wegenbelasting": {} - }, - "root_type": "Expense" - }, - "TUSSENREKENINGEN": { - "Betaalwijze cadeaubonnen": { - "account_type": "Cash" - }, - "Betaalwijze chipknip": { - "account_type": "Cash" - }, - "Betaalwijze contant": { - "account_type": "Cash" - }, - "Betaalwijze pin": { - "account_type": "Cash" - }, - "Inkopen Nederland hoog": { - "account_type": "Cash" - }, - "Inkopen Nederland laag": { - "account_type": "Cash" - }, - "Inkopen Nederland onbelast": { - "account_type": "Cash" - }, - "Inkopen Nederland overig": { - "account_type": "Cash" - }, - "Inkopen Nederland verlegd": { - "account_type": "Cash" - }, - "Inkopen binnen EU hoog": { - "account_type": "Cash" - }, - "Inkopen binnen EU laag": { - "account_type": "Cash" - }, - "Inkopen binnen EU overig": { - "account_type": "Cash" - }, - "Inkopen buiten EU hoog": { - "account_type": "Cash" - }, - "Inkopen buiten EU laag": { - "account_type": "Cash" - }, - "Inkopen buiten EU overig": { - "account_type": "Cash" - }, - "Kassa 1": { - "account_type": "Cash" - }, - "Kassa 2": { - "account_type": "Cash" - }, - "Netto lonen": { - "account_type": "Cash" - }, - "Tegenrekening Inkopen": { - "account_type": "Cash" - }, - "Tussenrek. autom. betalingen": { - "account_type": "Cash" - }, - "Tussenrek. autom. loonbetalingen": { - "account_type": "Cash" - }, - "Tussenrek. cadeaubonbetalingen": { - "account_type": "Cash" - }, - "Tussenrekening balans": { - "account_type": "Cash" - }, - "Tussenrekening chipknip": { - "account_type": "Cash" - }, - "Tussenrekening correcties": { - "account_type": "Cash" - }, - "Tussenrekening pin": { - "account_type": "Cash" - }, - "Vraagposten": { - "account_type": "Cash" - }, - "root_type": "Asset" + "HUISVESTINGSKOSTEN": { + "Assurantie onroerend goed": {}, + "Belastingen onr. Goed": {}, + "Energiekosten": {}, + "Groot onderhoud onr. Goed": {}, + "Huur": {}, + "Huurwaarde woongedeelte": {}, + "Onderhoud onroerend goed": {}, + "Ontvangen huren": {}, + "Overige huisvestingskosten": {}, + "Pacht": {}, + "Schoonmaakkosten": {}, + "Toevoeging egalisatieres. Groot onderhoud": {} + }, + "KANTOORKOSTEN": { + "Administratiekosten": {}, + "Contributies/abonnementen": {}, + "Huur kantoorapparatuur": {}, + "Internetaansluiting": {}, + "Kantoorbenodigdh./drukw.": {}, + "Onderhoud kantoorinvent.": {}, + "Overige kantoorkosten": {}, + "Porti": {}, + "Telefoon/telefax": {} + }, + "OVERIGE BATEN EN LASTEN": { + "Betaalde schadevergoed.": {}, + "Boekverlies vaste activa": {}, + "Boekwinst van vaste activa": {}, + "K.O. regeling OB": {}, + "Kasverschillen": {}, + "Kosten loonbelasting": {}, + "Kosten omzetbelasting": {}, + "Nadelige koersverschillen": {}, + "Naheffing bedrijfsver.": {}, + "Ontvangen schadevergoed.": {}, + "Overige baten": {}, + "Overige lasten": {}, + "Voordelige koersverschil.": {} + }, + "PERSONEELSKOSTEN": { + "Autokostenvergoeding": {}, + "Bedrijfskleding": {}, + "Belastingvrije uitkeringen": {}, + "Bijzondere beloningen": {}, + "Congressen, seminars en symposia": {}, + "Gereedschapsgeld": {}, + "Geschenken personeel": {}, + "Gratificaties": {}, + "Inhouding pensioenpremies": {}, + "Inhouding sociale lasten": {}, + "Kantinekosten": {}, + "Lonen en salarissen": {}, + "Loonwerk": {}, + "Managementvergoedingen": {}, + "Opleidingskosten": {}, + "Oprenting stamrechtverpl.": {}, + "Overhevelingstoeslag": {}, + "Overige kostenverg.": {}, + "Overige personeelskosten": {}, + "Overige uitkeringen": {}, + "Pensioenpremies": {}, + "Provisie 1": {}, + "Reiskosten": {}, + "Rijwielvergoeding": {}, + "Sociale lasten": {}, + "Tanti\u00e8mes": {}, + "Thuiswerkers": {}, + "Toev. Backservice pens.verpl.": {}, + "Toevoeging pensioenverpl.": {}, + "Uitkering ziekengeld": {}, + "Uitzendkrachten": {}, + "Vakantiebonnen": {}, + "Vakantiegeld": {}, + "Vergoeding studiekosten": {}, + "Wervingskosten personeel": {} + }, + "VERKOOPKOSTEN": { + "Advertenties": {}, + "Afschrijving dubieuze deb.": {}, + "Beurskosten": {}, + "Etalagekosten": {}, + "Exportkosten": {}, + "Kascorrecties": {}, + "Overige verkoopkosten": {}, + "Provisie": {}, + "Reclame": {}, + "Reis en verblijfkosten": {}, + "Relatiegeschenken": {}, + "Representatiekosten": {}, + "Uitgaande vrachten": {}, + "Veilingkosten": {}, + "Verpakkingsmateriaal 1": {}, + "Websitekosten": {} + }, + "VERVOERSKOSTEN": { + "Assuranties auto's": {}, + "Brandstoffen": {}, + "Leasing auto's": {}, + "Onderhoud personenauto's": {}, + "Onderhoud vrachtauto's": {}, + "Overige vervoerskosten": {}, + "Priv\u00e9-gebruik auto's": {}, + "Wegenbelasting": {} + }, + "VOORRAAD GEREED PRODUCT EN ONDERHANDEN WERK": { + "Betalingskort. crediteuren": {}, + "Garantiekosten": {}, + "Hulpmaterialen": {}, + "Inkomende vrachten": { + "account_type": "Expenses Included In Valuation" + }, + "Inkoop import buiten EU hoog": {}, + "Inkoop import buiten EU laag": {}, + "Inkoop import buiten EU overig": {}, + "Inkoopbonussen": {}, + "Inkoopkosten": {}, + "Inkoopprovisie": {}, + "Inkopen BTW verlegd": {}, + "Inkopen EU hoog tarief": {}, + "Inkopen EU laag tarief": {}, + "Inkopen EU overig": {}, + "Inkopen hoog": {}, + "Inkopen laag": {}, + "Inkopen nul": {}, + "Inkopen overig": {}, + "Invoerkosten": {}, + "Kosten inkoopvereniging": {}, + "Kostprijs omzet grondstoffen": { + "account_type": "Cost of Goods Sold" + }, + "Kostprijs omzet handelsgoederen": {}, + "Onttrekking uitgev.garantie": {}, + "Priv\u00e9-gebruik goederen": {}, + "Stock aanpassing": { + "account_type": "Stock Adjustment" + }, + "Tegenrekening inkoop": {}, + "Toev. Voorz. incour. grondst.": {}, + "Toevoeging garantieverpl.": {}, + "Toevoeging voorz. incour. handelsgoed.": {}, + "Uitbesteed werk": {}, + "Voorz. Incourourant grondst.": {}, + "Voorz.incour. handelsgoed.": {}, + "root_type": "Expense" + }, + "root_type": "Expense" + } }, "VASTE ACTIVA, EIGEN VERMOGEN, LANGLOPEND VREEMD VERMOGEN EN VOORZIENINGEN": { "EIGEN VERMOGEN": { @@ -602,7 +665,7 @@ "account_type": "Equity" } }, - "root_type": "Asset" + "root_type": "Equity" }, "VERKOOPRESULTATEN": { "Diensten fabric. 0% niet-EU": {}, @@ -627,67 +690,6 @@ "Verleende Kredietbep. fabricage": {}, "Verleende Kredietbep. handel": {}, "root_type": "Income" - }, - "VOORRAAD GEREED PRODUCT EN ONDERHANDEN WERK": { - "Betalingskort. crediteuren": {}, - "Garantiekosten": {}, - "Hulpmaterialen": {}, - "Inkomende vrachten": { - "account_type": "Expenses Included In Valuation" - }, - "Inkoop import buiten EU hoog": {}, - "Inkoop import buiten EU laag": {}, - "Inkoop import buiten EU overig": {}, - "Inkoopbonussen": {}, - "Inkoopkosten": {}, - "Inkoopprovisie": {}, - "Inkopen BTW verlegd": {}, - "Inkopen EU hoog tarief": {}, - "Inkopen EU laag tarief": {}, - "Inkopen EU overig": {}, - "Inkopen hoog": {}, - "Inkopen laag": {}, - "Inkopen nul": {}, - "Inkopen overig": {}, - "Invoerkosten": {}, - "Kosten inkoopvereniging": {}, - "Kostprijs omzet grondstoffen": { - "account_type": "Cost of Goods Sold" - }, - "Kostprijs omzet handelsgoederen": {}, - "Onttrekking uitgev.garantie": {}, - "Priv\u00e9-gebruik goederen": {}, - "Stock aanpassing": { - "account_type": "Stock Adjustment" - }, - "Tegenrekening inkoop": {}, - "Toev. Voorz. incour. grondst.": {}, - "Toevoeging garantieverpl.": {}, - "Toevoeging voorz. incour. handelsgoed.": {}, - "Uitbesteed werk": {}, - "Voorz. Incourourant grondst.": {}, - "Voorz.incour. handelsgoed.": {}, - "root_type": "Expense" - }, - "VOORRAAD GRONDSTOFFEN, HULPMATERIALEN EN HANDELSGOEDEREN": { - "Emballage": {}, - "Gereed product 1": {}, - "Gereed product 2": {}, - "Goederen 1": {}, - "Goederen 2": {}, - "Goederen in consignatie": {}, - "Goederen onderweg": {}, - "Grondstoffen 1": {}, - "Grondstoffen 2": {}, - "Halffabrikaten 1": {}, - "Halffabrikaten 2": {}, - "Hulpstoffen 1": {}, - "Hulpstoffen 2": {}, - "Kantoorbenodigdheden": {}, - "Onderhanden werk": {}, - "Verpakkingsmateriaal": {}, - "Zegels": {}, - "root_type": "Asset" } } } \ No newline at end of file From 30e4052a76d41e84617506ecfd143a6a8e6df567 Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Tue, 4 Jul 2023 17:41:30 +0530 Subject: [PATCH 47/99] feat(accounts): standardize additional columns implementation for sales/purchase reports (#36000) --- .../item_wise_purchase_register.py | 49 +++++++---------- .../item_wise_sales_register.py | 53 +++++++------------ .../purchase_register/purchase_register.py | 28 +++++----- .../report/sales_register/sales_register.py | 52 ++++++++---------- erpnext/accounts/report/utils.py | 31 ++++++++++- 5 files changed, 99 insertions(+), 114 deletions(-) diff --git a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py index 6fdb2f337c..050e6bc5d2 100644 --- a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py +++ b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py @@ -15,20 +15,21 @@ from erpnext.accounts.report.item_wise_sales_register.item_wise_sales_register i get_group_by_conditions, get_tax_accounts, ) +from erpnext.accounts.report.utils import get_query_columns, get_values_for_columns def execute(filters=None): return _execute(filters) -def _execute(filters=None, additional_table_columns=None, additional_query_columns=None): +def _execute(filters=None, additional_table_columns=None): if not filters: filters = {} columns = get_columns(additional_table_columns, filters) company_currency = erpnext.get_company_currency(filters.company) - item_list = get_items(filters, additional_query_columns) + item_list = get_items(filters, get_query_columns(additional_table_columns)) aii_account_map = get_aii_accounts() if item_list: itemised_tax, tax_columns = get_tax_accounts( @@ -79,28 +80,20 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum "posting_date": d.posting_date, "supplier": d.supplier, "supplier_name": d.supplier_name, + **get_values_for_columns(additional_table_columns, d), + "credit_to": d.credit_to, + "mode_of_payment": d.mode_of_payment, + "project": d.project, + "company": d.company, + "purchase_order": d.purchase_order, + "purchase_receipt": purchase_receipt, + "expense_account": expense_account, + "stock_qty": d.stock_qty, + "stock_uom": d.stock_uom, + "rate": d.base_net_amount / d.stock_qty if d.stock_qty else d.base_net_amount, + "amount": d.base_net_amount, } - if additional_query_columns: - for col in additional_query_columns: - row.update({col: d.get(col)}) - - row.update( - { - "credit_to": d.credit_to, - "mode_of_payment": d.mode_of_payment, - "project": d.project, - "company": d.company, - "purchase_order": d.purchase_order, - "purchase_receipt": purchase_receipt, - "expense_account": expense_account, - "stock_qty": d.stock_qty, - "stock_uom": d.stock_uom, - "rate": d.base_net_amount / d.stock_qty if d.stock_qty else d.base_net_amount, - "amount": d.base_net_amount, - } - ) - total_tax = 0 for tax in tax_columns: item_tax = itemised_tax.get(d.name, {}).get(tax, {}) @@ -317,11 +310,6 @@ def get_conditions(filters): def get_items(filters, additional_query_columns): conditions = get_conditions(filters) - if additional_query_columns: - additional_query_columns = ", " + ", ".join(additional_query_columns) - else: - additional_query_columns = "" - return frappe.db.sql( """ select @@ -340,11 +328,10 @@ def get_items(filters, additional_query_columns): from `tabPurchase Invoice`, `tabPurchase Invoice Item`, `tabItem` where `tabPurchase Invoice`.name = `tabPurchase Invoice Item`.`parent` and `tabItem`.name = `tabPurchase Invoice Item`.`item_code` and - `tabPurchase Invoice`.docstatus = 1 %s + `tabPurchase Invoice`.docstatus = 1 {1} """.format( - additional_query_columns - ) - % (conditions), + additional_query_columns, conditions + ), filters, as_dict=1, ) diff --git a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py index bd7d02e043..4d24dd9076 100644 --- a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py +++ b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py @@ -9,6 +9,7 @@ from frappe.utils import cstr, flt from frappe.utils.xlsxutils import handle_html from erpnext.accounts.report.sales_register.sales_register import get_mode_of_payments +from erpnext.accounts.report.utils import get_query_columns, get_values_for_columns from erpnext.selling.report.item_wise_sales_history.item_wise_sales_history import ( get_customer_details, ) @@ -18,19 +19,14 @@ def execute(filters=None): return _execute(filters) -def _execute( - filters=None, - additional_table_columns=None, - additional_query_columns=None, - additional_conditions=None, -): +def _execute(filters=None, additional_table_columns=None, additional_conditions=None): if not filters: filters = {} columns = get_columns(additional_table_columns, filters) company_currency = frappe.get_cached_value("Company", filters.get("company"), "default_currency") - item_list = get_items(filters, additional_query_columns, additional_conditions) + item_list = get_items(filters, get_query_columns(additional_table_columns), additional_conditions) if item_list: itemised_tax, tax_columns = get_tax_accounts(item_list, columns, company_currency) @@ -79,30 +75,22 @@ def _execute( "customer": d.customer, "customer_name": customer_record.customer_name, "customer_group": customer_record.customer_group, + **get_values_for_columns(additional_table_columns, d), + "debit_to": d.debit_to, + "mode_of_payment": ", ".join(mode_of_payments.get(d.parent, [])), + "territory": d.territory, + "project": d.project, + "company": d.company, + "sales_order": d.sales_order, + "delivery_note": d.delivery_note, + "income_account": d.unrealized_profit_loss_account + if d.is_internal_customer == 1 + else d.income_account, + "cost_center": d.cost_center, + "stock_qty": d.stock_qty, + "stock_uom": d.stock_uom, } - if additional_query_columns: - for col in additional_query_columns: - row.update({col: d.get(col)}) - - row.update( - { - "debit_to": d.debit_to, - "mode_of_payment": ", ".join(mode_of_payments.get(d.parent, [])), - "territory": d.territory, - "project": d.project, - "company": d.company, - "sales_order": d.sales_order, - "delivery_note": d.delivery_note, - "income_account": d.unrealized_profit_loss_account - if d.is_internal_customer == 1 - else d.income_account, - "cost_center": d.cost_center, - "stock_qty": d.stock_qty, - "stock_uom": d.stock_uom, - } - ) - if d.stock_uom != d.uom and d.stock_qty: row.update({"rate": (d.base_net_rate * d.qty) / d.stock_qty, "amount": d.base_net_amount}) else: @@ -394,11 +382,6 @@ def get_group_by_conditions(filters, doctype): def get_items(filters, additional_query_columns, additional_conditions=None): conditions = get_conditions(filters, additional_conditions) - if additional_query_columns: - additional_query_columns = ", " + ", ".join(additional_query_columns) - else: - additional_query_columns = "" - return frappe.db.sql( """ select @@ -424,7 +407,7 @@ def get_items(filters, additional_query_columns, additional_conditions=None): `tabItem`.name = `tabSales Invoice Item`.`item_code` and `tabSales Invoice`.docstatus = 1 {1} """.format( - additional_query_columns or "", conditions + additional_query_columns, conditions ), filters, as_dict=1, diff --git a/erpnext/accounts/report/purchase_register/purchase_register.py b/erpnext/accounts/report/purchase_register/purchase_register.py index a05d581207..69827aca69 100644 --- a/erpnext/accounts/report/purchase_register/purchase_register.py +++ b/erpnext/accounts/report/purchase_register/purchase_register.py @@ -10,17 +10,18 @@ from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( get_accounting_dimensions, get_dimension_with_children, ) +from erpnext.accounts.report.utils import get_query_columns, get_values_for_columns def execute(filters=None): return _execute(filters) -def _execute(filters=None, additional_table_columns=None, additional_query_columns=None): +def _execute(filters=None, additional_table_columns=None): if not filters: filters = {} - invoice_list = get_invoices(filters, additional_query_columns) + invoice_list = get_invoices(filters, get_query_columns(additional_table_columns)) columns, expense_accounts, tax_accounts, unrealized_profit_loss_accounts = get_columns( invoice_list, additional_table_columns ) @@ -47,13 +48,12 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum purchase_receipt = list(set(invoice_po_pr_map.get(inv.name, {}).get("purchase_receipt", []))) project = list(set(invoice_po_pr_map.get(inv.name, {}).get("project", []))) - row = [inv.name, inv.posting_date, inv.supplier, inv.supplier_name] - - if additional_query_columns: - for col in additional_query_columns: - row.append(inv.get(col)) - - row += [ + row = [ + inv.name, + inv.posting_date, + inv.supplier, + inv.supplier_name, + *get_values_for_columns(additional_table_columns, inv).values(), supplier_details.get(inv.supplier), # supplier_group inv.tax_id, inv.credit_to, @@ -244,9 +244,6 @@ def get_conditions(filters): def get_invoices(filters, additional_query_columns): - if additional_query_columns: - additional_query_columns = ", " + ", ".join(additional_query_columns) - conditions = get_conditions(filters) return frappe.db.sql( """ @@ -255,11 +252,10 @@ def get_invoices(filters, additional_query_columns): remarks, base_net_total, base_grand_total, outstanding_amount, mode_of_payment {0} from `tabPurchase Invoice` - where docstatus = 1 %s + where docstatus = 1 {1} order by posting_date desc, name desc""".format( - additional_query_columns or "" - ) - % conditions, + additional_query_columns, conditions + ), filters, as_dict=1, ) diff --git a/erpnext/accounts/report/sales_register/sales_register.py b/erpnext/accounts/report/sales_register/sales_register.py index b333901d7b..291c7d976e 100644 --- a/erpnext/accounts/report/sales_register/sales_register.py +++ b/erpnext/accounts/report/sales_register/sales_register.py @@ -11,17 +11,18 @@ from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( get_accounting_dimensions, get_dimension_with_children, ) +from erpnext.accounts.report.utils import get_query_columns, get_values_for_columns def execute(filters=None): return _execute(filters) -def _execute(filters, additional_table_columns=None, additional_query_columns=None): +def _execute(filters, additional_table_columns=None): if not filters: filters = frappe._dict({}) - invoice_list = get_invoices(filters, additional_query_columns) + invoice_list = get_invoices(filters, get_query_columns(additional_table_columns)) columns, income_accounts, tax_accounts, unrealized_profit_loss_accounts = get_columns( invoice_list, additional_table_columns ) @@ -54,30 +55,22 @@ def _execute(filters, additional_table_columns=None, additional_query_columns=No "posting_date": inv.posting_date, "customer": inv.customer, "customer_name": inv.customer_name, + **get_values_for_columns(additional_table_columns, inv), + "customer_group": inv.get("customer_group"), + "territory": inv.get("territory"), + "tax_id": inv.get("tax_id"), + "receivable_account": inv.debit_to, + "mode_of_payment": ", ".join(mode_of_payments.get(inv.name, [])), + "project": inv.project, + "owner": inv.owner, + "remarks": inv.remarks, + "sales_order": ", ".join(sales_order), + "delivery_note": ", ".join(delivery_note), + "cost_center": ", ".join(cost_center), + "warehouse": ", ".join(warehouse), + "currency": company_currency, } - if additional_query_columns: - for col in additional_query_columns: - row.update({col: inv.get(col)}) - - row.update( - { - "customer_group": inv.get("customer_group"), - "territory": inv.get("territory"), - "tax_id": inv.get("tax_id"), - "receivable_account": inv.debit_to, - "mode_of_payment": ", ".join(mode_of_payments.get(inv.name, [])), - "project": inv.project, - "owner": inv.owner, - "remarks": inv.remarks, - "sales_order": ", ".join(sales_order), - "delivery_note": ", ".join(delivery_note), - "cost_center": ", ".join(cost_center), - "warehouse": ", ".join(warehouse), - "currency": company_currency, - } - ) - # map income values base_net_total = 0 for income_acc in income_accounts: @@ -402,9 +395,6 @@ def get_conditions(filters): def get_invoices(filters, additional_query_columns): - if additional_query_columns: - additional_query_columns = ", " + ", ".join(additional_query_columns) - conditions = get_conditions(filters) return frappe.db.sql( """ @@ -413,10 +403,10 @@ def get_invoices(filters, additional_query_columns): base_net_total, base_grand_total, base_rounded_total, outstanding_amount, is_internal_customer, represents_company, company {0} from `tabSales Invoice` - where docstatus = 1 %s order by posting_date desc, name desc""".format( - additional_query_columns or "" - ) - % conditions, + where docstatus = 1 {1} + order by posting_date desc, name desc""".format( + additional_query_columns, conditions + ), filters, as_dict=1, ) diff --git a/erpnext/accounts/report/utils.py b/erpnext/accounts/report/utils.py index 97cc1c4a13..781481bd0d 100644 --- a/erpnext/accounts/report/utils.py +++ b/erpnext/accounts/report/utils.py @@ -1,5 +1,5 @@ import frappe -from frappe.utils import flt, formatdate, get_datetime_str +from frappe.utils import flt, formatdate, get_datetime_str, get_table_name from erpnext import get_company_currency, get_default_company from erpnext.accounts.doctype.fiscal_year.fiscal_year import get_from_and_to_date @@ -151,3 +151,32 @@ def get_invoiced_item_gross_margin( result = sum(d.gross_profit for d in result) return result + + +def get_query_columns(report_columns): + if not report_columns: + return "" + + columns = [] + for column in report_columns: + fieldname = column["fieldname"] + + if doctype := column.get("_doctype"): + columns.append(f"`{get_table_name(doctype)}`.`{fieldname}`") + else: + columns.append(fieldname) + + return ", " + ", ".join(columns) + + +def get_values_for_columns(report_columns, report_row): + values = {} + + if not report_columns: + return values + + for column in report_columns: + fieldname = column["fieldname"] + values[fieldname] = report_row.get(fieldname) + + return values From 0a17c78a363e81be5b7e5bfd55f93ade122c176c Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 4 Jul 2023 17:49:07 +0530 Subject: [PATCH 48/99] fix: Share ledger showing cancelled docs (#35993) --- erpnext/accounts/report/share_ledger/share_ledger.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/share_ledger/share_ledger.py b/erpnext/accounts/report/share_ledger/share_ledger.py index d6c3bd059f..629528e5cc 100644 --- a/erpnext/accounts/report/share_ledger/share_ledger.py +++ b/erpnext/accounts/report/share_ledger/share_ledger.py @@ -67,8 +67,9 @@ def get_all_transfers(date, shareholder): # condition = 'AND company = %(company)s ' return frappe.db.sql( """SELECT * FROM `tabShare Transfer` - WHERE (DATE(date) <= %(date)s AND from_shareholder = %(shareholder)s {condition}) - OR (DATE(date) <= %(date)s AND to_shareholder = %(shareholder)s {condition}) + WHERE ((DATE(date) <= %(date)s AND from_shareholder = %(shareholder)s {condition}) + OR (DATE(date) <= %(date)s AND to_shareholder = %(shareholder)s {condition})) + AND docstatus = 1 ORDER BY date""".format( condition=condition ), From 98281341b99ccb5e210e7cf18b18743b2373b5db Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 4 Jul 2023 22:46:00 +0530 Subject: [PATCH 49/99] fix: handle loan_repayment's posting_date datetime in bank_clearance_summary report (backport #36004) (#36005) * fix: handle loan_repayment's posting_date datetime in bank_clearance_summary report (#36004) (cherry picked from commit 937e1fb0245af291789ccd88f168d0402d9a00d8) # Conflicts: # erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py * chore: resolving conflicts --------- Co-authored-by: Anand Baburajan --- .../report/bank_clearance_summary/bank_clearance_summary.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py b/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py index e3127b346f..7e0bdea80c 100644 --- a/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py +++ b/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py @@ -73,7 +73,7 @@ def get_entries(filters): return sorted( entries, - key=lambda k: k[2] or getdate(nowdate()), + key=lambda k: k[2].strftime("%H%M%S") or getdate(nowdate()), ) From 4badac8e9e43473c663c49f3f4d3fa4bf3172941 Mon Sep 17 00:00:00 2001 From: Raffael Meyer <14891507+barredterra@users.noreply.github.com> Date: Wed, 5 Jul 2023 18:05:41 +0200 Subject: [PATCH 50/99] fix(Payment Entry): compare rounded amount (#36011) --- erpnext/accounts/doctype/payment_entry/payment_entry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index c0fd63e9a2..8cf36c186e 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -235,7 +235,7 @@ class PaymentEntry(AccountsController): # The reference has already been partly paid elif ( latest.outstanding_amount < latest.invoice_amount - and d.outstanding_amount != latest.outstanding_amount + and flt(d.outstanding_amount, d.precision("outstanding_amount")) != latest.outstanding_amount ): frappe.throw( _( From 4f51c5a433ca8cf3fd688e262e100f63c98b65f9 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 3 Jul 2023 14:35:33 +0530 Subject: [PATCH 51/99] refactor: submit and make JV through background job --- .../exchange_rate_revaluation.py | 6 +++ erpnext/accounts/utils.py | 44 +++++++++++++++++++ erpnext/hooks.py | 4 ++ erpnext/setup/doctype/company/company.json | 9 +++- 4 files changed, 62 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py index 598db642f3..c52ea24f25 100644 --- a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py +++ b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py @@ -93,6 +93,12 @@ class ExchangeRateRevaluation(Document): return True + def fetch_and_calculate_accounts_data(self): + accounts = self.get_accounts_data() + if accounts: + for acc in accounts: + self.append("accounts", acc) + @frappe.whitelist() def get_accounts_data(self): self.validate_mandatory() diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 9000b0d32e..31473db675 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -1408,6 +1408,50 @@ def check_and_delete_linked_reports(report): frappe.delete_doc("Desktop Icon", icon) +def create_err_and_its_journals(companies: list = None) -> None: + if companies: + for company in companies: + err = frappe.new_doc("Exchange Rate Revaluation") + err.company = company.name + err.posting_date = nowdate() + err.rounding_loss_allowance = 0.0 + + err.fetch_and_calculate_accounts_data() + if err.accounts: + err.save().submit() + response = err.make_jv_entries() + + if company.submit_err_jv: + jv = response.get("revaluation_jv", None) + jv and frappe.get_doc("Journal Entry", jv).submit() + jv = response.get("zero_balance_jv", None) + jv and frappe.get_doc("Journal Entry", jv).submit() + + +def auto_create_exchange_rate_revaluation_daily() -> None: + """ + Executed by background job + """ + companies = frappe.db.get_all( + "Company", + filters={"auto_exchange_rate_revaluation": 1, "auto_err_frequency": "Daily"}, + fields=["name", "submit_err_jv"], + ) + create_err_and_its_journals(companies) + + +def auto_create_exchange_rate_revaluation_weekly() -> None: + """ + Executed by background job + """ + companies = frappe.db.get_all( + "Company", + filters={"auto_exchange_rate_revaluation": 1, "auto_err_frequency": "Weekly"}, + fields=["name", "submit_err_jv"], + ) + create_err_and_its_journals(companies) + + def get_payment_ledger_entries(gl_entries, cancel=0): ple_map = [] if gl_entries: diff --git a/erpnext/hooks.py b/erpnext/hooks.py index a6d939e74f..d02d318b2d 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -415,6 +415,10 @@ scheduler_events = { "erpnext.selling.doctype.quotation.quotation.set_expired_status", "erpnext.buying.doctype.supplier_quotation.supplier_quotation.set_expired_status", "erpnext.accounts.doctype.process_statement_of_accounts.process_statement_of_accounts.send_auto_email", + "erpnext.accounts.utils.auto_create_exchange_rate_revaluation_daily", + ], + "weekly": [ + "erpnext.accounts.utils.auto_create_exchange_rate_revaluation_weekly", ], "daily_long": [ "erpnext.setup.doctype.email_digest.email_digest.send", diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json index 7e55ca4fc0..ed2852e87a 100644 --- a/erpnext/setup/doctype/company/company.json +++ b/erpnext/setup/doctype/company/company.json @@ -98,6 +98,7 @@ "exchange_rate_revaluation_settings_section", "auto_exchange_rate_revaluation", "auto_err_frequency", + "submit_err_jv", "budget_detail", "exception_budget_approver_role", "registration_info", @@ -751,6 +752,12 @@ "fieldtype": "Select", "label": "Frequency", "options": "Daily\nWeekly" + }, + { + "default": "0", + "fieldname": "submit_err_jv", + "fieldtype": "Check", + "label": "Submit ERR Journals?" } ], "icon": "fa fa-building", @@ -758,7 +765,7 @@ "image_field": "company_logo", "is_tree": 1, "links": [], - "modified": "2023-07-03 14:29:56.262188", + "modified": "2023-07-07 05:41:41.537256", "modified_by": "Administrator", "module": "Setup", "name": "Company", From 828e647019973e7c1fca6b0db1c8aad5d91433ae Mon Sep 17 00:00:00 2001 From: Navin Balaji <54995833+navinbalaji@users.noreply.github.com> Date: Sun, 9 Jul 2023 14:13:48 +0530 Subject: [PATCH 52/99] fix: bank reconciliation tool variable issue (#36022) fix: bank reconciliation tool variable issue (#36022) --- .../bank_reconciliation_tool/bank_reconciliation_tool.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js index 0647a5ccf3..d961ead642 100644 --- a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js +++ b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js @@ -19,7 +19,7 @@ frappe.ui.form.on("Bank Reconciliation Tool", { onload: function (frm) { // Set default filter dates - today = frappe.datetime.get_today() + let today = frappe.datetime.get_today() frm.doc.bank_statement_from_date = frappe.datetime.add_months(today, -1); frm.doc.bank_statement_to_date = today; frm.trigger('bank_account'); From 353d765140a0ad2af2bc8432159d051579d5d568 Mon Sep 17 00:00:00 2001 From: Raffael Meyer <14891507+barredterra@users.noreply.github.com> Date: Sun, 9 Jul 2023 10:44:34 +0200 Subject: [PATCH 53/99] fix: German translations (#35990) * fix: add missing German translation * fix: wrong German translation --- erpnext/translations/de.csv | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/translations/de.csv b/erpnext/translations/de.csv index 26a775e9de..e30236479b 100644 --- a/erpnext/translations/de.csv +++ b/erpnext/translations/de.csv @@ -3839,6 +3839,7 @@ No outstanding invoices found,Keine offenen Rechnungen gefunden, No outstanding invoices found for the {0} {1} which qualify the filters you have specified.,"Für {0} {1} wurden keine ausstehenden Rechnungen gefunden, die die von Ihnen angegebenen Filter qualifizieren.", No outstanding invoices require exchange rate revaluation,Keine ausstehenden Rechnungen erfordern eine Neubewertung des Wechselkurses, No reviews yet,Noch keine Bewertungen, +No Stock Available Currently,Derzeit kein Lagerbestand verfügbar, No views yet,Noch keine Ansichten, Non stock items,Nicht vorrätige Artikel, Not Allowed,Nicht Erlaubt, @@ -8079,7 +8080,7 @@ Price Not UOM Dependent,Preis nicht UOM abhängig, Applicable for Countries,Anwenden für Länder, Price List Country,Preisliste Land, MAT-PRE-.YYYY.-,MAT-PRE-.JJJJ.-, -Supplier Delivery Note,Lieferumfang, +Supplier Delivery Note,Lieferschein Nr., Time at which materials were received,"Zeitpunkt, zu dem Materialien empfangen wurden", Return Against Purchase Receipt,Zurück zum Kaufbeleg, Rate at which supplier's currency is converted to company's base currency,"Kurs, zu dem die Währung des Lieferanten in die Basiswährung des Unternehmens umgerechnet wird", From 4d07e20b05289c297ae49d0221f3840afe4986ec Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 9 Jul 2023 20:16:12 +0530 Subject: [PATCH 54/99] fix: Opening balance in presentation currency in Trial Balance report (#36036) --- .../consolidated_financial_statement.py | 2 +- erpnext/accounts/report/financial_statements.py | 2 +- .../report/general_ledger/general_ledger.py | 2 +- .../report/trial_balance/trial_balance.py | 15 +++++++++++---- erpnext/accounts/report/utils.py | 3 +-- 5 files changed, 15 insertions(+), 9 deletions(-) diff --git a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py index a6447549e6..6e39ee9944 100644 --- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py +++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py @@ -650,7 +650,7 @@ def set_gl_entries_by_account( if filters and filters.get("presentation_currency") != d.default_currency: currency_info["company"] = d.name currency_info["company_currency"] = d.default_currency - convert_to_presentation_currency(gl_entries, currency_info, filters.get("company")) + convert_to_presentation_currency(gl_entries, currency_info) for entry in gl_entries: if entry.account_number: diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py index 8a47e1c011..f3a892ba43 100644 --- a/erpnext/accounts/report/financial_statements.py +++ b/erpnext/accounts/report/financial_statements.py @@ -462,7 +462,7 @@ def set_gl_entries_by_account( ) if filters and filters.get("presentation_currency"): - convert_to_presentation_currency(gl_entries, get_currency(filters), filters.get("company")) + convert_to_presentation_currency(gl_entries, get_currency(filters)) for entry in gl_entries: gl_entries_by_account.setdefault(entry.account, []).append(entry) diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py index d47e3da313..d7af167e38 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.py +++ b/erpnext/accounts/report/general_ledger/general_ledger.py @@ -204,7 +204,7 @@ def get_gl_entries(filters, accounting_dimensions): ) if filters.get("presentation_currency"): - return convert_to_presentation_currency(gl_entries, currency_map, filters.get("company")) + return convert_to_presentation_currency(gl_entries, currency_map) else: return gl_entries diff --git a/erpnext/accounts/report/trial_balance/trial_balance.py b/erpnext/accounts/report/trial_balance/trial_balance.py index 22bebb7d19..d51c4c4acb 100644 --- a/erpnext/accounts/report/trial_balance/trial_balance.py +++ b/erpnext/accounts/report/trial_balance/trial_balance.py @@ -17,6 +17,7 @@ from erpnext.accounts.report.financial_statements import ( filter_out_zero_value_rows, set_gl_entries_by_account, ) +from erpnext.accounts.report.utils import convert_to_presentation_currency, get_currency value_fields = ( "opening_debit", @@ -178,8 +179,8 @@ def get_rootwise_opening_balances(filters, report_type): "opening_credit": 0.0, }, ) - opening[d.account]["opening_debit"] += flt(d.opening_debit) - opening[d.account]["opening_credit"] += flt(d.opening_credit) + opening[d.account]["opening_debit"] += flt(d.debit) + opening[d.account]["opening_credit"] += flt(d.credit) return opening @@ -194,8 +195,11 @@ def get_opening_balance( frappe.qb.from_(closing_balance) .select( closing_balance.account, - Sum(closing_balance.debit).as_("opening_debit"), - Sum(closing_balance.credit).as_("opening_credit"), + closing_balance.account_currency, + Sum(closing_balance.debit).as_("debit"), + Sum(closing_balance.credit).as_("credit"), + Sum(closing_balance.debit_in_account_currency).as_("debit_in_account_currency"), + Sum(closing_balance.credit_in_account_currency).as_("credit_in_account_currency"), ) .where( (closing_balance.company == filters.company) @@ -282,6 +286,9 @@ def get_opening_balance( gle = opening_balance.run(as_dict=1) + if filters and filters.get("presentation_currency"): + convert_to_presentation_currency(gle, get_currency(filters)) + return gle diff --git a/erpnext/accounts/report/utils.py b/erpnext/accounts/report/utils.py index 781481bd0d..7ea1fac105 100644 --- a/erpnext/accounts/report/utils.py +++ b/erpnext/accounts/report/utils.py @@ -78,7 +78,7 @@ def get_rate_as_at(date, from_currency, to_currency): return rate -def convert_to_presentation_currency(gl_entries, currency_info, company): +def convert_to_presentation_currency(gl_entries, currency_info): """ Take a list of GL Entries and change the 'debit' and 'credit' values to currencies in `currency_info`. @@ -93,7 +93,6 @@ def convert_to_presentation_currency(gl_entries, currency_info, company): account_currencies = list(set(entry["account_currency"] for entry in gl_entries)) for entry in gl_entries: - account = entry["account"] debit = flt(entry["debit"]) credit = flt(entry["credit"]) debit_in_account_currency = flt(entry["debit_in_account_currency"]) From ef7fd7548c08e60004f4e640021e878060171a74 Mon Sep 17 00:00:00 2001 From: aioaccount <92444849+aioaccount@users.noreply.github.com> Date: Sun, 9 Jul 2023 21:47:35 +0700 Subject: [PATCH 55/99] fix: Vietnamese translation of "Company" (#35887) fix: Vietnamese translation of "Company" --- erpnext/translations/vi.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/translations/vi.csv b/erpnext/translations/vi.csv index 23290a13a6..543eadb855 100644 --- a/erpnext/translations/vi.csv +++ b/erpnext/translations/vi.csv @@ -3641,7 +3641,7 @@ Click on the link below to verify your email and confirm the appointment,Nhấp Close,Đóng, Communication,Liên lạc, Compact Item Print,Nhỏ gọn mục Print, -Company,Giỏ hàng Giá liệt kê, +Company,Công ty, Company of asset {0} and purchase document {1} doesn't matches.,Công ty tài sản {0} và tài liệu mua hàng {1} không khớp., Compare BOMs for changes in Raw Materials and Operations,So sánh các BOM cho những thay đổi trong Nguyên liệu thô và Hoạt động, Compare List function takes on list arguments,Chức năng So sánh Danh sách đảm nhận đối số danh sách, From af28f95c60ac470b004613e2f4d9ef466f52ce9c Mon Sep 17 00:00:00 2001 From: Raffael Meyer <14891507+barredterra@users.noreply.github.com> Date: Sun, 9 Jul 2023 16:49:53 +0200 Subject: [PATCH 56/99] refactor(Payment Entry): translatable strings (#36017) * refactor(Payment Entry): translatable strings * fix: German translations --- .../doctype/payment_entry/payment_entry.py | 55 ++-- erpnext/translations/de.csv | 247 ++++++++++-------- 2 files changed, 161 insertions(+), 141 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 8cf36c186e..65ed4669b1 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -8,6 +8,7 @@ from functools import reduce import frappe from frappe import ValidationError, _, qb, scrub, throw from frappe.utils import cint, comma_or, flt, getdate, nowdate +from frappe.utils.data import comma_and, fmt_money import erpnext from erpnext.accounts.doctype.bank_account.bank_account import ( @@ -124,13 +125,16 @@ class PaymentEntry(AccountsController): self.set(self.party_account_field, liability_account) - msg = "Book Advance Payments as Liability option is chosen. Paid From account changed from {0} to {1}.".format( - frappe.bold(self.party_account), - frappe.bold(liability_account), + frappe.msgprint( + _( + "Book Advance Payments as Liability option is chosen. Paid From account changed from {0} to {1}." + ).format( + frappe.bold(self.party_account), + frappe.bold(liability_account), + ), + alert=True, ) - frappe.msgprint(_(msg), alert=True) - def on_cancel(self): self.ignore_linked_doctypes = ( "GL Entry", @@ -230,7 +234,7 @@ class PaymentEntry(AccountsController): # The reference has already been fully paid if not latest: frappe.throw( - _("{0} {1} has already been fully paid.").format(d.reference_doctype, d.reference_name) + _("{0} {1} has already been fully paid.").format(_(d.reference_doctype), d.reference_name) ) # The reference has already been partly paid elif ( @@ -240,7 +244,7 @@ class PaymentEntry(AccountsController): frappe.throw( _( "{0} {1} has already been partly paid. Please use the 'Get Outstanding Invoice' or the 'Get Outstanding Orders' button to get the latest outstanding amounts." - ).format(d.reference_doctype, d.reference_name) + ).format(_(d.reference_doctype), d.reference_name) ) fail_message = _("Row #{0}: Allocated Amount cannot be greater than outstanding amount.") @@ -342,7 +346,7 @@ class PaymentEntry(AccountsController): def validate_party_details(self): if self.party: if not frappe.db.exists(self.party_type, self.party): - frappe.throw(_("Invalid {0}: {1}").format(self.party_type, self.party)) + frappe.throw(_("{0} {1} does not exist").format(_(self.party_type), self.party)) def set_exchange_rate(self, ref_doc=None): self.set_source_exchange_rate(ref_doc) @@ -391,7 +395,9 @@ class PaymentEntry(AccountsController): continue if d.reference_doctype not in valid_reference_doctypes: frappe.throw( - _("Reference Doctype must be one of {0}").format(comma_or(valid_reference_doctypes)) + _("Reference Doctype must be one of {0}").format( + comma_or((_(d) for d in valid_reference_doctypes)) + ) ) elif d.reference_name: @@ -404,7 +410,7 @@ class PaymentEntry(AccountsController): if self.party != ref_doc.get(scrub(self.party_type)): frappe.throw( _("{0} {1} is not associated with {2} {3}").format( - d.reference_doctype, d.reference_name, self.party_type, self.party + _(d.reference_doctype), d.reference_name, _(self.party_type), self.party ) ) else: @@ -426,18 +432,18 @@ class PaymentEntry(AccountsController): ): frappe.throw( _("{0} {1} is associated with {2}, but Party Account is {3}").format( - d.reference_doctype, d.reference_name, ref_party_account, self.party_account + _(d.reference_doctype), d.reference_name, ref_party_account, self.party_account ) ) if ref_doc.doctype == "Purchase Invoice" and ref_doc.get("on_hold"): frappe.throw( - _("{0} {1} is on hold").format(d.reference_doctype, d.reference_name), - title=_("Invalid Invoice"), + _("{0} {1} is on hold").format(_(d.reference_doctype), d.reference_name), + title=_("Invalid Purchase Invoice"), ) if ref_doc.docstatus != 1: - frappe.throw(_("{0} {1} must be submitted").format(d.reference_doctype, d.reference_name)) + frappe.throw(_("{0} {1} must be submitted").format(_(d.reference_doctype), d.reference_name)) def get_valid_reference_doctypes(self): if self.party_type == "Customer": @@ -463,14 +469,13 @@ class PaymentEntry(AccountsController): if outstanding_amount <= 0 and not is_return: no_oustanding_refs.setdefault(d.reference_doctype, []).append(d) - for k, v in no_oustanding_refs.items(): + for reference_doctype, references in no_oustanding_refs.items(): frappe.msgprint( _( - "{} - {} now has {} as it had no outstanding amount left before submitting the Payment Entry." + "References {0} of type {1} had no outstanding amount left before submitting the Payment Entry. Now they have a negative outstanding amount." ).format( - _(k), - frappe.bold(", ".join(d.reference_name for d in v)), - frappe.bold(_("negative outstanding amount")), + frappe.bold(comma_and((d.reference_name for d in references))), + _(reference_doctype), ) + "

" + _("If this is undesirable please cancel the corresponding Payment Entry."), @@ -505,7 +510,7 @@ class PaymentEntry(AccountsController): if not valid: frappe.throw( _("Against Journal Entry {0} does not have any unmatched {1} entry").format( - d.reference_name, dr_or_cr + d.reference_name, _(dr_or_cr) ) ) @@ -572,7 +577,7 @@ class PaymentEntry(AccountsController): if allocated_amount > outstanding: frappe.throw( _("Row #{0}: Cannot allocate more than {1} against payment term {2}").format( - idx, outstanding, key[0] + idx, fmt_money(outstanding), key[0] ) ) @@ -876,7 +881,7 @@ class PaymentEntry(AccountsController): elif paid_amount - additional_charges > total_negative_outstanding: frappe.throw( _("Paid Amount cannot be greater than total negative outstanding amount {0}").format( - total_negative_outstanding + fmt_money(total_negative_outstanding) ), InvalidPaymentEntry, ) @@ -1546,7 +1551,7 @@ def get_outstanding_reference_documents(args, validate=False): _( "No outstanding {0} found for the {1} {2} which qualify the filters you have specified." ).format( - ref_document_type, _(args.get("party_type")).lower(), frappe.bold(args.get("party")) + _(ref_document_type), _(args.get("party_type")).lower(), frappe.bold(args.get("party")) ) ) @@ -1758,7 +1763,7 @@ def get_negative_outstanding_invoices( def get_party_details(company, party_type, party, date, cost_center=None): bank_account = "" if not frappe.db.exists(party_type, party): - frappe.throw(_("Invalid {0}: {1}").format(party_type, party)) + frappe.throw(_("{0} {1} does not exist").format(_(party_type), party)) party_account = get_party_account(party_type, party, company) account_currency = get_account_currency(party_account) @@ -1912,7 +1917,7 @@ def get_payment_entry( if dt in ("Sales Order", "Purchase Order") and flt(doc.per_billed, 2) >= ( 100.0 + over_billing_allowance ): - frappe.throw(_("Can only make payment against unbilled {0}").format(dt)) + frappe.throw(_("Can only make payment against unbilled {0}").format(_(dt))) if not party_type: party_type = set_party_type(dt) diff --git a/erpnext/translations/de.csv b/erpnext/translations/de.csv index e30236479b..5f0a8dc735 100644 --- a/erpnext/translations/de.csv +++ b/erpnext/translations/de.csv @@ -158,7 +158,7 @@ Advertising,Werbung, Aerospace,Luft- und Raumfahrt, Against,Zu, Against Account,Gegenkonto, -Against Journal Entry {0} does not have any unmatched {1} entry,"""Zu Buchungssatz"" {0} hat nur abgeglichene {1} Buchungen", +Against Journal Entry {0} does not have any unmatched {1} entry,Buchungssatz {0} hat keinen offenen Eintrag auf der {1}-Seite, Against Journal Entry {0} is already adjusted against some other voucher,"""Zu Buchungssatz"" {0} ist bereits mit einem anderen Beleg abgeglichen", Against Supplier Invoice {0} dated {1},Zu Eingangsrechnung {0} vom {1}, Against Voucher,Gegenbeleg, @@ -209,10 +209,10 @@ Amount of Integrated Tax,Betrag der integrierten Steuer, Amount of TDS Deducted,Betrag der abgezogenen TDS, Amount should not be less than zero.,Betrag sollte nicht kleiner als Null sein., Amount to Bill,Rechnungsbetrag, -Amount {0} {1} against {2} {3},Menge {0} {1} gegen {2} {3}, -Amount {0} {1} deducted against {2},Menge {0} {1} abgezogen gegen {2}, -Amount {0} {1} transferred from {2} to {3},Menge {0} {1} übertragen von {2} auf {3}, -Amount {0} {1} {2} {3},Menge {0} {1} {2} {3}, +Amount {0} {1} against {2} {3},Betrag {0} {1} gegen {2} {3}, +Amount {0} {1} deducted against {2},Betrag {0} {1} abgezogen gegen {2}, +Amount {0} {1} transferred from {2} to {3},Betrag {0} {1} wurde von {2} zu {3} transferiert, +Amount {0} {1} {2} {3},Betrag {0} {1} {2} {3}, Amt,Menge, "An Item Group exists with same name, please change the item name or rename the item group",Eine Artikelgruppe mit dem gleichen Namen existiert bereits. Bitte den Artikelnamen ändern oder die Artikelgruppe umbenennen, An academic term with this 'Academic Year' {0} and 'Term Name' {1} already exists. Please modify these entries and try again.,"Ein Semester mit ""Semesterjahr""'{0} und ""Semesternamen"" {1} ist bereits vorhanden. Bitte ändern Sie diese entsprechend und versuchen Sie es erneut.", @@ -279,7 +279,7 @@ Asset Name,Name Vermögenswert, Asset Received But Not Billed,"Vermögenswert erhalten, aber nicht in Rechnung gestellt", Asset Value Adjustment,Anpassung Vermögenswert, "Asset cannot be cancelled, as it is already {0}","Vermögenswert kann nicht rückgängig gemacht werden, da es ohnehin schon {0} ist", -Asset scrapped via Journal Entry {0},Vermögenswert über Journaleintrag {0} entsorgt, +Asset scrapped via Journal Entry {0},Vermögenswert über Buchungssatz {0} entsorgt, "Asset {0} cannot be scrapped, as it is already {1}",Anlagewert-{0} ist bereits entsorgt {1}, Asset {0} does not belong to company {1},Vermögenswert {0} gehört nicht zu Unternehmen {1}., Asset {0} must be submitted,Vermögenswert {0} muss eingereicht werden., @@ -440,7 +440,7 @@ Can be approved by {0},Kann von {0} genehmigt werden, "Can not filter based on Account, if grouped by Account","Wenn nach Konto gruppiert wurde, kann nicht auf Grundlage des Kontos gefiltert werden.", "Can not filter based on Voucher No, if grouped by Voucher","Wenn nach Beleg gruppiert wurde, kann nicht auf Grundlage von Belegen gefiltert werden.", "Can not mark Inpatient Record Discharged, there are Unbilled Invoices {0}","Kann den entlassenen Krankenhauskatheter nicht markieren, es gibt keine fakturierten Rechnungen {0}", -Can only make payment against unbilled {0},Zahlung kann nur zu einer noch nicht abgerechneten {0} erstellt werden, +Can only make payment against unbilled {0},Zahlung kann nur zu einem noch nicht abgerechneten Beleg vom Typ {0} erstellt werden, Can refer row only if the charge type is 'On Previous Row Amount' or 'Previous Row Total',"Kann sich nur auf eine Zeile beziehen, wenn die Berechnungsart der Kosten entweder ""auf vorherige Zeilensumme"" oder ""auf vorherigen Zeilenbetrag"" ist", "Can't change valuation method, as there are transactions against some items which does not have it's own valuation method","Kann die Bewertungsmethode nicht ändern, da es Transaktionen gegen einige Posten gibt, für die es keine eigene Bewertungsmethode gibt", Can't create standard criteria. Please rename the criteria,Kann keine Standardkriterien erstellen. Bitte benennen Sie die Kriterien um, @@ -448,7 +448,7 @@ Cancel,Abbrechen, Cancel Material Visit {0} before cancelling this Warranty Claim,Materialkontrolle {0} stornieren vor Abbruch dieses Garantieantrags, Cancel Material Visits {0} before cancelling this Maintenance Visit,Materialkontrolle {0} stornieren vor Abbruch dieses Wartungsbesuchs, Cancel Subscription,Abonnement beenden, -Cancel the journal entry {0} first,Brechen Sie zuerst den Journaleintrag {0} ab, +Cancel the journal entry {0} first,Brechen Sie zuerst den Buchungssatz {0} ab, Canceled,Abgebrochen, "Cannot Submit, Employees left to mark attendance","Kann nicht übergeben werden, Mitarbeiter sind zur Teilnahme zugelassen", Cannot be a fixed asset item as Stock Ledger is created.,"Kann keine Anlageposition sein, wenn das Stock Ledger erstellt wird.", @@ -473,9 +473,11 @@ Cannot deduct when category is for 'Valuation' or 'Vaulation and Total',"Kann ni Cannot enroll more than {0} students for this student group.,Kann nicht mehr als {0} Studenten für diese Studentengruppe einschreiben., Cannot find active Leave Period,Aktive Abwesenheitszeit kann nicht gefunden werden, Cannot produce more Item {0} than Sales Order quantity {1},"Es können nicht mehr Artikel {0} produziert werden, als die über den Auftrag bestellte Stückzahl {1}", +Cannot pay to Customer without any negative outstanding invoice,"Es kann nicht an den Kunden gezahlt werden, ohne dass eine Gutschrift vorhanden ist", Cannot promote Employee with status Left,Mitarbeiter mit Status "Links" kann nicht gefördert werden, +Cannot receive from Supplier without any negative outstanding invoice,"Es kann nicht vom Lieferanten empfangen werden, ohne dass eine Gutschrift vorhanden ist", Cannot refer row number greater than or equal to current row number for this Charge type,"Für diese Berechnungsart kann keine Zeilennummern zugeschrieben werden, die größer oder gleich der aktuellen Zeilennummer ist", -Cannot select charge type as 'On Previous Row Amount' or 'On Previous Row Total' for first row,"Die Berechnungsart kann für die erste Zeile nicht auf ""bezogen auf Menge der vorhergenden Zeile"" oder auf ""bezogen auf Gesamtmenge der vorhergenden Zeile"" gesetzt werden", +Cannot select charge type as 'On Previous Row Amount' or 'On Previous Row Total' for first row,"Die Berechnungsart kann für die erste Zeile nicht auf „Bezogen auf Betrag der vorhergenden Zeile“ oder auf „Bezogen auf Gesamtbetrag der vorhergenden Zeilen“ gesetzt werden", Cannot set as Lost as Sales Order is made.,"Kann nicht als verloren gekennzeichnet werden, da ein Auftrag dazu existiert.", Cannot set authorization on basis of Discount for {0},Genehmigung kann nicht auf der Basis des Rabattes für {0} festgelegt werden, Cannot set multiple Item Defaults for a company.,Es können nicht mehrere Artikelstandards für ein Unternehmen festgelegt werden., @@ -512,7 +514,7 @@ Change Template Code,Vorlagencode ändern, Changing Customer Group for the selected Customer is not allowed.,Die Änderung der Kundengruppe für den ausgewählten Kunden ist nicht zulässig., Chapter,Gruppe, Chapter information.,Gruppeninformation, -Charge of type 'Actual' in row {0} cannot be included in Item Rate,"Kosten für den Typ ""real"" in Zeile {0} können nicht in den Artikelpreis mit eingeschlossen werden", +Charge of type 'Actual' in row {0} cannot be included in Item Rate or Paid Amount,"Kosten für den Typ „Tatsächlich“ in Zeile {0} können nicht in den Artikelpreis oder den bezahlen Betrag einfließen", Chargeble,Belastung, Charges are updated in Purchase Receipt against each item,Kosten werden im Kaufbeleg für jede Position aktualisiert, "Charges will be distributed proportionately based on item qty or amount, as per your selection",Die Kosten werden gemäß Ihrer Wahl anteilig verteilt basierend auf Artikelmenge oder -preis, @@ -655,7 +657,7 @@ Create Inter Company Journal Entry,Erstellen Sie einen Inter Company Journal Ein Create Invoice,Rechnung erstellen, Create Invoices,Rechnungen erstellen, Create Job Card,Jobkarte erstellen, -Create Journal Entry,Journaleintrag erstellen, +Create Journal Entry,Buchungssatz erstellen, Create Lead,Lead erstellen, Create Leads,Leads erstellen, Create Maintenance Visit,Wartungsbesuch anlegen, @@ -835,7 +837,7 @@ Did not find any item called {0},Hat keinen Artikel finden genannt {0}, Diff Qty,Diff Menge, Difference Account,Differenzkonto, "Difference Account must be a Asset/Liability type account, since this Stock Reconciliation is an Opening Entry","Differenzkonto muss ein Vermögens-/Verbindlichkeiten-Konto sein, da dieser Lagerabgleich eine Eröffnungsbuchung ist", -Difference Amount,Differenzmenge, +Difference Amount,Differenzbetrag, Difference Amount must be zero,Differenzbetrag muss Null sein, Different UOM for items will lead to incorrect (Total) Net Weight value. Make sure that Net Weight of each item is in the same UOM.,"Unterschiedliche Maßeinheiten für Artikel führen zu falschen Werten für das (Gesamt-)Nettogewicht. Es muss sicher gestellt sein, dass das Nettogewicht jedes einzelnen Artikels in der gleichen Maßeinheit angegeben ist.", Direct Expenses,Direkte Aufwendungen, @@ -1031,7 +1033,7 @@ Fieldname,Feldname, Fields,Felder, Fill the form and save it,Formular ausfüllen und speichern, Filter Employees By (Optional),Mitarbeiter filtern nach (optional), -"Filter Fields Row #{0}: Fieldname {1} must be of type ""Link"" or ""Table MultiSelect""",Filterfelder Zeile # {0}: Feldname {1} muss vom Typ "Link" oder "Tabelle MultiSelect" sein, +"Filter Fields Row #{0}: Fieldname {1} must be of type ""Link"" or ""Table MultiSelect""",Filterfelder Zeile {0}: Feldname {1} muss vom Typ "Link" oder "Tabelle MultiSelect" sein, Filter Total Zero Qty,Gesamtmenge filtern, Finance Book,Finanzbuch, Financial / accounting year.,Finanz-/Rechnungsjahr, @@ -1307,6 +1309,7 @@ Invalid GSTIN! A GSTIN must have 15 characters.,Ungültige GSTIN! Eine GSTIN mus Invalid GSTIN! First 2 digits of GSTIN should match with State number {0}.,Ungültige GSTIN! Die ersten beiden Ziffern von GSTIN sollten mit der Statusnummer {0} übereinstimmen., Invalid GSTIN! The input you've entered doesn't match the format of GSTIN.,Ungültige GSTIN! Die von Ihnen eingegebene Eingabe stimmt nicht mit dem Format von GSTIN überein., Invalid Posting Time,Ungültige Buchungszeit, +Invalid Purchase Invoice,Ungültige Einkaufsrechnung, Invalid attribute {0} {1},Ungültiges Attribut {0} {1}, Invalid quantity specified for item {0}. Quantity should be greater than 0.,Ungültzige Anzahl für Artikel {0} angegeben. Anzahl sollte größer als 0 sein., Invalid reference {0} {1},Ungültige Referenz {0} {1}, @@ -1769,7 +1772,7 @@ Odometer,Tacho, Office Equipments,Büroausstattung, Office Maintenance Expenses,Büro-Wartungskosten, Office Rent,Büromiete, -On Hold,In Wartestellung, +On Hold,Auf Eis gelegt, On Net Total,Auf Nettosumme, One customer can be part of only single Loyalty Program.,Ein Kunde kann Teil eines einzigen Treueprogramms sein., Online Auctions,Online-Auktionen, @@ -1859,7 +1862,7 @@ Packing Slip,Packzettel, Packing Slip(s) cancelled,Packzettel storniert, Paid,Bezahlt, Paid Amount,Gezahlter Betrag, -Paid Amount cannot be greater than total negative outstanding amount {0},Gezahlten Betrag kann nicht größer sein als die Gesamt negativ ausstehenden Betrag {0}, +Paid Amount cannot be greater than total negative outstanding amount {0},"Der gezahlte Betrag darf nicht größer sein als der gesamte, negative, ausstehende Betrag {0}", Paid amount + Write Off Amount can not be greater than Grand Total,Summe aus gezahltem Betrag + ausgebuchter Betrag darf nicht größer der Gesamtsumme sein, Paid and Not Delivered,Bezahlt und nicht ausgeliefert, Parameter,Parameter, @@ -1911,7 +1914,7 @@ Payment Terms,Zahlungsbedingungen, Payment Terms Template,Vorlage Zahlungsbedingungen, Payment Terms based on conditions,Zahlungsbedingungen basieren auf Bedingungen, Payment Type,Zahlungsart, -"Payment Type must be one of Receive, Pay and Internal Transfer","Zahlungsart muss eine der Receive sein, Pay und interne Übertragung", +"Payment Type must be one of Receive, Pay and Internal Transfer","Zahlungsart muss entweder 'Empfangen', 'Zahlen' oder 'Interner Transfer' sein", Payment against {0} {1} cannot be greater than Outstanding Amount {2},Zahlung zu {0} {1} kann nicht größer als ausstehender Betrag {2} sein, Payment of {0} from {1} to {2},Zahlung von {0} von {1} an {2}, Payment request {0} created,Zahlungsaufforderung {0} erstellt, @@ -2293,9 +2296,9 @@ Read blog,Blog lesen, Read the ERPNext Manual,Lesen Sie das ERPNext-Handbuch, Reading Uploaded File,Hochgeladene Datei lesen, Real Estate,Immobilien, -Reason For Putting On Hold,Grund für das Halten, -Reason for Hold,Grund für das Halten, -Reason for hold: ,Grund für das Halten:, +Reason For Putting On Hold,Grund für das auf Eis legen, +Reason for Hold,Grund für das auf Eis legen, +Reason for hold: ,Grund für das auf Eis legen:, Receipt,Kaufbeleg, Receipt document must be submitted,Eingangsbeleg muss vorgelegt werden, Receivable,Forderung, @@ -2315,18 +2318,20 @@ Ref Date,Ref-Datum, Reference,Referenz, Reference #{0} dated {1},Referenz #{0} vom {1}, Reference Date,Referenzdatum, -Reference Doctype must be one of {0},Referenz Doctype muss man von {0}, +Reference Doctype must be one of {0},Referenz-Typ muss eine von {0} sein, Reference Document,Referenzdokument, Reference Document Type,Referenz-Dokumententyp, Reference No & Reference Date is required for {0},Referenznr. & Referenz-Tag sind erforderlich für {0}, -Reference No and Reference Date is mandatory for Bank transaction,Referenznummer und Referenzdatum ist obligatorisch für Bankengeschäft, -Reference No is mandatory if you entered Reference Date,"Referenznr. ist zwingend erforderlich, wenn Referenz-Tag eingegeben wurde", +Reference No and Reference Date is mandatory for Bank transaction,Referenznummer und Referenzdatum sind Pflichtfelder, +Reference No is mandatory if you entered Reference Date,"Referenznummer ist ein Pflichtfeld, wenn ein Referenzdatum eingegeben wurde", Reference No.,Referenznummer., Reference Number,Referenznummer, Reference Owner,Referenz Besitzer, Reference Type,Referenz-Typ, "Reference: {0}, Item Code: {1} and Customer: {2}","Referenz: {0}, Item Code: {1} und Kunde: {2}", References,Referenzen, +References {0} of type {1} had no outstanding amount left before submitting the Payment Entry. Now they have a negative outstanding amount.,"Die Referenzen {0} vom Typ {1} hatten keinen ausstehenden Betrag mehr, bevor die Zahlung gebucht wurde. Jetzt haben sie einen negativen ausstehenden Betrag.", +If this is undesirable please cancel the corresponding Payment Entry.,"Falls dies nicht erwünscht ist, stornieren Sie bitte die entsprechende Zahlung.", Refresh Token,Aktualisieren Token, Region,Region, Register,Neu registrieren, @@ -2405,7 +2410,7 @@ Return,Zurück, Return / Credit Note,Return / Gutschrift, Return / Debit Note,Return / Lastschrift, Returns,Retouren, -Reverse Journal Entry,Journaleintrag umkehren, +Reverse Journal Entry,Buchungssatz umkehren, Review Invitation Sent,Einladung überprüfen gesendet, Review and Action,Überprüfung und Aktion, Role,Rolle, @@ -2418,63 +2423,64 @@ Root cannot have a parent cost center,Root kann keine übergeordnete Kostenstell Round Off,Abschliessen, Rounded Total,Gerundete Gesamtsumme, Route,Route, -Row # {0}: ,Zeile # {0}:, -Row # {0}: Batch No must be same as {1} {2},Zeile # {0}: Chargennummer muss dieselbe sein wie {1} {2}, -Row # {0}: Cannot return more than {1} for Item {2},Zeile # {0}: Es kann nicht mehr als {1} für Artikel {2} zurückgegeben werden, -Row # {0}: Rate cannot be greater than the rate used in {1} {2},"Row # {0}: Die Rate kann nicht größer sein als die Rate, die in {1} {2}", -Row # {0}: Serial No is mandatory,Zeile # {0}: Seriennummer ist zwingend erforderlich, -Row # {0}: Serial No {1} does not match with {2} {3},Zeile # {0}: Seriennummer {1} stimmt nicht mit {2} {3} überein, -Row #{0} (Payment Table): Amount must be negative,Zeilennr. {0} (Zahlungstabelle): Betrag muss negativ sein, -Row #{0} (Payment Table): Amount must be positive,Zeile # {0} (Zahlungstabelle): Betrag muss positiv sein, -Row #{0}: Account {1} does not belong to company {2},Zeile # {0}: Konto {1} gehört nicht zur Unternehmen {2}, -Row #{0}: Allocated Amount cannot be greater than outstanding amount.,Zeile # {0}: Zugeordneter Betrag darf nicht größer als ausstehender Betrag sein., -"Row #{0}: Asset {1} cannot be submitted, it is already {2}","Zeile Nr. {0}: Vermögenswert {1} kann nicht vorgelegt werden, es ist bereits {2}", -Row #{0}: Cannot set Rate if amount is greater than billed amount for Item {1}.,"Zeilennr. {0}: Die Rate kann nicht festgelegt werden, wenn der Betrag für Artikel {1} höher als der Rechnungsbetrag ist.", -Row #{0}: Clearance date {1} cannot be before Cheque Date {2},Row # {0}: Räumungsdatum {1} kann nicht vor dem Scheck Datum sein {2}, -Row #{0}: Duplicate entry in References {1} {2},Zeile # {0}: Eintrag in Referenzen {1} {2} duplizieren, -Row #{0}: Expected Delivery Date cannot be before Purchase Order Date,Row # {0}: Voraussichtlicher Liefertermin kann nicht vor Bestelldatum sein, -Row #{0}: Item added,Zeile # {0}: Element hinzugefügt, -Row #{0}: Journal Entry {1} does not have account {2} or already matched against another voucher,Row # {0}: Journal Entry {1} nicht Konto {2} oder bereits abgestimmt gegen einen anderen Gutschein, -Row #{0}: Not allowed to change Supplier as Purchase Order already exists,"Zeile #{0}: Es ist nicht erlaubt den Lieferanten zu wechseln, da bereits eine Bestellung vorhanden ist", -Row #{0}: Please set reorder quantity,Zeile #{0}: Bitte Nachbestellmenge angeben, -Row #{0}: Please specify Serial No for Item {1},Zeile #{0}: Bitte Seriennummer für Artikel {1} angeben, -Row #{0}: Qty increased by 1,Zeile # {0}: Menge um 1 erhöht, -Row #{0}: Rate must be same as {1}: {2} ({3} / {4}) ,Zeile #{0}: Preis muss derselbe wie {1}: {2} ({3} / {4}) sein, -Row #{0}: Reference Document Type must be one of Expense Claim or Journal Entry,Row # {0}: Referenzdokumenttyp muss entweder 'Auslagenabrechnung' oder 'Journaleintrag' sein, -"Row #{0}: Reference Document Type must be one of Purchase Order, Purchase Invoice or Journal Entry","Row # {0}: Referenzdokumenttyp muss eine der Bestellung, Rechnung oder Kaufjournaleintrag sein", -Row #{0}: Rejected Qty can not be entered in Purchase Return,Zeile #{0}: Abgelehnte Menge kann nicht in Kaufrückgabe eingegeben werden, -Row #{0}: Rejected Warehouse is mandatory against rejected Item {1},Row # {0}: Abgelehnt Warehouse ist obligatorisch gegen zurückgewiesen Artikel {1}, -Row #{0}: Reqd by Date cannot be before Transaction Date,Zeilennr. {0}: Erforderlich nach Datum darf nicht vor dem Transaktionsdatum liegen, -Row #{0}: Set Supplier for item {1},Zeile #{0}: Lieferanten für Artikel {1} einstellen, -Row #{0}: Status must be {1} for Invoice Discounting {2},Zeile # {0}: Status muss {1} für Rechnungsrabatt {2} sein, -"Row #{0}: The batch {1} has only {2} qty. Please select another batch which has {3} qty available or split the row into multiple rows, to deliver/issue from multiple batches","Zeile # {0}: Der Batch {1} hat nur {2} Menge. Bitte wähle eine andere Charge aus, die {3} Menge zur Verfügung hat oder die Zeile in mehrere Zeilen aufteilt, um aus mehreren Chargen zu liefern / auszutauschen", -Row #{0}: Timings conflicts with row {1},Zeile #{0}: Timing-Konflikte mit Zeile {1}, -Row #{0}: {1} can not be negative for item {2},Row # {0}: {1} kann für Artikel nicht negativ sein {2}, -Row No {0}: Amount cannot be greater than Pending Amount against Expense Claim {1}. Pending Amount is {2},Zeile Nr. {0}: Der Betrag kann nicht größer als der ausstehende Betrag der Auslagenabrechnung {1} sein. Der ausstehende Betrag ist {2}, +Row # {0}: ,Zeile {0}:, +Row # {0}: Batch No must be same as {1} {2},Zeile {0}: Chargennummer muss dieselbe sein wie {1} {2}, +Row # {0}: Cannot return more than {1} for Item {2},Zeile {0}: Es kann nicht mehr als {1} für Artikel {2} zurückgegeben werden, +Row # {0}: Rate cannot be greater than the rate used in {1} {2},"Zeile {0}: Die Rate kann nicht größer sein als die Rate, die in {1} {2}", +Row # {0}: Serial No is mandatory,Zeile {0}: Seriennummer ist zwingend erforderlich, +Row # {0}: Serial No {1} does not match with {2} {3},Zeile {0}: Seriennummer {1} stimmt nicht mit {2} {3} überein, +Row #{0} (Payment Table): Amount must be negative,Zeile {0} (Zahlungstabelle): Betrag muss negativ sein, +Row #{0} (Payment Table): Amount must be positive,Zeile {0} (Zahlungstabelle): Betrag muss positiv sein, +Row #{0}: Account {1} does not belong to company {2},Zeile {0}: Konto {1} gehört nicht zur Unternehmen {2}, +Row #{0}: Allocated Amount cannot be greater than outstanding amount.,Zeile {0}: Zugeordneter Betrag darf nicht größer als ausstehender Betrag sein., +"Row #{0}: Asset {1} cannot be submitted, it is already {2}","Zeile {0}: Vermögenswert {1} kann nicht vorgelegt werden, es ist bereits {2}", +Row #{0}: Cannot set Rate if amount is greater than billed amount for Item {1}.,"Zeile {0}: Die Rate kann nicht festgelegt werden, wenn der Betrag für Artikel {1} höher als der Rechnungsbetrag ist.", +Row #{0}: Cannot allocate more than {1} against payment term {2},Zeile {0}: Es kann nicht mehr als {1} zu Zahlungsbedingung {2} zugeordnet werden, +Row #{0}: Clearance date {1} cannot be before Cheque Date {2},Zeile {0}: Räumungsdatum {1} kann nicht vor dem Scheck Datum sein {2}, +Row #{0}: Duplicate entry in References {1} {2},Referenz {1} {2} in Zeile {0} kommt doppelt vor, +Row #{0}: Expected Delivery Date cannot be before Purchase Order Date,Zeile {0}: Voraussichtlicher Liefertermin kann nicht vor Bestelldatum sein, +Row #{0}: Item added,Zeile {0}: Element hinzugefügt, +Row #{0}: Journal Entry {1} does not have account {2} or already matched against another voucher,Zeile {0}: Buchungssatz {1} betrifft nicht Konto {2} oder bereits mit einem anderen Beleg verrechnet, +Row #{0}: Not allowed to change Supplier as Purchase Order already exists,"Zeile {0}: Es ist nicht erlaubt den Lieferanten zu wechseln, da bereits eine Bestellung vorhanden ist", +Row #{0}: Please set reorder quantity,Zeile {0}: Bitte Nachbestellmenge angeben, +Row #{0}: Please specify Serial No for Item {1},Zeile {0}: Bitte Seriennummer für Artikel {1} angeben, +Row #{0}: Qty increased by 1,Zeile {0}: Menge um 1 erhöht, +Row #{0}: Rate must be same as {1}: {2} ({3} / {4}) ,Zeile {0}: Preis muss derselbe wie {1}: {2} ({3} / {4}) sein, +Row #{0}: Reference Document Type must be one of Expense Claim or Journal Entry,Zeile {0}: Referenzdokumenttyp muss entweder 'Auslagenabrechnung' oder 'Buchungssatz' sein, +"Row #{0}: Reference Document Type must be one of Purchase Order, Purchase Invoice or Journal Entry","Zeile {0}: Referenzdokumenttyp muss eine der Bestellung, Eingangsrechnung oder Buchungssatz sein", +Row #{0}: Rejected Qty can not be entered in Purchase Return,Zeile {0}: Abgelehnte Menge kann nicht in Kaufrückgabe eingegeben werden, +Row #{0}: Rejected Warehouse is mandatory against rejected Item {1},Zeile {0}: Abgelehnt Warehouse ist obligatorisch gegen zurückgewiesen Artikel {1}, +Row #{0}: Reqd by Date cannot be before Transaction Date,Zeile {0}: Erforderlich nach Datum darf nicht vor dem Transaktionsdatum liegen, +Row #{0}: Set Supplier for item {1},Zeile {0}: Lieferanten für Artikel {1} einstellen, +Row #{0}: Status must be {1} for Invoice Discounting {2},Zeile {0}: Status muss {1} für Rechnungsrabatt {2} sein, +"Row #{0}: The batch {1} has only {2} qty. Please select another batch which has {3} qty available or split the row into multiple rows, to deliver/issue from multiple batches","Zeile {0}: Der Batch {1} hat nur {2} Menge. Bitte wähle eine andere Charge aus, die {3} Menge zur Verfügung hat oder die Zeile in mehrere Zeilen aufteilt, um aus mehreren Chargen zu liefern / auszutauschen", +Row #{0}: Timings conflicts with row {1},Zeile {0}: Timing-Konflikte mit Zeile {1}, +Row #{0}: {1} can not be negative for item {2},Zeile {0}: {1} kann für Artikel nicht negativ sein {2}, +Row No {0}: Amount cannot be greater than Pending Amount against Expense Claim {1}. Pending Amount is {2},Zeile {0}: Der Betrag kann nicht größer als der ausstehende Betrag der Auslagenabrechnung {1} sein. Der ausstehende Betrag ist {2}, Row {0} : Operation is required against the raw material item {1},Zeile {0}: Vorgang ist für die Rohmaterialposition {1} erforderlich, Row {0}# Allocated amount {1} cannot be greater than unclaimed amount {2},Zeile {0} # Der zugewiesene Betrag {1} darf nicht größer sein als der nicht beanspruchte Betrag {2}, Row {0}# Item {1} cannot be transferred more than {2} against Purchase Order {3},Zeile {0} # Artikel {1} kann nicht mehr als {2} gegen Bestellung {3} übertragen werden., -Row {0}# Paid Amount cannot be greater than requested advance amount,Zeile Nr. {0}: Bezahlter Betrag darf nicht größer sein als der geforderte Anzahlungsbetrag, -Row {0}: Activity Type is mandatory.,Row {0}: Leistungsart ist obligatorisch., -Row {0}: Advance against Customer must be credit,Row {0}: Voraus gegen Kunde muss Kredit, -Row {0}: Advance against Supplier must be debit,Row {0}: Voraus gegen Lieferant muss belasten werden, +Row {0}# Paid Amount cannot be greater than requested advance amount,Zeile {0}: Bezahlter Betrag darf nicht größer sein als der geforderte Anzahlungsbetrag, +Row {0}: Activity Type is mandatory.,Zeile {0}: Leistungsart ist obligatorisch., +Row {0}: Advance against Customer must be credit,Zeile {0}: Voraus gegen Kunde muss Kredit, +Row {0}: Advance against Supplier must be debit,Zeile {0}: Voraus gegen Lieferant muss belasten werden, Row {0}: Allocated amount {1} must be less than or equals to Payment Entry amount {2},Zeile {0}: Zugewiesener Betrag {1} muss kleiner oder gleich der Zahlungsmenge {2} sein, Row {0}: Allocated amount {1} must be less than or equals to invoice outstanding amount {2},Zeile {0}: Zugeteilte Menge {1} muss kleiner als oder gleich dem ausstehenden Betrag in Rechnung {2} sein, Row {0}: An Reorder entry already exists for this warehouse {1},Zeile {0}: Es gibt bereits eine Nachbestellungsbuchung für dieses Lager {1}, Row {0}: Bill of Materials not found for the Item {1},Zeile {0}: Bill of Materials nicht für den Artikel gefunden {1}, -Row {0}: Conversion Factor is mandatory,Row {0}: Umrechnungsfaktor ist zwingend erfoderlich, +Row {0}: Conversion Factor is mandatory,Zeile {0}: Umrechnungsfaktor ist zwingend erfoderlich, Row {0}: Cost center is required for an item {1},Zeile {0}: Kostenstelle ist für einen Eintrag {1} erforderlich, Row {0}: Credit entry can not be linked with a {1},Zeile {0}: Habenbuchung kann nicht mit ein(em) {1} verknüpft werden, Row {0}: Currency of the BOM #{1} should be equal to the selected currency {2},Zeile {0}: Währung der Stückliste # {1} sollte der gewählten Währung entsprechen {2}, Row {0}: Debit entry can not be linked with a {1},Zeile {0}: Sollbuchung kann nicht mit ein(em) {1} verknüpft werden, Row {0}: Depreciation Start Date is required,Zeile {0}: Das Abschreibungsstartdatum ist erforderlich, -Row {0}: Enter location for the asset item {1},Zeile Nr. {0}: Geben Sie den Speicherort für das Vermögenswert {1} ein., +Row {0}: Enter location for the asset item {1},Zeile {0}: Geben Sie einen Ort für den Vermögenswert {1} ein., Row {0}: Exchange Rate is mandatory,Zeile {0}: Wechselkurs ist erforderlich, Row {0}: Expected Value After Useful Life must be less than Gross Purchase Amount,Zeile {0}: Erwarteter Wert nach Nutzungsdauer muss kleiner als Brutto Kaufbetrag sein, -Row {0}: From Time and To Time is mandatory.,Row {0}: Von Zeit und zu Zeit ist obligatorisch., +Row {0}: From Time and To Time is mandatory.,Zeile {0}: Von Zeit und zu Zeit ist obligatorisch., Row {0}: From Time and To Time of {1} is overlapping with {2},Zeile {0}: Zeitüberlappung in {1} mit {2}, Row {0}: From time must be less than to time,Zeile {0}: Von Zeit zu Zeit muss kleiner sein, -Row {0}: Hours value must be greater than zero.,Row {0}: Stunden-Wert muss größer als Null sein., +Row {0}: Hours value must be greater than zero.,Zeile {0}: Stunden-Wert muss größer als Null sein., Row {0}: Invalid reference {1},Zeile {0}: Ungültige Referenz {1}, Row {0}: Party / Account does not match with {1} / {2} in {3} {4},Zeile {0}: Partei / Konto stimmt nicht mit {1} / {2} in {3} {4} überein, Row {0}: Party Type and Party is required for Receivable / Payable account {1},Zeile {0}: Partei-Typ und Partei sind für Forderungen-/Verbindlichkeiten-Konto {1} zwingend erforderlich, @@ -2869,7 +2875,7 @@ Supplier Group master.,Lieferantengruppenstamm, Supplier Id,Lieferanten-ID, Supplier Invoice Date cannot be greater than Posting Date,Lieferant Rechnungsdatum kann nicht größer sein als Datum der Veröffentlichung, Supplier Invoice No,Lieferantenrechnungsnr., -Supplier Invoice No exists in Purchase Invoice {0},Lieferantenrechnung existiert in Kauf Rechnung {0}, +Supplier Invoice No exists in Purchase Invoice {0},Die Rechnungsnummer des Lieferanten wurde bereits in Eingangsrechnung {0} verwendet, Supplier Name,Lieferantenname, Supplier Part No,Lieferant Teile-Nr, Supplier Quotation,Lieferantenangebot, @@ -2929,7 +2935,7 @@ Template of terms or contract.,Vorlage für Geschäftsbedingungen oder Vertrag, Templates of supplier scorecard criteria.,Vorlagen der Lieferanten-Scorecard-Kriterien., Templates of supplier scorecard variables.,Vorlagen der Lieferanten-Scorecard-Variablen., Templates of supplier standings.,Vorlagen der Lieferantenwertung., -Temporarily on Hold,Vorübergehend in der Warteschleife, +Temporarily on Hold,Vorübergehend auf Eis gelegt, Temporary,Temporär, Temporary Accounts,Temporäre Konten, Temporary Opening,Temporäre Eröffnungskonten, @@ -3071,7 +3077,7 @@ Total Budget,Gesamtbudget; Gesamtetat, Total Collected: {0},Gesammelt gesammelt: {0}, Total Commission,Gesamtprovision, Total Contribution Amount: {0},Gesamtbeitragsbetrag: {0}, -Total Credit/ Debit Amount should be same as linked Journal Entry,Der Gesamtkreditbetrag sollte identisch mit dem verknüpften Journaleintrag sein, +Total Credit/ Debit Amount should be same as linked Journal Entry,Der Gesamtkreditbetrag sollte identisch mit dem verknüpften Buchungssatz sein, Total Debit must be equal to Total Credit. The difference is {0},Gesamt-Soll muss gleich Gesamt-Haben sein. Die Differenz ist {0}, Total Deduction,Gesamtabzug, Total Invoiced Amount,Gesamtrechnungsbetrag, @@ -3404,7 +3410,7 @@ on,Am, {0} is not in Optional Holiday List,{0} befindet sich nicht in der optionalen Feiertagsliste, {0} is not in a valid Payroll Period,{0} befindet sich nicht in einer gültigen Abrechnungsperiode, {0} is now the default Fiscal Year. Please refresh your browser for the change to take effect.,"{0} ist jetzt das Standardgeschäftsjahr. Bitte aktualisieren Sie Ihren Browser, damit die Änderungen wirksam werden.", -{0} is on hold till {1},{0} ist zurückgestellt bis {1}, +{0} is on hold till {1},{0} ist auf Eis gelegt bis {1}, {0} item found.,{0} Artikel gefunden., {0} items found.,{0} Artikel gefunden., {0} items in progress,{0} Elemente in Bearbeitung, @@ -3424,6 +3430,8 @@ on,Am, {0} variants created.,{0} Varianten erstellt., {0} {1} created,{0} {1} erstellt, {0} {1} does not exist,{0} {1} existiert nicht, +{0} {1} has already been fully paid.,{0} {1} wurde bereits vollständig bezahlt., +{0} {1} has already been partly paid. Please use the 'Get Outstanding Invoice' or the 'Get Outstanding Orders' button to get the latest outstanding amounts.,"{0} {1} wurde bereits teilweise bezahlt. Bitte nutzen Sie den Button 'Ausstehende Rechnungen aufrufen', um die aktuell ausstehenden Beträge zu erhalten.", {0} {1} has been modified. Please refresh.,{0} {1} wurde geändert. Bitte aktualisieren., {0} {1} has not been submitted so the action cannot be completed,"{0} {1} sind nicht gebucht, deshalb kann die Aktion nicht abgeschlossen werden", "{0} {1} is associated with {2}, but Party Account is {3}","{0} {1} ist mit {2} verbunden, aber das Gegenkonto ist {3}", @@ -3435,9 +3443,10 @@ on,Am, {0} {1} is frozen,{0} {1} ist gesperrt, {0} {1} is fully billed,{0} {1} wird voll in Rechnung gestellt, {0} {1} is not active,{0} {1} ist nicht aktiv, -{0} {1} is not associated with {2} {3},{0} {1} ist nicht mit {2} {3} verknüpft, +{0} {1} is not associated with {2} {3},{0} {1} gehört nicht zu {2} {3}, {0} {1} is not present in the parent company,{0} {1} ist in der Muttergesellschaft nicht vorhanden, {0} {1} is not submitted,{0} {1} wurde nicht übertragen, +{0} {1} is on hold,{0} {1} liegt derzeit auf Eis, {0} {1} is {2},{0} {1} ist {2}, {0} {1} must be submitted,{0} {1} muss vorgelegt werden, {0} {1} not in any active Fiscal Year.,{0} {1} nicht in einem aktiven Geschäftsjahr., @@ -3566,7 +3575,7 @@ Account is mandatory to get payment entries,"Das Konto ist obligatorisch, um Zah Account is not set for the dashboard chart {0},Konto ist nicht für das Dashboard-Diagramm {0} festgelegt., Account {0} does not exists in the dashboard chart {1},Konto {0} ist im Dashboard-Diagramm {1} nicht vorhanden, Account: {0} is capital Work in progress and can not be updated by Journal Entry,Konto: {0} ist in Bearbeitung und kann von Journal Entry nicht aktualisiert werden, -Account: {0} is not permitted under Payment Entry,Konto: {0} ist unter Zahlungseingang nicht zulässig, +Account: {0} is not permitted under Payment Entry,Konto {0} kann nicht in Zahlung verwendet werden, Accounting Dimension {0} is required for 'Balance Sheet' account {1}.,Die Buchhaltungsdimension {0} ist für das Bilanzkonto {1} erforderlich., Accounting Dimension {0} is required for 'Profit and Loss' account {1}.,Die Buchhaltungsdimension {0} ist für das Konto {1} "Gewinn und Verlust" erforderlich., Accounting Masters,Accounting Masters, @@ -3626,6 +3635,8 @@ Billing Interval Count cannot be less than 1,Die Anzahl der Abrechnungsintervall Blue,Blau, Book,Buchen, Book Appointment,Einen Termin verabreden, +Book Advance Payments as Liability option is chosen. Paid From account changed from {0} to {1}.,"Die Option 'Anzahlungen als Verbindlichkeit buchen' ist aktiviert. Das Ausgangskonto wurde von {0} auf {1} geändert.", +Book Advance Payments in Separate Party Account,Anzahlungen als Verbindlichkeit buchen, Brand,Marke, Browse,Durchsuchen, Call Connected,Anruf verbunden, @@ -3835,6 +3846,7 @@ No correct answer is set for {0},Für {0} ist keine richtige Antwort festgelegt. No description,Keine Beschreibung, No issue has been raised by the caller.,Der Anrufer hat kein Problem angesprochen., No items to publish,Keine Artikel zu veröffentlichen, +No outstanding {0} found for the {1} {2} which qualify the filters you have specified.,"Für {1} {2} wurden kein ausstehender Beleg vom Typ {0} gefunden, der den angegebenen Filtern entspricht.", No outstanding invoices found,Keine offenen Rechnungen gefunden, No outstanding invoices found for the {0} {1} which qualify the filters you have specified.,"Für {0} {1} wurden keine ausstehenden Rechnungen gefunden, die die von Ihnen angegebenen Filter qualifizieren.", No outstanding invoices require exchange rate revaluation,Keine ausstehenden Rechnungen erfordern eine Neubewertung des Wechselkurses, @@ -3925,8 +3937,8 @@ Publish More Items,Veröffentlichen Sie weitere Elemente, Publish Your First Items,Veröffentlichen Sie Ihre ersten Artikel, Publish {0} Items,{0} Elemente veröffentlichen, Published Items,Veröffentlichte Artikel, -Purchase Invoice cannot be made against an existing asset {0},Kaufrechnung kann nicht für ein vorhandenes Asset erstellt werden {0}, -Purchase Invoices,Rechnungen kaufen, +Purchase Invoice cannot be made against an existing asset {0},Eingangsrechnung kann nicht für ein vorhandenes Asset erstellt werden {0}, +Purchase Invoices,Eingangsrechnungen, Purchase Orders,Kauforder, Purchase Receipt doesn't have any Item for which Retain Sample is enabled.,"Der Kaufbeleg enthält keinen Artikel, für den die Option "Probe aufbewahren" aktiviert ist.", Purchase Return,Warenrücksendung, @@ -3962,20 +3974,20 @@ Review,Rezension, Room,Zimmer, Room Type,Zimmertyp, Row # ,Zeile #, -Row #{0}: Accepted Warehouse and Supplier Warehouse cannot be same,Zeile # {0}: Akzeptiertes Lager und Lieferantenlager können nicht identisch sein, -Row #{0}: Cannot delete item {1} which has already been billed.,Zeile # {0}: Der bereits abgerechnete Artikel {1} kann nicht gelöscht werden., -Row #{0}: Cannot delete item {1} which has already been delivered,"Zeile # {0}: Element {1}, das bereits geliefert wurde, kann nicht gelöscht werden", -Row #{0}: Cannot delete item {1} which has already been received,"Zeile # {0}: Element {1}, das bereits empfangen wurde, kann nicht gelöscht werden", -Row #{0}: Cannot delete item {1} which has work order assigned to it.,"Zeile # {0}: Element {1}, dem ein Arbeitsauftrag zugewiesen wurde, kann nicht gelöscht werden.", -Row #{0}: Cannot delete item {1} which is assigned to customer's purchase order.,"Zeile # {0}: Artikel {1}, der der Bestellung des Kunden zugeordnet ist, kann nicht gelöscht werden.", -Row #{0}: Cannot select Supplier Warehouse while suppling raw materials to subcontractor,"Zeile # {0}: Supplier Warehouse kann nicht ausgewählt werden, während Rohstoffe an Subunternehmer geliefert werden", -Row #{0}: Cost Center {1} does not belong to company {2},Zeile # {0}: Kostenstelle {1} gehört nicht zu Firma {2}, -Row #{0}: Operation {1} is not completed for {2} qty of finished goods in Work Order {3}. Please update operation status via Job Card {4}.,Zeile # {0}: Vorgang {1} ist für {2} Fertigwarenmenge im Fertigungsauftrag {3} nicht abgeschlossen. Bitte aktualisieren Sie den Betriebsstatus über die Jobkarte {4}., -Row #{0}: Payment document is required to complete the transaction,"Zeile # {0}: Der Zahlungsbeleg ist erforderlich, um die Transaktion abzuschließen", -Row #{0}: Serial No {1} does not belong to Batch {2},Zeile # {0}: Seriennummer {1} gehört nicht zu Charge {2}, -Row #{0}: Service End Date cannot be before Invoice Posting Date,Zeile # {0}: Das Service-Enddatum darf nicht vor dem Rechnungsbuchungsdatum liegen, -Row #{0}: Service Start Date cannot be greater than Service End Date,Zeile # {0}: Das Servicestartdatum darf nicht höher als das Serviceenddatum sein, -Row #{0}: Service Start and End Date is required for deferred accounting,Zeile # {0}: Das Start- und Enddatum des Service ist für die aufgeschobene Abrechnung erforderlich, +Row #{0}: Accepted Warehouse and Supplier Warehouse cannot be same,Zeile {0}: Akzeptiertes Lager und Lieferantenlager können nicht identisch sein, +Row #{0}: Cannot delete item {1} which has already been billed.,Zeile {0}: Der bereits abgerechnete Artikel {1} kann nicht gelöscht werden., +Row #{0}: Cannot delete item {1} which has already been delivered,"Zeile {0}: Element {1}, das bereits geliefert wurde, kann nicht gelöscht werden", +Row #{0}: Cannot delete item {1} which has already been received,"Zeile {0}: Element {1}, das bereits empfangen wurde, kann nicht gelöscht werden", +Row #{0}: Cannot delete item {1} which has work order assigned to it.,"Zeile {0}: Element {1}, dem ein Arbeitsauftrag zugewiesen wurde, kann nicht gelöscht werden.", +Row #{0}: Cannot delete item {1} which is assigned to customer's purchase order.,"Zeile {0}: Artikel {1}, der der Bestellung des Kunden zugeordnet ist, kann nicht gelöscht werden.", +Row #{0}: Cannot select Supplier Warehouse while suppling raw materials to subcontractor,"Zeile {0}: Supplier Warehouse kann nicht ausgewählt werden, während Rohstoffe an Subunternehmer geliefert werden", +Row #{0}: Cost Center {1} does not belong to company {2},Zeile {0}: Kostenstelle {1} gehört nicht zu Firma {2}, +Row #{0}: Operation {1} is not completed for {2} qty of finished goods in Work Order {3}. Please update operation status via Job Card {4}.,Zeile {0}: Vorgang {1} ist für {2} Fertigwarenmenge im Fertigungsauftrag {3} nicht abgeschlossen. Bitte aktualisieren Sie den Betriebsstatus über die Jobkarte {4}., +Row #{0}: Payment document is required to complete the transaction,"Zeile {0}: Der Zahlungsbeleg ist erforderlich, um die Transaktion abzuschließen", +Row #{0}: Serial No {1} does not belong to Batch {2},Zeile {0}: Seriennummer {1} gehört nicht zu Charge {2}, +Row #{0}: Service End Date cannot be before Invoice Posting Date,Zeile {0}: Das Service-Enddatum darf nicht vor dem Rechnungsbuchungsdatum liegen, +Row #{0}: Service Start Date cannot be greater than Service End Date,Zeile {0}: Das Servicestartdatum darf nicht höher als das Serviceenddatum sein, +Row #{0}: Service Start and End Date is required for deferred accounting,Zeile {0}: Das Start- und Enddatum des Service ist für die aufgeschobene Abrechnung erforderlich, Row {0}: Invalid Item Tax Template for item {1},Zeile {0}: Ungültige Artikelsteuervorlage für Artikel {1}, Row {0}: Quantity not available for {4} in warehouse {1} at posting time of the entry ({2} {3}),Zeile {0}: Menge für {4} in Lager {1} zum Buchungszeitpunkt des Eintrags nicht verfügbar ({2} {3}), Row {0}: user has not applied the rule {1} on the item {2},Zeile {0}: Der Nutzer hat die Regel {1} nicht auf das Element {2} angewendet., @@ -4237,7 +4249,7 @@ to,An, Cards,Karten, Percentage,Prozentsatz, Failed to setup defaults for country {0}. Please contact support@erpnext.com,Fehler beim Einrichten der Standardeinstellungen für Land {0}. Bitte wenden Sie sich an support@erpnext.com, -Row #{0}: Item {1} is not a Serialized/Batched Item. It cannot have a Serial No/Batch No against it.,Zeile # {0}: Element {1} ist kein serialisiertes / gestapeltes Element. Es kann keine Seriennummer / Chargennummer dagegen haben., +Row #{0}: Item {1} is not a Serialized/Batched Item. It cannot have a Serial No/Batch No against it.,Zeile {0}: Element {1} ist kein serialisiertes / gestapeltes Element. Es kann keine Seriennummer / Chargennummer dagegen haben., Please set {0},Bitte {0} setzen, Please set {0},Bitte setzen Sie {0},supplier Draft,Entwurf,"docstatus,=,0" @@ -4324,7 +4336,7 @@ Row {0}: {1} is required in the expenses table to book an expense claim.,"Zeile Set the default account for the {0} {1},Legen Sie das Standardkonto für {0} {1} fest, (Half Day),(Halber Tag), Income Tax Slab,Einkommensteuerplatte, -Row #{0}: Cannot set amount or formula for Salary Component {1} with Variable Based On Taxable Salary,Zeile # {0}: Betrag oder Formel für Gehaltskomponente {1} mit Variable basierend auf steuerpflichtigem Gehalt kann nicht festgelegt werden, +Row #{0}: Cannot set amount or formula for Salary Component {1} with Variable Based On Taxable Salary,Zeile {0}: Betrag oder Formel für Gehaltskomponente {1} mit Variable basierend auf steuerpflichtigem Gehalt kann nicht festgelegt werden, Row #{}: {} of {} should be {}. Please modify the account or select a different account.,Zeile # {}: {} von {} sollte {} sein. Bitte ändern Sie das Konto oder wählen Sie ein anderes Konto aus., Row #{}: Please asign task to a member.,Zeile # {}: Bitte weisen Sie einem Mitglied eine Aufgabe zu., Process Failed,Prozess fehlgeschlagen, @@ -4333,10 +4345,11 @@ Please set Warehouse in Woocommerce Settings,Bitte stellen Sie Warehouse in den Row {0}: Delivery Warehouse ({1}) and Customer Warehouse ({2}) can not be same,Zeile {0}: Lieferlager ({1}) und Kundenlager ({2}) können nicht identisch sein, Row {0}: Due Date in the Payment Terms table cannot be before Posting Date,Zeile {0}: Fälligkeitsdatum in der Tabelle "Zahlungsbedingungen" darf nicht vor dem Buchungsdatum liegen, Cannot find {} for item {}. Please set the same in Item Master or Stock Settings.,{} Für Element {} kann nicht gefunden werden. Bitte stellen Sie dasselbe in den Artikelstamm- oder Lagereinstellungen ein., -Row #{0}: The batch {1} has already expired.,Zeile # {0}: Der Stapel {1} ist bereits abgelaufen., +Row #{0}: The batch {1} has already expired.,Zeile {0}: Der Stapel {1} ist bereits abgelaufen., Start Year and End Year are mandatory,Startjahr und Endjahr sind obligatorisch, GL Entry,Buchung zum Hauptbuch, Cannot allocate more than {0} against payment term {1},Es kann nicht mehr als {0} für die Zahlungsbedingung {1} zugeordnet werden., +Cannot receive from customer against negative outstanding,Negativer Gesamtbetrag kann nicht vom Kunden empfangen werden, The root account {0} must be a group,Das Root-Konto {0} muss eine Gruppe sein, Shipping rule not applicable for country {0} in Shipping Address,Versandregel gilt nicht für Land {0} in Versandadresse, Get Payments from,Zahlungen erhalten von, @@ -4476,7 +4489,7 @@ Determine Address Tax Category From,Adresssteuerkategorie bestimmen von, Over Billing Allowance (%),Mehr als Abrechnungsbetrag (%), Credit Controller,Kredit-Controller, Check Supplier Invoice Number Uniqueness,"Aktivieren, damit dieselbe Lieferantenrechnungsnummer nur einmal vorkommen kann", -Make Payment via Journal Entry,Zahlung über Journaleintrag, +Make Payment via Journal Entry,Zahlung über Buchungssatz, Unlink Payment on Cancellation of Invoice,Zahlung bei Stornierung der Rechnung aufheben, Book Asset Depreciation Entry Automatically,Vermögensabschreibung automatisch verbuchen, Automatically Add Taxes and Charges from Item Tax Template,Steuern und Gebühren aus Artikelsteuervorlage automatisch hinzufügen, @@ -4545,7 +4558,7 @@ Bank Statement Transaction Entry,Kontoauszug Transaktionseintrag, Bank Transaction Entries,Banktransaktionseinträge, New Transactions,Neue Transaktionen, Match Transaction to Invoices,Transaktion mit Rechnungen abgleichen, -Create New Payment/Journal Entry,Erstellen Sie eine neue Zahlung / Journaleintrag, +Create New Payment/Journal Entry,Erstellen Sie eine neue Zahlung / Buchungssatz, Submit/Reconcile Payments,Zahlungen absenden / abstimmen, Matching Invoices,Passende Rechnungen, Payment Invoice Items,Zahlung Rechnungspositionen, @@ -4798,6 +4811,7 @@ Account Paid To,Eingangskonto, Paid Amount (Company Currency),Gezahlter Betrag (Unternehmenswährung), Received Amount,erhaltenen Betrag, Received Amount (Company Currency),Erhaltene Menge (Gesellschaft Währung), +Received Amount cannot be greater than Paid Amount,Der erhaltene Betrag darf nicht größer sein als der gezahlte Betrag, Get Outstanding Invoice,Erhalten Sie eine ausstehende Rechnung, Payment References,Bezahlung Referenzen, Writeoff,Abschreiben, @@ -4946,7 +4960,7 @@ Apply Tax Withholding Amount,Steuereinbehaltungsbetrag anwenden, Accounting Dimensions ,Buchhaltung Dimensionen, Supplier Invoice Details,Lieferant Rechnungsdetails, Supplier Invoice Date,Lieferantenrechnungsdatum, -Return Against Purchase Invoice,Zurück zur Eingangsrechnung, +Return Against Purchase Invoice,Gutschrift zur Eingangsrechnung, Select Supplier Address,Lieferantenadresse auswählen, Contact Person,Kontaktperson, Select Shipping Address,Lieferadresse auswählen, @@ -4996,7 +5010,7 @@ Terms,Geschäftsbedingungen, Terms and Conditions1,Allgemeine Geschäftsbedingungen1, Group same items,Gruppe gleichen Artikel, Print Language,Drucksprache, -"Once set, this invoice will be on hold till the set date","Einmal eingestellt, wird diese Rechnung bis zum festgelegten Datum gehalten", +"Once set, this invoice will be on hold till the set date","Einmal eingestellt, liegt diese Rechnung bis zum festgelegten Datum auf Eis", Credit To,Gutschreiben auf, Party Account Currency,Währung des Kontos der Partei, Against Expense Account,Zu Aufwandskonto, @@ -5320,7 +5334,7 @@ Asset Owner,Eigentümer des Vermögenswertes, Asset Owner Company,Eigentümergesellschaft, Custodian,Depotbank, Disposal Date,Verkauf Datum, -Journal Entry for Scrap,Journaleintrag für Ausschuss, +Journal Entry for Scrap,Buchungssatz für Ausschuss, Available-for-use Date,Verfügbarkeitsdatum, Calculate Depreciation,Abschreibung berechnen, Allow Monthly Depreciation,Monatliche Abschreibung zulassen, @@ -5482,8 +5496,8 @@ Default Bank Account,Standardbankkonto, Is Transporter,Ist Transporter, Represents Company,Repräsentiert das Unternehmen, Supplier Type,Lieferantentyp, -Allow Purchase Invoice Creation Without Purchase Order,Erstellen von Kaufrechnungen ohne Bestellung zulassen, -Allow Purchase Invoice Creation Without Purchase Receipt,Zulassen der Erstellung von Kaufrechnungen ohne Kaufbeleg, +Allow Purchase Invoice Creation Without Purchase Order,Erstellen von Eingangsrechnung ohne Bestellung zulassen, +Allow Purchase Invoice Creation Without Purchase Receipt,Erstellen von Eingangsrechnung ohne Kaufbeleg ohne Kaufbeleg zulassen, Warn RFQs,Warnung Ausschreibungen, Warn POs,Warnen Sie POs, Prevent RFQs,Vermeidung von Ausschreibungen, @@ -7668,7 +7682,7 @@ Enable Perpetual Inventory,Permanente Inventur aktivieren, Default Inventory Account,Standard Inventurkonto, Stock Adjustment Account,Bestandskorrektur-Konto, Fixed Asset Depreciation Settings,Einstellungen Abschreibung von Anlagevermögen, -Series for Asset Depreciation Entry (Journal Entry),Serie für Abschreibungs-Eintrag (Journaleintrag), +Series for Asset Depreciation Entry (Journal Entry),Serie für Abschreibungs-Eintrag (Buchungssatz), Gain/Loss Account on Asset Disposal,Gewinn-/Verlustrechnung auf die Veräußerung von Vermögenswerten, Asset Depreciation Cost Center,Kostenstelle für Vermögenswertabschreibung, Budget Detail,Budget-Detail, @@ -8454,10 +8468,10 @@ Automatically Process Deferred Accounting Entry,Aufgeschobene Buchungsbuchung au Bank Clearance,Bankfreigabe, Bank Clearance Detail,Bankfreigabedetail, Update Cost Center Name / Number,Name / Nummer der Kostenstelle aktualisieren, -Journal Entry Template,Journaleintragsvorlage, +Journal Entry Template,Buchungssatz-Vorlage, Template Title,Vorlagentitel, -Journal Entry Type,Journaleintragstyp, -Journal Entry Template Account,Journaleintragsvorlagenkonto, +Journal Entry Type,Buchungssatz-Typ, +Journal Entry Template Account,Buchungssatzvorlagenkonto, Process Deferred Accounting,Aufgeschobene Buchhaltung verarbeiten, Manual entry cannot be created! Disable automatic entry for deferred accounting in accounts settings and try again,Manuelle Eingabe kann nicht erstellt werden! Deaktivieren Sie die automatische Eingabe für die verzögerte Buchhaltung in den Konteneinstellungen und versuchen Sie es erneut, End date cannot be before start date,Das Enddatum darf nicht vor dem Startdatum liegen, @@ -8532,7 +8546,7 @@ Deferred Accounting Settings,Aufgeschobene Buchhaltungseinstellungen, Book Deferred Entries Based On,Buch verzögerte Einträge basierend auf, Days,Tage, Months,Monate, -Book Deferred Entries Via Journal Entry,Buch verzögerte Einträge über Journaleintrag, +Book Deferred Entries Via Journal Entry,Separaten Buchungssatz für latente Buchungen erstellen, Submit Journal Entries,Journaleinträge senden, If this is unchecked Journal Entries will be saved in a Draft state and will have to be submitted manually,"Wenn dieses Kontrollkästchen deaktiviert ist, werden Journaleinträge in einem Entwurfsstatus gespeichert und müssen manuell übermittelt werden", Enable Distributed Cost Center,Aktivieren Sie die verteilte Kostenstelle, @@ -8560,7 +8574,7 @@ Dunning Letter,Mahnbrief, Reference Detail No,Referenz Detail Nr, Custom Remarks,Benutzerdefinierte Bemerkungen, Please select a Company first.,Bitte wählen Sie zuerst eine Firma aus., -"Row #{0}: Reference Document Type must be one of Sales Order, Sales Invoice, Journal Entry or Dunning","Zeile # {0}: Der Referenzdokumenttyp muss Auftrag, Ausgangsrechnung, Journaleintrag oder Mahnwesen sein", +"Row #{0}: Reference Document Type must be one of Sales Order, Sales Invoice, Journal Entry or Dunning","Zeile {0}: Der Referenzdokumenttyp muss Auftrag, Ausgangsrechnung, Buchungssatz oder Mahnung sein", POS Closing Entry,POS Closing Entry, POS Opening Entry,POS-Eröffnungseintrag, POS Transactions,POS-Transaktionen, @@ -8679,9 +8693,9 @@ Depreciation Posting Date,Buchungsdatum der Abschreibung, "By default, the Supplier Name is set as per the Supplier Name entered. If you want Suppliers to be named by a ","Standardmäßig wird der Lieferantenname gemäß dem eingegebenen Lieferantennamen festgelegt. Wenn Sie möchten, dass Lieferanten von a benannt werden", choose the 'Naming Series' option.,Wählen Sie die Option "Naming Series"., Configure the default Price List when creating a new Purchase transaction. Item prices will be fetched from this Price List.,Konfigurieren Sie die Standardpreisliste beim Erstellen einer neuen Kauftransaktion. Artikelpreise werden aus dieser Preisliste abgerufen., -"If this option is configured 'Yes', ERPNext will prevent you from creating a Purchase Invoice or Receipt without creating a Purchase Order first. This configuration can be overridden for a particular supplier by enabling the 'Allow Purchase Invoice Creation Without Purchase Order' checkbox in the Supplier master.","Wenn diese Option auf "Ja" konfiguriert ist, verhindert ERPNext, dass Sie eine Kaufrechnung oder einen Beleg erstellen können, ohne zuvor eine Bestellung zu erstellen. Diese Konfiguration kann für einen bestimmten Lieferanten überschrieben werden, indem das Kontrollkästchen "Erstellung von Eingangsrechnungen ohne Bestellung zulassen" im Lieferantenstamm aktiviert wird.", -"If this option is configured 'Yes', ERPNext will prevent you from creating a Purchase Invoice without creating a Purchase Receipt first. This configuration can be overridden for a particular supplier by enabling the 'Allow Purchase Invoice Creation Without Purchase Receipt' checkbox in the Supplier master.","Wenn diese Option auf "Ja" konfiguriert ist, verhindert ERPNext, dass Sie eine Kaufrechnung erstellen können, ohne zuvor einen Kaufbeleg zu erstellen. Diese Konfiguration kann für einen bestimmten Lieferanten überschrieben werden, indem das Kontrollkästchen "Erstellung von Kaufrechnungen ohne Kaufbeleg zulassen" im Lieferantenstamm aktiviert wird.", -Quantity & Stock,Menge & Lager, +"If this option is configured 'Yes', ERPNext will prevent you from creating a Purchase Invoice or Receipt without creating a Purchase Order first. This configuration can be overridden for a particular supplier by enabling the 'Allow Purchase Invoice Creation Without Purchase Order' checkbox in the Supplier master.","Wenn diese Option auf 'Ja' gesetzt ist, validiert ERPNext, dass Sie eine Bestellung angelegt haben, bevor Sie eine Eingangsrechnung oder einen Kaufbeleg erfassen können. Diese Konfiguration kann für einzelne Lieferanten überschrieben werden, indem Sie die Option 'Erstellung von Eingangsrechnungen ohne Bestellung zulassen' im Lieferantenstamm aktivieren.", +"If this option is configured 'Yes', ERPNext will prevent you from creating a Purchase Invoice without creating a Purchase Receipt first. This configuration can be overridden for a particular supplier by enabling the 'Allow Purchase Invoice Creation Without Purchase Receipt' checkbox in the Supplier master.","Wenn diese Option auf 'Ja' gesetzt ist, validiert ERPNext, dass Sie einen Kaufbeleg angelegt haben, bevor Sie eine Eingangsrechnung erfasen können. Diese Konfiguration kann für einzelne Lieferanten überschrieben werden, indem Sie die Option 'Erstellung von Kaufrechnungen ohne Kaufbeleg zulassen' im Lieferantenstamm aktivieren.", +Quantity & Stock,Menge & Lager, Call Details,Anrufdetails, Authorised By,Authorisiert von, Signee (Company),Unterzeichner (Firma), @@ -8915,7 +8929,7 @@ Rented To Date,Bisher vermietet, Monthly Eligible Amount,Monatlicher förderfähiger Betrag, Total Eligible HRA Exemption,Insgesamt berechtigte HRA-Befreiung, Validating Employee Attendance...,Überprüfung der Mitarbeiterbeteiligung ..., -Submitting Salary Slips and creating Journal Entry...,Einreichen von Gehaltsabrechnungen und Erstellen eines Journaleintrags ..., +Submitting Salary Slips and creating Journal Entry...,Gehaltsabrechnungen werden gebucht und Buchungssätze erstellt..., Calculate Payroll Working Days Based On,Berechnen Sie die Arbeitstage der Personalabrechnung basierend auf, Consider Unmarked Attendance As,Betrachten Sie die nicht markierte Teilnahme als, Fraction of Daily Salary for Half Day,Bruchteil des Tagesgehalts für einen halben Tag, @@ -8968,12 +8982,12 @@ Show 'Scan Barcode' field above every child table to insert Items with ease.,"Ze "If blank, parent Warehouse Account or company default will be considered in transactions","Wenn leer, wird das übergeordnete Lagerkonto oder der Firmenstandard bei Transaktionen berücksichtigt", Service Level Agreement Details,Details zum Service Level Agreement, Service Level Agreement Status,Status des Service Level Agreements, -On Hold Since,In der Warteschleife seit, +On Hold Since,Auf Eis gelegt seit, Total Hold Time,Gesamte Haltezeit, Response Details,Antwortdetails, Average Response Time,Durchschnittliche Reaktionszeit, User Resolution Time,Benutzerauflösungszeit, -SLA is on hold since {0},"SLA wird gehalten, da {0}", +SLA is on hold since {0},"SLA ist seit {0} auf Eis gelegt", Pause SLA On Status,SLA On Status anhalten, Pause SLA On,SLA anhalten Ein, Greetings Section,Grüße Abschnitt, @@ -9187,6 +9201,7 @@ Total Assets,Gesamtvermögen, New Assets (This Year),Neue Vermögenswerte (dieses Jahr), Row #{}: Depreciation Posting Date should not be equal to Available for Use Date.,Zeile # {}: Das Buchungsdatum der Abschreibung sollte nicht dem Datum der Verfügbarkeit entsprechen., Incorrect Date,Falsches Datum, +Incorrect Payment Type,Falsche Zahlungsart, Invalid Gross Purchase Amount,Ungültiger Bruttokaufbetrag, There are active maintenance or repairs against the asset. You must complete all of them before cancelling the asset.,"Es gibt aktive Wartungs- oder Reparaturarbeiten am Vermögenswert. Sie müssen alle Schritte ausführen, bevor Sie das Asset stornieren können.", % Complete,% Komplett, @@ -9209,7 +9224,7 @@ Please check your Plaid client ID and secret values,Bitte überprüfen Sie Ihre Bank transaction creation error,Fehler beim Erstellen der Banküberweisung, Unit of Measurement,Maßeinheit, Fiscal Year {0} Does Not Exist,Geschäftsjahr {0} existiert nicht, -Row # {0}: Returned Item {1} does not exist in {2} {3},Zeile # {0}: Zurückgegebenes Element {1} ist in {2} {3} nicht vorhanden, +Row # {0}: Returned Item {1} does not exist in {2} {3},Zeile {0}: Zurückgegebenes Element {1} ist in {2} {3} nicht vorhanden, Valuation type charges can not be marked as Inclusive,Bewertungsgebühren können nicht als Inklusiv gekennzeichnet werden, You do not have permissions to {} items in a {}.,Sie haben keine Berechtigungen für {} Elemente in einem {}., Insufficient Permissions,Nicht ausreichende Berechtigungen, @@ -9220,7 +9235,7 @@ Invalid Value,Ungültiger Wert, The value {0} is already assigned to an existing Item {1}.,Der Wert {0} ist bereits einem vorhandenen Element {1} zugeordnet., "To still proceed with editing this Attribute Value, enable {0} in Item Variant Settings.","Aktivieren Sie {0} in den Einstellungen für Elementvarianten, um mit der Bearbeitung dieses Attributwerts fortzufahren.", Edit Not Allowed,Bearbeiten nicht erlaubt, -Row #{0}: Item {1} is already fully received in Purchase Order {2},Zeile # {0}: Artikel {1} ist bereits vollständig in der Bestellung {2} eingegangen., +Row #{0}: Item {1} is already fully received in Purchase Order {2},Zeile {0}: Artikel {1} ist bereits vollständig in der Bestellung {2} eingegangen., You cannot create or cancel any accounting entries with in the closed Accounting Period {0},Sie können im abgeschlossenen Abrechnungszeitraum {0} keine Buchhaltungseinträge mit erstellen oder stornieren., POS Invoice should have {} field checked.,Für die POS-Rechnung sollte das Feld {} aktiviert sein., Invalid Item,Ungültiger Artikel, @@ -9315,7 +9330,7 @@ Items Required,Erforderliche Artikel, Operation {0} does not belong to the work order {1},Operation {0} gehört nicht zum Arbeitsauftrag {1}, Print UOM after Quantity,UOM nach Menge drucken, Set default {0} account for perpetual inventory for non stock items,Legen Sie das Standardkonto {0} für die fortlaufende Bestandsaufnahme für nicht vorrätige Artikel fest, -Row #{0}: Child Item should not be a Product Bundle. Please remove Item {1} and Save,Zeile # {0}: Untergeordnetes Element sollte kein Produktpaket sein. Bitte entfernen Sie Artikel {1} und speichern Sie, +Row #{0}: Child Item should not be a Product Bundle. Please remove Item {1} and Save,Zeile {0}: Untergeordnetes Element sollte kein Produktpaket sein. Bitte entfernen Sie Artikel {1} und speichern Sie, Credit limit reached for customer {0},Kreditlimit für Kunde erreicht {0}, Could not auto create Customer due to the following missing mandatory field(s):,Der Kunde konnte aufgrund der folgenden fehlenden Pflichtfelder nicht automatisch erstellt werden:, Please create Customer from Lead {0}.,Bitte erstellen Sie einen Kunden aus Lead {0}., @@ -9327,7 +9342,7 @@ Payroll date can not be less than employee's joining date.,Das Abrechnungsdatum From date can not be less than employee's joining date.,Ab dem Datum darf das Beitrittsdatum des Mitarbeiters nicht unterschritten werden., To date can not be greater than employee's relieving date.,Bisher kann das Entlastungsdatum des Mitarbeiters nicht überschritten werden., Payroll date can not be greater than employee's relieving date.,Das Abrechnungsdatum darf nicht größer sein als das Entlastungsdatum des Mitarbeiters., -Row #{0}: Please enter the result value for {1},Zeile # {0}: Bitte geben Sie den Ergebniswert für {1} ein, +Row #{0}: Please enter the result value for {1},Zeile {0}: Bitte geben Sie den Ergebniswert für {1} ein, Mandatory Results,Obligatorische Ergebnisse, Sales Invoice or Patient Encounter is required to create Lab Tests,Für die Erstellung von Labortests ist eine Ausgangsrechnung oder eine Patientenbegegnung erforderlich, Insufficient Data,Unzureichende Daten, @@ -9335,12 +9350,12 @@ Lab Test(s) {0} created successfully,Labortest (e) {0} erfolgreich erstellt, Test :,Prüfung :, Sample Collection {0} has been created,Die Probensammlung {0} wurde erstellt, Normal Range: ,Normalbereich:, -Row #{0}: Check Out datetime cannot be less than Check In datetime,Zeile # {0}: Die Check-out-Datumszeit darf nicht kleiner als die Check-In-Datumszeit sein, +Row #{0}: Check Out datetime cannot be less than Check In datetime,Zeile {0}: Die Check-out-Datumszeit darf nicht kleiner als die Check-In-Datumszeit sein, "Missing required details, did not create Inpatient Record","Fehlende erforderliche Details, keine stationäre Aufzeichnung erstellt", Unbilled Invoices,Nicht in Rechnung gestellte Rechnungen, Standard Selling Rate should be greater than zero.,Die Standardverkaufsrate sollte größer als Null sein., Conversion Factor is mandatory,Der Umrechnungsfaktor ist obligatorisch, -Row #{0}: Conversion Factor is mandatory,Zeile # {0}: Der Umrechnungsfaktor ist obligatorisch, +Row #{0}: Conversion Factor is mandatory,Zeile {0}: Der Umrechnungsfaktor ist obligatorisch, Sample Quantity cannot be negative or 0,Die Probenmenge darf nicht negativ oder 0 sein, Invalid Quantity,Ungültige Menge, "Please set defaults for Customer Group, Territory and Selling Price List in Selling Settings","Bitte legen Sie in den Verkaufseinstellungen die Standardeinstellungen für Kundengruppe, Gebiet und Verkaufspreisliste fest", @@ -9532,7 +9547,7 @@ Row #{}: No batch selected against item: {}. Please select a batch or remove it Payment amount cannot be less than or equal to 0,Der Zahlungsbetrag darf nicht kleiner oder gleich 0 sein, Please enter the phone number first,Bitte geben Sie zuerst die Telefonnummer ein, Row #{}: {} {} does not exist.,Zeile # {}: {} {} existiert nicht., -Row #{0}: {1} is required to create the Opening {2} Invoices,"Zeile # {0}: {1} ist erforderlich, um die Eröffnungsrechnungen {2} zu erstellen", +Row #{0}: {1} is required to create the Opening {2} Invoices,"Zeile {0}: {1} ist erforderlich, um die Eröffnungsrechnungen {2} zu erstellen", You had {} errors while creating opening invoices. Check {} for more details,Beim Erstellen von Eröffnungsrechnungen sind {} Fehler aufgetreten. Überprüfen Sie {} auf weitere Details, Error Occured,Fehler aufgetreten, Opening Invoice Creation In Progress,Öffnen der Rechnungserstellung läuft, @@ -9544,7 +9559,7 @@ Stock Transactions for Item {0} under warehouse {1} cannot be posted before this Posting future stock transactions are not allowed due to Immutable Ledger,Das Buchen zukünftiger Lagertransaktionen ist aufgrund des unveränderlichen Hauptbuchs nicht zulässig, A BOM with name {0} already exists for item {1}.,Für Artikel {1} ist bereits eine Stückliste mit dem Namen {0} vorhanden., {0}{1} Did you rename the item? Please contact Administrator / Tech support,{0} {1} Haben Sie den Artikel umbenannt? Bitte wenden Sie sich an den Administrator / technischen Support, -At row #{0}: the sequence id {1} cannot be less than previous row sequence id {2},In Zeile # {0}: Die Sequenz-ID {1} darf nicht kleiner sein als die vorherige Zeilen-Sequenz-ID {2}., +At row #{0}: the sequence id {1} cannot be less than previous row sequence id {2},In Zeile {0}: Die Sequenz-ID {1} darf nicht kleiner sein als die vorherige Zeilen-Sequenz-ID {2}., The {0} ({1}) must be equal to {2} ({3}),Die {0} ({1}) muss gleich {2} ({3}) sein., "{0}, complete the operation {1} before the operation {2}.","{0}, schließen Sie die Operation {1} vor der Operation {2} ab.", Cannot ensure delivery by Serial No as Item {0} is added with and without Ensure Delivery by Serial No.,"Die Lieferung per Seriennummer kann nicht sichergestellt werden, da Artikel {0} mit und ohne Lieferung per Seriennummer hinzugefügt wird.", From 674af156969819879b114779fec28eb51b68f50e Mon Sep 17 00:00:00 2001 From: Gursheen Kaur Anand <40693548+GursheenK@users.noreply.github.com> Date: Sun, 9 Jul 2023 20:41:12 +0530 Subject: [PATCH 57/99] fix: deferred accounting entries on accounts frozen (#35978) * fix: accounts frozen entries in deferred accounting * test: accounts frozen date in deferred accounting * fix: reset account settings after running test * fix: resolve conflicts * fix: modify expected gle when deferred accounting is disabled through JE * fix: change posting date when accounts not frozen --- erpnext/accounts/deferred_revenue.py | 88 +++++++++++-------- .../test_process_deferred_accounting.py | 36 +++++--- 2 files changed, 77 insertions(+), 47 deletions(-) diff --git a/erpnext/accounts/deferred_revenue.py b/erpnext/accounts/deferred_revenue.py index 45e04ee6b0..fb49ef3a42 100644 --- a/erpnext/accounts/deferred_revenue.py +++ b/erpnext/accounts/deferred_revenue.py @@ -136,7 +136,7 @@ def convert_deferred_revenue_to_income( send_mail(deferred_process) -def get_booking_dates(doc, item, posting_date=None): +def get_booking_dates(doc, item, posting_date=None, prev_posting_date=None): if not posting_date: posting_date = add_days(today(), -1) @@ -146,39 +146,42 @@ def get_booking_dates(doc, item, posting_date=None): "deferred_revenue_account" if doc.doctype == "Sales Invoice" else "deferred_expense_account" ) - prev_gl_entry = frappe.db.sql( - """ - select name, posting_date from `tabGL Entry` where company=%s and account=%s and - voucher_type=%s and voucher_no=%s and voucher_detail_no=%s - and is_cancelled = 0 - order by posting_date desc limit 1 - """, - (doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), - as_dict=True, - ) + if not prev_posting_date: + prev_gl_entry = frappe.db.sql( + """ + select name, posting_date from `tabGL Entry` where company=%s and account=%s and + voucher_type=%s and voucher_no=%s and voucher_detail_no=%s + and is_cancelled = 0 + order by posting_date desc limit 1 + """, + (doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), + as_dict=True, + ) - prev_gl_via_je = frappe.db.sql( - """ - SELECT p.name, p.posting_date FROM `tabJournal Entry` p, `tabJournal Entry Account` c - WHERE p.name = c.parent and p.company=%s and c.account=%s - and c.reference_type=%s and c.reference_name=%s - and c.reference_detail_no=%s and c.docstatus < 2 order by posting_date desc limit 1 - """, - (doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), - as_dict=True, - ) + prev_gl_via_je = frappe.db.sql( + """ + SELECT p.name, p.posting_date FROM `tabJournal Entry` p, `tabJournal Entry Account` c + WHERE p.name = c.parent and p.company=%s and c.account=%s + and c.reference_type=%s and c.reference_name=%s + and c.reference_detail_no=%s and c.docstatus < 2 order by posting_date desc limit 1 + """, + (doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), + as_dict=True, + ) - if prev_gl_via_je: - if (not prev_gl_entry) or ( - prev_gl_entry and prev_gl_entry[0].posting_date < prev_gl_via_je[0].posting_date - ): - prev_gl_entry = prev_gl_via_je + if prev_gl_via_je: + if (not prev_gl_entry) or ( + prev_gl_entry and prev_gl_entry[0].posting_date < prev_gl_via_je[0].posting_date + ): + prev_gl_entry = prev_gl_via_je + + if prev_gl_entry: + start_date = getdate(add_days(prev_gl_entry[0].posting_date, 1)) + else: + start_date = item.service_start_date - if prev_gl_entry: - start_date = getdate(add_days(prev_gl_entry[0].posting_date, 1)) else: - start_date = item.service_start_date - + start_date = getdate(add_days(prev_posting_date, 1)) end_date = get_last_day(start_date) if end_date >= item.service_end_date: end_date = item.service_end_date @@ -341,9 +344,15 @@ def book_deferred_income_or_expense(doc, deferred_process, posting_date=None): accounts_frozen_upto = frappe.get_cached_value("Accounts Settings", "None", "acc_frozen_upto") def _book_deferred_revenue_or_expense( - item, via_journal_entry, submit_journal_entry, book_deferred_entries_based_on + item, + via_journal_entry, + submit_journal_entry, + book_deferred_entries_based_on, + prev_posting_date=None, ): - start_date, end_date, last_gl_entry = get_booking_dates(doc, item, posting_date=posting_date) + start_date, end_date, last_gl_entry = get_booking_dates( + doc, item, posting_date=posting_date, prev_posting_date=prev_posting_date + ) if not (start_date and end_date): return @@ -377,9 +386,12 @@ def book_deferred_income_or_expense(doc, deferred_process, posting_date=None): if not amount: return + gl_posting_date = end_date + prev_posting_date = None # check if books nor frozen till endate: if accounts_frozen_upto and getdate(end_date) <= getdate(accounts_frozen_upto): - end_date = get_last_day(add_days(accounts_frozen_upto, 1)) + gl_posting_date = get_last_day(add_days(accounts_frozen_upto, 1)) + prev_posting_date = end_date if via_journal_entry: book_revenue_via_journal_entry( @@ -388,7 +400,7 @@ def book_deferred_income_or_expense(doc, deferred_process, posting_date=None): debit_account, amount, base_amount, - end_date, + gl_posting_date, project, account_currency, item.cost_center, @@ -404,7 +416,7 @@ def book_deferred_income_or_expense(doc, deferred_process, posting_date=None): against, amount, base_amount, - end_date, + gl_posting_date, project, account_currency, item.cost_center, @@ -418,7 +430,11 @@ def book_deferred_income_or_expense(doc, deferred_process, posting_date=None): if getdate(end_date) < getdate(posting_date) and not last_gl_entry: _book_deferred_revenue_or_expense( - item, via_journal_entry, submit_journal_entry, book_deferred_entries_based_on + item, + via_journal_entry, + submit_journal_entry, + book_deferred_entries_based_on, + prev_posting_date, ) via_journal_entry = cint( diff --git a/erpnext/accounts/doctype/process_deferred_accounting/test_process_deferred_accounting.py b/erpnext/accounts/doctype/process_deferred_accounting/test_process_deferred_accounting.py index 83646c90ba..263621dcf4 100644 --- a/erpnext/accounts/doctype/process_deferred_accounting/test_process_deferred_accounting.py +++ b/erpnext/accounts/doctype/process_deferred_accounting/test_process_deferred_accounting.py @@ -16,8 +16,10 @@ from erpnext.stock.doctype.item.test_item import create_item class TestProcessDeferredAccounting(unittest.TestCase): def test_creation_of_ledger_entry_on_submit(self): """test creation of gl entries on submission of document""" + change_acc_settings(acc_frozen_upto="2023-05-31", book_deferred_entries_based_on="Months") + deferred_account = create_account( - account_name="Deferred Revenue", + account_name="Deferred Revenue for Accounts Frozen", parent_account="Current Liabilities - _TC", company="_Test Company", ) @@ -29,21 +31,21 @@ class TestProcessDeferredAccounting(unittest.TestCase): item.save() si = create_sales_invoice( - item=item.name, update_stock=0, posting_date="2019-01-10", do_not_submit=True + item=item.name, rate=3000, update_stock=0, posting_date="2023-07-01", do_not_submit=True ) si.items[0].enable_deferred_revenue = 1 - si.items[0].service_start_date = "2019-01-10" - si.items[0].service_end_date = "2019-03-15" + si.items[0].service_start_date = "2023-05-01" + si.items[0].service_end_date = "2023-07-31" si.items[0].deferred_revenue_account = deferred_account si.save() si.submit() - process_deferred_accounting = frappe.get_doc( + process_deferred_accounting = doc = frappe.get_doc( dict( doctype="Process Deferred Accounting", - posting_date="2019-01-01", - start_date="2019-01-01", - end_date="2019-01-31", + posting_date="2023-07-01", + start_date="2023-05-01", + end_date="2023-06-30", type="Income", ) ) @@ -52,11 +54,16 @@ class TestProcessDeferredAccounting(unittest.TestCase): process_deferred_accounting.submit() expected_gle = [ - [deferred_account, 33.85, 0.0, "2019-01-31"], - ["Sales - _TC", 0.0, 33.85, "2019-01-31"], + ["Debtors - _TC", 3000, 0.0, "2023-07-01"], + [deferred_account, 0.0, 3000, "2023-07-01"], + ["Sales - _TC", 0.0, 1000, "2023-06-30"], + [deferred_account, 1000, 0.0, "2023-06-30"], + ["Sales - _TC", 0.0, 1000, "2023-06-30"], + [deferred_account, 1000, 0.0, "2023-06-30"], ] - check_gl_entries(self, si.name, expected_gle, "2019-01-31") + check_gl_entries(self, si.name, expected_gle, "2023-07-01") + change_acc_settings() def test_pda_submission_and_cancellation(self): pda = frappe.get_doc( @@ -70,3 +77,10 @@ class TestProcessDeferredAccounting(unittest.TestCase): ) pda.submit() pda.cancel() + + +def change_acc_settings(acc_frozen_upto="", book_deferred_entries_based_on="Days"): + acc_settings = frappe.get_doc("Accounts Settings", "Accounts Settings") + acc_settings.acc_frozen_upto = acc_frozen_upto + acc_settings.book_deferred_entries_based_on = book_deferred_entries_based_on + acc_settings.save() From 46fe9ac5cd70a1178a0dd6f52e30961fe4ca800e Mon Sep 17 00:00:00 2001 From: aioaccount <92444849+aioaccount@users.noreply.github.com> Date: Sun, 9 Jul 2023 22:11:52 +0700 Subject: [PATCH 58/99] fix: labels and translations (#35963) fix: labels and translations * fix: Vietnamese translation of customer * fix: Vietnamese translation of bill --- erpnext/translations/vi.csv | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/erpnext/translations/vi.csv b/erpnext/translations/vi.csv index 543eadb855..e3761373ef 100644 --- a/erpnext/translations/vi.csv +++ b/erpnext/translations/vi.csv @@ -100,7 +100,7 @@ Active,có hiệu lực, Activity Cost exists for Employee {0} against Activity Type - {1},Chi phí hoạt động tồn tại cho Nhân viên {0} đối với Kiểu công việc - {1}, Activity Cost per Employee,Chi phí hoạt động cho một nhân viên, Activity Type,Loại hoạt động, -Actual Cost,Gia thật, +Actual Cost,Giá thật, Actual Delivery Date,Ngày giao hàng thực tế, Actual Qty,Số lượng thực tế, Actual Qty is mandatory,Số lượng thực tế là bắt buộc, @@ -727,14 +727,14 @@ Current Qty,Số lượng hiện tại, Current invoice {0} is missing,Hóa đơn hiện tại {0} bị thiếu, Custom HTML,Tuỳ chỉnh HTML, Custom?,Tùy chỉnh?, -Customer,khách hàng, +Customer,Khách Hàng, Customer Addresses And Contacts,Địa chỉ Khách hàng Và Liên hệ, Customer Contact,Liên hệ Khách hàng, Customer Database.,Cơ sở dữ liệu khách hàng., Customer Group,Nhóm khách hàng, Customer LPO,Khách hàng LPO, Customer LPO No.,Số LPO của khách hàng, -Customer Name,tên khách hàng, +Customer Name,Tên khách hàng, Customer POS Id,POS ID Khách hàng, Customer Service,Dịch vụ chăm sóc khách hàng, Customer and Supplier,Khách hàng và nhà cung cấp, @@ -743,7 +743,7 @@ Customer isn't enrolled in any Loyalty Program,Khách hàng không được đă Customer required for 'Customerwise Discount',"Khách hàng phải có cho 'Giảm giá phù hợp KH """, Customer {0} does not belong to project {1},Khách hàng {0} không thuộc về dự án {1}, Customer {0} is created.,Đã tạo {0} khách hàng., -Customers in Queue,Khách hàng ở Queue, +Customers in Queue,Khách hàng trong hàng đợi, Customize Homepage Sections,Tùy chỉnh phần Trang chủ, Customizing Forms,Các hình thức tùy biến, Daily Project Summary for {0},Tóm tắt dự án hàng ngày cho {0}, @@ -7589,7 +7589,7 @@ Contribution (%),Đóng góp (%), Contribution to Net Total,Đóng góp cho tổng số, Selling Settings,thiết lập thông số bán hàng, Settings for Selling Module,Thiết lập module bán hàng, -Customer Naming By,đặt tên khách hàng theo, +Customer Naming By,Đặt tên khách hàng theo, Campaign Naming By,Đặt tên chiến dịch theo, Default Customer Group,Nhóm khách hàng mặc định, Default Territory,Địa bàn mặc định, @@ -7617,7 +7617,7 @@ Average Discount,Giảm giá trung bình, Customerwise Discount,Giảm giá 1 cách thông minh, Itemwise Discount,Mẫu hàng thông minh giảm giá, Customer or Item,Khách hàng hoặc mục, -Customer / Item Name,Khách hàng / tên hàng hóa, +Customer / Item Name,Khách hàng / Tên hàng hóa, Authorized Value,Giá trị được ủy quyền, Applicable To (Role),Để áp dụng (Role), Applicable To (Employee),Để áp dụng (nhân viên), @@ -7686,8 +7686,8 @@ From Currency,Từ tệ, To Currency,Tới tiền tệ, For Buying,Để mua, For Selling,Để bán, -Customer Group Name,Tên Nhóm khách hàng, -Parent Customer Group,Nhóm mẹ của nhóm khách hàng, +Customer Group Name,Tên Nhóm Khách Hàng, +Parent Customer Group,Nhóm cha của nhóm khách hàng, Only leaf nodes are allowed in transaction,Chỉ các nút lá được cho phép trong giao dịch, Mention if non-standard receivable account applicable,Đề cập đến nếu tài khoản phải thu phi tiêu chuẩn áp dụng, Credit Limits,Hạn mức tín dụng, @@ -9065,7 +9065,7 @@ Time Required (In Mins),Thời gian cần thiết (Trong phút), From Posting Date,Từ ngày đăng, To Posting Date,Đến ngày đăng, No records found,Không có dữ liệu được tìm thấy, -Customer/Lead Name,Tên khách hàng / khách hàng tiềm năng, +Customer/Lead Name,Tên khách hàng / Khách hàng tiềm năng, Unmarked Days,Ngày không đánh dấu, Jan,tháng một, Feb,Tháng hai, From 407642869a7de8f3e77b0aba89f546e3cf3afb5b Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Mon, 10 Jul 2023 12:48:47 +0530 Subject: [PATCH 59/99] ci: auto release beta version [skip ci] --- .github/workflows/initiate_release.yml | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/.github/workflows/initiate_release.yml b/.github/workflows/initiate_release.yml index ef38974ae2..ee60bad104 100644 --- a/.github/workflows/initiate_release.yml +++ b/.github/workflows/initiate_release.yml @@ -9,7 +9,7 @@ on: workflow_dispatch: jobs: - release: + stable-release: name: Release runs-on: ubuntu-latest strategy: @@ -30,3 +30,23 @@ jobs: head: version-${{ matrix.version }}-hotfix env: GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }} + + beta-release: + name: Release + runs-on: ubuntu-latest + strategy: + fail-fast: false + + steps: + - uses: octokit/request-action@v2.x + with: + route: POST /repos/{owner}/{repo}/pulls + owner: frappe + repo: erpnext + title: |- + "chore: release v15 beta" + body: "Automated beta release." + base: version-15-beta + head: develop + env: + GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }} From aeaf8fd89c362423e3fc6e9c5a80da5c7709d382 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 10 Jul 2023 13:03:10 +0530 Subject: [PATCH 60/99] fix: incorrect stock levels in the Batch --- erpnext/stock/doctype/batch/batch.js | 2 - erpnext/stock/doctype/batch/test_batch.py | 67 +++++++++++++++++++ .../serial_and_batch_bundle.py | 31 ++++++--- 3 files changed, 88 insertions(+), 12 deletions(-) diff --git a/erpnext/stock/doctype/batch/batch.js b/erpnext/stock/doctype/batch/batch.js index fa8b2bee55..3b07e4e80c 100644 --- a/erpnext/stock/doctype/batch/batch.js +++ b/erpnext/stock/doctype/batch/batch.js @@ -47,8 +47,6 @@ frappe.ui.form.on('Batch', { return; } - debugger - const section = frm.dashboard.add_section('', __("Stock Levels")); // sort by qty diff --git a/erpnext/stock/doctype/batch/test_batch.py b/erpnext/stock/doctype/batch/test_batch.py index 0e4132db8e..7fb672c0cb 100644 --- a/erpnext/stock/doctype/batch/test_batch.py +++ b/erpnext/stock/doctype/batch/test_batch.py @@ -59,6 +59,73 @@ class TestBatch(FrappeTestCase): return receipt + def test_batch_stock_levels(self, batch_qty=100): + """Test automated batch creation from Purchase Receipt""" + self.make_batch_item("ITEM-BATCH-1") + + receipt = frappe.get_doc( + dict( + doctype="Purchase Receipt", + supplier="_Test Supplier", + company="_Test Company", + items=[dict(item_code="ITEM-BATCH-1", qty=10, rate=10, warehouse="Stores - _TC")], + ) + ).insert() + receipt.submit() + + receipt.load_from_db() + batch_no = get_batch_from_bundle(receipt.items[0].serial_and_batch_bundle) + + bundle_id = ( + SerialBatchCreation( + { + "item_code": "ITEM-BATCH-1", + "warehouse": "_Test Warehouse - _TC", + "actual_qty": 20, + "voucher_type": "Purchase Receipt", + "batches": frappe._dict({batch_no: 20}), + "type_of_transaction": "Inward", + "company": receipt.company, + } + ) + .make_serial_and_batch_bundle() + .name + ) + + receipt2 = frappe.get_doc( + dict( + doctype="Purchase Receipt", + supplier="_Test Supplier", + company="_Test Company", + items=[ + dict( + item_code="ITEM-BATCH-1", + qty=20, + rate=10, + warehouse="_Test Warehouse - _TC", + serial_and_batch_bundle=bundle_id, + ) + ], + ) + ).insert() + receipt2.submit() + + receipt.load_from_db() + receipt2.load_from_db() + + self.assertTrue(receipt.items[0].serial_and_batch_bundle) + self.assertTrue(receipt2.items[0].serial_and_batch_bundle) + + batchwise_qty = frappe._dict({}) + for receipt in [receipt, receipt2]: + batch_no = get_batch_from_bundle(receipt.items[0].serial_and_batch_bundle) + key = (batch_no, receipt.items[0].warehouse) + batchwise_qty[key] = receipt.items[0].qty + + batches = get_batch_qty(batch_no) + for d in batches: + self.assertEqual(d.qty, batchwise_qty[(d.batch_no, d.warehouse)]) + def test_stock_entry_incoming(self): """Test batch creation via Stock Entry (Work Order)""" diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py index 75b6ec7ef8..0c6d33bae2 100644 --- a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py +++ b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py @@ -1272,24 +1272,29 @@ def get_reserved_batches_for_pos(kwargs): if ids: for d in get_serial_batch_ledgers(kwargs.item_code, docstatus=1, name=ids): - if d.batch_no not in pos_batches: - pos_batches[d.batch_no] = frappe._dict( + key = (d.batch_no, d.warehouse) + if key not in pos_batches: + pos_batches[key] = frappe._dict( { "qty": d.qty, "warehouse": d.warehouse, } ) else: - pos_batches[d.batch_no].qty += d.qty + pos_batches[key].qty += d.qty for row in pos_invoices: if not row.batch_no: continue - if row.batch_no in pos_batches: - pos_batches[row.batch_no] -= row.qty * -1 if row.is_return else row.qty + if kwargs.get("batch_no") and row.batch_no != kwargs.get("batch_no"): + continue + + key = (row.batch_no, row.warehouse) + if key in pos_batches: + pos_batches[key] -= row.qty * -1 if row.is_return else row.qty else: - pos_batches[row.batch_no] = frappe._dict( + pos_batches[key] = frappe._dict( { "qty": (row.qty * -1 if row.is_return else row.qty), "warehouse": row.warehouse, @@ -1309,6 +1314,7 @@ def get_auto_batch_nos(kwargs): update_available_batches(available_batches, stock_ledgers_batches, pos_invoice_batches) available_batches = list(filter(lambda x: x.qty > 0, available_batches)) + if not qty: return available_batches @@ -1351,10 +1357,11 @@ def get_qty_based_available_batches(available_batches, qty): def update_available_batches(available_batches, reserved_batches=None, pos_invoice_batches=None): for batches in [reserved_batches, pos_invoice_batches]: if batches: - for batch_no, data in batches.items(): + for key, data in batches.items(): + batch_no, warehouse = key batch_not_exists = True for batch in available_batches: - if batch.batch_no == batch_no and batch.warehouse == data.warehouse: + if batch.batch_no == batch_no and batch.warehouse == warehouse: batch.qty += data.qty batch_not_exists = False @@ -1563,7 +1570,7 @@ def get_stock_ledgers_batches(kwargs): .groupby(stock_ledger_entry.batch_no, stock_ledger_entry.warehouse) ) - for field in ["warehouse", "item_code"]: + for field in ["warehouse", "item_code", "batch_no"]: if not kwargs.get(field): continue @@ -1582,6 +1589,10 @@ def get_stock_ledgers_batches(kwargs): data = query.run(as_dict=True) batches = {} for d in data: - batches[d.batch_no] = d + key = (d.batch_no, d.warehouse) + if key not in batches: + batches[key] = d + else: + batches[key].qty += d.qty return batches From d618aaef3240d5051cacdae72f35d07b9b02dc06 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 4 Jul 2023 17:26:48 +0530 Subject: [PATCH 61/99] fix: accepted warehouse and rejected warehouse can't be same --- .../purchase_invoice/purchase_invoice.json | 3 ++- .../purchase_invoice_item.json | 3 ++- erpnext/controllers/buying_controller.py | 17 +++++++++++------ .../purchase_receipt/purchase_receipt.json | 3 ++- .../purchase_receipt/test_purchase_receipt.py | 9 +++++++++ .../purchase_receipt_item.json | 3 ++- .../subcontracting_receipt.json | 3 ++- .../subcontracting_receipt.py | 18 ++++++++++++++---- .../subcontracting_receipt_item.json | 3 ++- 9 files changed, 46 insertions(+), 16 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index e247e80253..d8759e95b8 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -549,6 +549,7 @@ "depends_on": "update_stock", "fieldname": "rejected_warehouse", "fieldtype": "Link", + "ignore_user_permissions": 1, "label": "Rejected Warehouse", "no_copy": 1, "options": "Warehouse", @@ -1576,7 +1577,7 @@ "idx": 204, "is_submittable": 1, "links": [], - "modified": "2023-06-03 16:21:54.637245", + "modified": "2023-07-04 17:22:59.145031", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice", diff --git a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json index c5187a2f46..4afc4512ff 100644 --- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json +++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json @@ -423,6 +423,7 @@ { "fieldname": "rejected_warehouse", "fieldtype": "Link", + "ignore_user_permissions": 1, "label": "Rejected Warehouse", "options": "Warehouse" }, @@ -904,7 +905,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2023-07-02 18:39:41.495723", + "modified": "2023-07-04 17:22:21.501152", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice Item", diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index fec494a84c..7b7c53ecfe 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -437,18 +437,23 @@ class BuyingController(SubcontractingController): # validate rate with ref PR def validate_rejected_warehouse(self): - for d in self.get("items"): - if flt(d.rejected_qty) and not d.rejected_warehouse: + for item in self.get("items"): + if flt(item.rejected_qty) and not item.rejected_warehouse: if self.rejected_warehouse: - d.rejected_warehouse = self.rejected_warehouse + item.rejected_warehouse = self.rejected_warehouse - if not d.rejected_warehouse: + if not item.rejected_warehouse: frappe.throw( - _("Row #{0}: Rejected Warehouse is mandatory against rejected Item {1}").format( - d.idx, d.item_code + _("Row #{0}: Rejected Warehouse is mandatory for the rejected Item {1}").format( + item.idx, item.item_code ) ) + if item.get("rejected_warehouse") and (item.get("rejected_warehouse") == item.get("warehouse")): + frappe.throw( + _("Row #{0}: Accepted Warehouse and Rejected Warehouse cannot be same").format(item.idx) + ) + # validate accepted and rejected qty def validate_accepted_rejected_qty(self): for d in self.get("items"): diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json index b41e971c8a..912b9086dd 100755 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json @@ -438,6 +438,7 @@ { "fieldname": "rejected_warehouse", "fieldtype": "Link", + "ignore_user_permissions": 1, "label": "Rejected Warehouse", "no_copy": 1, "oldfieldname": "rejected_warehouse", @@ -1240,7 +1241,7 @@ "idx": 261, "is_submittable": 1, "links": [], - "modified": "2023-06-03 16:23:20.781368", + "modified": "2023-07-04 17:23:17.025390", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt", diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index 07d6e86795..ced894634f 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -350,6 +350,15 @@ class TestPurchaseReceipt(FrappeTestCase): pr.cancel() self.assertFalse(frappe.db.get_value("Serial No", pr_row_1_serial_no, "warehouse")) + def test_rejected_warehouse_filter(self): + pr = frappe.copy_doc(test_records[0]) + pr.get("items")[0].item_code = "_Test Serialized Item With Series" + pr.get("items")[0].qty = 3 + pr.get("items")[0].rejected_qty = 2 + pr.get("items")[0].received_qty = 5 + pr.get("items")[0].rejected_warehouse = pr.get("items")[0].warehouse + self.assertRaises(frappe.ValidationError, pr.save) + def test_rejected_serial_no(self): pr = frappe.copy_doc(test_records[0]) pr.get("items")[0].item_code = "_Test Serialized Item With Series" diff --git a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json index 3929616f7c..bc5e8a0f37 100644 --- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json +++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json @@ -502,6 +502,7 @@ { "fieldname": "rejected_warehouse", "fieldtype": "Link", + "ignore_user_permissions": 1, "label": "Rejected Warehouse", "no_copy": 1, "oldfieldname": "rejected_warehouse", @@ -1058,7 +1059,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2023-07-02 18:40:48.152637", + "modified": "2023-07-04 17:22:02.830029", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt Item", diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json index 9dee3aae46..4b3cc8365c 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json @@ -251,6 +251,7 @@ "description": "Sets 'Rejected Warehouse' in each row of the Items table.", "fieldname": "rejected_warehouse", "fieldtype": "Link", + "ignore_user_permissions": 1, "label": "Rejected Warehouse", "no_copy": 1, "options": "Warehouse", @@ -630,7 +631,7 @@ "in_create": 1, "is_submittable": 1, "links": [], - "modified": "2023-06-03 16:18:39.088518", + "modified": "2023-07-06 18:43:16.171842", "modified_by": "Administrator", "module": "Subcontracting", "name": "Subcontracting Receipt", diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py index 4af38e516f..60746d95f3 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py @@ -192,13 +192,23 @@ class SubcontractingReceipt(SubcontractingController): self.total = total_amount def validate_rejected_warehouse(self): - if not self.rejected_warehouse: - for item in self.items: - if item.rejected_qty: + for item in self.items: + if flt(item.rejected_qty) and not item.rejected_warehouse: + if self.rejected_warehouse: + item.rejected_warehouse = self.rejected_warehouse + + if not item.rejected_warehouse: frappe.throw( - _("Rejected Warehouse is mandatory against rejected Item {0}").format(item.item_code) + _("Row #{0}: Rejected Warehouse is mandatory for the rejected Item {1}").format( + item.idx, item.item_code + ) ) + if item.get("rejected_warehouse") and (item.get("rejected_warehouse") == item.get("warehouse")): + frappe.throw( + _("Row #{0}: Accepted Warehouse and Rejected Warehouse cannot be same").format(item.idx) + ) + def validate_available_qty_for_consumption(self): for item in self.get("supplied_items"): precision = item.precision("consumed_qty") diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json b/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json index d550b75839..d72878061c 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json +++ b/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json @@ -254,6 +254,7 @@ "depends_on": "eval: !parent.is_return", "fieldname": "rejected_warehouse", "fieldtype": "Link", + "ignore_user_permissions": 1, "label": "Rejected Warehouse", "no_copy": 1, "options": "Warehouse", @@ -494,7 +495,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2023-03-12 14:00:41.418681", + "modified": "2023-07-06 18:43:45.599761", "modified_by": "Administrator", "module": "Subcontracting", "name": "Subcontracting Receipt Item", From 281607678954872b83c2ac19f4bdd3f4855bb32a Mon Sep 17 00:00:00 2001 From: MohsinAli Date: Mon, 10 Jul 2023 14:09:41 +0530 Subject: [PATCH 62/99] 1052, "Column 'qty' in field list is ambiguous in work_order.py --- erpnext/manufacturing/doctype/work_order/work_order.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index 79b1e798ed..7c15bf9234 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -1026,7 +1026,7 @@ class WorkOrder(Document): consumed_qty = frappe.db.sql( """ SELECT - SUM(qty) + SUM(detail.qty) FROM `tabStock Entry` entry, `tabStock Entry Detail` detail From b3a99e38cc1cd7319ab91a860e83d10dbc91164d Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 10 Jul 2023 14:31:19 +0530 Subject: [PATCH 63/99] chore: add asset depr posting error in error log (backport #36052) (#36055) chore: add asset depr posting error in error log (#36052) (cherry picked from commit 0f9a6ee70acc88a1190fc66fb95a3bd5d6dea02f) Co-authored-by: Anand Baburajan --- erpnext/assets/doctype/asset/depreciation.py | 29 ++++++++++++-------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/erpnext/assets/doctype/asset/depreciation.py b/erpnext/assets/doctype/asset/depreciation.py index 259568a24b..e1431eae17 100644 --- a/erpnext/assets/doctype/asset/depreciation.py +++ b/erpnext/assets/doctype/asset/depreciation.py @@ -40,6 +40,7 @@ def post_depreciation_entries(date=None): date = today() failed_asset_names = [] + error_log_names = [] for asset_name in get_depreciable_assets(date): asset_doc = frappe.get_doc("Asset", asset_name) @@ -50,10 +51,12 @@ def post_depreciation_entries(date=None): except Exception as e: frappe.db.rollback() failed_asset_names.append(asset_name) + error_log = frappe.log_error(e) + error_log_names.append(error_log.name) if failed_asset_names: set_depr_entry_posting_status_for_failed_assets(failed_asset_names) - notify_depr_entry_posting_error(failed_asset_names) + notify_depr_entry_posting_error(failed_asset_names, error_log_names) frappe.db.commit() @@ -239,7 +242,7 @@ def set_depr_entry_posting_status_for_failed_assets(failed_asset_names): frappe.db.set_value("Asset", asset_name, "depr_entry_posting_status", "Failed") -def notify_depr_entry_posting_error(failed_asset_names): +def notify_depr_entry_posting_error(failed_asset_names, error_log_names): recipients = get_users_with_role("Accounts Manager") if not recipients: @@ -247,7 +250,8 @@ def notify_depr_entry_posting_error(failed_asset_names): subject = _("Error while posting depreciation entries") - asset_links = get_comma_separated_asset_links(failed_asset_names) + asset_links = get_comma_separated_links(failed_asset_names, "Asset") + error_log_links = get_comma_separated_links(error_log_names, "Error Log") message = ( _("Hello,") @@ -257,23 +261,26 @@ def notify_depr_entry_posting_error(failed_asset_names): ) + "." + "

" - + _( - "Please raise a support ticket and share this email, or forward this email to your development team so that they can find the issue in the developer console by manually creating the depreciation entry via the asset's depreciation schedule table." + + _("Here are the error logs for the aforementioned failed depreciation entries: {0}").format( + error_log_links ) + + "." + + "

" + + _("Please share this email with your support team so that they can find and fix the issue.") ) frappe.sendmail(recipients=recipients, subject=subject, message=message) -def get_comma_separated_asset_links(asset_names): - asset_links = [] +def get_comma_separated_links(names, doctype): + links = [] - for asset_name in asset_names: - asset_links.append(get_link_to_form("Asset", asset_name)) + for name in names: + links.append(get_link_to_form(doctype, name)) - asset_links = ", ".join(asset_links) + links = ", ".join(links) - return asset_links + return links @frappe.whitelist() From 5c820ecc20f261b26d02b94e3778c331bb7ec0be Mon Sep 17 00:00:00 2001 From: Dany Robert Date: Mon, 10 Jul 2023 16:20:45 +0530 Subject: [PATCH 64/99] fix: precision causing outstanding issue on partly paid invoices (#36030) * fix: precision causing outstanding issue on partly paid invoices * chore: linters --- erpnext/accounts/doctype/payment_entry/payment_entry.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 65ed4669b1..699447bb7f 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -237,10 +237,9 @@ class PaymentEntry(AccountsController): _("{0} {1} has already been fully paid.").format(_(d.reference_doctype), d.reference_name) ) # The reference has already been partly paid - elif ( - latest.outstanding_amount < latest.invoice_amount - and flt(d.outstanding_amount, d.precision("outstanding_amount")) != latest.outstanding_amount - ): + elif latest.outstanding_amount < latest.invoice_amount and flt( + d.outstanding_amount, d.precision("outstanding_amount") + ) != flt(latest.outstanding_amount, d.precision("outstanding_amount")): frappe.throw( _( "{0} {1} has already been partly paid. Please use the 'Get Outstanding Invoice' or the 'Get Outstanding Orders' button to get the latest outstanding amounts." From 6a10ae662ce102dd4014a85a2dd6a51a87692652 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 10 Jul 2023 17:06:02 +0530 Subject: [PATCH 65/99] fix: Delivery Note return valuation --- .../controllers/sales_and_purchase_return.py | 6 +++- .../delivery_note/test_delivery_note.py | 31 +++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py index 954668055e..173e812dbd 100644 --- a/erpnext/controllers/sales_and_purchase_return.py +++ b/erpnext/controllers/sales_and_purchase_return.py @@ -669,7 +669,11 @@ def get_filters( if reference_voucher_detail_no: filters["voucher_detail_no"] = reference_voucher_detail_no - if item_row and item_row.get("warehouse"): + if ( + voucher_type in ["Purchase Receipt", "Purchase Invoice"] + and item_row + and item_row.get("warehouse") + ): filters["warehouse"] = item_row.get("warehouse") return filters diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py index 8baae8a19c..0ef3027bce 100644 --- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py @@ -318,6 +318,37 @@ class TestDeliveryNote(FrappeTestCase): self.assertEqual(dn.per_returned, 100) self.assertEqual(dn.status, "Return Issued") + def test_delivery_note_return_valuation_on_different_warehuose(self): + from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse + + company = frappe.db.get_value("Warehouse", "Stores - TCP1", "company") + item_code = "Test Return Valuation For DN" + make_item("Test Return Valuation For DN", {"is_stock_item": 1}) + return_warehouse = create_warehouse("Returned Test Warehouse", company=company) + + make_stock_entry(item_code=item_code, target="Stores - TCP1", qty=5, basic_rate=150) + + dn = create_delivery_note( + item_code=item_code, + qty=5, + rate=500, + warehouse="Stores - TCP1", + company=company, + expense_account="Cost of Goods Sold - TCP1", + cost_center="Main - TCP1", + ) + + dn.submit() + self.assertEqual(dn.items[0].incoming_rate, 150) + + from erpnext.controllers.sales_and_purchase_return import make_return_doc + + return_dn = make_return_doc(dn.doctype, dn.name) + return_dn.items[0].warehouse = return_warehouse + return_dn.save().submit() + + self.assertEqual(return_dn.items[0].incoming_rate, 150) + def test_return_single_item_from_bundled_items(self): company = frappe.db.get_value("Warehouse", "Stores - TCP1", "company") From 49c61e7ebb40c5e812600757584d908313fdf5a8 Mon Sep 17 00:00:00 2001 From: Wolfram Schmidt Date: Mon, 10 Jul 2023 14:33:50 +0200 Subject: [PATCH 66/99] fix: Add company filter in list view (#36047) fix: Add company filter in list view --- .../doctype/item_tax_template/item_tax_template.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/item_tax_template/item_tax_template.json b/erpnext/accounts/doctype/item_tax_template/item_tax_template.json index b42d712d88..87f0ad1048 100644 --- a/erpnext/accounts/doctype/item_tax_template/item_tax_template.json +++ b/erpnext/accounts/doctype/item_tax_template/item_tax_template.json @@ -35,6 +35,7 @@ { "fieldname": "company", "fieldtype": "Link", + "in_filter": 1, "in_list_view": 1, "label": "Company", "options": "Company", @@ -56,7 +57,7 @@ } ], "links": [], - "modified": "2022-01-18 21:11:23.105589", + "modified": "2023-07-09 18:11:23.105589", "modified_by": "Administrator", "module": "Accounts", "name": "Item Tax Template", @@ -102,4 +103,4 @@ "states": [], "title_field": "title", "track_changes": 1 -} \ No newline at end of file +} From 361a35708852d2b8dda2a8dfcb33119ff743b94d Mon Sep 17 00:00:00 2001 From: Dany Robert Date: Mon, 10 Jul 2023 19:32:59 +0530 Subject: [PATCH 67/99] fix: payment entry `voucher_type` error (#35779) * fix: payment entry `voucher_type` error * chore: linters --- .../doctype/payment_entry/payment_entry.py | 96 ++++++++++--------- 1 file changed, 50 insertions(+), 46 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 699447bb7f..dcd7295bae 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -1432,6 +1432,9 @@ def get_outstanding_reference_documents(args, validate=False): if args.get("party_type") == "Member": return + if not args.get("get_outstanding_invoices") and not args.get("get_orders_to_be_billed"): + args["get_outstanding_invoices"] = True + ple = qb.DocType("Payment Ledger Entry") common_filter = [] accounting_dimensions_filter = [] @@ -1626,60 +1629,59 @@ def get_orders_to_be_billed( cost_center=None, filters=None, ): + voucher_type = None if party_type == "Customer": voucher_type = "Sales Order" elif party_type == "Supplier": voucher_type = "Purchase Order" - elif party_type == "Employee": - voucher_type = None + + if not voucher_type: + return [] # Add cost center condition - if voucher_type: - doc = frappe.get_doc({"doctype": voucher_type}) - condition = "" - if doc and hasattr(doc, "cost_center") and doc.cost_center: - condition = " and cost_center='%s'" % cost_center + doc = frappe.get_doc({"doctype": voucher_type}) + condition = "" + if doc and hasattr(doc, "cost_center") and doc.cost_center: + condition = " and cost_center='%s'" % cost_center - orders = [] - if voucher_type: - if party_account_currency == company_currency: - grand_total_field = "base_grand_total" - rounded_total_field = "base_rounded_total" - else: - grand_total_field = "grand_total" - rounded_total_field = "rounded_total" + if party_account_currency == company_currency: + grand_total_field = "base_grand_total" + rounded_total_field = "base_rounded_total" + else: + grand_total_field = "grand_total" + rounded_total_field = "rounded_total" - orders = frappe.db.sql( - """ - select - name as voucher_no, - if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) as invoice_amount, - (if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) - advance_paid) as outstanding_amount, - transaction_date as posting_date - from - `tab{voucher_type}` - where - {party_type} = %s - and docstatus = 1 - and company = %s - and ifnull(status, "") != "Closed" - and if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) > advance_paid - and abs(100 - per_billed) > 0.01 - {condition} - order by - transaction_date, name - """.format( - **{ - "rounded_total_field": rounded_total_field, - "grand_total_field": grand_total_field, - "voucher_type": voucher_type, - "party_type": scrub(party_type), - "condition": condition, - } - ), - (party, company), - as_dict=True, - ) + orders = frappe.db.sql( + """ + select + name as voucher_no, + if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) as invoice_amount, + (if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) - advance_paid) as outstanding_amount, + transaction_date as posting_date + from + `tab{voucher_type}` + where + {party_type} = %s + and docstatus = 1 + and company = %s + and ifnull(status, "") != "Closed" + and if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) > advance_paid + and abs(100 - per_billed) > 0.01 + {condition} + order by + transaction_date, name + """.format( + **{ + "rounded_total_field": rounded_total_field, + "grand_total_field": grand_total_field, + "voucher_type": voucher_type, + "party_type": scrub(party_type), + "condition": condition, + } + ), + (party, company), + as_dict=True, + ) order_list = [] for d in orders: @@ -1712,6 +1714,8 @@ def get_negative_outstanding_invoices( cost_center=None, condition=None, ): + if party_type not in ["Customer", "Supplier"]: + return [] voucher_type = "Sales Invoice" if party_type == "Customer" else "Purchase Invoice" account = "debit_to" if voucher_type == "Sales Invoice" else "credit_to" supplier_condition = "" From 872a23c77d6fed002be10bbcc476e75ad7ba63e2 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 10 Jul 2023 20:34:54 +0530 Subject: [PATCH 68/99] fix: also check on_hold (#35910) fix: also check on_hold (#35910) (cherry picked from commit 5aa02b8571efdc1710a9b3069a18a80ed17856a2) Co-authored-by: RJPvT <48353029+RJPvT@users.noreply.github.com> --- erpnext/accounts/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 31473db675..8b44b22e3d 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -850,7 +850,7 @@ def get_held_invoices(party_type, party): if party_type == "Supplier": held_invoices = frappe.db.sql( - "select name from `tabPurchase Invoice` where release_date IS NOT NULL and release_date > CURDATE()", + "select name from `tabPurchase Invoice` where on_hold = 1 and release_date IS NOT NULL and release_date > CURDATE()", as_dict=1, ) held_invoices = set(d["name"] for d in held_invoices) From bf84e0d441809d14ffa1c6e8d1bdedd63a43095c Mon Sep 17 00:00:00 2001 From: Raffael Meyer <14891507+barredterra@users.noreply.github.com> Date: Mon, 10 Jul 2023 17:52:06 +0200 Subject: [PATCH 69/99] refactor: remove frappe.dynamic_link (#35096) --- erpnext/accounts/doctype/bank/bank.js | 3 --- erpnext/accounts/doctype/shareholder/shareholder.js | 2 -- erpnext/buying/doctype/supplier/supplier.js | 2 -- erpnext/crm/doctype/lead/lead.js | 5 ----- erpnext/crm/doctype/prospect/prospect.js | 2 -- erpnext/selling/doctype/customer/customer.js | 2 -- erpnext/setup/doctype/company/company.js | 2 -- erpnext/setup/doctype/sales_partner/sales_partner.js | 2 -- erpnext/stock/doctype/manufacturer/manufacturer.js | 1 - erpnext/stock/doctype/warehouse/warehouse.js | 6 ------ 10 files changed, 27 deletions(-) diff --git a/erpnext/accounts/doctype/bank/bank.js b/erpnext/accounts/doctype/bank/bank.js index 35d606ba3a..6667193a54 100644 --- a/erpnext/accounts/doctype/bank/bank.js +++ b/erpnext/accounts/doctype/bank/bank.js @@ -8,9 +8,6 @@ frappe.ui.form.on('Bank', { }, refresh: function(frm) { add_fields_to_mapping_table(frm); - - frappe.dynamic_link = { doc: frm.doc, fieldname: 'name', doctype: 'Bank' }; - frm.toggle_display(['address_html','contact_html'], !frm.doc.__islocal); if (frm.doc.__islocal) { diff --git a/erpnext/accounts/doctype/shareholder/shareholder.js b/erpnext/accounts/doctype/shareholder/shareholder.js index c6f101e7f3..544d417a0e 100644 --- a/erpnext/accounts/doctype/shareholder/shareholder.js +++ b/erpnext/accounts/doctype/shareholder/shareholder.js @@ -3,8 +3,6 @@ frappe.ui.form.on('Shareholder', { refresh: function(frm) { - frappe.dynamic_link = { doc: frm.doc, fieldname: 'name', doctype: 'Shareholder' }; - frm.toggle_display(['contact_html'], !frm.doc.__islocal); if (frm.doc.__islocal) { diff --git a/erpnext/buying/doctype/supplier/supplier.js b/erpnext/buying/doctype/supplier/supplier.js index 5b95d0fde3..372ca56b86 100644 --- a/erpnext/buying/doctype/supplier/supplier.js +++ b/erpnext/buying/doctype/supplier/supplier.js @@ -66,8 +66,6 @@ frappe.ui.form.on("Supplier", { }, refresh: function (frm) { - frappe.dynamic_link = { doc: frm.doc, fieldname: 'name', doctype: 'Supplier' } - if (frappe.defaults.get_default("supp_master_name") != "Naming Series") { frm.toggle_display("naming_series", false); } else { diff --git a/erpnext/crm/doctype/lead/lead.js b/erpnext/crm/doctype/lead/lead.js index b98a27ede8..9ac54183a2 100644 --- a/erpnext/crm/doctype/lead/lead.js +++ b/erpnext/crm/doctype/lead/lead.js @@ -30,11 +30,6 @@ erpnext.LeadController = class LeadController extends frappe.ui.form.Controller var me = this; let doc = this.frm.doc; erpnext.toggle_naming_series(); - frappe.dynamic_link = { - doc: doc, - fieldname: 'name', - doctype: 'Lead' - }; if (!this.frm.is_new() && doc.__onload && !doc.__onload.is_customer) { this.frm.add_custom_button(__("Customer"), this.make_customer, __("Create")); diff --git a/erpnext/crm/doctype/prospect/prospect.js b/erpnext/crm/doctype/prospect/prospect.js index 495ed291ae..c1a7ff576c 100644 --- a/erpnext/crm/doctype/prospect/prospect.js +++ b/erpnext/crm/doctype/prospect/prospect.js @@ -3,8 +3,6 @@ frappe.ui.form.on('Prospect', { refresh (frm) { - frappe.dynamic_link = { doc: frm.doc, fieldname: "name", doctype: frm.doctype }; - if (!frm.is_new() && frappe.boot.user.can_create.includes("Customer")) { frm.add_custom_button(__("Customer"), function() { frappe.model.open_mapped_doc({ diff --git a/erpnext/selling/doctype/customer/customer.js b/erpnext/selling/doctype/customer/customer.js index 540e767d32..60f0941559 100644 --- a/erpnext/selling/doctype/customer/customer.js +++ b/erpnext/selling/doctype/customer/customer.js @@ -131,8 +131,6 @@ frappe.ui.form.on("Customer", { erpnext.toggle_naming_series(); } - frappe.dynamic_link = {doc: frm.doc, fieldname: 'name', doctype: 'Customer'} - if(!frm.doc.__islocal) { frappe.contacts.render_address_and_contact(frm); diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js index 333538722e..f4682c1b80 100644 --- a/erpnext/setup/doctype/company/company.js +++ b/erpnext/setup/doctype/company/company.js @@ -81,8 +81,6 @@ frappe.ui.form.on("Company", { disbale_coa_fields(frm); frappe.contacts.render_address_and_contact(frm); - frappe.dynamic_link = {doc: frm.doc, fieldname: 'name', doctype: 'Company'} - if (frappe.perm.has_perm("Cost Center", 0, 'read')) { frm.add_custom_button(__('Cost Centers'), function() { frappe.set_route('Tree', 'Cost Center', {'company': frm.doc.name}); diff --git a/erpnext/setup/doctype/sales_partner/sales_partner.js b/erpnext/setup/doctype/sales_partner/sales_partner.js index 5656d43e85..f9e3770560 100644 --- a/erpnext/setup/doctype/sales_partner/sales_partner.js +++ b/erpnext/setup/doctype/sales_partner/sales_partner.js @@ -3,8 +3,6 @@ frappe.ui.form.on('Sales Partner', { refresh: function(frm) { - frappe.dynamic_link = {doc: frm.doc, fieldname: 'name', doctype: 'Sales Partner'} - if(frm.doc.__islocal){ hide_field(['address_html', 'contact_html', 'address_contacts']); frappe.contacts.clear_address_and_contact(frm); diff --git a/erpnext/stock/doctype/manufacturer/manufacturer.js b/erpnext/stock/doctype/manufacturer/manufacturer.js index bb7e314e14..5b4990f08b 100644 --- a/erpnext/stock/doctype/manufacturer/manufacturer.js +++ b/erpnext/stock/doctype/manufacturer/manufacturer.js @@ -3,7 +3,6 @@ frappe.ui.form.on('Manufacturer', { refresh: function(frm) { - frappe.dynamic_link = { doc: frm.doc, fieldname: 'name', doctype: 'Manufacturer' }; if (frm.doc.__islocal) { hide_field(['address_html','contact_html']); frappe.contacts.clear_address_and_contact(frm); diff --git a/erpnext/stock/doctype/warehouse/warehouse.js b/erpnext/stock/doctype/warehouse/warehouse.js index 746a1cbaf1..3819c0b24a 100644 --- a/erpnext/stock/doctype/warehouse/warehouse.js +++ b/erpnext/stock/doctype/warehouse/warehouse.js @@ -83,12 +83,6 @@ frappe.ui.form.on("Warehouse", { } frm.toggle_enable(["is_group", "company"], false); - - frappe.dynamic_link = { - doc: frm.doc, - fieldname: "name", - doctype: "Warehouse", - }; }, }); From 176966daabea4fe59a85858583f47ff5bd6ef38f Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 11 Jul 2023 10:04:17 +0530 Subject: [PATCH 70/99] fix: possible type error on ERR creation --- .../exchange_rate_revaluation/exchange_rate_revaluation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py index 598db642f3..89f827a485 100644 --- a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py +++ b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py @@ -252,8 +252,8 @@ class ExchangeRateRevaluation(Document): new_balance_in_base_currency = 0 new_balance_in_account_currency = 0 - current_exchange_rate = calculate_exchange_rate_using_last_gle( - company, d.account, d.party_type, d.party + current_exchange_rate = ( + calculate_exchange_rate_using_last_gle(company, d.account, d.party_type, d.party) or 0.0 ) gain_loss = new_balance_in_account_currency - ( From ce9164ec69f6cbc67edae644dd0e0e17ff39af74 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 11 Jul 2023 12:03:38 +0530 Subject: [PATCH 71/99] fix: Validate for missing expense account (#36078) * fix: Validate for missing expense account * fix: Validate for missing expense account --- erpnext/controllers/stock_controller.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 5137e03058..caf4b6f18b 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -201,6 +201,12 @@ class StockController(AccountsController): warehouse_asset_account = warehouse_account[item_row.get("warehouse")]["account"] expense_account = frappe.get_cached_value("Company", self.company, "default_expense_account") + if not expense_account: + frappe.throw( + _( + "Please set default cost of goods sold account in company {0} for booking rounding gain and loss during stock transfer" + ).format(frappe.bold(self.company)) + ) gl_list.append( self.get_gl_dict( From be5881280fae54c1de9d0cbb4f90d54f958aa7ed Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Tue, 11 Jul 2023 17:09:23 +0530 Subject: [PATCH 72/99] fix: incorrect status in MR created from PP (#36085) --- .../manufacturing/doctype/production_plan/production_plan.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index 6dc1ff6a49..5f957a5442 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -697,10 +697,9 @@ class ProductionPlan(Document): material_request.flags.ignore_permissions = 1 material_request.run_method("set_missing_values") + material_request.save() if self.get("submit_material_request"): material_request.submit() - else: - material_request.save() frappe.flags.mute_messages = False From c16a5814d41610136ce00e5aca4269ea3d308971 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 11 Jul 2023 17:51:27 +0530 Subject: [PATCH 73/99] fix: circular dependency during reposting causing timeout error --- .../purchase_receipt/test_purchase_receipt.py | 32 +++++++++++++++++++ erpnext/stock/stock_ledger.py | 20 ++++++++++-- 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index 07d6e86795..8a38614c0a 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -1956,6 +1956,32 @@ class TestPurchaseReceipt(FrappeTestCase): ste5.reload() self.assertEqual(ste5.items[0].valuation_rate, 275.00) + ste6 = make_stock_entry( + purpose="Material Transfer", + posting_date=add_days(today(), -3), + source=warehouse1, + target=warehouse, + item_code=item_code, + qty=20, + company=pr.company, + ) + + ste6.reload() + self.assertEqual(ste6.items[0].valuation_rate, 275.00) + + ste7 = make_stock_entry( + purpose="Material Transfer", + posting_date=add_days(today(), -3), + source=warehouse, + target=warehouse1, + item_code=item_code, + qty=20, + company=pr.company, + ) + + ste7.reload() + self.assertEqual(ste7.items[0].valuation_rate, 275.00) + create_landed_cost_voucher("Purchase Receipt", pr.name, pr.company, charges=2500 * -1) pr.reload() @@ -1976,6 +2002,12 @@ class TestPurchaseReceipt(FrappeTestCase): ste5.reload() self.assertEqual(ste5.items[0].valuation_rate, valuation_rate) + ste6.reload() + self.assertEqual(ste6.items[0].valuation_rate, valuation_rate) + + ste7.reload() + self.assertEqual(ste7.items[0].valuation_rate, valuation_rate) + def prepare_data_for_internal_transfer(): from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 7b1eae5545..5abb8e827f 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -645,7 +645,7 @@ class update_entries_after(object): def update_distinct_item_warehouses(self, dependant_sle): key = (dependant_sle.item_code, dependant_sle.warehouse) - val = frappe._dict({"sle": dependant_sle}) + val = frappe._dict({"sle": dependant_sle, "dependent_voucher_detail_nos": []}) if key not in self.distinct_item_warehouses: self.distinct_item_warehouses[key] = val @@ -654,13 +654,26 @@ class update_entries_after(object): existing_sle_posting_date = ( self.distinct_item_warehouses[key].get("sle", {}).get("posting_date") ) + + dependent_voucher_detail_nos = self.get_dependent_voucher_detail_nos(key) + if getdate(dependant_sle.posting_date) < getdate(existing_sle_posting_date): val.sle_changed = True self.distinct_item_warehouses[key] = val self.new_items_found = True - elif self.distinct_item_warehouses[key].get("reposting_status"): - self.distinct_item_warehouses[key] = val + elif dependant_sle.voucher_detail_no not in set(dependent_voucher_detail_nos): + # Future dependent voucher needs to be repost to get the correct stock value + # If dependent voucher has not reposted, then add it to the list + dependent_voucher_detail_nos.append(dependant_sle.voucher_detail_no) self.new_items_found = True + val.dependent_voucher_detail_nos = dependent_voucher_detail_nos + self.distinct_item_warehouses[key] = val + + def get_dependent_voucher_detail_nos(self, key): + if "dependent_voucher_detail_nos" not in self.distinct_item_warehouses[key]: + self.distinct_item_warehouses[key].dependent_voucher_detail_nos = [] + + return self.distinct_item_warehouses[key].dependent_voucher_detail_nos def process_sle(self, sle): # previous sle data for this warehouse @@ -1370,6 +1383,7 @@ def get_sle_by_voucher_detail_no(voucher_detail_no, excluded_sle=None): "qty_after_transaction", "posting_date", "posting_time", + "voucher_detail_no", "timestamp(posting_date, posting_time) as timestamp", ], as_dict=1, From 7e4b6683e6a51777530701e390f8c98589950160 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Tue, 11 Jul 2023 18:19:29 +0530 Subject: [PATCH 74/99] fix: Dont bold URL parts closes https://github.com/frappe/frappe/issues/21445 --- erpnext/stock/doctype/item/item.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index 93d799a395..ef4155e48a 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -773,7 +773,7 @@ class Item(Document): rows = "" for docname, attr_list in not_included.items(): - link = "{0}".format(frappe.bold(_(docname))) + link = f"{frappe.bold(docname)}" rows += table_row(link, body(attr_list)) error_description = _( From 85802870921069889a92a764f80d7ba874a8a339 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 12 Jul 2023 08:26:49 +0530 Subject: [PATCH 75/99] fix: allow manual asset receipt mov from nowhere (backport #36093) (#36094) fix: allow manual asset receipt mov from nowhere (#36093) (cherry picked from commit 4aaa1a15d7dfe9ad81d3cc1f000dac3e324cfa6f) Co-authored-by: Anand Baburajan --- .../doctype/asset_movement/asset_movement.js | 2 +- .../doctype/asset_movement/asset_movement.py | 31 +++++++------------ 2 files changed, 12 insertions(+), 21 deletions(-) diff --git a/erpnext/assets/doctype/asset_movement/asset_movement.js b/erpnext/assets/doctype/asset_movement/asset_movement.js index f9c600731b..4ccc3f8013 100644 --- a/erpnext/assets/doctype/asset_movement/asset_movement.js +++ b/erpnext/assets/doctype/asset_movement/asset_movement.js @@ -63,7 +63,7 @@ frappe.ui.form.on('Asset Movement', { fieldnames_to_be_altered = { target_location: { read_only: 0, reqd: 1 }, source_location: { read_only: 1, reqd: 0 }, - from_employee: { read_only: 0, reqd: 1 }, + from_employee: { read_only: 0, reqd: 0 }, to_employee: { read_only: 1, reqd: 0 } }; } diff --git a/erpnext/assets/doctype/asset_movement/asset_movement.py b/erpnext/assets/doctype/asset_movement/asset_movement.py index b58ca10482..22055dcb73 100644 --- a/erpnext/assets/doctype/asset_movement/asset_movement.py +++ b/erpnext/assets/doctype/asset_movement/asset_movement.py @@ -62,29 +62,20 @@ class AssetMovement(Document): frappe.throw(_("Source and Target Location cannot be same")) if self.purpose == "Receipt": - # only when asset is bought and first entry is made - if not d.source_location and not (d.target_location or d.to_employee): + if not (d.source_location or d.from_employee) and not (d.target_location or d.to_employee): frappe.throw( _("Target Location or To Employee is required while receiving Asset {0}").format(d.asset) ) - elif d.source_location: - # when asset is received from an employee - if d.target_location and not d.from_employee: - frappe.throw( - _("From employee is required while receiving Asset {0} to a target location").format( - d.asset - ) - ) - if d.from_employee and not d.target_location: - frappe.throw( - _("Target Location is required while receiving Asset {0} from an employee").format(d.asset) - ) - if d.to_employee and d.target_location: - frappe.throw( - _( - "Asset {0} cannot be received at a location and given to employee in a single movement" - ).format(d.asset) - ) + elif d.from_employee and not d.target_location: + frappe.throw( + _("Target Location is required while receiving Asset {0} from an employee").format(d.asset) + ) + elif d.to_employee and d.target_location: + frappe.throw( + _( + "Asset {0} cannot be received at a location and given to an employee in a single movement" + ).format(d.asset) + ) def validate_employee(self): for d in self.assets: From 0340bfc90da1bc4567ee01db03d3ef89669c007a Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Wed, 12 Jul 2023 12:17:20 +0530 Subject: [PATCH 76/99] ci: regen release notes with GH API (#36098) [skip ci] --- .github/workflows/release_notes.yml | 38 +++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 .github/workflows/release_notes.yml diff --git a/.github/workflows/release_notes.yml b/.github/workflows/release_notes.yml new file mode 100644 index 0000000000..91139dea65 --- /dev/null +++ b/.github/workflows/release_notes.yml @@ -0,0 +1,38 @@ +# This action: +# +# 1. Generates release notes using github API. +# 2. Strips unnecessary info like chore/style etc from notes. +# 3. Updates release info. + +# This action needs to be maintained on all branches that do releases. + +name: 'Release Notes' + +on: + workflow_dispatch: + inputs: + tag_name: + description: 'Tag of release like v13.0.0' + required: true + type: string + release: + types: [released] + +permissions: + contents: read + +jobs: + regen-notes: + name: 'Regenerate release notes' + runs-on: ubuntu-latest + + steps: + - name: Update notes + run: | + NEW_NOTES=$(gh api --method POST -H "Accept: application/vnd.github+json" /repos/frappe/frappe/releases/generate-notes -f tag_name=$RELEASE_TAG | jq -r '.body' | sed -E '/^\* (chore|ci|test|docs|style)/d' ) + RELEASE_ID=$(gh api -H "Accept: application/vnd.github+json" /repos/frappe/frappe/releases/tags/$RELEASE_TAG | jq -r '.id') + gh api --method PATCH -H "Accept: application/vnd.github+json" /repos/frappe/frappe/releases/$RELEASE_ID -f body="$NEW_NOTES" + + env: + GH_TOKEN: ${{ secrets.RELEASE_TOKEN }} + RELEASE_TAG: ${{ github.event.inputs.tag_name || github.event.release.tag_name }} From 596a14e34fd58df0c2377a93ad56b21e70edbd3f Mon Sep 17 00:00:00 2001 From: Gursheen Kaur Anand <40693548+GursheenK@users.noreply.github.com> Date: Wed, 12 Jul 2023 15:49:17 +0530 Subject: [PATCH 77/99] feat: add project filter in reports importing financial statements js file (#36097) feat: add project filter in financial statements js file --- .../gross_and_net_profit_report.js | 8 -------- .../profit_and_loss_statement.js | 10 ---------- erpnext/public/js/financial_statements.js | 10 ++++++++++ 3 files changed, 10 insertions(+), 18 deletions(-) diff --git a/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.js b/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.js index 8dc5ab36dd..92cf36ebc5 100644 --- a/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.js +++ b/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.js @@ -12,14 +12,6 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { erpnext.financial_statements); frappe.query_reports["Gross and Net Profit Report"]["filters"].push( - { - "fieldname": "project", - "label": __("Project"), - "fieldtype": "MultiSelectList", - get_data: function(txt) { - return frappe.db.get_link_options('Project', txt); - } - }, { "fieldname": "accumulated_values", "label": __("Accumulated Values"), diff --git a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js index 298d83894c..e794f270c2 100644 --- a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js +++ b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js @@ -9,16 +9,6 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { erpnext.utils.add_dimensions('Profit and Loss Statement', 10); frappe.query_reports["Profit and Loss Statement"]["filters"].push( - { - "fieldname": "project", - "label": __("Project"), - "fieldtype": "MultiSelectList", - get_data: function(txt) { - return frappe.db.get_link_options('Project', txt, { - company: frappe.query_report.get_filter_value("company") - }); - }, - }, { "fieldname": "include_default_book_entries", "label": __("Include Default Book Entries"), diff --git a/erpnext/public/js/financial_statements.js b/erpnext/public/js/financial_statements.js index b0082bdb28..2b50a75e72 100644 --- a/erpnext/public/js/financial_statements.js +++ b/erpnext/public/js/financial_statements.js @@ -182,6 +182,16 @@ function get_filters() { company: frappe.query_report.get_filter_value("company") }); } + }, + { + "fieldname": "project", + "label": __("Project"), + "fieldtype": "MultiSelectList", + get_data: function(txt) { + return frappe.db.get_link_options('Project', txt, { + company: frappe.query_report.get_filter_value("company") + }); + }, } ] From 5f307f92e0f9f90c79cfac802c5aa05de3a9d6d8 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Thu, 13 Jul 2023 05:44:58 +0530 Subject: [PATCH 78/99] refactor: `Batch Item Expiry Status` report (#36106) --- .../batch_item_expiry_status.js | 16 +- .../batch_item_expiry_status.py | 145 +++++++----------- 2 files changed, 74 insertions(+), 87 deletions(-) diff --git a/erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.js b/erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.js index a7d7149c38..48a72a2bfe 100644 --- a/erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.js +++ b/erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.js @@ -9,13 +9,27 @@ frappe.query_reports["Batch Item Expiry Status"] = { "fieldtype": "Date", "width": "80", "default": frappe.sys_defaults.year_start_date, + "reqd": 1, }, { "fieldname":"to_date", "label": __("To Date"), "fieldtype": "Date", "width": "80", - "default": frappe.datetime.get_today() + "default": frappe.datetime.get_today(), + "reqd": 1, + }, + { + "fieldname":"item", + "label": __("Item"), + "fieldtype": "Link", + "options": "Item", + "width": "100", + "get_query": function () { + return { + filters: {"has_batch_no": 1} + } + } } ] } diff --git a/erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.py b/erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.py index ef7d6e6816..5661e8b260 100644 --- a/erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.py +++ b/erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.py @@ -4,113 +4,86 @@ import frappe from frappe import _ -from frappe.query_builder.functions import IfNull -from frappe.utils import cint, getdate +from frappe.query_builder.functions import Date def execute(filters=None): - if not filters: - filters = {} + validate_filters(filters) - float_precision = cint(frappe.db.get_default("float_precision")) or 3 - - columns = get_columns(filters) - item_map = get_item_details(filters) - iwb_map = get_item_warehouse_batch_map(filters, float_precision) - - data = [] - for item in sorted(iwb_map): - for wh in sorted(iwb_map[item]): - for batch in sorted(iwb_map[item][wh]): - qty_dict = iwb_map[item][wh][batch] - - data.append( - [ - item, - item_map[item]["item_name"], - item_map[item]["description"], - wh, - batch, - frappe.db.get_value("Batch", batch, "expiry_date"), - qty_dict.expiry_status, - ] - ) + columns = get_columns() + data = get_data(filters) return columns, data -def get_columns(filters): - """return columns based on filters""" +def validate_filters(filters): + if not filters: + frappe.throw(_("Please select the required filters")) - columns = ( - [_("Item") + ":Link/Item:100"] - + [_("Item Name") + "::150"] - + [_("Description") + "::150"] - + [_("Warehouse") + ":Link/Warehouse:100"] - + [_("Batch") + ":Link/Batch:100"] - + [_("Expires On") + ":Date:90"] - + [_("Expiry (In Days)") + ":Int:120"] - ) - - return columns - - -def get_stock_ledger_entries(filters): if not filters.get("from_date"): frappe.throw(_("'From Date' is required")) if not filters.get("to_date"): frappe.throw(_("'To Date' is required")) - sle = frappe.qb.DocType("Stock Ledger Entry") - query = ( - frappe.qb.from_(sle) - .select(sle.item_code, sle.batch_no, sle.warehouse, sle.posting_date, sle.actual_qty) - .where( - (sle.is_cancelled == 0) - & (sle.docstatus < 2) - & (IfNull(sle.batch_no, "") != "") - & (sle.posting_date <= filters["to_date"]) - ) - .orderby(sle.item_code, sle.warehouse) + +def get_columns(): + return ( + [_("Item") + ":Link/Item:150"] + + [_("Item Name") + "::150"] + + [_("Batch") + ":Link/Batch:150"] + + [_("Stock UOM") + ":Link/UOM:100"] + + [_("Quantity") + ":Float:100"] + + [_("Expires On") + ":Date:100"] + + [_("Expiry (In Days)") + ":Int:130"] ) - return query.run(as_dict=True) +def get_data(filters): + data = [] -def get_item_warehouse_batch_map(filters, float_precision): - sle = get_stock_ledger_entries(filters) - iwb_map = {} - - from_date = getdate(filters["from_date"]) - to_date = getdate(filters["to_date"]) - - for d in sle: - iwb_map.setdefault(d.item_code, {}).setdefault(d.warehouse, {}).setdefault( - d.batch_no, frappe._dict({"expires_on": None, "expiry_status": None}) + for batch in get_batch_details(filters): + data.append( + [ + batch.item, + batch.item_name, + batch.name, + batch.stock_uom, + batch.batch_qty, + batch.expiry_date, + max((batch.expiry_date - frappe.utils.datetime.date.today()).days, 0) + if batch.expiry_date + else None, + ] ) - qty_dict = iwb_map[d.item_code][d.warehouse][d.batch_no] - - expiry_date_unicode = frappe.db.get_value("Batch", d.batch_no, "expiry_date") - qty_dict.expires_on = expiry_date_unicode - - exp_date = frappe.utils.data.getdate(expiry_date_unicode) - qty_dict.expires_on = exp_date - - expires_in_days = (exp_date - frappe.utils.datetime.date.today()).days - - if expires_in_days > 0: - qty_dict.expiry_status = expires_in_days - else: - qty_dict.expiry_status = 0 - - return iwb_map + return data -def get_item_details(filters): - item_map = {} - for d in (frappe.qb.from_("Item").select("name", "item_name", "description")).run(as_dict=True): - item_map.setdefault(d.name, d) +def get_batch_details(filters): + batch = frappe.qb.DocType("Batch") + query = ( + frappe.qb.from_(batch) + .select( + batch.name, + batch.creation, + batch.expiry_date, + batch.item, + batch.item_name, + batch.stock_uom, + batch.batch_qty, + ) + .where( + (batch.disabled == 0) + & (batch.batch_qty > 0) + & ( + (Date(batch.creation) >= filters["from_date"]) & (Date(batch.creation) <= filters["to_date"]) + ) + ) + .orderby(batch.creation) + ) - return item_map + if filters.get("item"): + query = query.where(batch.item == filters["item"]) + + return query.run(as_dict=True) From d631c7dffaab22709cfbeaf6d306a9eba3c2a4ca Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 13 Jul 2023 16:10:05 +0530 Subject: [PATCH 79/99] fix: Accounts closing balance patch (#36113) --- erpnext/patches/v14_0/update_closing_balances.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/patches/v14_0/update_closing_balances.py b/erpnext/patches/v14_0/update_closing_balances.py index d66467775c..9a814f3ee4 100644 --- a/erpnext/patches/v14_0/update_closing_balances.py +++ b/erpnext/patches/v14_0/update_closing_balances.py @@ -62,7 +62,10 @@ def execute(): entry["closing_date"] = pcv_doc.posting_date entry["period_closing_voucher"] = pcv_doc.name - make_closing_entries(gl_entries + closing_entries, voucher_name=pcv.name) + entries = gl_entries + closing_entries + if entries: + make_closing_entries(entries, voucher_name=pcv.name) + company_wise_order[pcv.company].append(pcv.posting_date) i += 1 From aa18b25a71430d11923bbfb5abca79214c0a467f Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Thu, 13 Jul 2023 13:29:07 +0200 Subject: [PATCH 80/99] feat: add local holidays --- .../doctype/holiday_list/holiday_list.js | 28 +- .../doctype/holiday_list/holiday_list.json | 436 +++--------------- .../doctype/holiday_list/holiday_list.py | 71 ++- pyproject.toml | 1 + 4 files changed, 143 insertions(+), 393 deletions(-) diff --git a/erpnext/setup/doctype/holiday_list/holiday_list.js b/erpnext/setup/doctype/holiday_list/holiday_list.js index ea033c7ed9..dc4cd9fd11 100644 --- a/erpnext/setup/doctype/holiday_list/holiday_list.js +++ b/erpnext/setup/doctype/holiday_list/holiday_list.js @@ -6,13 +6,39 @@ frappe.ui.form.on("Holiday List", { if (frm.doc.holidays) { frm.set_value("total_holidays", frm.doc.holidays.length); } + + frm.call("get_supported_countries").then(r => { + frm.subdivisions_by_country = r.message; + frm.set_df_property("country", "options", Object.keys(r.message)); + + if (frm.doc.country) { + frm.trigger("set_subdivisions"); + } + }); }, from_date: function(frm) { if (frm.doc.from_date && !frm.doc.to_date) { var a_year_from_start = frappe.datetime.add_months(frm.doc.from_date, 12); frm.set_value("to_date", frappe.datetime.add_days(a_year_from_start, -1)); } - } + }, + country: function(frm) { + frm.set_value("subdivision", ""); + + if (frm.doc.country) { + frm.trigger("set_subdivisions"); + } + }, + set_subdivisions: function(frm) { + const subdivisions = frm.subdivisions_by_country[frm.doc.country]; + if (subdivisions.length > 0) { + frm.set_df_property("subdivision", "options", frm.subdivisions_by_country[frm.doc.country]); + frm.set_df_property("subdivision", "hidden", 0); + } else { + frm.set_df_property("subdivision", "options", ""); + frm.set_df_property("subdivision", "hidden", 1); + } + }, }); frappe.tour["Holiday List"] = [ diff --git a/erpnext/setup/doctype/holiday_list/holiday_list.json b/erpnext/setup/doctype/holiday_list/holiday_list.json index 4bbe6a6cb2..2d24db28c8 100644 --- a/erpnext/setup/doctype/holiday_list/holiday_list.json +++ b/erpnext/setup/doctype/holiday_list/holiday_list.json @@ -1,480 +1,166 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, + "actions": [], "allow_import": 1, "allow_rename": 1, "autoname": "field:holiday_list_name", - "beta": 0, "creation": "2013-01-10 16:34:14", - "custom": 0, - "docstatus": 0, "doctype": "DocType", "document_type": "Setup", - "editable_grid": 0, "engine": "InnoDB", + "field_order": [ + "holiday_list_name", + "from_date", + "to_date", + "column_break_4", + "total_holidays", + "add_weekly_holidays", + "weekly_off", + "get_weekly_off_dates", + "add_local_holidays", + "country", + "subdivision", + "get_local_holidays", + "holidays_section", + "holidays", + "clear_table", + "section_break_9", + "color" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "holiday_list_name", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Holiday List Name", - "length": 0, - "no_copy": 0, "oldfieldname": "holiday_list_name", "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, "unique": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "from_date", "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "From Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "to_date", "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "To Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_4", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "total_holidays", "fieldtype": "Int", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Total Holidays", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, - "columns": 0, + "depends_on": "eval: doc.from_date && doc.to_date", "fieldname": "add_weekly_holidays", "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Add Weekly Holidays", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Add Weekly Holidays" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "weekly_off", "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, "in_standard_filter": 1, "label": "Weekly Off", - "length": 0, "no_copy": 1, "options": "\nSunday\nMonday\nTuesday\nWednesday\nThursday\nFriday\nSaturday", - "permlevel": 0, "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "report_hide": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "get_weekly_off_dates", "fieldtype": "Button", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Add to Holidays", - "length": 0, - "no_copy": 0, - "options": "get_weekly_off_dates", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "get_weekly_off_dates" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "holidays_section", "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Holidays", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Holidays" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "holidays", "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Holidays", - "length": 0, - "no_copy": 0, "oldfieldname": "holiday_list_details", "oldfieldtype": "Table", - "options": "Holiday", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Holiday" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "clear_table", "fieldtype": "Button", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Clear Table", - "length": 0, - "no_copy": 0, - "options": "clear_table", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "clear_table" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "section_break_9", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Section Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "color", "fieldtype": "Color", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Color", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "print_hide": 1 + }, + { + "fieldname": "country", + "fieldtype": "Select", + "label": "Country" + }, + { + "depends_on": "country", + "fieldname": "subdivision", + "fieldtype": "Select", + "label": "Subdivision" + }, + { + "collapsible": 1, + "depends_on": "eval: doc.from_date && doc.to_date", + "fieldname": "add_local_holidays", + "fieldtype": "Section Break", + "label": "Add Local Holidays" + }, + { + "fieldname": "get_local_holidays", + "fieldtype": "Button", + "label": "Add to Holidays", + "options": "get_local_holidays" } ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, "icon": "fa fa-calendar", "idx": 1, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-07-03 07:22:46.474096", + "links": [], + "modified": "2023-07-13 13:12:32.082690", "modified_by": "Administrator", "module": "Setup", "name": "Holiday List", + "naming_rule": "By fieldname", "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, "create": 1, "delete": 1, "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "HR Manager", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 } ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, "sort_field": "modified", "sort_order": "DESC", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 + "states": [] } \ No newline at end of file diff --git a/erpnext/setup/doctype/holiday_list/holiday_list.py b/erpnext/setup/doctype/holiday_list/holiday_list.py index 84d0d35287..1aec032a47 100644 --- a/erpnext/setup/doctype/holiday_list/holiday_list.py +++ b/erpnext/setup/doctype/holiday_list/holiday_list.py @@ -3,11 +3,14 @@ import json +from datetime import date import frappe from frappe import _, throw from frappe.model.document import Document -from frappe.utils import cint, formatdate, getdate, today +from frappe.utils import formatdate, getdate, today +from holidays import country_holidays +from holidays.utils import list_supported_countries class OverlapError(frappe.ValidationError): @@ -21,25 +24,59 @@ class HolidayList(Document): @frappe.whitelist() def get_weekly_off_dates(self): - self.validate_values() - date_list = self.get_weekly_off_date_list(self.from_date, self.to_date) - last_idx = max( - [cint(d.idx) for d in self.get("holidays")] - or [ - 0, - ] - ) - for i, d in enumerate(date_list): - ch = self.append("holidays", {}) - ch.description = _(self.weekly_off) - ch.holiday_date = d - ch.weekly_off = 1 - ch.idx = last_idx + i + 1 - - def validate_values(self): if not self.weekly_off: throw(_("Please select weekly off day")) + existing_holidays = self.get_holidays() + + for d in self.get_weekly_off_date_list(self.from_date, self.to_date): + if d in existing_holidays: + continue + + self.append("holidays", {"description": _(self.weekly_off), "holiday_date": d, "weekly_off": 1}) + + self.sort_holidays() + + @frappe.whitelist() + def get_supported_countries(self): + return list_supported_countries() + + @frappe.whitelist() + def get_local_holidays(self): + if not self.country: + throw(_("Please select Country")) + + existing_holidays = self.get_holidays() + system_language = frappe.db.get_single_value("System Settings", "language") + from_date = getdate(self.from_date) + to_date = getdate(self.to_date) + + for holiday_date, holiday_name in country_holidays( + self.country, + subdiv=self.subdivision, + years=[from_date.year, to_date.year], + language=system_language, + ).items(): + if holiday_date in existing_holidays: + continue + + if holiday_date < from_date or holiday_date > to_date: + continue + + self.append( + "holidays", {"description": holiday_name, "holiday_date": holiday_date, "weekly_off": 0} + ) + + self.sort_holidays() + + def sort_holidays(self): + self.holidays.sort(key=lambda x: getdate(x.holiday_date)) + for i in range(len(self.holidays)): + self.holidays[i].idx = i + 1 + + def get_holidays(self) -> list[date]: + return [getdate(holiday.holiday_date) for holiday in self.holidays] + def validate_days(self): if getdate(self.from_date) > getdate(self.to_date): throw(_("To Date cannot be before From Date")) diff --git a/pyproject.toml b/pyproject.toml index 012ffb17a6..3e0dfb29b4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,6 +13,7 @@ dependencies = [ "Unidecode~=1.3.6", "barcodenumber~=0.5.0", "rapidfuzz~=2.15.0", + "holidays~=0.28", # integration dependencies "gocardless-pro~=1.22.0", From fd23bd043404f8ccfe4e688789a0aadbadb06755 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Thu, 13 Jul 2023 14:13:33 +0200 Subject: [PATCH 81/99] test(Holiday List): weekly off and local holidays --- .../doctype/holiday_list/test_holiday_list.py | 37 ++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/erpnext/setup/doctype/holiday_list/test_holiday_list.py b/erpnext/setup/doctype/holiday_list/test_holiday_list.py index d32cfe8265..23b08fd117 100644 --- a/erpnext/setup/doctype/holiday_list/test_holiday_list.py +++ b/erpnext/setup/doctype/holiday_list/test_holiday_list.py @@ -3,7 +3,7 @@ import unittest from contextlib import contextmanager -from datetime import timedelta +from datetime import date, timedelta import frappe from frappe.utils import getdate @@ -23,6 +23,41 @@ class TestHolidayList(unittest.TestCase): fetched_holiday_list = frappe.get_value("Holiday List", holiday_list.name) self.assertEqual(holiday_list.name, fetched_holiday_list) + def test_weekly_off(self): + holiday_list = frappe.new_doc("Holiday List") + holiday_list.from_date = "2023-01-01" + holiday_list.to_date = "2023-02-28" + holiday_list.weekly_off = "Sunday" + holiday_list.get_weekly_off_dates() + + holidays = [holiday.holiday_date for holiday in holiday_list.holidays] + + self.assertNotIn(date(2022, 12, 25), holidays) + self.assertIn(date(2023, 1, 1), holidays) + self.assertIn(date(2023, 1, 8), holidays) + self.assertIn(date(2023, 1, 15), holidays) + self.assertIn(date(2023, 1, 22), holidays) + self.assertIn(date(2023, 1, 29), holidays) + self.assertIn(date(2023, 2, 5), holidays) + self.assertIn(date(2023, 2, 12), holidays) + self.assertIn(date(2023, 2, 19), holidays) + self.assertIn(date(2023, 2, 26), holidays) + self.assertNotIn(date(2023, 3, 5), holidays) + + def test_local_holidays(self): + holiday_list = frappe.new_doc("Holiday List") + holiday_list.from_date = "2023-04-01" + holiday_list.to_date = "2023-04-30" + holiday_list.country = "DE" + holiday_list.subdivision = "SN" + holiday_list.get_local_holidays() + + holidays = [holiday.holiday_date for holiday in holiday_list.holidays] + self.assertNotIn(date(2023, 1, 1), holidays) + self.assertIn(date(2023, 4, 7), holidays) + self.assertIn(date(2023, 4, 10), holidays) + self.assertNotIn(date(2023, 5, 1), holidays) + def make_holiday_list( name, from_date=getdate() - timedelta(days=10), to_date=getdate(), holiday_dates=None From b5f6a1cc20271fd538f1184ad91b131044057339 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Thu, 13 Jul 2023 21:03:46 +0530 Subject: [PATCH 82/99] ci: fix repo name in relase notes workflow [skip ci] --- .github/workflows/release_notes.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release_notes.yml b/.github/workflows/release_notes.yml index 91139dea65..e765a66f69 100644 --- a/.github/workflows/release_notes.yml +++ b/.github/workflows/release_notes.yml @@ -29,9 +29,9 @@ jobs: steps: - name: Update notes run: | - NEW_NOTES=$(gh api --method POST -H "Accept: application/vnd.github+json" /repos/frappe/frappe/releases/generate-notes -f tag_name=$RELEASE_TAG | jq -r '.body' | sed -E '/^\* (chore|ci|test|docs|style)/d' ) - RELEASE_ID=$(gh api -H "Accept: application/vnd.github+json" /repos/frappe/frappe/releases/tags/$RELEASE_TAG | jq -r '.id') - gh api --method PATCH -H "Accept: application/vnd.github+json" /repos/frappe/frappe/releases/$RELEASE_ID -f body="$NEW_NOTES" + NEW_NOTES=$(gh api --method POST -H "Accept: application/vnd.github+json" /repos/frappe/erpnext/releases/generate-notes -f tag_name=$RELEASE_TAG | jq -r '.body' | sed -E '/^\* (chore|ci|test|docs|style)/d' ) + RELEASE_ID=$(gh api -H "Accept: application/vnd.github+json" /repos/frappe/erpnext/releases/tags/$RELEASE_TAG | jq -r '.id') + gh api --method PATCH -H "Accept: application/vnd.github+json" /repos/frappe/erpnext/releases/$RELEASE_ID -f body="$NEW_NOTES" env: GH_TOKEN: ${{ secrets.RELEASE_TOKEN }} From b4bd978791166e24b126683fc2f9dde8eadb4341 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 14 Jul 2023 10:28:36 +0530 Subject: [PATCH 83/99] fix: Account balance patch and query fixes (#36117) --- .../report/trial_balance/trial_balance.py | 7 +- erpnext/patches.txt | 2 +- .../patches/v14_0/update_closing_balances.py | 101 +++++++++--------- 3 files changed, 59 insertions(+), 51 deletions(-) diff --git a/erpnext/accounts/report/trial_balance/trial_balance.py b/erpnext/accounts/report/trial_balance/trial_balance.py index d51c4c4acb..7a8b7dc581 100644 --- a/erpnext/accounts/report/trial_balance/trial_balance.py +++ b/erpnext/accounts/report/trial_balance/trial_balance.py @@ -159,6 +159,8 @@ def get_rootwise_opening_balances(filters, report_type): accounting_dimensions, period_closing_voucher=last_period_closing_voucher[0].name, ) + + # Report getting generate from the mid of a fiscal year if getdate(last_period_closing_voucher[0].posting_date) < getdate( add_days(filters.from_date, -1) ): @@ -220,7 +222,10 @@ def get_opening_balance( if start_date: opening_balance = opening_balance.where(closing_balance.posting_date >= start_date) opening_balance = opening_balance.where(closing_balance.is_opening == "No") - opening_balance = opening_balance.where(closing_balance.posting_date < filters.from_date) + else: + opening_balance = opening_balance.where( + (closing_balance.posting_date < filters.from_date) | (closing_balance.is_opening == "Yes") + ) if ( not filters.show_unclosed_fy_pl_balances diff --git a/erpnext/patches.txt b/erpnext/patches.txt index b3b9bc60b7..f9d9ebbdb3 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -317,7 +317,7 @@ erpnext.patches.v13_0.update_docs_link erpnext.patches.v15_0.update_asset_value_for_manual_depr_entries erpnext.patches.v15_0.update_gpa_and_ndb_for_assdeprsch erpnext.patches.v14_0.create_accounting_dimensions_for_closing_balance -erpnext.patches.v14_0.update_closing_balances #17-05-2023 +erpnext.patches.v14_0.update_closing_balances #14-07-2023 execute:frappe.db.set_single_value("Accounts Settings", "merge_similar_account_heads", 0) # below migration patches should always run last erpnext.patches.v14_0.migrate_gl_to_payment_ledger diff --git a/erpnext/patches/v14_0/update_closing_balances.py b/erpnext/patches/v14_0/update_closing_balances.py index 9a814f3ee4..8849c11fca 100644 --- a/erpnext/patches/v14_0/update_closing_balances.py +++ b/erpnext/patches/v14_0/update_closing_balances.py @@ -13,59 +13,62 @@ from erpnext.accounts.utils import get_fiscal_year def execute(): frappe.db.truncate("Account Closing Balance") - i = 0 - company_wise_order = {} - for pcv in frappe.db.get_all( - "Period Closing Voucher", - fields=["company", "posting_date", "name"], - filters={"docstatus": 1}, - order_by="posting_date", - ): + for company in frappe.get_all("Company", pluck="name"): + i = 0 + company_wise_order = {} + for pcv in frappe.db.get_all( + "Period Closing Voucher", + fields=["company", "posting_date", "name"], + filters={"docstatus": 1, "company": company}, + order_by="posting_date", + ): - company_wise_order.setdefault(pcv.company, []) - if pcv.posting_date not in company_wise_order[pcv.company]: - pcv_doc = frappe.get_doc("Period Closing Voucher", pcv.name) - pcv_doc.year_start_date = get_fiscal_year( - pcv.posting_date, pcv.fiscal_year, company=pcv.company - )[1] + company_wise_order.setdefault(pcv.company, []) + if pcv.posting_date not in company_wise_order[pcv.company]: + pcv_doc = frappe.get_doc("Period Closing Voucher", pcv.name) + pcv_doc.year_start_date = get_fiscal_year( + pcv.posting_date, pcv.fiscal_year, company=pcv.company + )[1] - # get gl entries against pcv - gl_entries = frappe.db.get_all( - "GL Entry", filters={"voucher_no": pcv.name, "is_cancelled": 0}, fields=["*"] - ) - for entry in gl_entries: - entry["is_period_closing_voucher_entry"] = 1 - entry["closing_date"] = pcv_doc.posting_date - entry["period_closing_voucher"] = pcv_doc.name - - # get all gl entries for the year - closing_entries = frappe.db.get_all( - "GL Entry", - filters={ - "is_cancelled": 0, - "voucher_no": ["!=", pcv.name], - "posting_date": ["between", [pcv_doc.year_start_date, pcv.posting_date]], - "is_opening": "No", - }, - fields=["*"], - ) - - if i == 0: - # add opening entries only for the first pcv - closing_entries += frappe.db.get_all( - "GL Entry", - filters={"is_cancelled": 0, "is_opening": "Yes"}, - fields=["*"], + # get gl entries against pcv + gl_entries = frappe.db.get_all( + "GL Entry", filters={"voucher_no": pcv.name, "is_cancelled": 0}, fields=["*"] ) + for entry in gl_entries: + entry["is_period_closing_voucher_entry"] = 1 + entry["closing_date"] = pcv_doc.posting_date + entry["period_closing_voucher"] = pcv_doc.name - for entry in closing_entries: - entry["closing_date"] = pcv_doc.posting_date - entry["period_closing_voucher"] = pcv_doc.name + closing_entries = [] - entries = gl_entries + closing_entries - if entries: - make_closing_entries(entries, voucher_name=pcv.name) + if pcv.posting_date not in company_wise_order[pcv.company]: + # get all gl entries for the year + closing_entries = frappe.db.get_all( + "GL Entry", + filters={ + "is_cancelled": 0, + "voucher_no": ["!=", pcv.name], + "posting_date": ["between", [pcv_doc.year_start_date, pcv.posting_date]], + "is_opening": "No", + }, + fields=["*"], + ) - company_wise_order[pcv.company].append(pcv.posting_date) + if i == 0: + # add opening entries only for the first pcv + closing_entries += frappe.db.get_all( + "GL Entry", + filters={"is_cancelled": 0, "is_opening": "Yes"}, + fields=["*"], + ) - i += 1 + for entry in closing_entries: + entry["closing_date"] = pcv_doc.posting_date + entry["period_closing_voucher"] = pcv_doc.name + + entries = gl_entries + closing_entries + + if entries: + make_closing_entries(entries, voucher_name=pcv.name) + i += 1 + company_wise_order[pcv.company].append(pcv.posting_date) From d5fe1432f80c661de6c0d12ff1d0d56c40e96cd2 Mon Sep 17 00:00:00 2001 From: Raffael Meyer <14891507+barredterra@users.noreply.github.com> Date: Fri, 14 Jul 2023 08:57:35 +0200 Subject: [PATCH 84/99] fix: improve "Update Items" modal (#36105) * fix: make "Update Items" modal larger * fix: remove conversion factor from overview Conversion factor doesn't make much sense without two different UOMs next to it, hence moving it to row detail view --- erpnext/public/js/utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index a859a671b0..8633be8c42 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -632,7 +632,6 @@ erpnext.utils.update_child_items = function(opts) { fields.splice(3, 0, { fieldtype: 'Float', fieldname: "conversion_factor", - in_list_view: 1, label: __("Conversion Factor"), precision: get_precision('conversion_factor') }) @@ -640,6 +639,7 @@ erpnext.utils.update_child_items = function(opts) { new frappe.ui.Dialog({ title: __("Update Items"), + size: "extra-large", fields: [ { fieldname: "trans_items", From 3b884efca93be67384f1008cf1aed4c2a6505645 Mon Sep 17 00:00:00 2001 From: "Kitti U. @ Ecosoft" Date: Fri, 14 Jul 2023 14:33:00 +0700 Subject: [PATCH 85/99] fix: get_dimension with_cost_center_and_project=false is not working. (#35974) * fix: get_dimension with_cost_center_and_project=false is not working. with_cost_center_and_project is no python str, and it always evaluated as True, despite JS call it with false * chore: Linting Issues --------- Co-authored-by: Deepesh Garg --- .../doctype/accounting_dimension/accounting_dimension.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py index 81ff6a52db..15c84d462f 100644 --- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py +++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py @@ -271,6 +271,12 @@ def get_dimensions(with_cost_center_and_project=False): as_dict=1, ) + if isinstance(with_cost_center_and_project, str): + if with_cost_center_and_project.lower().strip() == "true": + with_cost_center_and_project = True + else: + with_cost_center_and_project = False + if with_cost_center_and_project: dimension_filters.extend( [ From 4888d75e72fe53d98d62e734f3c6f0abe8edd0e3 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Fri, 14 Jul 2023 11:59:45 +0200 Subject: [PATCH 86/99] feat(Holiday List): display localized country name --- .../setup/doctype/holiday_list/holiday_list.js | 10 +++++++--- .../setup/doctype/holiday_list/holiday_list.json | 2 +- .../setup/doctype/holiday_list/holiday_list.py | 16 +++++++++++++++- 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/erpnext/setup/doctype/holiday_list/holiday_list.js b/erpnext/setup/doctype/holiday_list/holiday_list.js index dc4cd9fd11..8df49e1581 100644 --- a/erpnext/setup/doctype/holiday_list/holiday_list.js +++ b/erpnext/setup/doctype/holiday_list/holiday_list.js @@ -8,8 +8,12 @@ frappe.ui.form.on("Holiday List", { } frm.call("get_supported_countries").then(r => { - frm.subdivisions_by_country = r.message; - frm.set_df_property("country", "options", Object.keys(r.message)); + frm.subdivisions_by_country = r.message.subdivisions_by_country; + frm.set_df_property( + "country", + "options", + r.message.countries.sort((a, b) => a.label.localeCompare(b.label)) + ); if (frm.doc.country) { frm.trigger("set_subdivisions"); @@ -31,7 +35,7 @@ frappe.ui.form.on("Holiday List", { }, set_subdivisions: function(frm) { const subdivisions = frm.subdivisions_by_country[frm.doc.country]; - if (subdivisions.length > 0) { + if (subdivisions && subdivisions.length > 0) { frm.set_df_property("subdivision", "options", frm.subdivisions_by_country[frm.doc.country]); frm.set_df_property("subdivision", "hidden", 0); } else { diff --git a/erpnext/setup/doctype/holiday_list/holiday_list.json b/erpnext/setup/doctype/holiday_list/holiday_list.json index 2d24db28c8..e9b848fdf5 100644 --- a/erpnext/setup/doctype/holiday_list/holiday_list.json +++ b/erpnext/setup/doctype/holiday_list/holiday_list.json @@ -141,7 +141,7 @@ "icon": "fa fa-calendar", "idx": 1, "links": [], - "modified": "2023-07-13 13:12:32.082690", + "modified": "2023-07-14 11:29:12.537263", "modified_by": "Administrator", "module": "Setup", "name": "Holiday List", diff --git a/erpnext/setup/doctype/holiday_list/holiday_list.py b/erpnext/setup/doctype/holiday_list/holiday_list.py index 1aec032a47..0b26a62ce6 100644 --- a/erpnext/setup/doctype/holiday_list/holiday_list.py +++ b/erpnext/setup/doctype/holiday_list/holiday_list.py @@ -6,6 +6,7 @@ import json from datetime import date import frappe +from babel import Locale from frappe import _, throw from frappe.model.document import Document from frappe.utils import formatdate, getdate, today @@ -39,7 +40,15 @@ class HolidayList(Document): @frappe.whitelist() def get_supported_countries(self): - return list_supported_countries() + subdivisions_by_country = list_supported_countries() + countries = [ + {"value": country, "label": local_country_name(country)} + for country in subdivisions_by_country.keys() + ] + return { + "countries": countries, + "subdivisions_by_country": subdivisions_by_country, + } @frappe.whitelist() def get_local_holidays(self): @@ -157,3 +166,8 @@ def is_holiday(holiday_list, date=None): ) else: return False + + +def local_country_name(country_code: str) -> str: + """Return the localized country name for the given country code.""" + return Locale.parse(frappe.local.lang).territories.get(country_code, country_code) From 509061f05be9329ab2aa4801c33091818cb787fb Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Fri, 14 Jul 2023 12:14:01 +0200 Subject: [PATCH 87/99] fix: German translations of Holiday List --- erpnext/translations/de.csv | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/erpnext/translations/de.csv b/erpnext/translations/de.csv index 5f0a8dc735..ad9897c43d 100644 --- a/erpnext/translations/de.csv +++ b/erpnext/translations/de.csv @@ -1219,7 +1219,7 @@ High Sensitivity,Hohe Empfindlichkeit, Hold,Anhalten, Hold Invoice,Rechnung zurückhalten, Holiday,Urlaub, -Holiday List,Urlaubsübersicht, +Holiday List,Feiertagsliste, Hotel Rooms of type {0} are unavailable on {1},Hotelzimmer vom Typ {0} sind auf {1} nicht verfügbar, Hotels,Hotels, Hourly,Stündlich, @@ -3317,7 +3317,7 @@ Workflow,Workflow, Working,In Bearbeitung, Working Hours,Arbeitszeit, Workstation,Arbeitsplatz, -Workstation is closed on the following dates as per Holiday List: {0},Arbeitsplatz ist an folgenden Tagen gemäß der Urlaubsliste geschlossen: {0}, +Workstation is closed on the following dates as per Holiday List: {0},Arbeitsplatz ist an folgenden Tagen gemäß der Feiertagsliste geschlossen: {0}, Wrapping up,Aufwickeln, Wrong Password,Falsches Passwort, Year start date or end date is overlapping with {0}. To avoid please set company,"Jahresbeginn oder Enddatum überlappt mit {0}. Bitte ein Unternehmen wählen, um dies zu verhindern", @@ -3583,6 +3583,7 @@ Accounting Period overlaps with {0},Abrechnungszeitraum überschneidet sich mit Activity,Aktivität, Add / Manage Email Accounts.,Hinzufügen/Verwalten von E-Mail-Konten, Add Child,Unterpunkt hinzufügen, +Add Local Holidays,Lokale Feiertage hinzufügen, Add Multiple,Mehrere hinzufügen, Add Participants,Teilnehmer hinzufügen, Add to Featured Item,Zum empfohlenen Artikel hinzufügen, @@ -4046,6 +4047,7 @@ Stock Ledger ID,Bestandsbuch-ID, Stock Value ({0}) and Account Balance ({1}) are out of sync for account {2} and it's linked warehouses.,Der Bestandswert ({0}) und der Kontostand ({1}) sind für das Konto {2} und die verknüpften Lager nicht synchron., Stores - {0},Stores - {0}, Student with email {0} does not exist,Der Student mit der E-Mail-Adresse {0} existiert nicht, +Subdivision,Teilgebiet, Submit Review,Bewertung abschicken, Submitted,Gebucht, Supplier Addresses And Contacts,Lieferanten-Adressen und Kontaktdaten, @@ -6497,7 +6499,7 @@ Reports to,Vorgesetzter, Attendance and Leave Details,Anwesenheits- und Urlaubsdetails, Leave Policy,Urlaubsrichtlinie, Attendance Device ID (Biometric/RF tag ID),Anwesenheitsgeräte-ID (biometrische / RF-Tag-ID), -Applicable Holiday List,Geltende Urlaubsliste, +Applicable Holiday List,Geltende Feiertagsliste, Default Shift,Standardverschiebung, Salary Details,Gehaltsdetails, Salary Mode,Gehaltsmodus, @@ -6662,12 +6664,12 @@ Unclaimed amount,Nicht beanspruchter Betrag, Expense Claim Detail,Auslage, Expense Date,Datum der Auslage, Expense Claim Type,Art der Auslagenabrechnung, -Holiday List Name,Urlaubslistenname, -Total Holidays,Insgesamt Feiertage, -Add Weekly Holidays,Wöchentliche Feiertage hinzufügen, +Holiday List Name,Name der Feiertagsliste, +Total Holidays,Insgesamt freie Tage, +Add Weekly Holidays,Wöchentlich freie Tage hinzufügen, Weekly Off,Wöchentlich frei, -Add to Holidays,Zu Feiertagen hinzufügen, -Holidays,Ferien, +Add to Holidays,Zu freien Tagen hinzufügen, +Holidays,Arbeitsfreie Tage, Clear Table,Tabelle leeren, HR Settings,Einstellungen zum Modul Personalwesen, Employee Settings,Mitarbeitereinstellungen, @@ -6777,7 +6779,7 @@ Transaction Name,Transaktionsname, Is Carry Forward,Ist Übertrag, Is Expired,Ist abgelaufen, Is Leave Without Pay,Ist unbezahlter Urlaub, -Holiday List for Optional Leave,Urlaubsliste für optionalen Urlaub, +Holiday List for Optional Leave,Feiertagsliste für optionalen Urlaub, Leave Allocations,Zuteilungen verlassen, Leave Policy Details,Urlaubsrichtliniendetails, Leave Policy Detail,Urlaubsrichtliniendetail, @@ -7646,7 +7648,7 @@ Legal Entity / Subsidiary with a separate Chart of Accounts belonging to the Org Change Abbreviation,Abkürzung ändern, Parent Company,Muttergesellschaft, Default Values,Standardwerte, -Default Holiday List,Standard-Urlaubsliste, +Default Holiday List,Standard Feiertagsliste, Default Selling Terms,Standardverkaufsbedingungen, Default Buying Terms,Standard-Einkaufsbedingungen, Create Chart Of Accounts Based On,"Kontenplan erstellen, basierend auf", From 8271a39cdb00ebf3d20ab635b298ee1edbf19311 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Fri, 14 Jul 2023 12:16:49 +0200 Subject: [PATCH 88/99] fix(Holiday List): use current user's language For consistency with "weekly off" descriptions --- erpnext/setup/doctype/holiday_list/holiday_list.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/erpnext/setup/doctype/holiday_list/holiday_list.py b/erpnext/setup/doctype/holiday_list/holiday_list.py index 0b26a62ce6..d463356243 100644 --- a/erpnext/setup/doctype/holiday_list/holiday_list.py +++ b/erpnext/setup/doctype/holiday_list/holiday_list.py @@ -56,7 +56,6 @@ class HolidayList(Document): throw(_("Please select Country")) existing_holidays = self.get_holidays() - system_language = frappe.db.get_single_value("System Settings", "language") from_date = getdate(self.from_date) to_date = getdate(self.to_date) @@ -64,7 +63,7 @@ class HolidayList(Document): self.country, subdiv=self.subdivision, years=[from_date.year, to_date.year], - language=system_language, + language=frappe.local.lang, ).items(): if holiday_date in existing_holidays: continue From ac9ad8ec364fcf507357062db37355b18af66ba9 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 14 Jul 2023 15:56:59 +0530 Subject: [PATCH 89/99] fix: Handle multi-company in patch (#36127) fix: Handle multi-compnay in patch --- erpnext/patches/v14_0/update_closing_balances.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/patches/v14_0/update_closing_balances.py b/erpnext/patches/v14_0/update_closing_balances.py index 8849c11fca..2947b98740 100644 --- a/erpnext/patches/v14_0/update_closing_balances.py +++ b/erpnext/patches/v14_0/update_closing_balances.py @@ -50,6 +50,7 @@ def execute(): "voucher_no": ["!=", pcv.name], "posting_date": ["between", [pcv_doc.year_start_date, pcv.posting_date]], "is_opening": "No", + "company": company, }, fields=["*"], ) @@ -58,7 +59,7 @@ def execute(): # add opening entries only for the first pcv closing_entries += frappe.db.get_all( "GL Entry", - filters={"is_cancelled": 0, "is_opening": "Yes"}, + filters={"is_cancelled": 0, "is_opening": "Yes", "company": company}, fields=["*"], ) From 8aff5a1dab6292dd85f94d5570ef9e5cef3a506c Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Fri, 14 Jul 2023 12:33:27 +0200 Subject: [PATCH 90/99] fix(Holiday List): allow empty value --- erpnext/setup/doctype/holiday_list/holiday_list.js | 14 +++++++------- erpnext/setup/doctype/holiday_list/holiday_list.py | 2 +- erpnext/translations/de.csv | 3 +++ 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/erpnext/setup/doctype/holiday_list/holiday_list.js b/erpnext/setup/doctype/holiday_list/holiday_list.js index 8df49e1581..8384ccfe21 100644 --- a/erpnext/setup/doctype/holiday_list/holiday_list.js +++ b/erpnext/setup/doctype/holiday_list/holiday_list.js @@ -9,11 +9,10 @@ frappe.ui.form.on("Holiday List", { frm.call("get_supported_countries").then(r => { frm.subdivisions_by_country = r.message.subdivisions_by_country; - frm.set_df_property( - "country", - "options", - r.message.countries.sort((a, b) => a.label.localeCompare(b.label)) - ); + const countries = r.message.countries.sort((a, b) => a.label.localeCompare(b.label)); + countries.unshift({ value: "", label: __("Select Country ...") }); + + frm.set_df_property("country", "options", countries); if (frm.doc.country) { frm.trigger("set_subdivisions"); @@ -34,9 +33,10 @@ frappe.ui.form.on("Holiday List", { } }, set_subdivisions: function(frm) { - const subdivisions = frm.subdivisions_by_country[frm.doc.country]; + const subdivisions = [...frm.subdivisions_by_country[frm.doc.country]]; if (subdivisions && subdivisions.length > 0) { - frm.set_df_property("subdivision", "options", frm.subdivisions_by_country[frm.doc.country]); + subdivisions.unshift({ value: "", label: __("Select Subdivision ...") }); + frm.set_df_property("subdivision", "options", subdivisions); frm.set_df_property("subdivision", "hidden", 0); } else { frm.set_df_property("subdivision", "options", ""); diff --git a/erpnext/setup/doctype/holiday_list/holiday_list.py b/erpnext/setup/doctype/holiday_list/holiday_list.py index d463356243..2ef4e655b2 100644 --- a/erpnext/setup/doctype/holiday_list/holiday_list.py +++ b/erpnext/setup/doctype/holiday_list/holiday_list.py @@ -53,7 +53,7 @@ class HolidayList(Document): @frappe.whitelist() def get_local_holidays(self): if not self.country: - throw(_("Please select Country")) + throw(_("Please select a country")) existing_holidays = self.get_holidays() from_date = getdate(self.from_date) diff --git a/erpnext/translations/de.csv b/erpnext/translations/de.csv index ad9897c43d..31eec6e317 100644 --- a/erpnext/translations/de.csv +++ b/erpnext/translations/de.csv @@ -4004,6 +4004,8 @@ Search for a payment,Suche nach einer Zahlung, Search for anything ...,Nach etwas suchen ..., Search results for,Suchergebnisse für, Select All,Alles auswählen, +Select Country ...,Land auswählen ..., +Select Subdivision ...,Teilgebiet auswählen ..., Select Difference Account,Wählen Sie Differenzkonto, Select a Default Priority.,Wählen Sie eine Standardpriorität., Select a company,Wählen Sie eine Firma aus, @@ -4194,6 +4196,7 @@ Mode Of Payment,Zahlungsart, No students Found,Keine Schüler gefunden, Not in Stock,Nicht lagernd, Please select a Customer,Bitte wählen Sie einen Kunden aus, +Please select a country,Bitte wählen Sie ein Land aus, Printed On,Gedruckt auf, Received From,Erhalten von, Sales Person,Verkäufer, From dab9688410cca4565f28cfcd7660dbce3732ae02 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Fri, 14 Jul 2023 13:33:55 +0200 Subject: [PATCH 91/99] refactor(Holiday List): use autocomplete fieldtype --- erpnext/setup/doctype/holiday_list/holiday_list.js | 12 +++++------- erpnext/setup/doctype/holiday_list/holiday_list.json | 6 +++--- erpnext/translations/de.csv | 2 -- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/erpnext/setup/doctype/holiday_list/holiday_list.js b/erpnext/setup/doctype/holiday_list/holiday_list.js index 8384ccfe21..90d9f1b6f5 100644 --- a/erpnext/setup/doctype/holiday_list/holiday_list.js +++ b/erpnext/setup/doctype/holiday_list/holiday_list.js @@ -9,10 +9,9 @@ frappe.ui.form.on("Holiday List", { frm.call("get_supported_countries").then(r => { frm.subdivisions_by_country = r.message.subdivisions_by_country; - const countries = r.message.countries.sort((a, b) => a.label.localeCompare(b.label)); - countries.unshift({ value: "", label: __("Select Country ...") }); - - frm.set_df_property("country", "options", countries); + frm.fields_dict.country.set_data( + r.message.countries.sort((a, b) => a.label.localeCompare(b.label)) + ); if (frm.doc.country) { frm.trigger("set_subdivisions"); @@ -35,11 +34,10 @@ frappe.ui.form.on("Holiday List", { set_subdivisions: function(frm) { const subdivisions = [...frm.subdivisions_by_country[frm.doc.country]]; if (subdivisions && subdivisions.length > 0) { - subdivisions.unshift({ value: "", label: __("Select Subdivision ...") }); - frm.set_df_property("subdivision", "options", subdivisions); + frm.fields_dict.subdivision.set_data(subdivisions); frm.set_df_property("subdivision", "hidden", 0); } else { - frm.set_df_property("subdivision", "options", ""); + frm.fields_dict.subdivision.set_data([]); frm.set_df_property("subdivision", "hidden", 1); } }, diff --git a/erpnext/setup/doctype/holiday_list/holiday_list.json b/erpnext/setup/doctype/holiday_list/holiday_list.json index e9b848fdf5..45671d181b 100644 --- a/erpnext/setup/doctype/holiday_list/holiday_list.json +++ b/erpnext/setup/doctype/holiday_list/holiday_list.json @@ -115,13 +115,13 @@ }, { "fieldname": "country", - "fieldtype": "Select", + "fieldtype": "Autocomplete", "label": "Country" }, { "depends_on": "country", "fieldname": "subdivision", - "fieldtype": "Select", + "fieldtype": "Autocomplete", "label": "Subdivision" }, { @@ -141,7 +141,7 @@ "icon": "fa fa-calendar", "idx": 1, "links": [], - "modified": "2023-07-14 11:29:12.537263", + "modified": "2023-07-14 13:28:53.156421", "modified_by": "Administrator", "module": "Setup", "name": "Holiday List", diff --git a/erpnext/translations/de.csv b/erpnext/translations/de.csv index 31eec6e317..e30a5d0e91 100644 --- a/erpnext/translations/de.csv +++ b/erpnext/translations/de.csv @@ -4004,8 +4004,6 @@ Search for a payment,Suche nach einer Zahlung, Search for anything ...,Nach etwas suchen ..., Search results for,Suchergebnisse für, Select All,Alles auswählen, -Select Country ...,Land auswählen ..., -Select Subdivision ...,Teilgebiet auswählen ..., Select Difference Account,Wählen Sie Differenzkonto, Select a Default Priority.,Wählen Sie eine Standardpriorität., Select a company,Wählen Sie eine Firma aus, From e4128a5c91badd63c70a1e72e6d244727f9f059e Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Fri, 14 Jul 2023 17:17:24 +0530 Subject: [PATCH 92/99] perf: index `variant_of` and `attribute` in `Item Variant Attribute` --- .../item_variant_attribute.json | 432 +++--------------- 1 file changed, 76 insertions(+), 356 deletions(-) diff --git a/erpnext/stock/doctype/item_variant_attribute/item_variant_attribute.json b/erpnext/stock/doctype/item_variant_attribute/item_variant_attribute.json index 6d02ea9db0..9699ecbb3d 100644 --- a/erpnext/stock/doctype/item_variant_attribute/item_variant_attribute.json +++ b/erpnext/stock/doctype/item_variant_attribute/item_variant_attribute.json @@ -1,370 +1,90 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "", - "beta": 0, - "creation": "2015-05-19 05:12:30.344797", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Other", - "editable_grid": 1, + "actions": [], + "creation": "2015-05-19 05:12:30.344797", + "doctype": "DocType", + "document_type": "Other", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "variant_of", + "attribute", + "column_break_2", + "attribute_value", + "numeric_values", + "section_break_4", + "from_range", + "increment", + "column_break_8", + "to_range" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "variant_of", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Variant Of", - "length": 0, - "no_copy": 0, - "options": "Item", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "variant_of", + "fieldtype": "Link", + "label": "Variant Of", + "options": "Item", + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "attribute", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Attribute", - "length": 0, - "no_copy": 0, - "options": "Item Attribute", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "attribute", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Attribute", + "options": "Item Attribute", + "reqd": 1, + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_2", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "attribute_value", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Attribute Value", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "attribute_value", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Attribute Value" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "has_variants", - "fieldname": "numeric_values", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Numeric Values", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "depends_on": "has_variants", + "fieldname": "numeric_values", + "fieldtype": "Check", + "label": "Numeric Values" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "numeric_values", - "fieldname": "section_break_4", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "numeric_values", + "fieldname": "section_break_4", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "from_range", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "From Range", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "from_range", + "fieldtype": "Float", + "label": "From Range" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "increment", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Increment", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "increment", + "fieldtype": "Float", + "label": "Increment" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_8", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_8", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "to_range", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "To Range", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "to_range", + "fieldtype": "Float", + "label": "To Range" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "icon": "", - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2019-01-03 15:36:59.129006", - "modified_by": "Administrator", - "module": "Stock", - "name": "Item Variant Attribute", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 + ], + "istable": 1, + "links": [], + "modified": "2023-07-14 17:15:19.112119", + "modified_by": "Administrator", + "module": "Stock", + "name": "Item Variant Attribute", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] } \ No newline at end of file From 04400eb2e41997adcf28338752fc3cfb1a617730 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Fri, 14 Jul 2023 17:18:55 +0530 Subject: [PATCH 93/99] perf: index `disabled` in `Item` --- erpnext/stock/doctype/item/item.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json index 34adbebc07..87c2a7ea69 100644 --- a/erpnext/stock/doctype/item/item.json +++ b/erpnext/stock/doctype/item/item.json @@ -194,7 +194,8 @@ "default": "0", "fieldname": "disabled", "fieldtype": "Check", - "label": "Disabled" + "label": "Disabled", + "search_index": 1 }, { "default": "0", @@ -911,7 +912,7 @@ "index_web_pages_for_search": 1, "links": [], "make_attachments_public": 1, - "modified": "2023-02-14 04:48:26.343620", + "modified": "2023-07-14 17:18:18.658942", "modified_by": "Administrator", "module": "Stock", "name": "Item", From d95559a53cb5084cfe1a3291cbafc700d18445c6 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Fri, 14 Jul 2023 17:32:39 +0530 Subject: [PATCH 94/99] fix: patch for exotel --- erpnext/patches.txt | 3 +- .../exotel_integration_deprecation_warning.py | 10 ----- .../v15_0/remove_exotel_integration.py | 37 +++++++++++++++++++ 3 files changed, 39 insertions(+), 11 deletions(-) delete mode 100644 erpnext/patches/v13_0/exotel_integration_deprecation_warning.py create mode 100644 erpnext/patches/v15_0/remove_exotel_integration.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index f9d9ebbdb3..6fa4b5a85a 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -333,4 +333,5 @@ execute:frappe.delete_doc('DocType', 'Cash Flow Mapping Accounts', ignore_missin erpnext.patches.v14_0.cleanup_workspaces erpnext.patches.v15_0.remove_loan_management_module #2023-07-03 erpnext.patches.v14_0.set_report_in_process_SOA -erpnext.buying.doctype.supplier.patches.migrate_supplier_portal_users \ No newline at end of file +erpnext.buying.doctype.supplier.patches.migrate_supplier_portal_users +erpnext.patches.v15_0.remove_exotel_integration diff --git a/erpnext/patches/v13_0/exotel_integration_deprecation_warning.py b/erpnext/patches/v13_0/exotel_integration_deprecation_warning.py deleted file mode 100644 index 6e84ba9176..0000000000 --- a/erpnext/patches/v13_0/exotel_integration_deprecation_warning.py +++ /dev/null @@ -1,10 +0,0 @@ -import click - - -def execute(): - - click.secho( - "Exotel integration is moved to a separate app and will be removed from ERPNext in version-14.\n" - "Please install the app to continue using the integration: https://github.com/frappe/exotel_integration", - fg="yellow", - ) diff --git a/erpnext/patches/v15_0/remove_exotel_integration.py b/erpnext/patches/v15_0/remove_exotel_integration.py new file mode 100644 index 0000000000..a37773f337 --- /dev/null +++ b/erpnext/patches/v15_0/remove_exotel_integration.py @@ -0,0 +1,37 @@ +from contextlib import suppress + +import click +import frappe +from frappe import _ +from frappe.desk.doctype.notification_log.notification_log import make_notification_logs +from frappe.utils.user import get_system_managers + +SETTINGS_DOCTYPE = "Exotel Settings" + + +def execute(): + if "exotel_integration" in frappe.get_installed_apps(): + return + + with suppress(Exception): + exotel = frappe.get_doc(SETTINGS_DOCTYPE) + if exotel.enabled: + notify_existing_users() + + frappe.delete_doc("DocType", SETTINGS_DOCTYPE) + + +def notify_existing_users(): + click.secho( + "Exotel integration is moved to a separate app and will be removed from ERPNext in version-15.\n" + "Please install the app to continue using the integration: https://github.com/frappe/exotel_integration", + fg="yellow", + ) + + notification = { + "subject": _( + "WARNING: Exotel app has been separated from ERPNext, please install the app to continue using Exotel integration." + ), + "type": "Alert", + } + make_notification_logs(notification, get_system_managers(only_name=True)) From 41b6b739c0328954187c65091c464cd081bfb890 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Fri, 14 Jul 2023 17:37:13 +0530 Subject: [PATCH 95/99] fix: touch modified to migrate --- .../workspace/erpnext_integrations/erpnext_integrations.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/erpnext_integrations/workspace/erpnext_integrations/erpnext_integrations.json b/erpnext/erpnext_integrations/workspace/erpnext_integrations/erpnext_integrations.json index 6737713316..5c4be6ffaa 100644 --- a/erpnext/erpnext_integrations/workspace/erpnext_integrations/erpnext_integrations.json +++ b/erpnext/erpnext_integrations/workspace/erpnext_integrations/erpnext_integrations.json @@ -241,7 +241,7 @@ "type": "Link" } ], - "modified": "2023-05-24 14:47:25.984717", + "modified": "2023-05-24 14:47:26.984717", "modified_by": "Administrator", "module": "ERPNext Integrations", "name": "ERPNext Integrations", From 8f5b94f5fd6a7ba283f412cb644193264e6f15c7 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Fri, 14 Jul 2023 18:01:11 +0530 Subject: [PATCH 96/99] fix: `TypeError` while creating WO from PP (#36136) --- .../manufacturing/doctype/production_plan/production_plan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index 5f957a5442..a988badd74 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -621,7 +621,7 @@ class ProductionPlan(Document): def create_work_order(self, item): from erpnext.manufacturing.doctype.work_order.work_order import OverProductionError - if item.get("qty") <= 0: + if flt(item.get("qty")) <= 0: return wo = frappe.new_doc("Work Order") From 297c7e833c41c3e867a9bfb66e39f8dfe12eb2b6 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 14 Jul 2023 18:39:37 +0530 Subject: [PATCH 97/99] fix: Opening entries showing up incorrectly in TB report (#36135) * fix: Opening entries showing up incorrectly in TB report * chore: Linting Issue --- erpnext/accounts/report/financial_statements.py | 2 +- erpnext/accounts/report/trial_balance/trial_balance.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py index f3a892ba43..db9609debe 100644 --- a/erpnext/accounts/report/financial_statements.py +++ b/erpnext/accounts/report/financial_statements.py @@ -416,6 +416,7 @@ def set_gl_entries_by_account( filters, gl_entries_by_account, ignore_closing_entries=False, + ignore_opening_entries=False, ): """Returns a dict like { "account": [gl entries], ... }""" gl_entries = [] @@ -426,7 +427,6 @@ def set_gl_entries_by_account( pluck="name", ) - ignore_opening_entries = False if accounts_list: # For balance sheet if not from_date: diff --git a/erpnext/accounts/report/trial_balance/trial_balance.py b/erpnext/accounts/report/trial_balance/trial_balance.py index 7a8b7dc581..5176c31be7 100644 --- a/erpnext/accounts/report/trial_balance/trial_balance.py +++ b/erpnext/accounts/report/trial_balance/trial_balance.py @@ -117,6 +117,7 @@ def get_data(filters): filters, gl_entries_by_account, ignore_closing_entries=not flt(filters.with_period_closing_entry), + ignore_opening_entries=True, ) calculate_values(accounts, gl_entries_by_account, opening_balances) From bd9ef74ef7ebb56b8ba8691517124604ba60a628 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sun, 16 Jul 2023 11:34:42 +0530 Subject: [PATCH 98/99] perf: send SLA doctypes in boot This request is fired on every load, data rarely if ever changes though. --- erpnext/hooks.py | 5 + erpnext/public/js/utils.js | 158 +++++++++--------- .../service_level_agreement.py | 10 ++ 3 files changed, 90 insertions(+), 83 deletions(-) diff --git a/erpnext/hooks.py b/erpnext/hooks.py index d02d318b2d..28d79d1e56 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -611,3 +611,8 @@ global_search_doctypes = { additional_timeline_content = { "*": ["erpnext.telephony.doctype.call_log.call_log.get_linked_call_logs"] } + + +extend_bootinfo = [ + "erpnext.support.doctype.service_level_agreement.service_level_agreement.add_sla_doctypes", +] diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index 8633be8c42..13d35f3ccc 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -854,95 +854,87 @@ $(document).on('app_ready', function() { // Show SLA dashboard $(document).on('app_ready', function() { - frappe.call({ - method: 'erpnext.support.doctype.service_level_agreement.service_level_agreement.get_sla_doctypes', - callback: function(r) { - if (!r.message) - return; + $.each(frappe.boot.service_level_agreement_doctypes, function(_i, d) { + frappe.ui.form.on(d, { + onload: function(frm) { + if (!frm.doc.service_level_agreement) + return; - $.each(r.message, function(_i, d) { - frappe.ui.form.on(d, { - onload: function(frm) { - if (!frm.doc.service_level_agreement) - return; - - frappe.call({ - method: 'erpnext.support.doctype.service_level_agreement.service_level_agreement.get_service_level_agreement_filters', - args: { - doctype: frm.doc.doctype, - name: frm.doc.service_level_agreement, - customer: frm.doc.customer - }, - callback: function (r) { - if (r && r.message) { - frm.set_query('priority', function() { - return { - filters: { - 'name': ['in', r.message.priority], - } - }; - }); - frm.set_query('service_level_agreement', function() { - return { - filters: { - 'name': ['in', r.message.service_level_agreements], - } - }; - }); - } - } - }); + frappe.call({ + method: 'erpnext.support.doctype.service_level_agreement.service_level_agreement.get_service_level_agreement_filters', + args: { + doctype: frm.doc.doctype, + name: frm.doc.service_level_agreement, + customer: frm.doc.customer }, - - refresh: function(frm) { - if (frm.doc.status !== 'Closed' && frm.doc.service_level_agreement - && ['First Response Due', 'Resolution Due'].includes(frm.doc.agreement_status)) { - frappe.call({ - 'method': 'frappe.client.get', - args: { - doctype: 'Service Level Agreement', - name: frm.doc.service_level_agreement - }, - callback: function(data) { - let statuses = data.message.pause_sla_on; - const hold_statuses = []; - $.each(statuses, (_i, entry) => { - hold_statuses.push(entry.status); - }); - if (hold_statuses.includes(frm.doc.status)) { - frm.dashboard.clear_headline(); - let message = {'indicator': 'orange', 'msg': __('SLA is on hold since {0}', [moment(frm.doc.on_hold_since).fromNow(true)])}; - frm.dashboard.set_headline_alert( - '
' + - '
' + - ''+ message.msg +' ' + - '
' + - '
' - ); - } else { - set_time_to_resolve_and_response(frm, data.message.apply_sla_for_resolution); + callback: function (r) { + if (r && r.message) { + frm.set_query('priority', function() { + return { + filters: { + 'name': ['in', r.message.priority], } - } + }; + }); + frm.set_query('service_level_agreement', function() { + return { + filters: { + 'name': ['in', r.message.service_level_agreements], + } + }; }); - } else if (frm.doc.service_level_agreement) { - frm.dashboard.clear_headline(); - - let agreement_status = (frm.doc.agreement_status == 'Fulfilled') ? - {'indicator': 'green', 'msg': 'Service Level Agreement has been fulfilled'} : - {'indicator': 'red', 'msg': 'Service Level Agreement Failed'}; - - frm.dashboard.set_headline_alert( - '
' + - '
' + - ' ' + - '
' + - '
' - ); } - }, + } }); - }); - } + }, + + refresh: function(frm) { + if (frm.doc.status !== 'Closed' && frm.doc.service_level_agreement + && ['First Response Due', 'Resolution Due'].includes(frm.doc.agreement_status)) { + frappe.call({ + 'method': 'frappe.client.get', + args: { + doctype: 'Service Level Agreement', + name: frm.doc.service_level_agreement + }, + callback: function(data) { + let statuses = data.message.pause_sla_on; + const hold_statuses = []; + $.each(statuses, (_i, entry) => { + hold_statuses.push(entry.status); + }); + if (hold_statuses.includes(frm.doc.status)) { + frm.dashboard.clear_headline(); + let message = {'indicator': 'orange', 'msg': __('SLA is on hold since {0}', [moment(frm.doc.on_hold_since).fromNow(true)])}; + frm.dashboard.set_headline_alert( + '
' + + '
' + + ''+ message.msg +' ' + + '
' + + '
' + ); + } else { + set_time_to_resolve_and_response(frm, data.message.apply_sla_for_resolution); + } + } + }); + } else if (frm.doc.service_level_agreement) { + frm.dashboard.clear_headline(); + + let agreement_status = (frm.doc.agreement_status == 'Fulfilled') ? + {'indicator': 'green', 'msg': 'Service Level Agreement has been fulfilled'} : + {'indicator': 'red', 'msg': 'Service Level Agreement Failed'}; + + frm.dashboard.set_headline_alert( + '
' + + '
' + + ' ' + + '
' + + '
' + ); + } + }, + }); }); }); diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py index 2a078c4395..6c9bc54f7e 100644 --- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py +++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py @@ -21,6 +21,7 @@ from frappe.utils import ( time_diff_in_seconds, to_timedelta, ) +from frappe.utils.caching import redis_cache from frappe.utils.nestedset import get_ancestors_of from frappe.utils.safe_exec import get_safe_globals @@ -209,6 +210,10 @@ class ServiceLevelAgreement(Document): def on_update(self): set_documents_with_active_service_level_agreement() + def clear_cache(self): + get_sla_doctypes.clear_cache() + return super().clear_cache() + def create_docfields(self, meta, service_level_agreement_fields): last_index = len(meta.fields) @@ -990,6 +995,7 @@ def get_user_time(user, to_string=False): @frappe.whitelist() +@redis_cache() def get_sla_doctypes(): doctypes = [] data = frappe.get_all("Service Level Agreement", {"enabled": 1}, ["document_type"], distinct=1) @@ -998,3 +1004,7 @@ def get_sla_doctypes(): doctypes.append(entry.document_type) return doctypes + + +def add_sla_doctypes(bootinfo): + bootinfo.service_level_agreement_doctypes = get_sla_doctypes() From 6270607c6d49f91f11274cc4d5d1ef0b13eb19f1 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 16 Jul 2023 12:58:42 +0530 Subject: [PATCH 99/99] fix: Remove current fiscal year from Global Defaults (#35960) * fix: Remove current fiscal year from Global Defaults * fix: Remove button to set default * fix: Add utils to get fiscal year * fix: Incorrect import * feat: Add hook for naming series parser --- .../doctype/fiscal_year/fiscal_year.js | 11 - .../doctype/fiscal_year/fiscal_year.py | 25 +- .../consolidated_financial_statement.js | 6 +- .../deferred_revenue_and_expense.js | 6 +- .../deferred_revenue_and_expense.py | 5 +- .../test_deferred_revenue_and_expense.py | 7 +- .../dimension_wise_accounts_balance_report.js | 2 +- .../profitability_analysis.js | 2 +- .../report/trial_balance/trial_balance.js | 2 +- .../trial_balance_for_party.js | 2 +- erpnext/accounts/utils.py | 5 + .../fixed_asset_register.js | 4 +- erpnext/hooks.py | 5 + .../job_card_summary/job_card_summary.js | 2 +- erpnext/patches.txt | 1 + erpnext/public/js/financial_statements.js | 6 +- erpnext/public/js/utils.js | 17 + .../fichier_des_ecritures_comptables_[fec].js | 2 +- erpnext/regional/report/irs_1099/irs_1099.js | 2 +- .../global_defaults/global_defaults.json | 419 ++++-------------- .../global_defaults/global_defaults.py | 17 - .../operations/install_fixtures.py | 2 - 22 files changed, 137 insertions(+), 413 deletions(-) diff --git a/erpnext/accounts/doctype/fiscal_year/fiscal_year.js b/erpnext/accounts/doctype/fiscal_year/fiscal_year.js index bc77dac1cd..508b2eaf2a 100644 --- a/erpnext/accounts/doctype/fiscal_year/fiscal_year.js +++ b/erpnext/accounts/doctype/fiscal_year/fiscal_year.js @@ -8,17 +8,6 @@ frappe.ui.form.on('Fiscal Year', { frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)); } }, - refresh: function (frm) { - if (!frm.doc.__islocal && (frm.doc.name != frappe.sys_defaults.fiscal_year)) { - frm.add_custom_button(__("Set as Default"), () => frm.events.set_as_default(frm)); - frm.set_intro(__("To set this Fiscal Year as Default, click on 'Set as Default'")); - } else { - frm.set_intro(""); - } - }, - set_as_default: function(frm) { - return frm.call('set_as_default'); - }, year_start_date: function(frm) { if (!frm.doc.is_short_year) { let year_end_date = diff --git a/erpnext/accounts/doctype/fiscal_year/fiscal_year.py b/erpnext/accounts/doctype/fiscal_year/fiscal_year.py index 9d1b99b29b..0dfe569ec9 100644 --- a/erpnext/accounts/doctype/fiscal_year/fiscal_year.py +++ b/erpnext/accounts/doctype/fiscal_year/fiscal_year.py @@ -4,28 +4,12 @@ import frappe from dateutil.relativedelta import relativedelta -from frappe import _, msgprint +from frappe import _ from frappe.model.document import Document from frappe.utils import add_days, add_years, cstr, getdate class FiscalYear(Document): - @frappe.whitelist() - def set_as_default(self): - frappe.db.set_single_value("Global Defaults", "current_fiscal_year", self.name) - global_defaults = frappe.get_doc("Global Defaults") - global_defaults.check_permission("write") - global_defaults.on_update() - - # clear cache - frappe.clear_cache() - - msgprint( - _( - "{0} is now the default Fiscal Year. Please refresh your browser for the change to take effect." - ).format(self.name) - ) - def validate(self): self.validate_dates() self.validate_overlap() @@ -68,13 +52,6 @@ class FiscalYear(Document): frappe.cache().delete_value("fiscal_years") def on_trash(self): - global_defaults = frappe.get_doc("Global Defaults") - if global_defaults.current_fiscal_year == self.name: - frappe.throw( - _( - "You cannot delete Fiscal Year {0}. Fiscal Year {0} is set as default in Global Settings" - ).format(self.name) - ) frappe.cache().delete_value("fiscal_years") def validate_overlap(self): diff --git a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js index dd965a9813..d58fd95a84 100644 --- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js +++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js @@ -49,7 +49,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { "label": __("Start Year"), "fieldtype": "Link", "options": "Fiscal Year", - "default": frappe.defaults.get_user_default("fiscal_year"), + "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), "reqd": 1, on_change: () => { frappe.model.with_doc("Fiscal Year", frappe.query_report.get_filter_value('from_fiscal_year'), function(r) { @@ -65,7 +65,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { "label": __("End Year"), "fieldtype": "Link", "options": "Fiscal Year", - "default": frappe.defaults.get_user_default("fiscal_year"), + "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), "reqd": 1, on_change: () => { frappe.model.with_doc("Fiscal Year", frappe.query_report.get_filter_value('to_fiscal_year'), function(r) { @@ -139,7 +139,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { return value; }, onload: function() { - let fiscal_year = frappe.defaults.get_user_default("fiscal_year") + let fiscal_year = erpnext.utils.get_fiscal_year(frappe.datetime.get_today()); frappe.model.with_doc("Fiscal Year", fiscal_year, function(r) { var fy = frappe.model.get_doc("Fiscal Year", fiscal_year); diff --git a/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.js b/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.js index 0056b9e8f5..96e0c844ca 100644 --- a/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.js +++ b/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.js @@ -48,7 +48,7 @@ function get_filters() { "label": __("Start Year"), "fieldtype": "Link", "options": "Fiscal Year", - "default": frappe.defaults.get_user_default("fiscal_year"), + "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), "reqd": 1 }, { @@ -56,7 +56,7 @@ function get_filters() { "label": __("End Year"), "fieldtype": "Link", "options": "Fiscal Year", - "default": frappe.defaults.get_user_default("fiscal_year"), + "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), "reqd": 1 }, { @@ -100,7 +100,7 @@ frappe.query_reports["Deferred Revenue and Expense"] = { return default_formatter(value, row, column, data); }, onload: function(report){ - let fiscal_year = frappe.defaults.get_user_default("fiscal_year"); + let fiscal_year = erpnext.utils.get_fiscal_year(frappe.datetime.get_today()); frappe.model.with_doc("Fiscal Year", fiscal_year, function(r) { var fy = frappe.model.get_doc("Fiscal Year", fiscal_year); diff --git a/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.py b/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.py index 3e11643776..cad5325c6e 100644 --- a/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.py +++ b/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.py @@ -4,9 +4,10 @@ import frappe from frappe import _, qb from frappe.query_builder import Column, functions -from frappe.utils import add_days, date_diff, flt, get_first_day, get_last_day, rounded +from frappe.utils import add_days, date_diff, flt, get_first_day, get_last_day, getdate, rounded from erpnext.accounts.report.financial_statements import get_period_list +from erpnext.accounts.utils import get_fiscal_year class Deferred_Item(object): @@ -226,7 +227,7 @@ class Deferred_Revenue_and_Expense_Report(object): # If no filters are provided, get user defaults if not filters: - fiscal_year = frappe.get_doc("Fiscal Year", frappe.defaults.get_user_default("fiscal_year")) + fiscal_year = frappe.get_doc("Fiscal Year", get_fiscal_year(date=getdate())) self.filters = frappe._dict( { "company": frappe.defaults.get_user_default("Company"), diff --git a/erpnext/accounts/report/deferred_revenue_and_expense/test_deferred_revenue_and_expense.py b/erpnext/accounts/report/deferred_revenue_and_expense/test_deferred_revenue_and_expense.py index 023ff225ee..c84b843f1f 100644 --- a/erpnext/accounts/report/deferred_revenue_and_expense/test_deferred_revenue_and_expense.py +++ b/erpnext/accounts/report/deferred_revenue_and_expense/test_deferred_revenue_and_expense.py @@ -10,6 +10,7 @@ from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sal from erpnext.accounts.report.deferred_revenue_and_expense.deferred_revenue_and_expense import ( Deferred_Revenue_and_Expense_Report, ) +from erpnext.accounts.utils import get_fiscal_year from erpnext.buying.doctype.supplier.test_supplier import create_supplier from erpnext.stock.doctype.item.test_item import create_item @@ -116,7 +117,7 @@ class TestDeferredRevenueAndExpense(unittest.TestCase): pda.submit() # execute report - fiscal_year = frappe.get_doc("Fiscal Year", frappe.defaults.get_user_default("fiscal_year")) + fiscal_year = frappe.get_doc("Fiscal Year", get_fiscal_year(date="2021-05-01")) self.filters = frappe._dict( { "company": frappe.defaults.get_user_default("Company"), @@ -209,7 +210,7 @@ class TestDeferredRevenueAndExpense(unittest.TestCase): pda.submit() # execute report - fiscal_year = frappe.get_doc("Fiscal Year", frappe.defaults.get_user_default("fiscal_year")) + fiscal_year = frappe.get_doc("Fiscal Year", get_fiscal_year(date="2021-05-01")) self.filters = frappe._dict( { "company": frappe.defaults.get_user_default("Company"), @@ -297,7 +298,7 @@ class TestDeferredRevenueAndExpense(unittest.TestCase): pda.submit() # execute report - fiscal_year = frappe.get_doc("Fiscal Year", frappe.defaults.get_user_default("fiscal_year")) + fiscal_year = frappe.get_doc("Fiscal Year", get_fiscal_year(date="2021-05-01")) self.filters = frappe._dict( { "company": frappe.defaults.get_user_default("Company"), diff --git a/erpnext/accounts/report/dimension_wise_accounts_balance_report/dimension_wise_accounts_balance_report.js b/erpnext/accounts/report/dimension_wise_accounts_balance_report/dimension_wise_accounts_balance_report.js index ea05a35b25..9d416db4fd 100644 --- a/erpnext/accounts/report/dimension_wise_accounts_balance_report/dimension_wise_accounts_balance_report.js +++ b/erpnext/accounts/report/dimension_wise_accounts_balance_report/dimension_wise_accounts_balance_report.js @@ -18,7 +18,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { "label": __("Fiscal Year"), "fieldtype": "Link", "options": "Fiscal Year", - "default": frappe.defaults.get_user_default("fiscal_year"), + "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), "reqd": 1, "on_change": function(query_report) { var fiscal_year = query_report.get_values().fiscal_year; diff --git a/erpnext/accounts/report/profitability_analysis/profitability_analysis.js b/erpnext/accounts/report/profitability_analysis/profitability_analysis.js index 889ede5a82..6caebd34a2 100644 --- a/erpnext/accounts/report/profitability_analysis/profitability_analysis.js +++ b/erpnext/accounts/report/profitability_analysis/profitability_analysis.js @@ -25,7 +25,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { "label": __("Fiscal Year"), "fieldtype": "Link", "options": "Fiscal Year", - "default": frappe.defaults.get_user_default("fiscal_year"), + "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), "reqd": 1, "on_change": function(query_report) { var fiscal_year = query_report.get_values().fiscal_year; diff --git a/erpnext/accounts/report/trial_balance/trial_balance.js b/erpnext/accounts/report/trial_balance/trial_balance.js index 078b06519f..e45c3adcb6 100644 --- a/erpnext/accounts/report/trial_balance/trial_balance.js +++ b/erpnext/accounts/report/trial_balance/trial_balance.js @@ -17,7 +17,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { "label": __("Fiscal Year"), "fieldtype": "Link", "options": "Fiscal Year", - "default": frappe.defaults.get_user_default("fiscal_year"), + "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), "reqd": 1, "on_change": function(query_report) { var fiscal_year = query_report.get_values().fiscal_year; diff --git a/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.js b/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.js index 0e93035a35..0f7578cdc1 100644 --- a/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.js +++ b/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.js @@ -16,7 +16,7 @@ frappe.query_reports["Trial Balance for Party"] = { "label": __("Fiscal Year"), "fieldtype": "Link", "options": "Fiscal Year", - "default": frappe.defaults.get_user_default("fiscal_year"), + "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), "reqd": 1, "on_change": function(query_report) { var fiscal_year = query_report.get_values().fiscal_year; diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 8b44b22e3d..4b54483bc0 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -1110,6 +1110,11 @@ def get_autoname_with_number(number_value, doc_title, company): return " - ".join(parts) +def parse_naming_series_variable(doc, variable): + if variable == "FY": + return get_fiscal_year(date=doc.get("posting_date"), company=doc.get("company"))[0] + + @frappe.whitelist() def get_coa(doctype, parent, is_root, chart=None): from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import ( diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js index b788a32d6a..48b17f58fb 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js @@ -82,7 +82,7 @@ frappe.query_reports["Fixed Asset Register"] = { "label": __("Start Year"), "fieldtype": "Link", "options": "Fiscal Year", - "default": frappe.defaults.get_user_default("fiscal_year"), + "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), "depends_on": "eval: doc.filter_based_on == 'Fiscal Year'", }, { @@ -90,7 +90,7 @@ frappe.query_reports["Fixed Asset Register"] = { "label": __("End Year"), "fieldtype": "Link", "options": "Fiscal Year", - "default": frappe.defaults.get_user_default("fiscal_year"), + "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), "depends_on": "eval: doc.filter_based_on == 'Fiscal Year'", }, { diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 28d79d1e56..66f3de459b 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -354,6 +354,11 @@ doc_events = { }, } +# function should expect the variable and doc as arguments +naming_series_variables = { + "FY": "erpnext.accounts.utils.parse_naming_series_variable", +} + # On cancel event Payment Entry will be exempted and all linked submittable doctype will get cancelled. # to maintain data integrity we exempted payment entry. it will un-link when sales invoice get cancelled. # if payment entry not in auto cancel exempted doctypes it will cancel payment entry. diff --git a/erpnext/manufacturing/report/job_card_summary/job_card_summary.js b/erpnext/manufacturing/report/job_card_summary/job_card_summary.js index 782ce8110a..a874f22482 100644 --- a/erpnext/manufacturing/report/job_card_summary/job_card_summary.js +++ b/erpnext/manufacturing/report/job_card_summary/job_card_summary.js @@ -17,7 +17,7 @@ frappe.query_reports["Job Card Summary"] = { label: __("Fiscal Year"), fieldtype: "Link", options: "Fiscal Year", - default: frappe.defaults.get_user_default("fiscal_year"), + default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), reqd: 1, on_change: function(query_report) { var fiscal_year = query_report.get_values().fiscal_year; diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 6fa4b5a85a..73e0a95da9 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -334,4 +334,5 @@ erpnext.patches.v14_0.cleanup_workspaces erpnext.patches.v15_0.remove_loan_management_module #2023-07-03 erpnext.patches.v14_0.set_report_in_process_SOA erpnext.buying.doctype.supplier.patches.migrate_supplier_portal_users +execute:frappe.defaults.clear_default("fiscal_year") erpnext.patches.v15_0.remove_exotel_integration diff --git a/erpnext/public/js/financial_statements.js b/erpnext/public/js/financial_statements.js index 2b50a75e72..959cf507d5 100644 --- a/erpnext/public/js/financial_statements.js +++ b/erpnext/public/js/financial_statements.js @@ -56,7 +56,7 @@ erpnext.financial_statements = { // dropdown for links to other financial statements erpnext.financial_statements.filters = get_filters() - let fiscal_year = frappe.defaults.get_user_default("fiscal_year") + let fiscal_year = erpnext.utils.get_fiscal_year(frappe.datetime.get_today()); frappe.model.with_doc("Fiscal Year", fiscal_year, function(r) { var fy = frappe.model.get_doc("Fiscal Year", fiscal_year); @@ -137,7 +137,7 @@ function get_filters() { "label": __("Start Year"), "fieldtype": "Link", "options": "Fiscal Year", - "default": frappe.defaults.get_user_default("fiscal_year"), + "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), "reqd": 1, "depends_on": "eval:doc.filter_based_on == 'Fiscal Year'" }, @@ -146,7 +146,7 @@ function get_filters() { "label": __("End Year"), "fieldtype": "Link", "options": "Fiscal Year", - "default": frappe.defaults.get_user_default("fiscal_year"), + "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), "reqd": 1, "depends_on": "eval:doc.filter_based_on == 'Fiscal Year'" }, diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index 13d35f3ccc..8d6097d0a2 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -381,6 +381,23 @@ $.extend(erpnext.utils, { }); }); }); + }, + + get_fiscal_year: function(date) { + let fiscal_year = ''; + frappe.call({ + method: "erpnext.accounts.utils.get_fiscal_year", + args: { + date: date + }, + async: false, + callback: function(r) { + if (r.message) { + fiscal_year = r.message[0]; + } + } + }); + return fiscal_year; } }); diff --git a/erpnext/regional/report/fichier_des_ecritures_comptables_[fec]/fichier_des_ecritures_comptables_[fec].js b/erpnext/regional/report/fichier_des_ecritures_comptables_[fec]/fichier_des_ecritures_comptables_[fec].js index a4c7640c81..b85b58f636 100644 --- a/erpnext/regional/report/fichier_des_ecritures_comptables_[fec]/fichier_des_ecritures_comptables_[fec].js +++ b/erpnext/regional/report/fichier_des_ecritures_comptables_[fec]/fichier_des_ecritures_comptables_[fec].js @@ -16,7 +16,7 @@ frappe.query_reports["Fichier des Ecritures Comptables [FEC]"] = { "label": __("Fiscal Year"), "fieldtype": "Link", "options": "Fiscal Year", - "default": frappe.defaults.get_user_default("fiscal_year"), + "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), "reqd": 1 } ], diff --git a/erpnext/regional/report/irs_1099/irs_1099.js b/erpnext/regional/report/irs_1099/irs_1099.js index 070ff43f78..b3508e40a9 100644 --- a/erpnext/regional/report/irs_1099/irs_1099.js +++ b/erpnext/regional/report/irs_1099/irs_1099.js @@ -17,7 +17,7 @@ frappe.query_reports["IRS 1099"] = { "label": __("Fiscal Year"), "fieldtype": "Link", "options": "Fiscal Year", - "default": frappe.defaults.get_user_default("fiscal_year"), + "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), "reqd": 1, "width": 80, }, diff --git a/erpnext/setup/doctype/global_defaults/global_defaults.json b/erpnext/setup/doctype/global_defaults/global_defaults.json index bafb97a5d8..823d2ba7d7 100644 --- a/erpnext/setup/doctype/global_defaults/global_defaults.json +++ b/erpnext/setup/doctype/global_defaults/global_defaults.json @@ -1,352 +1,99 @@ { - "allow_copy": 1, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2013-05-02 17:53:24", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "editable_grid": 0, + "actions": [], + "allow_copy": 1, + "creation": "2013-05-02 17:53:24", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "default_company", + "country", + "default_distance_unit", + "column_break_8", + "default_currency", + "hide_currency_symbol", + "disable_rounded_total", + "disable_in_words" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "default_company", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Default Company", - "length": 0, - "no_copy": 0, - "options": "Company", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "default_company", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Default Company", + "options": "Company" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "current_fiscal_year", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Current Fiscal Year", - "length": 0, - "no_copy": 0, - "options": "Fiscal Year", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "country", + "fieldtype": "Link", + "label": "Country", + "options": "Country" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "country", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Country", - "length": 0, - "no_copy": 0, - "options": "Country", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "default_distance_unit", + "fieldtype": "Link", + "label": "Default Distance Unit", + "options": "UOM" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "fieldname": "default_distance_unit", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Default Distance Unit", - "length": 0, - "no_copy": 0, - "options": "UOM", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_8", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_8", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "INR", + "fieldname": "default_currency", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "in_list_view": 1, + "label": "Default Currency", + "options": "Currency", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "INR", - "fieldname": "default_currency", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Default Currency", - "length": 0, - "no_copy": 0, - "options": "Currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "description": "Do not show any symbol like $ etc next to currencies.", + "fieldname": "hide_currency_symbol", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Hide Currency Symbol", + "options": "\nNo\nYes" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "Do not show any symbol like $ etc next to currencies.", - "fieldname": "hide_currency_symbol", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Hide Currency Symbol", - "length": 0, - "no_copy": 0, - "options": "\nNo\nYes", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "description": "If disable, 'Rounded Total' field will not be visible in any transaction", + "fieldname": "disable_rounded_total", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Disable Rounded Total" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "If disable, 'Rounded Total' field will not be visible in any transaction", - "fieldname": "disable_rounded_total", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Disable Rounded Total", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "If disable, 'In Words' field will not be visible in any transaction", - "fieldname": "disable_in_words", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Disable In Words", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "default": "0", + "description": "If disable, 'In Words' field will not be visible in any transaction", + "fieldname": "disable_in_words", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Disable In Words" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "icon": "fa fa-cog", - "idx": 1, - "image_view": 0, - "in_create": 1, - "is_submittable": 0, - "issingle": 1, - "istable": 0, - "max_attachments": 0, - "menu_index": 0, - "modified": "2018-10-15 03:08:19.886212", - "modified_by": "Administrator", - "module": "Setup", - "name": "Global Defaults", - "owner": "Administrator", + ], + "icon": "fa fa-cog", + "idx": 1, + "in_create": 1, + "issingle": 1, + "links": [], + "modified": "2023-07-01 19:45:00.323953", + "modified_by": "Administrator", + "module": "Setup", + "name": "Global Defaults", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 0, - "read": 1, - "report": 0, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "read": 1, + "role": "System Manager", + "share": 1, "write": 1 } - ], - "quick_entry": 0, - "read_only": 1, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_order": "DESC", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 + ], + "read_only": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [] } \ No newline at end of file diff --git a/erpnext/setup/doctype/global_defaults/global_defaults.py b/erpnext/setup/doctype/global_defaults/global_defaults.py index 16e94343a3..fc80483e8e 100644 --- a/erpnext/setup/doctype/global_defaults/global_defaults.py +++ b/erpnext/setup/doctype/global_defaults/global_defaults.py @@ -10,7 +10,6 @@ from frappe.utils import cint keydict = { # "key in defaults": "key in Global Defaults" - "fiscal_year": "current_fiscal_year", "company": "default_company", "currency": "default_currency", "country": "country", @@ -29,22 +28,6 @@ class GlobalDefaults(Document): for key in keydict: frappe.db.set_default(key, self.get(keydict[key], "")) - # update year start date and year end date from fiscal_year - if self.current_fiscal_year: - if fiscal_year := frappe.get_all( - "Fiscal Year", - filters={"name": self.current_fiscal_year}, - fields=["year_start_date", "year_end_date"], - limit=1, - order_by=None, - ): - ysd = fiscal_year[0].year_start_date or "" - yed = fiscal_year[0].year_end_date or "" - - if ysd and yed: - frappe.db.set_default("year_start_date", ysd.strftime("%Y-%m-%d")) - frappe.db.set_default("year_end_date", yed.strftime("%Y-%m-%d")) - # enable default currency if self.default_currency: frappe.db.set_value("Currency", self.default_currency, "enabled", 1) diff --git a/erpnext/setup/setup_wizard/operations/install_fixtures.py b/erpnext/setup/setup_wizard/operations/install_fixtures.py index 8e61fe2872..535c87d652 100644 --- a/erpnext/setup/setup_wizard/operations/install_fixtures.py +++ b/erpnext/setup/setup_wizard/operations/install_fixtures.py @@ -462,11 +462,9 @@ def install_defaults(args=None): # nosemgrep def set_global_defaults(args): global_defaults = frappe.get_doc("Global Defaults", "Global Defaults") - current_fiscal_year = frappe.get_all("Fiscal Year")[0] global_defaults.update( { - "current_fiscal_year": current_fiscal_year.name, "default_currency": args.get("currency"), "default_company": args.get("company_name"), "country": args.get("country"),