From e4ed1d684d20dc3c7f5469ec4960941bf9db753c Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 5 Oct 2023 13:13:11 +0530 Subject: [PATCH 001/151] fix: fetch company details for Lead based quotation (cherry picked from commit f388864fd5a3ce95e0349d7fd37fb3878834262c) (cherry picked from commit c1d40a6bfa74c48bc10d493a464a1f2bdd8a9bbd) --- erpnext/crm/doctype/lead/lead.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/crm/doctype/lead/lead.py b/erpnext/crm/doctype/lead/lead.py index 460974972c5..294c41b9341 100644 --- a/erpnext/crm/doctype/lead/lead.py +++ b/erpnext/crm/doctype/lead/lead.py @@ -382,7 +382,7 @@ def get_lead_details(lead, posting_date=None, company=None): } ) - set_address_details(out, lead, "Lead") + set_address_details(out, lead, "Lead", company=company) taxes_and_charges = set_taxes( None, From 48ceead9d0eb335091399185ede9dfa07c622aee Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 11 Oct 2023 11:14:16 +0000 Subject: [PATCH 002/151] chore(release): Bumped to Version 14.43.1 ## [14.43.1](https://github.com/frappe/erpnext/compare/v14.43.0...v14.43.1) (2023-10-11) ### Bug Fixes * fetch company details for Lead based quotation ([e4ed1d6](https://github.com/frappe/erpnext/commit/e4ed1d684d20dc3c7f5469ec4960941bf9db753c)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 25e95bb009a..1d01d112e03 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "14.43.0" +__version__ = "14.43.1" def get_default_company(user=None): From 2815952a03cdbb11fec2fbfa1301f5076ae424ed Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Thu, 12 Oct 2023 02:15:37 +0000 Subject: [PATCH 003/151] chore(release): Bumped to Version 14.44.0 # [14.44.0](https://github.com/frappe/erpnext/compare/v14.43.1...v14.44.0) (2023-10-12) ### Bug Fixes * added validation for the batch on stock reco ([#37174](https://github.com/frappe/erpnext/issues/37174)) ([4c337a6](https://github.com/frappe/erpnext/commit/4c337a6f44127e474503862f3ff0d718c4eeffad)) * ageing summary in AR ([15d2024](https://github.com/frappe/erpnext/commit/15d2024b8e8349562cae0f979d1d483d4d969d32)) * allocate amt for payment term invoices ([b22ac13](https://github.com/frappe/erpnext/commit/b22ac137f50b979331eca4349702343f9af4714f)) * call validate before setting repost flag ([bec3e8e](https://github.com/frappe/erpnext/commit/bec3e8ed96f24ea6b048f88d23d3dbc6bc457e0f)) * do not run bg job for single doc ([4123e7b](https://github.com/frappe/erpnext/commit/4123e7b2447d7ed5de4957ac48ea1deff5a50649)) * **Employee:** enable `no_copy` for `relieving_date` (backport [#37344](https://github.com/frappe/erpnext/issues/37344)) ([#37358](https://github.com/frappe/erpnext/issues/37358)) ([2b38b78](https://github.com/frappe/erpnext/commit/2b38b780bab39d8e3cce62a77cae6fd0e971652b)) * exception on exporting errored rows ([e58b3b1](https://github.com/frappe/erpnext/commit/e58b3b11e9aee1da3e8610d19112cdb30801a9fb)) * fetch company details for Lead based quotation ([c1d40a6](https://github.com/frappe/erpnext/commit/c1d40a6bfa74c48bc10d493a464a1f2bdd8a9bbd)) * fetch dependent task subject and project (backport [#37401](https://github.com/frappe/erpnext/issues/37401)) ([#37421](https://github.com/frappe/erpnext/issues/37421)) ([0aad942](https://github.com/frappe/erpnext/commit/0aad942312da44584f30b44249baeeed15145926)) * ignore cancelled gle in voucher-wise balance report ([#36417](https://github.com/frappe/erpnext/issues/36417)) ([ee1255a](https://github.com/frappe/erpnext/commit/ee1255a716451015da11a8afe62b4c0af2193e0c)) * incorrect status of the returned purchase receipt ([#37300](https://github.com/frappe/erpnext/issues/37300)) ([63f4573](https://github.com/frappe/erpnext/commit/63f45739e05d728e8844320ff0c7dbafa8660acf)) * linting issues ([6c8a65e](https://github.com/frappe/erpnext/commit/6c8a65e03b9ab4b57f5c254566ea9a57c79dd2e8)) * negative valuation rate in PR return ([#37424](https://github.com/frappe/erpnext/issues/37424)) ([26ad688](https://github.com/frappe/erpnext/commit/26ad6885845962130cfb178fcbdbe2c4e75ce194)) * payment request rounding in multi-currency and on status update ([eed5863](https://github.com/frappe/erpnext/commit/eed58634ba09ff78bf041d93c63c25146b25ecb9)) * production plan reserved qty incorrect calculation (backport [#37400](https://github.com/frappe/erpnext/issues/37400)) ([#37458](https://github.com/frappe/erpnext/issues/37458)) ([573b159](https://github.com/frappe/erpnext/commit/573b159541b6aebbdcc86202576c38c69c947b1e)) * split inv allocated amt on server side ([06b0477](https://github.com/frappe/erpnext/commit/06b04770fc878c00e1073ca07af1c09286f92a08)) * typo in doctype name and qb ([606c99e](https://github.com/frappe/erpnext/commit/606c99e57cdd36951ef8472df58dcf54e862e476)) * **ux:** allow MR to Stop until fully received (backport [#37452](https://github.com/frappe/erpnext/issues/37452)) ([#37456](https://github.com/frappe/erpnext/issues/37456)) ([fb0b426](https://github.com/frappe/erpnext/commit/fb0b426fe435357ad49513ae15e2b12a845864e0)) * validation for si ([3dc68e3](https://github.com/frappe/erpnext/commit/3dc68e3b002ec0f072809d9d94a175b16ea98e8d)) ### Features * add repost btn in invoice ([cde848d](https://github.com/frappe/erpnext/commit/cde848dc7fa755201391fd024e94e0a6935c632e)) * allow on submit fields ([f5245f6](https://github.com/frappe/erpnext/commit/f5245f6b3fdf8f89702458fd7bb2e04e4eacd927)) * allow repost for pi ([2d13dda](https://github.com/frappe/erpnext/commit/2d13dda49c59784fee4257ff56ebf679617713f9)) * composite WIP asset ([#37352](https://github.com/frappe/erpnext/issues/37352)) ([0ecd7d2](https://github.com/frappe/erpnext/commit/0ecd7d2bf5eb522315776bae5633486d89791e00)) * disable currency exchange api. ([#33593](https://github.com/frappe/erpnext/issues/33593)) ([1ca0516](https://github.com/frappe/erpnext/commit/1ca0516fe549320838620f14dc0e7611a43350e7)) * filter on voucher no ([cb35218](https://github.com/frappe/erpnext/commit/cb35218eec8954c7419863c659d3176805a6f79d)) * introduce unreconcile doctype ([ae8355c](https://github.com/frappe/erpnext/commit/ae8355c95391d0b4223a8c17a1f0fa786f45ed96)) * UI for unreconcile ([9531a45](https://github.com/frappe/erpnext/commit/9531a45b941e9481c5c1d0f5520c3fef4d6b1b09)) * unreconcile support for journal entry ([cd2d335](https://github.com/frappe/erpnext/commit/cd2d335256ede5980a0757a80fabc5140adc3d10)) * validate negative stock for inventory dimension ([#37373](https://github.com/frappe/erpnext/issues/37373)) ([1480aca](https://github.com/frappe/erpnext/commit/1480acabb0faeae61c7c055bb7d1e81877b87cfb)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 1d01d112e03..26f7360563a 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "14.43.1" +__version__ = "14.44.0" def get_default_company(user=None): From bfa93cd3f6ac85b4d203e31cea0414c6765b5378 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 17 Oct 2023 18:19:47 +0530 Subject: [PATCH 004/151] chore: Add accounting dimensions to Sales Order Item table (cherry picked from commit e31db1891237aa22c19d71929f69d9db5596ae3c) # Conflicts: # erpnext/patches.txt --- .../accounting_dimension.py | 27 +++++++++++++++++++ erpnext/hooks.py | 1 + erpnext/patches.txt | 7 +++++ ...counting_dimensions_in_sales_order_item.py | 7 +++++ .../sales_order_item/sales_order_item.json | 11 ++++++-- 5 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 erpnext/patches/v14_0/create_accounting_dimensions_in_sales_order_item.py diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py index cfe5e6e8009..8afd313322e 100644 --- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py +++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py @@ -301,3 +301,30 @@ def get_dimensions(with_cost_center_and_project=False): default_dimensions_map[dimension.company][dimension.fieldname] = dimension.default_dimension return dimension_filters, default_dimensions_map + + +def create_accounting_dimensions_for_doctype(doctype): + accounting_dimensions = frappe.db.get_all( + "Accounting Dimension", fields=["fieldname", "label", "document_type", "disabled"] + ) + + if not accounting_dimensions: + return + + for d in accounting_dimensions: + field = frappe.db.get_value("Custom Field", {"dt": doctype, "fieldname": d.fieldname}) + + if field: + continue + + df = { + "fieldname": d.fieldname, + "label": d.label, + "fieldtype": "Link", + "options": d.document_type, + "insert_after": "accounting_dimensions_section", + } + + create_custom_field(doctype, df, ignore_validate=True) + + frappe.clear_cache(doctype=doctype) diff --git a/erpnext/hooks.py b/erpnext/hooks.py index b2a76f2038c..6d5dddd9e97 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -517,6 +517,7 @@ accounting_dimension_doctypes = [ "Sales Invoice Item", "Purchase Invoice Item", "Purchase Order Item", + "Sales Order Item", "Journal Entry Account", "Material Request Item", "Delivery Note Item", diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 37e70967388..ec4c405feee 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -341,5 +341,12 @@ execute:frappe.defaults.clear_default("fiscal_year") execute:frappe.db.set_single_value('Selling Settings', 'allow_negative_rates_for_items', 0) erpnext.patches.v14_0.correct_asset_value_if_je_with_workflow erpnext.patches.v14_0.migrate_deferred_accounts_to_item_defaults +<<<<<<< HEAD +======= +erpnext.patches.v14_0.update_invoicing_period_in_subscription +execute:frappe.delete_doc("Page", "welcome-to-erpnext") +erpnext.patches.v15_0.delete_payment_gateway_doctypes +erpnext.patches.v14_0.create_accounting_dimensions_in_sales_order_item +>>>>>>> e31db18912 (chore: Add accounting dimensions to Sales Order Item table) # below migration patch should always run last erpnext.patches.v14_0.migrate_gl_to_payment_ledger diff --git a/erpnext/patches/v14_0/create_accounting_dimensions_in_sales_order_item.py b/erpnext/patches/v14_0/create_accounting_dimensions_in_sales_order_item.py new file mode 100644 index 00000000000..8f77c35b129 --- /dev/null +++ b/erpnext/patches/v14_0/create_accounting_dimensions_in_sales_order_item.py @@ -0,0 +1,7 @@ +from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( + create_accounting_dimensions_for_doctype, +) + + +def execute(): + create_accounting_dimensions_for_doctype(doctype="Sales Order Item") diff --git a/erpnext/selling/doctype/sales_order_item/sales_order_item.json b/erpnext/selling/doctype/sales_order_item/sales_order_item.json index a3e9977244c..e9714113095 100644 --- a/erpnext/selling/doctype/sales_order_item/sales_order_item.json +++ b/erpnext/selling/doctype/sales_order_item/sales_order_item.json @@ -66,6 +66,7 @@ "total_weight", "column_break_21", "weight_uom", + "accounting_dimensions_section", "warehouse_and_reference", "warehouse", "target_warehouse", @@ -868,12 +869,18 @@ "label": "Production Plan Qty", "no_copy": 1, "read_only": 1 + }, + { + "collapsible": 1, + "fieldname": "accounting_dimensions_section", + "fieldtype": "Section Break", + "label": "Accounting Dimensions" } ], "idx": 1, "istable": 1, "links": [], - "modified": "2023-07-28 14:56:42.031636", + "modified": "2023-10-17 18:18:26.475259", "modified_by": "Administrator", "module": "Selling", "name": "Sales Order Item", @@ -884,4 +891,4 @@ "sort_order": "DESC", "states": [], "track_changes": 1 -} +} \ No newline at end of file From 7db69883649671024dcc81c669d0bcce0676a50a Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 18 Oct 2023 09:00:49 +0530 Subject: [PATCH 005/151] chore: resolve conflicts --- erpnext/patches.txt | 6 ------ 1 file changed, 6 deletions(-) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index ec4c405feee..abdd09383bb 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -341,12 +341,6 @@ execute:frappe.defaults.clear_default("fiscal_year") execute:frappe.db.set_single_value('Selling Settings', 'allow_negative_rates_for_items', 0) erpnext.patches.v14_0.correct_asset_value_if_je_with_workflow erpnext.patches.v14_0.migrate_deferred_accounts_to_item_defaults -<<<<<<< HEAD -======= -erpnext.patches.v14_0.update_invoicing_period_in_subscription -execute:frappe.delete_doc("Page", "welcome-to-erpnext") -erpnext.patches.v15_0.delete_payment_gateway_doctypes erpnext.patches.v14_0.create_accounting_dimensions_in_sales_order_item ->>>>>>> e31db18912 (chore: Add accounting dimensions to Sales Order Item table) # below migration patch should always run last erpnext.patches.v14_0.migrate_gl_to_payment_ledger From 022f85dd085c530955207e77d533f6d136f28156 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 18 Oct 2023 17:21:46 +0530 Subject: [PATCH 006/151] fix: e-commerce permissions for address (#37554) * fix: E-commerce permissions (cherry picked from commit f4d74990fe1cc2abda56359ce8d09644526c62a6) # Conflicts: # erpnext/controllers/selling_controller.py * chore: conflicts --------- Co-authored-by: Ankush Menat --- erpnext/accounts/party.py | 32 ++++++++++++++++------- erpnext/controllers/selling_controller.py | 6 +++-- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index 0c51e727c92..66b56684fae 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -6,11 +6,7 @@ from typing import Optional import frappe from frappe import _, msgprint, scrub -from frappe.contacts.doctype.address.address import ( - get_address_display, - get_company_address, - get_default_address, -) +from frappe.contacts.doctype.address.address import get_company_address, get_default_address from frappe.contacts.doctype.contact.contact import get_contact_details from frappe.core.doctype.user_permission.user_permission import get_permitted_documents from frappe.model.utils import get_fetch_values @@ -133,6 +129,7 @@ def _get_party_details( party_address, company_address, shipping_address, + ignore_permissions=ignore_permissions, ) set_contact_details(party_details, party, party_type) set_other_values(party_details, party, party_type) @@ -193,6 +190,8 @@ def set_address_details( party_address=None, company_address=None, shipping_address=None, + *, + ignore_permissions=False ): billing_address_field = ( "customer_address" if party_type == "Lead" else party_type.lower() + "_address" @@ -205,13 +204,17 @@ def set_address_details( get_fetch_values(doctype, billing_address_field, party_details[billing_address_field]) ) # address display - party_details.address_display = get_address_display(party_details[billing_address_field]) + party_details.address_display = render_address( + party_details[billing_address_field], check_permissions=not ignore_permissions + ) # shipping address if party_type in ["Customer", "Lead"]: party_details.shipping_address_name = shipping_address or get_party_shipping_address( party_type, party.name ) - party_details.shipping_address = get_address_display(party_details["shipping_address_name"]) + party_details.shipping_address = render_address( + party_details["shipping_address_name"], check_permissions=not ignore_permissions + ) if doctype: party_details.update( get_fetch_values(doctype, "shipping_address_name", party_details.shipping_address_name) @@ -229,7 +232,7 @@ def set_address_details( if shipping_address: party_details.update( shipping_address=shipping_address, - shipping_address_display=get_address_display(shipping_address), + shipping_address_display=render_address(shipping_address), **get_fetch_values(doctype, "shipping_address", shipping_address) ) @@ -238,7 +241,8 @@ def set_address_details( party_details.update( billing_address=party_details.company_address, billing_address_display=( - party_details.company_address_display or get_address_display(party_details.company_address) + party_details.company_address_display + or render_address(party_details.company_address, check_permissions=False) ), **get_fetch_values(doctype, "billing_address", party_details.company_address) ) @@ -957,3 +961,13 @@ def add_party_account(party_type, party, company, account): doc.append("accounts", accounts) doc.save() + + +def render_address(address, check_permissions=True): + try: + from frappe.contacts.doctype.address.address import render_address as _render + except ImportError: + # Older frappe versions where this function is not available + from frappe.contacts.doctype.address.address import get_address_display as _render + + return frappe.call(_render, address, check_permissions=check_permissions) diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index 5daaba8e892..f005a7f7a39 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -4,9 +4,9 @@ import frappe from frappe import _, bold, throw -from frappe.contacts.doctype.address.address import get_address_display from frappe.utils import cint, cstr, flt, get_link_to_form, nowtime +from erpnext.accounts.party import render_address from erpnext.controllers.accounts_controller import get_taxes_and_charges from erpnext.controllers.sales_and_purchase_return import get_rate_for_return from erpnext.controllers.stock_controller import StockController @@ -591,7 +591,9 @@ class SellingController(StockController): for address_field, address_display_field in address_dict.items(): if self.get(address_field): - self.set(address_display_field, get_address_display(self.get(address_field))) + self.set( + address_display_field, render_address(self.get(address_field), check_permissions=False) + ) def validate_for_duplicate_items(self): check_list, chk_dupl_itm = [], [] From 95abd7908f7548e14401390f3c74fc8679eaf8c2 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 19 Oct 2023 13:38:24 +0530 Subject: [PATCH 007/151] fix: payment entry count on supplier dashboard (backport #37571) (#37575) fix: payment entry count on supplier dashboard (#37571) (cherry picked from commit 10311ff114b7513b47df9be993fa568d6e586f1d) Co-authored-by: rohitwaghchaure --- erpnext/buying/doctype/supplier/supplier_dashboard.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/buying/doctype/supplier/supplier_dashboard.py b/erpnext/buying/doctype/supplier/supplier_dashboard.py index 11bb06e0caa..3bd306e6591 100644 --- a/erpnext/buying/doctype/supplier/supplier_dashboard.py +++ b/erpnext/buying/doctype/supplier/supplier_dashboard.py @@ -8,7 +8,7 @@ def get_data(): "This is based on transactions against this Supplier. See timeline below for details" ), "fieldname": "supplier", - "non_standard_fieldnames": {"Payment Entry": "party_name", "Bank Account": "party"}, + "non_standard_fieldnames": {"Payment Entry": "party", "Bank Account": "party"}, "transactions": [ {"label": _("Procurement"), "items": ["Request for Quotation", "Supplier Quotation"]}, {"label": _("Orders"), "items": ["Purchase Order", "Purchase Receipt", "Purchase Invoice"]}, From e1504efd406398f5688f705c863fc1e9bc49dae5 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 19 Oct 2023 13:50:55 +0530 Subject: [PATCH 008/151] fix: Issues related to RFQ and Supplier Quotation on Portal (backport #37565) (#37577) * fix: Issues related to RFQ and Supplier Quotation on Portal (#37565) fix: RFQ and Supplier Quotation for Portal (cherry picked from commit 2851a41310a050afee753c8ac396eb808d0d123c) * chore: removed backport changes --------- Co-authored-by: rohitwaghchaure --- erpnext/accounts/party.py | 30 +++++++++++++++++-- .../includes/order/order_macros.html | 2 +- erpnext/templates/includes/rfq.js | 4 +-- .../templates/includes/rfq/rfq_macros.html | 24 +++++++++------ erpnext/templates/pages/order.html | 4 +-- erpnext/templates/pages/rfq.html | 4 +-- 6 files changed, 50 insertions(+), 18 deletions(-) diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index 66b56684fae..150b56742e4 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -7,7 +7,6 @@ from typing import Optional import frappe from frappe import _, msgprint, scrub from frappe.contacts.doctype.address.address import get_company_address, get_default_address -from frappe.contacts.doctype.contact.contact import get_contact_details from frappe.core.doctype.user_permission.user_permission import get_permitted_documents from frappe.model.utils import get_fetch_values from frappe.query_builder.functions import Abs, Date, Sum @@ -294,7 +293,34 @@ def set_contact_details(party_details, party, party_type): } ) else: - party_details.update(get_contact_details(party_details.contact_person)) + fields = [ + "name as contact_person", + "salutation", + "first_name", + "last_name", + "email_id as contact_email", + "mobile_no as contact_mobile", + "phone as contact_phone", + "designation as contact_designation", + "department as contact_department", + ] + + contact_details = frappe.db.get_value( + "Contact", party_details.contact_person, fields, as_dict=True + ) + + contact_details.contact_display = " ".join( + filter( + None, + [ + contact_details.get("salutation"), + contact_details.get("first_name"), + contact_details.get("last_name"), + ], + ) + ) + + party_details.update(contact_details) def set_other_values(party_details, party, party_type): diff --git a/erpnext/templates/includes/order/order_macros.html b/erpnext/templates/includes/order/order_macros.html index d95b28961c6..8799a3b1eab 100644 --- a/erpnext/templates/includes/order/order_macros.html +++ b/erpnext/templates/includes/order/order_macros.html @@ -7,7 +7,7 @@ {% if d.thumbnail or d.image %} {{ product_image(d.thumbnail or d.image, no_border=True) }} {% else %} -
+
{{ frappe.utils.get_abbr(d.item_name) or "NA" }}
{% endif %} diff --git a/erpnext/templates/includes/rfq.js b/erpnext/templates/includes/rfq.js index 37beb5a584b..e78776fd29f 100644 --- a/erpnext/templates/includes/rfq.js +++ b/erpnext/templates/includes/rfq.js @@ -72,7 +72,7 @@ rfq = class rfq { } submit_rfq(){ - $('.btn-sm').click(function(){ + $('.btn-sm').click(function() { frappe.freeze(); frappe.call({ type: "POST", @@ -81,7 +81,7 @@ rfq = class rfq { doc: doc }, btn: this, - callback: function(r){ + callback: function(r) { frappe.unfreeze(); if(r.message){ $('.btn-sm').hide() diff --git a/erpnext/templates/includes/rfq/rfq_macros.html b/erpnext/templates/includes/rfq/rfq_macros.html index 88724c30de6..78ec6ff5f8b 100644 --- a/erpnext/templates/includes/rfq/rfq_macros.html +++ b/erpnext/templates/includes/rfq/rfq_macros.html @@ -1,19 +1,25 @@ {% from "erpnext/templates/includes/macros.html" import product_image_square, product_image %} {% macro item_name_and_description(d, doc) %} -
-
- {{ product_image(d.image) }} -
-
- {{ d.item_code }} -

{{ d.description }}

+
+
+ {% if d.image %} + {{ product_image(d.image) }} + {% else %} +
+ {{ frappe.utils.get_abbr(d.item_name)}} +
+ {% endif %} +
+
+ {{ d.item_code }} +

{{ d.description }}

{% set supplier_part_no = frappe.db.get_value("Item Supplier", {'parent': d.item_code, 'supplier': doc.supplier}, "supplier_part_no") %}

{% if supplier_part_no %} {{_("Supplier Part No") + ": "+ supplier_part_no}} {% endif %}

-
-
+
+
{% endmacro %} diff --git a/erpnext/templates/pages/order.html b/erpnext/templates/pages/order.html index bc34ad5ac50..d9cb75ac5ac 100644 --- a/erpnext/templates/pages/order.html +++ b/erpnext/templates/pages/order.html @@ -165,7 +165,6 @@
{% endif %} - {% if attachments %}
@@ -193,6 +192,7 @@ {% endif %} {% endblock %} + {% block script %} -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/erpnext/templates/pages/rfq.html b/erpnext/templates/pages/rfq.html index 6516482c230..d371bf2161d 100644 --- a/erpnext/templates/pages/rfq.html +++ b/erpnext/templates/pages/rfq.html @@ -1,7 +1,7 @@ {% extends "templates/web.html" %} {% block header %} -

{{ doc.name }}

+

{{ doc.name }}

{% endblock %} {% block script %} @@ -16,7 +16,7 @@ {% if doc.items %} + {{ _("Make Quotation") }} {% endif %} {% endblock %} From 6931db98f19b039d5cd53002c4070dce75dc5ca9 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Thu, 19 Oct 2023 11:36:28 +0000 Subject: [PATCH 009/151] chore(release): Bumped to Version 14.44.1 ## [14.44.1](https://github.com/frappe/erpnext/compare/v14.44.0...v14.44.1) (2023-10-19) ### Bug Fixes * billed_qty to show a sum of all invoiced qty from the purchase order item. (backport [#37539](https://github.com/frappe/erpnext/issues/37539)) ([#37558](https://github.com/frappe/erpnext/issues/37558)) ([ac7d6d6](https://github.com/frappe/erpnext/commit/ac7d6d6d59c96a2e05ba8f68ce47b3576ba4aa48)) * consider received qty while creating SO -> MR (backport [#37414](https://github.com/frappe/erpnext/issues/37414)) ([#37514](https://github.com/frappe/erpnext/issues/37514)) ([1b94510](https://github.com/frappe/erpnext/commit/1b94510f08cb50dd786eddc6a20f1ef5f321beb0)) * don't set finance books if gross_purchase_amount is not set (backport [#37480](https://github.com/frappe/erpnext/issues/37480)) ([#37482](https://github.com/frappe/erpnext/issues/37482)) ([0590f21](https://github.com/frappe/erpnext/commit/0590f21814c189df07d87bc08a33c9d4fed4b95e)) * e-commerce permissions for address ([#37554](https://github.com/frappe/erpnext/issues/37554)) ([022f85d](https://github.com/frappe/erpnext/commit/022f85dd085c530955207e77d533f6d136f28156)) * german tranlations of "Is Return" ([f9b2355](https://github.com/frappe/erpnext/commit/f9b2355066b04489b7f4a69270eeac5032d5c32c)) * GL Entries not getting created for PR Return (backport [#37513](https://github.com/frappe/erpnext/issues/37513)) ([#37516](https://github.com/frappe/erpnext/issues/37516)) ([c32258e](https://github.com/frappe/erpnext/commit/c32258e4b690758b4a2e667edd85caa99083ab62)) * **gp:** wrong `allocated_amount` on multi sales person invoice ([d266423](https://github.com/frappe/erpnext/commit/d266423011ff4c16f3e12231df34ffeeed56cf62)) * Incorrect vat amount in KSA VAT report ([44f7de0](https://github.com/frappe/erpnext/commit/44f7de0f31f4464f2823038356bb52f479fa3d5b)) * inflated total amt in TDS report using back calculation ([78e22af](https://github.com/frappe/erpnext/commit/78e22af3caf6b07be1b80a03f3cd78ee874e1925)) * Issues related to RFQ and Supplier Quotation on Portal (backport [#37565](https://github.com/frappe/erpnext/issues/37565)) ([#37577](https://github.com/frappe/erpnext/issues/37577)) ([e1504ef](https://github.com/frappe/erpnext/commit/e1504efd406398f5688f705c863fc1e9bc49dae5)) * keep customer/supplier website role by default ([76ef61c](https://github.com/frappe/erpnext/commit/76ef61c24fab6190790034da0e5e8ea1d3d2e242)) * keyerror on gl and pl comparision report ([6f143d3](https://github.com/frappe/erpnext/commit/6f143d35aab7258832acf8ded0ab42d339e33dc0)) * payment entry count on supplier dashboard (backport [#37571](https://github.com/frappe/erpnext/issues/37571)) ([#37575](https://github.com/frappe/erpnext/issues/37575)) ([95abd79](https://github.com/frappe/erpnext/commit/95abd7908f7548e14401390f3c74fc8679eaf8c2)) * same Serial No get mapped while creating SO -> DN ([#37527](https://github.com/frappe/erpnext/issues/37527)) ([5025850](https://github.com/frappe/erpnext/commit/50258502585d30791bd6716b4d2e2935721047f9)) * serial and batch no get removed on save of return DN ([#37476](https://github.com/frappe/erpnext/issues/37476)) ([f1814a1](https://github.com/frappe/erpnext/commit/f1814a1a2a1c82c83c0f04fcc27b3b92526dd906)) * Stock Reconciliation Insufficient Stock Error ([#37494](https://github.com/frappe/erpnext/issues/37494)) ([9406ddb](https://github.com/frappe/erpnext/commit/9406ddbff08d895a01aa2b9cd132879bea3a0fbc)) * **test:** project test case (backport [#37541](https://github.com/frappe/erpnext/issues/37541)) ([#37543](https://github.com/frappe/erpnext/issues/37543)) ([e23710b](https://github.com/frappe/erpnext/commit/e23710bf005c83abd84a1bc5cdaefcd17e3c7427)) * use `flt` to ignore TypeError ([#37481](https://github.com/frappe/erpnext/issues/37481)) ([d2b22db](https://github.com/frappe/erpnext/commit/d2b22db5001ae6544e872234c6c3434f24c5a6b1)) ### Performance Improvements * index `dn_detail` in `Delivery Note Item` (backport [#37528](https://github.com/frappe/erpnext/issues/37528)) ([#37530](https://github.com/frappe/erpnext/issues/37530)) ([001c230](https://github.com/frappe/erpnext/commit/001c230688b6cd757c7f0bd39afc2c1543c21080)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 26f7360563a..31f1fa066e4 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "14.44.0" +__version__ = "14.44.1" def get_default_company(user=None): From 8660faaa54aa4e35d0c339e1ba471adb1998b40c Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 20 Oct 2023 16:12:42 +0530 Subject: [PATCH 010/151] fix(delivery): rename dt fetch stop action (backport #37605) (#37606) fix(delivery): rename dt fetch stop action (cherry picked from commit 79d51a0a0b685909371e9bda68d9702fb287c53e) Co-authored-by: David Arnold --- erpnext/stock/doctype/delivery_trip/delivery_trip.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/delivery_trip/delivery_trip.js b/erpnext/stock/doctype/delivery_trip/delivery_trip.js index a6fbb66aa2b..45064d8fcb9 100755 --- a/erpnext/stock/doctype/delivery_trip/delivery_trip.js +++ b/erpnext/stock/doctype/delivery_trip/delivery_trip.js @@ -62,7 +62,7 @@ frappe.ui.form.on('Delivery Trip', { company: frm.doc.company, } }) - }, __("Get customers from")); + }, __("Get stops from")); } }, From 50daf701dd04d712e16c1353cae046981c5e16b7 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 20 Oct 2023 17:02:56 +0530 Subject: [PATCH 011/151] fix: incorrect cost center in the purchase invoice (backport #37591) (#37609) * fix: incorrect cost center in the purchase invoice (#37591) (cherry picked from commit 14b009b09355f53b1dfcd05d0f7ba918b0b25210) # Conflicts: # erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py * chore: fix conflicts --------- Co-authored-by: rohitwaghchaure --- .../purchase_invoice/test_purchase_invoice.py | 24 +++++++++++++++++++ erpnext/stock/doctype/item/test_item.py | 12 +++++++++- erpnext/stock/get_item_details.py | 6 +++++ 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index 170a163b45f..1f60a11908c 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -1858,6 +1858,30 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin): pi.load_from_db() self.assertFalse(pi.repost_required) + def test_default_cost_center_for_purchase(self): + from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center + + for c_center in ["_Test Cost Center Selling", "_Test Cost Center Buying"]: + create_cost_center(cost_center_name=c_center) + + item = create_item( + "_Test Cost Center Item For Purchase", + is_stock_item=1, + buying_cost_center="_Test Cost Center Buying - _TC", + selling_cost_center="_Test Cost Center Selling - _TC", + ) + + pi = make_purchase_invoice( + item=item.name, qty=1, rate=1000, update_stock=True, do_not_submit=True, cost_center="" + ) + + pi.items[0].cost_center = "" + pi.set_missing_values() + pi.calculate_taxes_and_totals() + pi.save() + + self.assertEqual(pi.items[0].cost_center, "_Test Cost Center Buying - _TC") + def check_gl_entries( doc, diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py index 53f6b7f8f17..9aa66a9a1ec 100644 --- a/erpnext/stock/doctype/item/test_item.py +++ b/erpnext/stock/doctype/item/test_item.py @@ -893,6 +893,8 @@ def create_item( opening_stock=0, is_fixed_asset=0, asset_category=None, + buying_cost_center=None, + selling_cost_center=None, company="_Test Company", ): if not frappe.db.exists("Item", item_code): @@ -910,7 +912,15 @@ def create_item( item.is_purchase_item = is_purchase_item item.is_customer_provided_item = is_customer_provided_item item.customer = customer or "" - item.append("item_defaults", {"default_warehouse": warehouse, "company": company}) + item.append( + "item_defaults", + { + "default_warehouse": warehouse, + "company": company, + "selling_cost_center": selling_cost_center, + "buying_cost_center": buying_cost_center, + }, + ) item.save() else: item = frappe.get_doc("Item", item_code) diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 92c945b254b..4a6ad70d291 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -771,6 +771,12 @@ def get_default_cost_center(args, item=None, item_group=None, brand=None, compan data = frappe.get_attr(path)(args.get("item_code"), company) if data and (data.selling_cost_center or data.buying_cost_center): + if args.get("customer") and data.selling_cost_center: + return data.selling_cost_center + + elif args.get("supplier") and data.buying_cost_center: + return data.buying_cost_center + return data.selling_cost_center or data.buying_cost_center if not cost_center and args.get("cost_center"): From 1cb9f4cf8b7ea4bb9e29c6b8ab171238a6c68305 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 23 Oct 2023 10:04:02 +0530 Subject: [PATCH 012/151] fix(minor): filter bank accounts in bank statement import (#37525) fix(minor): filter bank accounts in bank statement import (#37525) fix: filter by company in bank account (cherry picked from commit 9d392970f02b510799baa7123e1eb64fbb62dcf5) Co-authored-by: Gursheen Kaur Anand <40693548+GursheenK@users.noreply.github.com> --- .../bank_statement_import/bank_statement_import.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.js b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.js index a70af7a90e3..db68dfad79e 100644 --- a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.js +++ b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.js @@ -2,6 +2,16 @@ // For license information, please see license.txt frappe.ui.form.on("Bank Statement Import", { + onload(frm) { + frm.set_query("bank_account", function (doc) { + return { + filters: { + company: doc.company, + }, + }; + }); + }, + setup(frm) { frappe.realtime.on("data_import_refresh", ({ data_import }) => { frm.import_in_progress = false; From ec208b8df557649cfa4c70387ef5c3297c251866 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 23 Oct 2023 10:15:46 +0530 Subject: [PATCH 013/151] fix: set empty value for tax template in item details (#37496) * fix: set empty value for tax template in item details (#37496) * fix: empty tax template for items with invalid templates * fix: test for empty tax template * fix: test for item tax template calculation * fix: test for pos inv tax template calculation (cherry picked from commit b0d440c34b9cb4d0e0d75153c279ccaa6206253d) # Conflicts: # erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py * chore: resolve conflicts --------- Co-authored-by: Gursheen Kaur Anand <40693548+GursheenK@users.noreply.github.com> --- .../doctype/pos_invoice/test_pos_invoice.py | 116 ++++++++--------- .../sales_invoice/test_sales_invoice.py | 120 +++++++++--------- erpnext/stock/doctype/item/test_item.py | 10 +- erpnext/stock/get_item_details.py | 1 + 4 files changed, 123 insertions(+), 124 deletions(-) diff --git a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py index bd2fee3b0ad..4ea6ccb5859 100644 --- a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py @@ -5,6 +5,8 @@ import copy import unittest import frappe +from frappe import _ +from frappe.utils import add_days, nowdate from erpnext.accounts.doctype.pos_invoice.pos_invoice import make_sales_return from erpnext.accounts.doctype.pos_profile.test_pos_profile import make_pos_profile @@ -119,70 +121,64 @@ class TestPOSInvoice(unittest.TestCase): self.assertEqual(inv.grand_total, 5474.0) def test_tax_calculation_with_item_tax_template(self): - inv = create_pos_invoice(qty=84, rate=4.6, do_not_save=1) - item_row = inv.get("items")[0] + import json - add_items = [ - (54, "_Test Account Excise Duty @ 12 - _TC"), - (288, "_Test Account Excise Duty @ 15 - _TC"), - (144, "_Test Account Excise Duty @ 20 - _TC"), - (430, "_Test Item Tax Template 1 - _TC"), + from erpnext.stock.get_item_details import get_item_details + + # set tax template in item + item = frappe.get_cached_doc("Item", "_Test Item") + item.set( + "taxes", + [ + { + "item_tax_template": "_Test Account Excise Duty @ 15 - _TC", + "valid_from": add_days(nowdate(), -5), + } + ], + ) + item.save() + + # create POS invoice with item + pos_inv = create_pos_invoice(qty=84, rate=4.6, do_not_save=True) + item_details = get_item_details( + doc=pos_inv, + args={ + "item_code": item.item_code, + "company": pos_inv.company, + "doctype": "POS Invoice", + "conversion_rate": 1.0, + }, + ) + tax_map = json.loads(item_details.item_tax_rate) + for tax in tax_map: + pos_inv.append( + "taxes", + { + "charge_type": "On Net Total", + "account_head": tax, + "rate": tax_map[tax], + "description": "Test", + "cost_center": "_Test Cost Center - _TC", + }, + ) + pos_inv.submit() + pos_inv.load_from_db() + + # check if correct tax values are applied from tax template + self.assertEqual(pos_inv.net_total, 386.4) + + expected_taxes = [ + { + "tax_amount": 57.96, + "total": 444.36, + }, ] - for qty, item_tax_template in add_items: - item_row_copy = copy.deepcopy(item_row) - item_row_copy.qty = qty - item_row_copy.item_tax_template = item_tax_template - inv.append("items", item_row_copy) - inv.append( - "taxes", - { - "account_head": "_Test Account Excise Duty - _TC", - "charge_type": "On Net Total", - "cost_center": "_Test Cost Center - _TC", - "description": "Excise Duty", - "doctype": "Sales Taxes and Charges", - "rate": 11, - }, - ) - inv.append( - "taxes", - { - "account_head": "_Test Account Education Cess - _TC", - "charge_type": "On Net Total", - "cost_center": "_Test Cost Center - _TC", - "description": "Education Cess", - "doctype": "Sales Taxes and Charges", - "rate": 0, - }, - ) - inv.append( - "taxes", - { - "account_head": "_Test Account S&H Education Cess - _TC", - "charge_type": "On Net Total", - "cost_center": "_Test Cost Center - _TC", - "description": "S&H Education Cess", - "doctype": "Sales Taxes and Charges", - "rate": 3, - }, - ) - inv.insert() + for i in range(len(expected_taxes)): + for key in expected_taxes[i]: + self.assertEqual(expected_taxes[i][key], pos_inv.get("taxes")[i].get(key)) - self.assertEqual(inv.net_total, 4600) - - self.assertEqual(inv.get("taxes")[0].tax_amount, 502.41) - self.assertEqual(inv.get("taxes")[0].total, 5102.41) - - self.assertEqual(inv.get("taxes")[1].tax_amount, 197.80) - self.assertEqual(inv.get("taxes")[1].total, 5300.21) - - self.assertEqual(inv.get("taxes")[2].tax_amount, 375.36) - self.assertEqual(inv.get("taxes")[2].total, 5675.57) - - self.assertEqual(inv.grand_total, 5675.57) - self.assertEqual(inv.rounding_adjustment, 0.43) - self.assertEqual(inv.rounded_total, 5676.0) + self.assertEqual(pos_inv.get("base_total_taxes_and_charges"), 57.96) def test_tax_calculation_with_multiple_items_and_discount(self): inv = create_pos_invoice(qty=1, rate=75, do_not_save=True) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 272382e8c18..5174bd37d41 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -509,70 +509,72 @@ class TestSalesInvoice(FrappeTestCase): self.assertEqual(si.grand_total, 5474.0) def test_tax_calculation_with_item_tax_template(self): + import json + + from erpnext.stock.get_item_details import get_item_details + + # set tax template in item + item = frappe.get_cached_doc("Item", "_Test Item") + item.set( + "taxes", + [ + { + "item_tax_template": "_Test Item Tax Template 1 - _TC", + "valid_from": add_days(nowdate(), -5), + } + ], + ) + item.save() + + # create sales invoice with item si = create_sales_invoice(qty=84, rate=4.6, do_not_save=True) - item_row = si.get("items")[0] + item_details = get_item_details( + doc=si, + args={ + "item_code": item.item_code, + "company": si.company, + "doctype": "Sales Invoice", + "conversion_rate": 1.0, + }, + ) + tax_map = json.loads(item_details.item_tax_rate) + for tax in tax_map: + si.append( + "taxes", + { + "charge_type": "On Net Total", + "account_head": tax, + "rate": tax_map[tax], + "description": "Test", + "cost_center": "_Test Cost Center - _TC", + }, + ) + si.submit() + si.load_from_db() - add_items = [ - (54, "_Test Account Excise Duty @ 12 - _TC"), - (288, "_Test Account Excise Duty @ 15 - _TC"), - (144, "_Test Account Excise Duty @ 20 - _TC"), - (430, "_Test Item Tax Template 1 - _TC"), + # check if correct tax values are applied from tax template + self.assertEqual(si.net_total, 386.4) + + expected_taxes = [ + { + "tax_amount": 19.32, + "total": 405.72, + }, + { + "tax_amount": 38.64, + "total": 444.36, + }, + { + "tax_amount": 57.96, + "total": 502.32, + }, ] - for qty, item_tax_template in add_items: - item_row_copy = copy.deepcopy(item_row) - item_row_copy.qty = qty - item_row_copy.item_tax_template = item_tax_template - si.append("items", item_row_copy) - si.append( - "taxes", - { - "account_head": "_Test Account Excise Duty - _TC", - "charge_type": "On Net Total", - "cost_center": "_Test Cost Center - _TC", - "description": "Excise Duty", - "doctype": "Sales Taxes and Charges", - "rate": 11, - }, - ) - si.append( - "taxes", - { - "account_head": "_Test Account Education Cess - _TC", - "charge_type": "On Net Total", - "cost_center": "_Test Cost Center - _TC", - "description": "Education Cess", - "doctype": "Sales Taxes and Charges", - "rate": 0, - }, - ) - si.append( - "taxes", - { - "account_head": "_Test Account S&H Education Cess - _TC", - "charge_type": "On Net Total", - "cost_center": "_Test Cost Center - _TC", - "description": "S&H Education Cess", - "doctype": "Sales Taxes and Charges", - "rate": 3, - }, - ) - si.insert() + for i in range(len(expected_taxes)): + for key in expected_taxes[i]: + self.assertEqual(expected_taxes[i][key], si.get("taxes")[i].get(key)) - self.assertEqual(si.net_total, 4600) - - self.assertEqual(si.get("taxes")[0].tax_amount, 502.41) - self.assertEqual(si.get("taxes")[0].total, 5102.41) - - self.assertEqual(si.get("taxes")[1].tax_amount, 197.80) - self.assertEqual(si.get("taxes")[1].total, 5300.21) - - self.assertEqual(si.get("taxes")[2].tax_amount, 375.36) - self.assertEqual(si.get("taxes")[2].total, 5675.57) - - self.assertEqual(si.grand_total, 5675.57) - self.assertEqual(si.rounding_adjustment, 0.43) - self.assertEqual(si.rounded_total, 5676.0) + self.assertEqual(si.get("base_total_taxes_and_charges"), 115.92) def test_tax_calculation_with_multiple_items_and_discount(self): si = create_sales_invoice(qty=1, rate=75, do_not_save=True) diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py index 9aa66a9a1ec..f1d4f8a5859 100644 --- a/erpnext/stock/doctype/item/test_item.py +++ b/erpnext/stock/doctype/item/test_item.py @@ -163,7 +163,7 @@ class TestItem(FrappeTestCase): { "item_code": "_Test Item With Item Tax Template", "tax_category": "_Test Tax Category 2", - "item_tax_template": None, + "item_tax_template": "", }, { "item_code": "_Test Item Inherit Group Item Tax Template 1", @@ -178,7 +178,7 @@ class TestItem(FrappeTestCase): { "item_code": "_Test Item Inherit Group Item Tax Template 1", "tax_category": "_Test Tax Category 2", - "item_tax_template": None, + "item_tax_template": "", }, { "item_code": "_Test Item Inherit Group Item Tax Template 2", @@ -193,7 +193,7 @@ class TestItem(FrappeTestCase): { "item_code": "_Test Item Inherit Group Item Tax Template 2", "tax_category": "_Test Tax Category 2", - "item_tax_template": None, + "item_tax_template": "", }, { "item_code": "_Test Item Override Group Item Tax Template", @@ -208,12 +208,12 @@ class TestItem(FrappeTestCase): { "item_code": "_Test Item Override Group Item Tax Template", "tax_category": "_Test Tax Category 2", - "item_tax_template": None, + "item_tax_template": "", }, ] expected_item_tax_map = { - None: {}, + "": {}, "_Test Account Excise Duty @ 10 - _TC": {"_Test Account Excise Duty - _TC": 10}, "_Test Account Excise Duty @ 12 - _TC": {"_Test Account Excise Duty - _TC": 12}, "_Test Account Excise Duty @ 15 - _TC": {"_Test Account Excise Duty - _TC": 15}, diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 4a6ad70d291..9b910205be8 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -640,6 +640,7 @@ def _get_item_tax_template(args, taxes, out=None, for_validate=False): # all templates have validity and no template is valid if not taxes_with_validity and (not taxes_with_no_validity): + out["item_tax_template"] = "" return None # do not change if already a valid template From c05e0a4ffc7df7b2fb62e8e2c9fec7e71bef64aa Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 23 Oct 2023 11:01:23 +0530 Subject: [PATCH 014/151] fix(minor): filter tax template based on company in subscription (#37562) fix: filter tax template based on company (cherry picked from commit 1a2f659de2a06bea513ced0a5b8ff007ebec6437) Co-authored-by: Gursheen Anand --- erpnext/accounts/doctype/subscription/subscription.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/erpnext/accounts/doctype/subscription/subscription.js b/erpnext/accounts/doctype/subscription/subscription.js index 1a9066470a5..4b351f9d764 100644 --- a/erpnext/accounts/doctype/subscription/subscription.js +++ b/erpnext/accounts/doctype/subscription/subscription.js @@ -18,6 +18,14 @@ frappe.ui.form.on('Subscription', { } }; }); + + frm.set_query('sales_tax_template', function () { + return { + filters: { + company: frm.doc.company + } + }; + }); }, refresh: function(frm) { From 8e31379ecca19da28846ca5290ac07326022539f Mon Sep 17 00:00:00 2001 From: saeedkola Date: Mon, 23 Oct 2023 11:14:06 +0530 Subject: [PATCH 015/151] fix: Cash flow mapping fix (#37522) Cash flow mapping fix --- erpnext/accounts/report/cash_flow/custom_cash_flow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/cash_flow/custom_cash_flow.py b/erpnext/accounts/report/cash_flow/custom_cash_flow.py index b165c88c068..24e585e07f6 100644 --- a/erpnext/accounts/report/cash_flow/custom_cash_flow.py +++ b/erpnext/accounts/report/cash_flow/custom_cash_flow.py @@ -67,7 +67,7 @@ def setup_mappers(mappers): mapping["finance_costs"] = [] mapping["finance_costs_adjustments"] = [] doc = frappe.get_doc("Cash Flow Mapper", mapping["name"]) - mapping_names = [item.name for item in doc.accounts] + mapping_names = [item.mapping for item in doc.accounts] if not mapping_names: continue From 78b7c2642052073f93e089340bc56ad50b197152 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 23 Oct 2023 12:27:01 +0530 Subject: [PATCH 016/151] fix: remove from or target warehouse for non internal transfer entries (backport #37612) (#37628) fix: remove from or target warehouse for non internal transfer entries (#37612) (cherry picked from commit 5136fe196b4e3aab6bb18d2edf5effbfacd2b060) Co-authored-by: rohitwaghchaure --- erpnext/controllers/stock_controller.py | 22 +++++++++++++------ .../delivery_note/test_delivery_note.py | 15 +++++++++++++ .../purchase_receipt/test_purchase_receipt.py | 15 +++++++++++++ 3 files changed, 45 insertions(+), 7 deletions(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index e24d8fb661f..15ac1a86800 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -670,13 +670,21 @@ class StockController(AccountsController): d.stock_uom_rate = d.rate / (d.conversion_factor or 1) def validate_internal_transfer(self): - if ( - self.doctype in ("Sales Invoice", "Delivery Note", "Purchase Invoice", "Purchase Receipt") - and self.is_internal_transfer() - ): - self.validate_in_transit_warehouses() - self.validate_multi_currency() - self.validate_packed_items() + if self.doctype in ("Sales Invoice", "Delivery Note", "Purchase Invoice", "Purchase Receipt"): + if self.is_internal_transfer(): + self.validate_in_transit_warehouses() + self.validate_multi_currency() + self.validate_packed_items() + else: + self.validate_internal_transfer_warehouse() + + def validate_internal_transfer_warehouse(self): + for row in self.items: + if row.get("target_warehouse"): + row.target_warehouse = None + + if row.get("from_warehouse"): + row.from_warehouse = None def validate_in_transit_warehouses(self): if ( diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py index c6f3197a668..3ecfbad1d5d 100644 --- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py @@ -1315,6 +1315,21 @@ class TestDeliveryNote(FrappeTestCase): frappe.db.rollback() frappe.db.set_single_value("Selling Settings", "dont_reserve_sales_order_qty_on_sales_return", 0) + def non_internal_transfer_delivery_note(self): + from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse + + dn = create_delivery_note(do_not_submit=True) + warehouse = create_warehouse("Internal Transfer Warehouse", dn.company) + dn.items[0].db_set("target_warehouse", "warehouse") + + dn.reload() + + self.assertEqual(dn.items[0].target_warehouse, warehouse.name) + + dn.save() + dn.reload() + self.assertFalse(dn.items[0].target_warehouse) + def create_delivery_note(**args): dn = frappe.new_doc("Delivery Note") diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index a93d5b1bbbe..a61d8d258ff 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -2203,6 +2203,21 @@ class TestPurchaseReceipt(FrappeTestCase): for entry in gl_entries: self.assertEqual(abs(entry.debit + entry.credit), abs(sl_entries[0].stock_value_difference)) + def non_internal_transfer_purchase_receipt(self): + from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse + + pr_doc = make_purchase_receipt(do_not_submit=True) + warehouse = create_warehouse("Internal Transfer Warehouse", pr_doc.company) + pr_doc.items[0].db_set("target_warehouse", "warehouse") + + pr_doc.reload() + + self.assertEqual(pr_doc.items[0].from_warehouse, warehouse.name) + + pr_doc.save() + pr_doc.reload() + self.assertFalse(pr_doc.items[0].from_warehouse) + def prepare_data_for_internal_transfer(): from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier From ca13816ca177e20931ad0d27370ccadbf317a684 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 23 Oct 2023 15:11:33 +0530 Subject: [PATCH 017/151] Revert "fix: set empty value for tax template in item details (#37496)" Revert "fix: set empty value for tax template in item details (#37496)" This reverts commit ec208b8df557649cfa4c70387ef5c3297c251866. --- .../doctype/pos_invoice/test_pos_invoice.py | 116 +++++++++--------- .../sales_invoice/test_sales_invoice.py | 112 +++++++++-------- erpnext/stock/doctype/item/test_item.py | 10 +- erpnext/stock/get_item_details.py | 1 - 4 files changed, 120 insertions(+), 119 deletions(-) diff --git a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py index 4ea6ccb5859..bd2fee3b0ad 100644 --- a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py @@ -5,8 +5,6 @@ import copy import unittest import frappe -from frappe import _ -from frappe.utils import add_days, nowdate from erpnext.accounts.doctype.pos_invoice.pos_invoice import make_sales_return from erpnext.accounts.doctype.pos_profile.test_pos_profile import make_pos_profile @@ -121,64 +119,70 @@ class TestPOSInvoice(unittest.TestCase): self.assertEqual(inv.grand_total, 5474.0) def test_tax_calculation_with_item_tax_template(self): - import json + inv = create_pos_invoice(qty=84, rate=4.6, do_not_save=1) + item_row = inv.get("items")[0] - from erpnext.stock.get_item_details import get_item_details - - # set tax template in item - item = frappe.get_cached_doc("Item", "_Test Item") - item.set( - "taxes", - [ - { - "item_tax_template": "_Test Account Excise Duty @ 15 - _TC", - "valid_from": add_days(nowdate(), -5), - } - ], - ) - item.save() - - # create POS invoice with item - pos_inv = create_pos_invoice(qty=84, rate=4.6, do_not_save=True) - item_details = get_item_details( - doc=pos_inv, - args={ - "item_code": item.item_code, - "company": pos_inv.company, - "doctype": "POS Invoice", - "conversion_rate": 1.0, - }, - ) - tax_map = json.loads(item_details.item_tax_rate) - for tax in tax_map: - pos_inv.append( - "taxes", - { - "charge_type": "On Net Total", - "account_head": tax, - "rate": tax_map[tax], - "description": "Test", - "cost_center": "_Test Cost Center - _TC", - }, - ) - pos_inv.submit() - pos_inv.load_from_db() - - # check if correct tax values are applied from tax template - self.assertEqual(pos_inv.net_total, 386.4) - - expected_taxes = [ - { - "tax_amount": 57.96, - "total": 444.36, - }, + add_items = [ + (54, "_Test Account Excise Duty @ 12 - _TC"), + (288, "_Test Account Excise Duty @ 15 - _TC"), + (144, "_Test Account Excise Duty @ 20 - _TC"), + (430, "_Test Item Tax Template 1 - _TC"), ] + for qty, item_tax_template in add_items: + item_row_copy = copy.deepcopy(item_row) + item_row_copy.qty = qty + item_row_copy.item_tax_template = item_tax_template + inv.append("items", item_row_copy) - for i in range(len(expected_taxes)): - for key in expected_taxes[i]: - self.assertEqual(expected_taxes[i][key], pos_inv.get("taxes")[i].get(key)) + inv.append( + "taxes", + { + "account_head": "_Test Account Excise Duty - _TC", + "charge_type": "On Net Total", + "cost_center": "_Test Cost Center - _TC", + "description": "Excise Duty", + "doctype": "Sales Taxes and Charges", + "rate": 11, + }, + ) + inv.append( + "taxes", + { + "account_head": "_Test Account Education Cess - _TC", + "charge_type": "On Net Total", + "cost_center": "_Test Cost Center - _TC", + "description": "Education Cess", + "doctype": "Sales Taxes and Charges", + "rate": 0, + }, + ) + inv.append( + "taxes", + { + "account_head": "_Test Account S&H Education Cess - _TC", + "charge_type": "On Net Total", + "cost_center": "_Test Cost Center - _TC", + "description": "S&H Education Cess", + "doctype": "Sales Taxes and Charges", + "rate": 3, + }, + ) + inv.insert() - self.assertEqual(pos_inv.get("base_total_taxes_and_charges"), 57.96) + self.assertEqual(inv.net_total, 4600) + + self.assertEqual(inv.get("taxes")[0].tax_amount, 502.41) + self.assertEqual(inv.get("taxes")[0].total, 5102.41) + + self.assertEqual(inv.get("taxes")[1].tax_amount, 197.80) + self.assertEqual(inv.get("taxes")[1].total, 5300.21) + + self.assertEqual(inv.get("taxes")[2].tax_amount, 375.36) + self.assertEqual(inv.get("taxes")[2].total, 5675.57) + + self.assertEqual(inv.grand_total, 5675.57) + self.assertEqual(inv.rounding_adjustment, 0.43) + self.assertEqual(inv.rounded_total, 5676.0) def test_tax_calculation_with_multiple_items_and_discount(self): inv = create_pos_invoice(qty=1, rate=75, do_not_save=True) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 5174bd37d41..272382e8c18 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -509,72 +509,70 @@ class TestSalesInvoice(FrappeTestCase): self.assertEqual(si.grand_total, 5474.0) def test_tax_calculation_with_item_tax_template(self): - import json - - from erpnext.stock.get_item_details import get_item_details - - # set tax template in item - item = frappe.get_cached_doc("Item", "_Test Item") - item.set( - "taxes", - [ - { - "item_tax_template": "_Test Item Tax Template 1 - _TC", - "valid_from": add_days(nowdate(), -5), - } - ], - ) - item.save() - - # create sales invoice with item si = create_sales_invoice(qty=84, rate=4.6, do_not_save=True) - item_details = get_item_details( - doc=si, - args={ - "item_code": item.item_code, - "company": si.company, - "doctype": "Sales Invoice", - "conversion_rate": 1.0, + item_row = si.get("items")[0] + + add_items = [ + (54, "_Test Account Excise Duty @ 12 - _TC"), + (288, "_Test Account Excise Duty @ 15 - _TC"), + (144, "_Test Account Excise Duty @ 20 - _TC"), + (430, "_Test Item Tax Template 1 - _TC"), + ] + for qty, item_tax_template in add_items: + item_row_copy = copy.deepcopy(item_row) + item_row_copy.qty = qty + item_row_copy.item_tax_template = item_tax_template + si.append("items", item_row_copy) + + si.append( + "taxes", + { + "account_head": "_Test Account Excise Duty - _TC", + "charge_type": "On Net Total", + "cost_center": "_Test Cost Center - _TC", + "description": "Excise Duty", + "doctype": "Sales Taxes and Charges", + "rate": 11, }, ) - tax_map = json.loads(item_details.item_tax_rate) - for tax in tax_map: - si.append( - "taxes", - { - "charge_type": "On Net Total", - "account_head": tax, - "rate": tax_map[tax], - "description": "Test", - "cost_center": "_Test Cost Center - _TC", - }, - ) - si.submit() - si.load_from_db() - - # check if correct tax values are applied from tax template - self.assertEqual(si.net_total, 386.4) - - expected_taxes = [ + si.append( + "taxes", { - "tax_amount": 19.32, - "total": 405.72, + "account_head": "_Test Account Education Cess - _TC", + "charge_type": "On Net Total", + "cost_center": "_Test Cost Center - _TC", + "description": "Education Cess", + "doctype": "Sales Taxes and Charges", + "rate": 0, }, + ) + si.append( + "taxes", { - "tax_amount": 38.64, - "total": 444.36, + "account_head": "_Test Account S&H Education Cess - _TC", + "charge_type": "On Net Total", + "cost_center": "_Test Cost Center - _TC", + "description": "S&H Education Cess", + "doctype": "Sales Taxes and Charges", + "rate": 3, }, - { - "tax_amount": 57.96, - "total": 502.32, - }, - ] + ) + si.insert() - for i in range(len(expected_taxes)): - for key in expected_taxes[i]: - self.assertEqual(expected_taxes[i][key], si.get("taxes")[i].get(key)) + self.assertEqual(si.net_total, 4600) - self.assertEqual(si.get("base_total_taxes_and_charges"), 115.92) + self.assertEqual(si.get("taxes")[0].tax_amount, 502.41) + self.assertEqual(si.get("taxes")[0].total, 5102.41) + + self.assertEqual(si.get("taxes")[1].tax_amount, 197.80) + self.assertEqual(si.get("taxes")[1].total, 5300.21) + + self.assertEqual(si.get("taxes")[2].tax_amount, 375.36) + self.assertEqual(si.get("taxes")[2].total, 5675.57) + + self.assertEqual(si.grand_total, 5675.57) + self.assertEqual(si.rounding_adjustment, 0.43) + self.assertEqual(si.rounded_total, 5676.0) def test_tax_calculation_with_multiple_items_and_discount(self): si = create_sales_invoice(qty=1, rate=75, do_not_save=True) diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py index f1d4f8a5859..9aa66a9a1ec 100644 --- a/erpnext/stock/doctype/item/test_item.py +++ b/erpnext/stock/doctype/item/test_item.py @@ -163,7 +163,7 @@ class TestItem(FrappeTestCase): { "item_code": "_Test Item With Item Tax Template", "tax_category": "_Test Tax Category 2", - "item_tax_template": "", + "item_tax_template": None, }, { "item_code": "_Test Item Inherit Group Item Tax Template 1", @@ -178,7 +178,7 @@ class TestItem(FrappeTestCase): { "item_code": "_Test Item Inherit Group Item Tax Template 1", "tax_category": "_Test Tax Category 2", - "item_tax_template": "", + "item_tax_template": None, }, { "item_code": "_Test Item Inherit Group Item Tax Template 2", @@ -193,7 +193,7 @@ class TestItem(FrappeTestCase): { "item_code": "_Test Item Inherit Group Item Tax Template 2", "tax_category": "_Test Tax Category 2", - "item_tax_template": "", + "item_tax_template": None, }, { "item_code": "_Test Item Override Group Item Tax Template", @@ -208,12 +208,12 @@ class TestItem(FrappeTestCase): { "item_code": "_Test Item Override Group Item Tax Template", "tax_category": "_Test Tax Category 2", - "item_tax_template": "", + "item_tax_template": None, }, ] expected_item_tax_map = { - "": {}, + None: {}, "_Test Account Excise Duty @ 10 - _TC": {"_Test Account Excise Duty - _TC": 10}, "_Test Account Excise Duty @ 12 - _TC": {"_Test Account Excise Duty - _TC": 12}, "_Test Account Excise Duty @ 15 - _TC": {"_Test Account Excise Duty - _TC": 15}, diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 9b910205be8..4a6ad70d291 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -640,7 +640,6 @@ def _get_item_tax_template(args, taxes, out=None, for_validate=False): # all templates have validity and no template is valid if not taxes_with_validity and (not taxes_with_no_validity): - out["item_tax_template"] = "" return None # do not change if already a valid template From fa7fa85d92bbdba9a987c7ac41ac90cc53b64fed Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 20 Oct 2023 17:16:54 +0530 Subject: [PATCH 018/151] refactor: gain_loss posting date fields in the allocation table (cherry picked from commit 55dbcee36a2acf4aa41c66147c263a85ef606f81) --- .../payment_reconciliation_allocation.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation_allocation/payment_reconciliation_allocation.json b/erpnext/accounts/doctype/payment_reconciliation_allocation/payment_reconciliation_allocation.json index ec718aa70d3..2fddd85732e 100644 --- a/erpnext/accounts/doctype/payment_reconciliation_allocation/payment_reconciliation_allocation.json +++ b/erpnext/accounts/doctype/payment_reconciliation_allocation/payment_reconciliation_allocation.json @@ -151,11 +151,16 @@ "fieldtype": "Link", "label": "Cost Center", "options": "Cost Center" + }, + { + "fieldname": "gain_loss_posting_date", + "fieldtype": "Date", + "label": "Difference Posting Date" } ], "istable": 1, "links": [], - "modified": "2023-09-03 07:52:33.684217", + "modified": "2023-10-23 10:44:56.066303", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Reconciliation Allocation", From 063d658b0467ff9202dc397871843aa2033f52d1 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sun, 22 Oct 2023 08:59:52 +0530 Subject: [PATCH 019/151] refactor: introduce fields in popup (cherry picked from commit 5323bb7beeb6526d16bcb19fc2f3acd3a95927e6) --- .../payment_reconciliation/payment_reconciliation.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js index 6f1f34bc9f3..f05b96969cb 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js @@ -216,6 +216,7 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo this.data = []; const dialog = new frappe.ui.Dialog({ title: __("Select Difference Account"), + size: 'extra-large', fields: [ { fieldname: "allocation", @@ -239,6 +240,13 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo in_list_view: 1, read_only: 1 }, { + fieldtype:'Date', + fieldname:"gain_loss_posting_date", + label: __("Posting Date"), + in_list_view: 1, + reqd: 1, + }, { + fieldtype:'Link', options: 'Account', in_list_view: 1, @@ -272,6 +280,7 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo args.forEach(d => { frappe.model.set_value("Payment Reconciliation Allocation", d.docname, "difference_account", d.difference_account); + }); this.reconcile_payment_entries(); @@ -287,6 +296,7 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo 'reference_name': d.reference_name, 'difference_amount': d.difference_amount, 'difference_account': d.difference_account, + 'gain_loss_posting_date': d.gain_loss_posting_date }); } }); From 515bed8c808f2dc3ae720b24db5bdfc80edc06bf Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sun, 22 Oct 2023 20:26:45 +0530 Subject: [PATCH 020/151] refactor: pass gain loss posting date to controller (cherry picked from commit 7e600a6494d7f07c6fd2b8f1cc71857801a2573c) --- .../payment_reconciliation/payment_reconciliation.js | 2 ++ .../payment_reconciliation/payment_reconciliation.py | 2 ++ .../payment_reconciliation_allocation.json | 1 + erpnext/accounts/utils.py | 4 +++- erpnext/controllers/accounts_controller.py | 6 ++++-- 5 files changed, 12 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js index f05b96969cb..5cdedb73c09 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js @@ -280,6 +280,8 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo args.forEach(d => { frappe.model.set_value("Payment Reconciliation Allocation", d.docname, "difference_account", d.difference_account); + frappe.model.set_value("Payment Reconciliation Allocation", d.docname, + "gain_loss_posting_date", d.gain_loss_posting_date); }); diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index 08923e74266..897bbee0beb 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -315,6 +315,7 @@ class PaymentReconciliation(Document): res.difference_amount = self.get_difference_amount(pay, inv, res["allocated_amount"]) res.difference_account = default_exchange_gain_loss_account res.exchange_rate = inv.get("exchange_rate") + res.update({"gain_loss_posting_date": pay.get("posting_date")}) if pay.get("amount") == 0: entries.append(res) @@ -421,6 +422,7 @@ class PaymentReconciliation(Document): "allocated_amount": flt(row.get("allocated_amount")), "difference_amount": flt(row.get("difference_amount")), "difference_account": row.get("difference_account"), + "difference_posting_date": row.get("gain_loss_posting_date"), "cost_center": row.get("cost_center"), } ) diff --git a/erpnext/accounts/doctype/payment_reconciliation_allocation/payment_reconciliation_allocation.json b/erpnext/accounts/doctype/payment_reconciliation_allocation/payment_reconciliation_allocation.json index 2fddd85732e..5b8556e7c83 100644 --- a/erpnext/accounts/doctype/payment_reconciliation_allocation/payment_reconciliation_allocation.json +++ b/erpnext/accounts/doctype/payment_reconciliation_allocation/payment_reconciliation_allocation.json @@ -19,6 +19,7 @@ "is_advance", "section_break_5", "difference_amount", + "gain_loss_posting_date", "column_break_7", "difference_account", "exchange_rate", diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 76339713a22..4327a1f5531 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -657,7 +657,9 @@ def update_reference_in_payment_entry( if not skip_ref_details_update_for_pe: payment_entry.set_missing_ref_details() payment_entry.set_amounts() - payment_entry.make_exchange_gain_loss_journal() + payment_entry.make_exchange_gain_loss_journal( + frappe._dict({"difference_posting_date": d.difference_posting_date}) + ) if not do_not_save: payment_entry.save(ignore_permissions=True) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index c17866162b6..745c23a91c9 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1131,7 +1131,9 @@ class AccountsController(TransactionBase): self.name, arg.get("referenced_row"), ): - posting_date = frappe.db.get_value(arg.voucher_type, arg.voucher_no, "posting_date") + posting_date = arg.get("difference_posting_date") or frappe.db.get_value( + arg.voucher_type, arg.voucher_no, "posting_date" + ) je = create_gain_loss_journal( self.company, posting_date, @@ -1214,7 +1216,7 @@ class AccountsController(TransactionBase): je = create_gain_loss_journal( self.company, - self.posting_date, + args.get("difference_posting_date") if args else self.posting_date, self.party_type, self.party, party_account, From ae788e835816cc245ab9b8198b796b4d8afa3f48 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 23 Oct 2023 12:32:10 +0530 Subject: [PATCH 021/151] test: varying posting date for gain loss journal (cherry picked from commit 514d5434a3ae24e2c7839fbd76a115d6c0841513) --- .../tests/test_accounts_controller.py | 69 ++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/erpnext/controllers/tests/test_accounts_controller.py b/erpnext/controllers/tests/test_accounts_controller.py index 391258fde77..97d3c5c32de 100644 --- a/erpnext/controllers/tests/test_accounts_controller.py +++ b/erpnext/controllers/tests/test_accounts_controller.py @@ -7,7 +7,7 @@ import frappe from frappe import qb from frappe.query_builder.functions import Sum from frappe.tests.utils import FrappeTestCase, change_settings -from frappe.utils import add_days, flt, nowdate +from frappe.utils import add_days, flt, getdate, nowdate from erpnext import get_default_cost_center from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry @@ -614,6 +614,73 @@ class TestAccountsController(FrappeTestCase): self.assertEqual(exc_je_for_si, []) self.assertEqual(exc_je_for_pe, []) + def test_15_gain_loss_on_different_posting_date(self): + # Invoice in Foreign Currency + si = self.create_sales_invoice( + posting_date=add_days(nowdate(), -2), qty=2, conversion_rate=80, rate=1 + ) + # Payment + pe = ( + self.create_payment_entry(posting_date=add_days(nowdate(), -1), amount=2, source_exc_rate=75) + .save() + .submit() + ) + + # There should be outstanding in both currencies + si.reload() + self.assertEqual(si.outstanding_amount, 2) + self.assert_ledger_outstanding(si.doctype, si.name, 160.0, 2.0) + + # Reconcile the remaining amount + pr = frappe.get_doc("Payment Reconciliation") + pr.company = self.company + pr.party_type = "Customer" + pr.party = self.customer + pr.receivable_payable_account = self.debit_usd + pr.get_unreconciled_entries() + self.assertEqual(len(pr.invoices), 1) + self.assertEqual(len(pr.payments), 1) + invoices = [x.as_dict() for x in pr.invoices] + payments = [x.as_dict() for x in pr.payments] + pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments})) + pr.allocation[0].gain_loss_posting_date = add_days(nowdate(), 1) + pr.reconcile() + + # Exchange Gain/Loss Journal should've been created. + exc_je_for_si = self.get_journals_for(si.doctype, si.name) + exc_je_for_pe = self.get_journals_for(pe.doctype, pe.name) + self.assertNotEqual(exc_je_for_si, []) + self.assertEqual(len(exc_je_for_si), 1) + self.assertEqual(len(exc_je_for_pe), 1) + self.assertEqual(exc_je_for_si[0], exc_je_for_pe[0]) + + self.assertEqual( + frappe.db.get_value("Journal Entry", exc_je_for_si[0].parent, "posting_date"), + getdate(add_days(nowdate(), 1)), + ) + + self.assertEqual(len(pr.invoices), 0) + self.assertEqual(len(pr.payments), 0) + + # There should be no outstanding + si.reload() + self.assertEqual(si.outstanding_amount, 0) + self.assert_ledger_outstanding(si.doctype, si.name, 0.0, 0.0) + + # Cancel Payment + pe.reload() + pe.cancel() + + si.reload() + self.assertEqual(si.outstanding_amount, 2) + self.assert_ledger_outstanding(si.doctype, si.name, 160.0, 2.0) + + # Exchange Gain/Loss Journal should've been cancelled + exc_je_for_si = self.get_journals_for(si.doctype, si.name) + exc_je_for_pe = self.get_journals_for(pe.doctype, pe.name) + self.assertEqual(exc_je_for_si, []) + self.assertEqual(exc_je_for_pe, []) + def test_20_journal_against_sales_invoice(self): # Invoice in Foreign Currency si = self.create_sales_invoice(qty=1, conversion_rate=80, rate=1) From d71b885fb82fd645d607872ec211af4c20d88d31 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 19 Oct 2023 11:32:21 +0530 Subject: [PATCH 022/151] fix: overallocation on Payment with PO/SO (cherry picked from commit 23df4205f8abfca6764d84665cb9877703c1a470) # Conflicts: # erpnext/accounts/utils.py --- erpnext/accounts/utils.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 4327a1f5531..5c371517470 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -624,7 +624,12 @@ def update_reference_in_payment_entry( "outstanding_amount": d.outstanding_amount, "allocated_amount": d.allocated_amount, "exchange_rate": d.exchange_rate if d.exchange_gain_loss else payment_entry.get_exchange_rate(), +<<<<<<< HEAD "exchange_gain_loss": d.exchange_gain_loss, # only populated from invoice in case of advance allocation +======= + "exchange_gain_loss": d.exchange_gain_loss, + "account": d.account, +>>>>>>> 23df4205f8 (fix: overallocation on Payment with PO/SO) } if d.voucher_detail_no: @@ -636,22 +641,21 @@ def update_reference_in_payment_entry( existing_row.reference_doctype, existing_row.reference_name ).set_total_advance_paid() - original_row = existing_row.as_dict().copy() - existing_row.update(reference_details) + if d.allocated_amount <= existing_row.allocated_amount: + existing_row.allocated_amount -= d.allocated_amount - if d.allocated_amount < original_row.allocated_amount: new_row = payment_entry.append("references") new_row.docstatus = 1 for field in list(reference_details): - new_row.set(field, original_row[field]) + new_row.set(field, reference_details[field]) - new_row.allocated_amount = original_row.allocated_amount - d.allocated_amount else: new_row = payment_entry.append("references") new_row.docstatus = 1 new_row.update(reference_details) payment_entry.flags.ignore_validate_update_after_submit = True + payment_entry.clear_unallocated_reference_document_rows() payment_entry.setup_party_account_field() payment_entry.set_missing_values() if not skip_ref_details_update_for_pe: From c8922ad566ba5bffe97a61c544126abb264a60db Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 19 Oct 2023 14:03:21 +0530 Subject: [PATCH 023/151] test: overalloction on reconciliation when PO is involved (cherry picked from commit 946228d783cb58cd2809f4b0bc0a854c49cc4060) --- .../test_payment_reconciliation.py | 183 +++++++++++++++++- 1 file changed, 178 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py index 1d843abde1d..48d1cf2cc26 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py @@ -14,6 +14,7 @@ from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_pay from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice from erpnext.accounts.party import get_party_account +from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order from erpnext.stock.doctype.item.test_item import create_item test_dependencies = ["Item"] @@ -151,6 +152,64 @@ class TestPaymentReconciliation(FrappeTestCase): payment.posting_date = posting_date return payment + def create_purchase_invoice( + self, qty=1, rate=100, posting_date=nowdate(), do_not_save=False, do_not_submit=False + ): + """ + Helper function to populate default values in sales invoice + """ + pinv = make_purchase_invoice( + qty=qty, + rate=rate, + company=self.company, + customer=self.supplier, + item_code=self.item, + item_name=self.item, + cost_center=self.cost_center, + warehouse=self.warehouse, + debit_to=self.debit_to, + parent_cost_center=self.cost_center, + update_stock=0, + currency="INR", + is_pos=0, + is_return=0, + return_against=None, + income_account=self.income_account, + expense_account=self.expense_account, + do_not_save=do_not_save, + do_not_submit=do_not_submit, + ) + return pinv + + def create_purchase_order( + self, qty=1, rate=100, posting_date=nowdate(), do_not_save=False, do_not_submit=False + ): + """ + Helper function to populate default values in sales invoice + """ + pord = create_purchase_order( + qty=qty, + rate=rate, + company=self.company, + customer=self.supplier, + item_code=self.item, + item_name=self.item, + cost_center=self.cost_center, + warehouse=self.warehouse, + debit_to=self.debit_to, + parent_cost_center=self.cost_center, + update_stock=0, + currency="INR", + is_pos=0, + is_return=0, + return_against=None, + income_account=self.income_account, + expense_account=self.expense_account, + do_not_save=do_not_save, + do_not_submit=do_not_submit, + ) + return pord + def clear_old_entries(self): doctype_list = [ "GL Entry", @@ -163,13 +222,11 @@ class TestPaymentReconciliation(FrappeTestCase): for doctype in doctype_list: qb.from_(qb.DocType(doctype)).delete().where(qb.DocType(doctype).company == self.company).run() - def create_payment_reconciliation(self): + def create_payment_reconciliation(self, party_is_customer=True): pr = frappe.new_doc("Payment Reconciliation") pr.company = self.company - pr.party_type = ( - self.party_type if hasattr(self, "party_type") and self.party_type else "Customer" - ) - pr.party = self.customer + pr.party_type = "Customer" if party_is_customer else "Supplier" + pr.party = self.customer if party_is_customer else self.supplier pr.receivable_payable_account = get_party_account(pr.party_type, pr.party, pr.company) pr.from_invoice_date = pr.to_invoice_date = pr.from_payment_date = pr.to_payment_date = nowdate() return pr @@ -931,6 +988,7 @@ class TestPaymentReconciliation(FrappeTestCase): if invoice.invoice_number == pi.name: invoices.append(invoice.as_dict()) break + for payment in pr.payments: if payment.reference_name == pi_return.name: payments.append(payment.as_dict()) @@ -941,6 +999,121 @@ class TestPaymentReconciliation(FrappeTestCase): # Should not raise frappe.exceptions.ValidationError: Total Debit must be equal to Total Credit. pr.reconcile() + def test_reconciliation_from_purchase_order_to_multiple_invoices(self): + """ + Reconciling advance payment from PO/SO to multiple invoices should not cause overallocation + """ + + self.supplier = "_Test Supplier" + + pi1 = self.create_purchase_invoice(qty=10, rate=100) + pi2 = self.create_purchase_invoice(qty=10, rate=100) + po = self.create_purchase_order(qty=20, rate=100) + pay = get_payment_entry(po.doctype, po.name) + # Overpay Puchase Order + pay.paid_amount = 3000 + pay.save().submit() + # assert total allocated and unallocated before reconciliation + self.assertEqual( + ( + pay.references[0].reference_doctype, + pay.references[0].reference_name, + pay.references[0].allocated_amount, + ), + (po.doctype, po.name, 2000), + ) + self.assertEqual(pay.total_allocated_amount, 2000) + self.assertEqual(pay.unallocated_amount, 1000) + self.assertEqual(pay.difference_amount, 0) + + pr = self.create_payment_reconciliation(party_is_customer=False) + pr.get_unreconciled_entries() + + self.assertEqual(len(pr.invoices), 2) + self.assertEqual(len(pr.payments), 2) + + for x in pr.payments: + self.assertEqual((x.reference_type, x.reference_name), (pay.doctype, pay.name)) + + invoices = [x.as_dict() for x in pr.invoices] + payments = [x.as_dict() for x in pr.payments] + pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments})) + # partial allocation on pi1 and full allocate on pi2 + pr.allocation[0].allocated_amount = 100 + pr.reconcile() + + # assert references and total allocated and unallocated amount + pay.reload() + self.assertEqual(len(pay.references), 3) + self.assertEqual( + ( + pay.references[0].reference_doctype, + pay.references[0].reference_name, + pay.references[0].allocated_amount, + ), + (po.doctype, po.name, 900), + ) + self.assertEqual( + ( + pay.references[1].reference_doctype, + pay.references[1].reference_name, + pay.references[1].allocated_amount, + ), + (pi1.doctype, pi1.name, 100), + ) + self.assertEqual( + ( + pay.references[2].reference_doctype, + pay.references[2].reference_name, + pay.references[2].allocated_amount, + ), + (pi2.doctype, pi2.name, 1000), + ) + self.assertEqual(pay.total_allocated_amount, 2000) + self.assertEqual(pay.unallocated_amount, 1000) + self.assertEqual(pay.difference_amount, 0) + + pr.get_unreconciled_entries() + self.assertEqual(len(pr.invoices), 1) + self.assertEqual(len(pr.payments), 2) + + invoices = [x.as_dict() for x in pr.invoices] + payments = [x.as_dict() for x in pr.payments] + pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments})) + pr.reconcile() + + # assert references and total allocated and unallocated amount + pay.reload() + self.assertEqual(len(pay.references), 3) + # PO references should be removed now + self.assertEqual( + ( + pay.references[0].reference_doctype, + pay.references[0].reference_name, + pay.references[0].allocated_amount, + ), + (pi1.doctype, pi1.name, 100), + ) + self.assertEqual( + ( + pay.references[1].reference_doctype, + pay.references[1].reference_name, + pay.references[1].allocated_amount, + ), + (pi2.doctype, pi2.name, 1000), + ) + self.assertEqual( + ( + pay.references[2].reference_doctype, + pay.references[2].reference_name, + pay.references[2].allocated_amount, + ), + (pi1.doctype, pi1.name, 900), + ) + self.assertEqual(pay.total_allocated_amount, 2000) + self.assertEqual(pay.unallocated_amount, 1000) + self.assertEqual(pay.difference_amount, 0) + def make_customer(customer_name, currency=None): if not frappe.db.exists("Customer", customer_name): From c921d7d409870ded51fc6919629d2d1821550a1d Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 19 Oct 2023 14:57:05 +0530 Subject: [PATCH 024/151] refactor(test): make use of utility methods (cherry picked from commit 547993f80103fa192563a82447c39fe122918767) --- .../test_payment_reconciliation.py | 79 ++++++++++++------- 1 file changed, 51 insertions(+), 28 deletions(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py index 48d1cf2cc26..71bc498b494 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py @@ -86,26 +86,44 @@ class TestPaymentReconciliation(FrappeTestCase): self.customer5 = make_customer("_Test PR Customer 5", "EUR") def create_account(self): - account_name = "Debtors EUR" - if not frappe.db.get_value( - "Account", filters={"account_name": account_name, "company": self.company} - ): - acc = frappe.new_doc("Account") - acc.account_name = account_name - acc.parent_account = "Accounts Receivable - _PR" - acc.company = self.company - acc.account_currency = "EUR" - acc.account_type = "Receivable" - acc.insert() - else: - name = frappe.db.get_value( - "Account", - filters={"account_name": account_name, "company": self.company}, - fieldname="name", - pluck=True, - ) - acc = frappe.get_doc("Account", name) - self.debtors_eur = acc.name + accounts = [ + { + "attribute": "debtors_eur", + "account_name": "Debtors EUR", + "parent_account": "Accounts Receivable - _PR", + "account_currency": "EUR", + "account_type": "Receivable", + }, + { + "attribute": "creditors_usd", + "account_name": "Payable USD", + "parent_account": "Accounts Payable - _PR", + "account_currency": "USD", + "account_type": "Payable", + }, + ] + + for x in accounts: + x = frappe._dict(x) + if not frappe.db.get_value( + "Account", filters={"account_name": x.account_name, "company": self.company} + ): + acc = frappe.new_doc("Account") + acc.account_name = x.account_name + acc.parent_account = x.parent_account + acc.company = self.company + acc.account_currency = x.account_currency + acc.account_type = x.account_type + acc.insert() + else: + name = frappe.db.get_value( + "Account", + filters={"account_name": x.account_name, "company": self.company}, + fieldname="name", + pluck=True, + ) + acc = frappe.get_doc("Account", name) + setattr(self, x.attribute, acc.name) def create_sales_invoice( self, qty=1, rate=100, posting_date=nowdate(), do_not_save=False, do_not_submit=False @@ -963,9 +981,13 @@ class TestPaymentReconciliation(FrappeTestCase): self.assertEqual(pr.allocation[0].difference_amount, 0) def test_reconciliation_purchase_invoice_against_return(self): - pi = make_purchase_invoice( - supplier="_Test Supplier USD", currency="USD", conversion_rate=50 - ).submit() + self.supplier = "_Test Supplier USD" + pi = self.create_purchase_invoice(qty=5, rate=50, do_not_submit=True) + pi.supplier = self.supplier + pi.currency = "USD" + pi.conversion_rate = 50 + pi.credit_to = self.creditors_usd + pi.save().submit() pi_return = frappe.get_doc(pi.as_dict()) pi_return.name = None @@ -975,11 +997,12 @@ class TestPaymentReconciliation(FrappeTestCase): pi_return.items[0].qty = -pi_return.items[0].qty pi_return.submit() - self.company = "_Test Company" - self.party_type = "Supplier" - self.customer = "_Test Supplier USD" - - pr = self.create_payment_reconciliation() + pr = frappe.get_doc("Payment Reconciliation") + pr.company = self.company + pr.party_type = "Supplier" + pr.party = self.supplier + pr.receivable_payable_account = self.creditors_usd + pr.from_invoice_date = pr.to_invoice_date = pr.from_payment_date = pr.to_payment_date = nowdate() pr.get_unreconciled_entries() invoices = [] From 7b9daeff66fa8817dec99d1b222ea95295a8dffc Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 24 Oct 2023 08:09:22 +0530 Subject: [PATCH 025/151] chore: fix flakiness `test_sales_order_partial_advance_payment` (cherry picked from commit 4dff2c7a0dad1de840a2b1f53d51e9fe1682fa7f) --- erpnext/selling/doctype/sales_order/test_sales_order.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index 799ad555a52..ec400161e3a 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -1893,10 +1893,10 @@ class TestSalesOrder(FrappeTestCase): si.submit() pe.load_from_db() - self.assertEqual(pe.references[0].reference_name, si.name) - self.assertEqual(pe.references[0].allocated_amount, 200) - self.assertEqual(pe.references[1].reference_name, so.name) - self.assertEqual(pe.references[1].allocated_amount, 300) + self.assertEqual(pe.references[0].reference_name, so.name) + self.assertEqual(pe.references[0].allocated_amount, 300) + self.assertEqual(pe.references[1].reference_name, si.name) + self.assertEqual(pe.references[1].allocated_amount, 200) def test_delivered_item_material_request(self): "SO -> MR (Manufacture) -> WO. Test if WO Qty is updated in SO." From d576fc7490145bd7540f25ab19635754a05c80ad Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 24 Oct 2023 08:48:53 +0530 Subject: [PATCH 026/151] chore: resolve conflict --- erpnext/accounts/utils.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 5c371517470..53e1c1d76dc 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -624,12 +624,7 @@ def update_reference_in_payment_entry( "outstanding_amount": d.outstanding_amount, "allocated_amount": d.allocated_amount, "exchange_rate": d.exchange_rate if d.exchange_gain_loss else payment_entry.get_exchange_rate(), -<<<<<<< HEAD - "exchange_gain_loss": d.exchange_gain_loss, # only populated from invoice in case of advance allocation -======= "exchange_gain_loss": d.exchange_gain_loss, - "account": d.account, ->>>>>>> 23df4205f8 (fix: overallocation on Payment with PO/SO) } if d.voucher_detail_no: From 922ace407699f7f01a7d365306760117235045f4 Mon Sep 17 00:00:00 2001 From: Anand Baburajan Date: Tue, 24 Oct 2023 09:38:08 +0530 Subject: [PATCH 027/151] fix: close employee loan on write off (#37638) * fix: exclude written off amount while calculating loan repayment * fix: revert exclude written off amount while calculating loan repayment * fix: close employee loan on write off --- erpnext/loan_management/doctype/loan/loan.py | 5 --- .../doctype/loan_write_off/loan_write_off.py | 41 +++++++++++++++++++ 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/erpnext/loan_management/doctype/loan/loan.py b/erpnext/loan_management/doctype/loan/loan.py index 0c9c97f60fd..4a333df5a59 100644 --- a/erpnext/loan_management/doctype/loan/loan.py +++ b/erpnext/loan_management/doctype/loan/loan.py @@ -411,11 +411,6 @@ def close_unsecured_term_loan(loan): frappe.throw(_("Cannot close this loan until full repayment")) -def close_loan(loan, total_amount_paid): - frappe.db.set_value("Loan", loan, "total_amount_paid", total_amount_paid) - frappe.db.set_value("Loan", loan, "status", "Closed") - - @frappe.whitelist() def make_loan_disbursement(loan, company, applicant_type, applicant, pending_amount=0, as_dict=0): disbursement_entry = frappe.new_doc("Loan Disbursement") diff --git a/erpnext/loan_management/doctype/loan_write_off/loan_write_off.py b/erpnext/loan_management/doctype/loan_write_off/loan_write_off.py index 25aecf673bb..e920f08c70f 100644 --- a/erpnext/loan_management/doctype/loan_write_off/loan_write_off.py +++ b/erpnext/loan_management/doctype/loan_write_off/loan_write_off.py @@ -9,6 +9,9 @@ from frappe.utils import cint, flt, getdate import erpnext from erpnext.accounts.general_ledger import make_gl_entries from erpnext.controllers.accounts_controller import AccountsController +from erpnext.loan_management.doctype.loan_repayment.loan_repayment import ( + get_pending_principal_amount, +) class LoanWriteOff(AccountsController): @@ -39,11 +42,13 @@ class LoanWriteOff(AccountsController): def on_submit(self): self.update_outstanding_amount() self.make_gl_entries() + self.close_employee_loan() def on_cancel(self): self.update_outstanding_amount(cancel=1) self.ignore_linked_doctypes = ["GL Entry", "Payment Ledger Entry"] self.make_gl_entries(cancel=1) + self.close_employee_loan(cancel=1) def update_outstanding_amount(self, cancel=0): written_off_amount = frappe.db.get_value("Loan", self.loan, "written_off_amount") @@ -94,3 +99,39 @@ class LoanWriteOff(AccountsController): ) make_gl_entries(gl_entries, cancel=cancel, merge_entries=False) + + def close_employee_loan(self, cancel=0): + if not frappe.db.has_column("Loan", "repay_from_salary"): + return + + loan = frappe.get_value( + "Loan", + self.loan, + [ + "total_payment", + "total_principal_paid", + "loan_amount", + "total_interest_payable", + "written_off_amount", + "disbursed_amount", + "status", + "is_secured_loan", + "repay_from_salary", + "name", + ], + as_dict=1, + ) + + if loan.is_secured_loan or not loan.repay_from_salary: + return + + if not cancel: + pending_principal_amount = get_pending_principal_amount(loan) + + precision = cint(frappe.db.get_default("currency_precision")) or 2 + + if flt(pending_principal_amount, precision) <= 0: + frappe.db.set_value("Loan", loan.name, "status", "Closed") + frappe.msgprint(_("Loan {0} closed").format(loan.name)) + else: + frappe.db.set_value("Loan", loan.loan, "status", "Disbursed") From be90be37c78907819d51bc210f4a94fb42505d05 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 22 Sep 2023 11:30:47 +0530 Subject: [PATCH 028/151] refactor: button on PE to filter associated Journals (cherry picked from commit 150728deaaaf0ceb13c567d22f9c0910015245d6) # Conflicts: # erpnext/accounts/doctype/payment_entry/payment_entry.js --- .../accounts/doctype/payment_entry/payment_entry.js | 10 ++++++++++ erpnext/public/js/utils/unreconcile.js | 6 +++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index d5b047389ed..78a5bab630e 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -152,6 +152,16 @@ frappe.ui.form.on('Payment Entry', { frm.events.hide_unhide_fields(frm); frm.events.set_dynamic_labels(frm); frm.events.show_general_ledger(frm); +<<<<<<< HEAD +======= + erpnext.accounts.ledger_preview.show_accounting_ledger_preview(frm); + if(frm.doc.references.find((elem) => {return elem.exchange_gain_loss != 0})) { + frm.add_custom_button(__("View Exchange Gain/Loss Journals"), function() { + frappe.set_route("List", "Journal Entry", {"voucher_type": "Exchange Gain Or Loss", "reference_name": frm.doc.name}); + }, __('Actions')); + + } +>>>>>>> 150728deaa (refactor: button on PE to filter associated Journals) erpnext.accounts.unreconcile_payments.add_unreconcile_btn(frm); }, diff --git a/erpnext/public/js/utils/unreconcile.js b/erpnext/public/js/utils/unreconcile.js index bbdd51d6e54..fa00ed23620 100644 --- a/erpnext/public/js/utils/unreconcile.js +++ b/erpnext/public/js/utils/unreconcile.js @@ -17,7 +17,7 @@ erpnext.accounts.unreconcile_payments = { }, callback: function(r) { if (r.message) { - frm.add_custom_button(__("Un-Reconcile"), function() { + frm.add_custom_button(__("UnReconcile"), function() { erpnext.accounts.unreconcile_payments.build_unreconcile_dialog(frm); }, __('Actions')); } @@ -87,11 +87,11 @@ erpnext.accounts.unreconcile_payments = { unreconcile_dialog_fields[0].get_data = function(){ return r.message}; let d = new frappe.ui.Dialog({ - title: 'Un-Reconcile Allocations', + title: 'UnReconcile Allocations', fields: unreconcile_dialog_fields, size: 'large', cannot_add_rows: true, - primary_action_label: 'Un-Reconcile', + primary_action_label: 'UnReconcile', primary_action(values) { let selected_allocations = values.allocations.filter(x=>x.__checked); From 0b082f0edcfd8a5571849c106d8da8d3ace062be Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 24 Oct 2023 09:44:41 +0530 Subject: [PATCH 029/151] chore: resolve conflict --- erpnext/accounts/doctype/payment_entry/payment_entry.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index 78a5bab630e..d4ea816aa3b 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -152,16 +152,12 @@ frappe.ui.form.on('Payment Entry', { frm.events.hide_unhide_fields(frm); frm.events.set_dynamic_labels(frm); frm.events.show_general_ledger(frm); -<<<<<<< HEAD -======= - erpnext.accounts.ledger_preview.show_accounting_ledger_preview(frm); if(frm.doc.references.find((elem) => {return elem.exchange_gain_loss != 0})) { frm.add_custom_button(__("View Exchange Gain/Loss Journals"), function() { frappe.set_route("List", "Journal Entry", {"voucher_type": "Exchange Gain Or Loss", "reference_name": frm.doc.name}); }, __('Actions')); } ->>>>>>> 150728deaa (refactor: button on PE to filter associated Journals) erpnext.accounts.unreconcile_payments.add_unreconcile_btn(frm); }, From 638c271d705f34329b3d0be4664ba7e87338e05a Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 24 Oct 2023 18:03:34 +0530 Subject: [PATCH 030/151] fix: incorrect process loss validation for multiple finished items (backport #37576) (#37656) fix: incorrect process loss validation for multiple finished items (#37576) (cherry picked from commit 92cbe580e6fb6380a8d2c1e3420b09680826cb76) Co-authored-by: rohitwaghchaure --- .../stock/doctype/stock_entry/stock_entry.py | 42 +++++++++++-------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 9560b52f59c..54978d8aa36 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -451,31 +451,37 @@ class StockEntry(StockController): item_code.append(item.item_code) def validate_fg_completed_qty(self): - item_wise_qty = {} - if self.purpose == "Manufacture" and self.work_order: - for d in self.items: - if d.is_finished_item: - if self.process_loss_qty: - d.qty = self.fg_completed_qty - self.process_loss_qty + if self.purpose != "Manufacture": + return - item_wise_qty.setdefault(d.item_code, []).append(d.qty) + fg_qty = defaultdict(float) + for d in self.items: + if d.is_finished_item: + fg_qty[d.item_code] += flt(d.qty) + + if not fg_qty: + return precision = frappe.get_precision("Stock Entry Detail", "qty") - for item_code, qty_list in item_wise_qty.items(): - total = flt(sum(qty_list), precision) + fg_item = list(fg_qty.keys())[0] + fg_item_qty = flt(fg_qty[fg_item], precision) + fg_completed_qty = flt(self.fg_completed_qty, precision) - if (self.fg_completed_qty - total) > 0 and not self.process_loss_qty: - self.process_loss_qty = flt(self.fg_completed_qty - total, precision) - self.process_loss_percentage = flt(self.process_loss_qty * 100 / self.fg_completed_qty) + for d in self.items: + if not fg_qty.get(d.item_code): + continue - if self.process_loss_qty: - total += flt(self.process_loss_qty, precision) + if (fg_completed_qty - fg_item_qty) > 0: + self.process_loss_qty = fg_completed_qty - fg_item_qty - if self.fg_completed_qty != total: + if not self.process_loss_qty: + continue + + if fg_completed_qty != (flt(fg_item_qty) + flt(self.process_loss_qty, precision)): frappe.throw( - _("The finished product {0} quantity {1} and For Quantity {2} cannot be different").format( - frappe.bold(item_code), frappe.bold(total), frappe.bold(self.fg_completed_qty) - ) + _( + "Since there is a process loss of {0} units for the finished good {1}, you should reduce the quantity by {0} units for the finished good {1} in the Items Table." + ).format(frappe.bold(self.process_loss_qty), frappe.bold(d.item_code)) ) def validate_difference_account(self): From 72d32a49012329d33fd4ecea70988fbfbfce566f Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Tue, 24 Oct 2023 19:10:23 +0530 Subject: [PATCH 031/151] chore: fixed test cases related to Internal Transfer (#37659) --- .../sales_invoice/test_sales_invoice.py | 15 ++++++ .../purchase_receipt/test_purchase_receipt.py | 51 +++++++++++++++---- 2 files changed, 57 insertions(+), 9 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 272382e8c18..f707c4132fc 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -2552,6 +2552,7 @@ class TestSalesInvoice(FrappeTestCase): ) si = frappe.copy_doc(test_records[0]) + si.customer = "_Test Internal Customer 3" si.update_stock = 1 si.set_warehouse = "Finished Goods - _TC" si.set_target_warehouse = "Stores - _TC" @@ -3579,6 +3580,20 @@ def create_internal_parties(): allowed_to_interact_with="_Test Company with perpetual inventory", ) + create_internal_customer( + customer_name="_Test Internal Customer 3", + represents_company="_Test Company", + allowed_to_interact_with="_Test Company", + ) + + account = create_account( + account_name="Unrealized Profit", + parent_account="Current Liabilities - _TC", + company="_Test Company", + ) + + frappe.db.set_value("Company", "_Test Company", "unrealized_profit_loss_account", account) + create_internal_supplier( supplier_name="_Test Internal Supplier", represents_company="Wind Power LLC", diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index a61d8d258ff..404758ce94f 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -928,17 +928,33 @@ class TestPurchaseReceipt(FrappeTestCase): pr1.cancel() def test_stock_transfer_from_purchase_receipt(self): + from erpnext.stock.doctype.delivery_note.delivery_note import make_inter_company_purchase_receipt + from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note + + prepare_data_for_internal_transfer() + + customer = "_Test Internal Customer 2" + company = "_Test Company with perpetual inventory" + pr1 = make_purchase_receipt( - warehouse="Work In Progress - TCP1", company="_Test Company with perpetual inventory" + warehouse="Stores - TCP1", company="_Test Company with perpetual inventory" ) - pr = make_purchase_receipt( - company="_Test Company with perpetual inventory", warehouse="Stores - TCP1", do_not_save=1 + dn1 = create_delivery_note( + item_code=pr1.items[0].item_code, + company=company, + customer=customer, + cost_center="Main - TCP1", + expense_account="Cost of Goods Sold - TCP1", + qty=5, + rate=500, + warehouse="Stores - TCP1", + target_warehouse="Work In Progress - TCP1", ) - pr.supplier_warehouse = "" + pr = make_inter_company_purchase_receipt(dn1.name) pr.items[0].from_warehouse = "Work In Progress - TCP1" - + pr.items[0].warehouse = "Stores - TCP1" pr.submit() gl_entries = get_gl_entries("Purchase Receipt", pr.name) @@ -955,6 +971,11 @@ class TestPurchaseReceipt(FrappeTestCase): pr1.cancel() def test_stock_transfer_from_purchase_receipt_with_valuation(self): + from erpnext.stock.doctype.delivery_note.delivery_note import make_inter_company_purchase_receipt + from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note + + prepare_data_for_internal_transfer() + create_warehouse( "_Test Warehouse for Valuation", company="_Test Company with perpetual inventory", @@ -962,16 +983,28 @@ class TestPurchaseReceipt(FrappeTestCase): ) pr1 = make_purchase_receipt( - warehouse="_Test Warehouse for Valuation - TCP1", + warehouse="Stores - TCP1", company="_Test Company with perpetual inventory", ) - pr = make_purchase_receipt( - company="_Test Company with perpetual inventory", warehouse="Stores - TCP1", do_not_save=1 + customer = "_Test Internal Customer 2" + company = "_Test Company with perpetual inventory" + + dn1 = create_delivery_note( + item_code=pr1.items[0].item_code, + company=company, + customer=customer, + cost_center="Main - TCP1", + expense_account="Cost of Goods Sold - TCP1", + qty=5, + rate=50, + warehouse="Stores - TCP1", + target_warehouse="_Test Warehouse for Valuation - TCP1", ) + pr = make_inter_company_purchase_receipt(dn1.name) pr.items[0].from_warehouse = "_Test Warehouse for Valuation - TCP1" - pr.supplier_warehouse = "" + pr.items[0].warehouse = "Stores - TCP1" pr.append( "taxes", From 80774e2da14e8b78018a86b8f5bdd135630e99b2 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 25 Oct 2023 09:12:27 +0530 Subject: [PATCH 032/151] fix: GL Entries for receiving non CWIP assets using Purchase Receipt (#37660) * fix: GL Entries for receiving non CWIP assets using Purchase Receipt * fix: rearrange functions * chore: rearrange functions * chore: rearrange functions * fix: Purchase Invoice GL entires for assets * test: cwip accounting unit tests * chore: Attribute error * chore: Purchase Invoice tests * chore: Missing asset account * chore: Missing asset account * chore: update tests * fix: Internal transfer GL Entries * test: Deprecate tests * test: Depricate tests * test: Depricate tests * chore: make `Reserve Stock` checkbox visible in SO * refactor: rename field `Auto Reserve Stock for Sales Order` * feat: add fields to hold SO and SO Item ref in PR Item * test: Deprecate tests * test: Depricate tests * test: Depricate tests * refactor: Remove expense included in valuation accounts * chore: Add back default in transit warehousefield --------- Co-authored-by: s-aga-r --- .../purchase_invoice/purchase_invoice.py | 239 +------ .../sales_invoice/test_sales_invoice.py | 8 +- erpnext/accounts/general_ledger.py | 2 +- erpnext/assets/doctype/asset/test_asset.py | 9 +- .../doctype/purchase_order/purchase_order.py | 2 + erpnext/controllers/stock_controller.py | 8 +- erpnext/manufacturing/doctype/bom/bom.py | 8 +- ...se_account_in_landed_cost_voucher_taxes.py | 42 -- .../doctype/sales_order/sales_order.json | 2 +- erpnext/setup/doctype/company/company.js | 3 - erpnext/setup/doctype/company/company.json | 18 +- erpnext/setup/doctype/company/company.py | 3 - .../purchase_receipt/purchase_receipt.py | 631 ++++++++---------- .../purchase_receipt_item.json | 26 +- .../doctype/stock_entry/test_stock_entry.py | 12 +- .../subcontracting_receipt.py | 6 +- 16 files changed, 367 insertions(+), 652 deletions(-) delete mode 100644 erpnext/patches/v12_0/set_expense_account_in_landed_cost_voucher_taxes.py diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index c5e45d4f3a9..306208603c0 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -33,7 +33,7 @@ from erpnext.accounts.general_ledger import ( ) from erpnext.accounts.party import get_due_date, get_party_account 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.asset import 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 @@ -284,9 +284,6 @@ class PurchaseInvoice(BuyingController): # in case of auto inventory accounting, # expense account is always "Stock Received But Not Billed" for a stock item # except opening entry, drop-ship entry and fixed asset items - if item.item_code: - asset_category = frappe.get_cached_value("Item", item.item_code, "asset_category") - if ( auto_accounting_for_stock and item.item_code in stock_items @@ -353,22 +350,26 @@ class PurchaseInvoice(BuyingController): frappe.msgprint(msg, title=_("Expense Head Changed")) item.expense_account = stock_not_billed_account - - elif item.is_fixed_asset and not is_cwip_accounting_enabled(asset_category): + elif item.is_fixed_asset and item.pr_detail: + if not asset_received_but_not_billed: + asset_received_but_not_billed = self.get_company_default("asset_received_but_not_billed") + item.expense_account = asset_received_but_not_billed + elif item.is_fixed_asset: + account_type = ( + "capital_work_in_progress_account" + if is_cwip_accounting_enabled(item.asset_category) + else "fixed_asset_account" + ) asset_category_account = get_asset_category_account( - "fixed_asset_account", item=item.item_code, company=self.company + account_type, item=item.item_code, company=self.company ) if not asset_category_account: - form_link = get_link_to_form("Asset Category", asset_category) + form_link = get_link_to_form("Asset Category", item.asset_category) throw( _("Please set Fixed Asset Account in {} against {}.").format(form_link, self.company), title=_("Missing Account"), ) item.expense_account = asset_category_account - elif item.is_fixed_asset and item.pr_detail: - if not asset_received_but_not_billed: - asset_received_but_not_billed = self.get_company_default("asset_received_but_not_billed") - item.expense_account = asset_received_but_not_billed elif not item.expense_account and for_validate: throw(_("Expense account is mandatory for item {0}").format(item.item_code or item.item_name)) @@ -591,12 +592,12 @@ class PurchaseInvoice(BuyingController): def get_gl_entries(self, warehouse_account=None): self.auto_accounting_for_stock = erpnext.is_perpetual_inventory_enabled(self.company) + self.asset_received_but_not_billed = self.get_company_default("asset_received_but_not_billed") + if self.auto_accounting_for_stock: self.stock_received_but_not_billed = self.get_company_default("stock_received_but_not_billed") - self.expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation") else: self.stock_received_but_not_billed = None - self.expenses_included_in_valuation = None self.negative_expense_to_be_booked = 0.0 gl_entries = [] @@ -605,9 +606,6 @@ class PurchaseInvoice(BuyingController): self.make_item_gl_entries(gl_entries) self.make_precision_loss_gl_entry(gl_entries) - if self.check_asset_cwip_enabled(): - self.get_asset_gl_entry(gl_entries) - self.make_tax_gl_entries(gl_entries) self.make_internal_transfer_gl_entries(gl_entries) @@ -711,7 +709,11 @@ class PurchaseInvoice(BuyingController): if item.item_code: asset_category = frappe.get_cached_value("Item", item.item_code, "asset_category") - if self.update_stock and self.auto_accounting_for_stock and item.item_code in stock_items: + if ( + self.update_stock + and self.auto_accounting_for_stock + and (item.item_code in stock_items or item.is_fixed_asset) + ): # warehouse account warehouse_debit_amount = self.make_stock_adjustment_entry( gl_entries, item, voucher_wise_stock_value, account_currency @@ -826,9 +828,7 @@ class PurchaseInvoice(BuyingController): ) ) - elif not item.is_fixed_asset or ( - item.is_fixed_asset and not is_cwip_accounting_enabled(asset_category) - ): + else: expense_account = ( item.expense_account if (not item.enable_deferred_expense or self.is_return) @@ -921,40 +921,6 @@ class PurchaseInvoice(BuyingController): ) ) - # If asset is bought through this document and not linked to PR - if self.update_stock and item.landed_cost_voucher_amount: - expenses_included_in_asset_valuation = self.get_company_default( - "expenses_included_in_asset_valuation" - ) - # Amount added through landed-cost-voucher - gl_entries.append( - self.get_gl_dict( - { - "account": expenses_included_in_asset_valuation, - "against": expense_account, - "cost_center": item.cost_center, - "remarks": self.get("remarks") or _("Accounting Entry for Stock"), - "credit": flt(item.landed_cost_voucher_amount), - "project": item.project or self.project, - }, - item=item, - ) - ) - - gl_entries.append( - self.get_gl_dict( - { - "account": expense_account, - "against": expenses_included_in_asset_valuation, - "cost_center": item.cost_center, - "remarks": self.get("remarks") or _("Accounting Entry for Stock"), - "debit": flt(item.landed_cost_voucher_amount), - "project": item.project or self.project, - }, - item=item, - ) - ) - # update gross amount of asset bought through this document assets = frappe.db.get_all( "Asset", filters={"purchase_invoice": self.name, "item_code": item.item_code} @@ -979,11 +945,16 @@ class PurchaseInvoice(BuyingController): (item.purchase_receipt, valuation_tax_accounts), ) + stock_rbnb = ( + self.asset_received_but_not_billed + if item.is_fixed_asset + else self.stock_received_but_not_billed + ) if not negative_expense_booked_in_pr: gl_entries.append( self.get_gl_dict( { - "account": self.stock_received_but_not_billed, + "account": stock_rbnb, "against": self.supplier, "debit": flt(item.item_tax_amount, item.precision("item_tax_amount")), "remarks": self.remarks or _("Accounting Entry for Stock"), @@ -998,156 +969,12 @@ class PurchaseInvoice(BuyingController): item.item_tax_amount, item.precision("item_tax_amount") ) - def get_asset_gl_entry(self, gl_entries): - arbnb_account = None - eiiav_account = None - asset_eiiav_currency = None - - for item in self.get("items"): - if item.is_fixed_asset: - asset_amount = flt(item.net_amount) + flt(item.item_tax_amount / self.conversion_rate) - base_asset_amount = flt(item.base_net_amount + item.item_tax_amount) - - item_exp_acc_type = frappe.db.get_value("Account", item.expense_account, "account_type") - if not item.expense_account or item_exp_acc_type not in [ - "Asset Received But Not Billed", - "Fixed Asset", - ]: - if not arbnb_account: - arbnb_account = self.get_company_default("asset_received_but_not_billed") - item.expense_account = arbnb_account - - if not self.update_stock: - arbnb_currency = get_account_currency(item.expense_account) - gl_entries.append( - self.get_gl_dict( - { - "account": item.expense_account, - "against": self.supplier, - "remarks": self.get("remarks") or _("Accounting Entry for Asset"), - "debit": base_asset_amount, - "debit_in_account_currency": ( - base_asset_amount if arbnb_currency == self.company_currency else asset_amount - ), - "cost_center": item.cost_center, - "project": item.project or self.project, - }, - item=item, - ) - ) - - if item.item_tax_amount: - if not eiiav_account or not asset_eiiav_currency: - eiiav_account = self.get_company_default("expenses_included_in_asset_valuation") - asset_eiiav_currency = get_account_currency(eiiav_account) - - gl_entries.append( - self.get_gl_dict( - { - "account": eiiav_account, - "against": self.supplier, - "remarks": self.get("remarks") or _("Accounting Entry for Asset"), - "cost_center": item.cost_center, - "project": item.project or self.project, - "credit": item.item_tax_amount, - "credit_in_account_currency": ( - item.item_tax_amount - if asset_eiiav_currency == self.company_currency - else item.item_tax_amount / self.conversion_rate - ), - }, - item=item, - ) - ) - else: - cwip_account = get_asset_account( - "capital_work_in_progress_account", asset_category=item.asset_category, company=self.company - ) - - cwip_account_currency = get_account_currency(cwip_account) - gl_entries.append( - self.get_gl_dict( - { - "account": cwip_account, - "against": self.supplier, - "remarks": self.get("remarks") or _("Accounting Entry for Asset"), - "debit": base_asset_amount, - "debit_in_account_currency": ( - base_asset_amount if cwip_account_currency == self.company_currency else asset_amount - ), - "cost_center": self.cost_center, - "project": item.project or self.project, - }, - item=item, - ) - ) - - if item.item_tax_amount and not cint(erpnext.is_perpetual_inventory_enabled(self.company)): - if not eiiav_account or not asset_eiiav_currency: - eiiav_account = self.get_company_default("expenses_included_in_asset_valuation") - asset_eiiav_currency = get_account_currency(eiiav_account) - - gl_entries.append( - self.get_gl_dict( - { - "account": eiiav_account, - "against": self.supplier, - "remarks": self.get("remarks") or _("Accounting Entry for Asset"), - "cost_center": item.cost_center, - "credit": item.item_tax_amount, - "project": item.project or self.project, - "credit_in_account_currency": ( - item.item_tax_amount - if asset_eiiav_currency == self.company_currency - else item.item_tax_amount / self.conversion_rate - ), - }, - item=item, - ) - ) - - # Assets are bought through this document then it will be linked to this document - if flt(item.landed_cost_voucher_amount): - if not eiiav_account: - eiiav_account = self.get_company_default("expenses_included_in_asset_valuation") - - gl_entries.append( - self.get_gl_dict( - { - "account": eiiav_account, - "against": cwip_account, - "cost_center": item.cost_center, - "remarks": self.get("remarks") or _("Accounting Entry for Stock"), - "credit": flt(item.landed_cost_voucher_amount), - "project": item.project or self.project, - }, - item=item, - ) - ) - - gl_entries.append( - self.get_gl_dict( - { - "account": cwip_account, - "against": eiiav_account, - "cost_center": item.cost_center, - "remarks": self.get("remarks") or _("Accounting Entry for Stock"), - "debit": flt(item.landed_cost_voucher_amount), - "project": item.project or self.project, - }, - item=item, - ) - ) - - # update gross amount of assets bought through this document - assets = frappe.db.get_all( - "Asset", filters={"purchase_invoice": self.name, "item_code": item.item_code} - ) - for asset in assets: - frappe.db.set_value("Asset", asset.name, "gross_purchase_amount", flt(item.valuation_rate)) - frappe.db.set_value("Asset", asset.name, "purchase_receipt_amount", flt(item.valuation_rate)) - - return gl_entries + assets = frappe.db.get_all( + "Asset", filters={"purchase_invoice": self.name, "item_code": item.item_code} + ) + for asset in assets: + frappe.db.set_value("Asset", asset.name, "gross_purchase_amount", flt(item.valuation_rate)) + frappe.db.set_value("Asset", asset.name, "purchase_receipt_amount", flt(item.valuation_rate)) def make_stock_adjustment_entry( self, gl_entries, item, voucher_wise_stock_value, account_currency diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index f707c4132fc..79363dbeb1b 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -2497,12 +2497,6 @@ class TestSalesInvoice(FrappeTestCase): "stock_received_but_not_billed", "Stock Received But Not Billed - _TC1", ) - frappe.db.set_value( - "Company", - "_Test Company 1", - "expenses_included_in_valuation", - "Expenses Included In Valuation - _TC1", - ) # begin test si = create_sales_invoice( @@ -2540,7 +2534,7 @@ class TestSalesInvoice(FrappeTestCase): # tear down frappe.local.enable_perpetual_inventory["_Test Company 1"] = old_perpetual_inventory - frappe.db.set_value("Stock Settings", None, "allow_negative_stock", old_negative_stock) + frappe.db.set_single_value("Stock Settings", "allow_negative_stock", old_negative_stock) def test_sle_for_target_warehouse(self): se = make_stock_entry( diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py index ed6d9dbe026..07cc5dd734f 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -41,7 +41,7 @@ def make_gl_entries( from_repost=from_repost, ) save_entries(gl_map, adv_adj, update_outstanding, from_repost) - # Post GL Map proccess there may no be any GL Entries + # Post GL Map process there may no be any GL Entries elif gl_map: frappe.throw( _( diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index fc36df8aec5..89553f2a83a 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -186,6 +186,7 @@ class TestAsset(AssetSetup): def test_is_fixed_asset_set(self): asset = create_asset(is_existing_asset=1) doc = frappe.new_doc("Purchase Invoice") + doc.company = "_Test Company" doc.supplier = "_Test Supplier" doc.append("items", {"item_code": "Macbook Pro", "qty": 1, "asset": asset.name}) @@ -487,7 +488,7 @@ class TestAsset(AssetSetup): self.assertEqual("Asset Received But Not Billed - _TC", doc.items[0].expense_account) - # CWIP: Capital Work In Progress + # Capital Work In Progress def test_cwip_accounting(self): pr = make_purchase_receipt( item_code="Macbook Pro", qty=1, rate=5000, do_not_submit=True, location="Test Location" @@ -520,7 +521,8 @@ class TestAsset(AssetSetup): pr.submit() expected_gle = ( - ("Asset Received But Not Billed - _TC", 0.0, 5250.0), + ("_Test Account Shipping Charges - _TC", 0.0, 250.0), + ("Asset Received But Not Billed - _TC", 0.0, 5000.0), ("CWIP Account - _TC", 5250.0, 0.0), ) @@ -539,9 +541,8 @@ class TestAsset(AssetSetup): expected_gle = ( ("_Test Account Service Tax - _TC", 250.0, 0.0), ("_Test Account Shipping Charges - _TC", 250.0, 0.0), - ("Asset Received But Not Billed - _TC", 5250.0, 0.0), + ("Asset Received But Not Billed - _TC", 5000.0, 0.0), ("Creditors - _TC", 0.0, 5500.0), - ("Expenses Included In Asset Valuation - _TC", 0.0, 250.0), ) pi_gle = frappe.db.sql( diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index 06b9d29e69c..d87a855febf 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -522,6 +522,8 @@ def make_purchase_receipt(source_name, target_doc=None): "bom": "bom", "material_request": "material_request", "material_request_item": "material_request_item", + "sales_order": "sales_order", + "sales_order_item": "sales_order_item", }, "postprocess": update_item, "condition": lambda doc: abs(doc.received_qty) < abs(doc.qty) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 15ac1a86800..a72cc667399 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -62,9 +62,12 @@ class StockController(AccountsController): ) ) + is_asset_pr = any(d.get("is_fixed_asset") for d in self.get("items")) + if ( cint(erpnext.is_perpetual_inventory_enabled(self.company)) or provisional_accounting_for_non_stock_items + or is_asset_pr ): warehouse_account = get_warehouse_account_map(self.company) @@ -73,11 +76,6 @@ class StockController(AccountsController): gl_entries = self.get_gl_entries(warehouse_account) make_gl_entries(gl_entries, from_repost=from_repost) - elif self.doctype in ["Purchase Receipt", "Purchase Invoice"] and self.docstatus == 1: - gl_entries = [] - gl_entries = self.get_asset_gl_entry(gl_entries) - make_gl_entries(gl_entries, from_repost=from_repost) - def validate_serialized_batch(self): from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index 8058a5f8b75..8d593d12963 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -1172,12 +1172,12 @@ def get_children(parent=None, is_root=False, **filters): def add_additional_cost(stock_entry, work_order): # Add non stock items cost in the additional cost stock_entry.additional_costs = [] - expenses_included_in_valuation = frappe.get_cached_value( - "Company", work_order.company, "expenses_included_in_valuation" + default_expense_account = frappe.get_cached_value( + "Company", work_order.company, "default_expense_account" ) - add_non_stock_items_cost(stock_entry, work_order, expenses_included_in_valuation) - add_operations_cost(stock_entry, work_order, expenses_included_in_valuation) + add_non_stock_items_cost(stock_entry, work_order, default_expense_account) + add_operations_cost(stock_entry, work_order, default_expense_account) def add_non_stock_items_cost(stock_entry, work_order, expense_account): diff --git a/erpnext/patches/v12_0/set_expense_account_in_landed_cost_voucher_taxes.py b/erpnext/patches/v12_0/set_expense_account_in_landed_cost_voucher_taxes.py deleted file mode 100644 index 9588e026d34..00000000000 --- a/erpnext/patches/v12_0/set_expense_account_in_landed_cost_voucher_taxes.py +++ /dev/null @@ -1,42 +0,0 @@ -import frappe - - -def execute(): - frappe.reload_doctype("Landed Cost Taxes and Charges") - - company_account_map = frappe._dict( - frappe.db.sql( - """ - SELECT name, expenses_included_in_valuation from `tabCompany` - """ - ) - ) - - for company, account in company_account_map.items(): - frappe.db.sql( - """ - UPDATE - `tabLanded Cost Taxes and Charges` t, `tabLanded Cost Voucher` l - SET - t.expense_account = %s - WHERE - l.docstatus = 1 - AND l.company = %s - AND t.parent = l.name - """, - (account, company), - ) - - frappe.db.sql( - """ - UPDATE - `tabLanded Cost Taxes and Charges` t, `tabStock Entry` s - SET - t.expense_account = %s - WHERE - s.docstatus = 1 - AND s.company = %s - AND t.parent = s.name - """, - (account, company), - ) diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json index 4f498fb20d5..490bd7a9830 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.json +++ b/erpnext/selling/doctype/sales_order/sales_order.json @@ -1631,7 +1631,7 @@ "idx": 105, "is_submittable": 1, "links": [], - "modified": "2023-04-20 11:14:01.036202", + "modified": "2023-10-18 12:41:54.813462", "modified_by": "Administrator", "module": "Selling", "name": "Sales Order", diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js index 6aa400a53c7..15f32b15c5e 100644 --- a/erpnext/setup/doctype/company/company.js +++ b/erpnext/setup/doctype/company/company.js @@ -223,7 +223,6 @@ erpnext.company.setup_queries = function(frm) { ["cost_center", {}], ["round_off_cost_center", {}], ["depreciation_cost_center", {}], - ["expenses_included_in_asset_valuation", {"account_type": "Expenses Included In Asset Valuation"}], ["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"]]}], @@ -236,8 +235,6 @@ erpnext.company.setup_queries = function(frm) { $.each([ ["stock_adjustment_account", {"root_type": "Expense", "account_type": "Stock Adjustment"}], - ["expenses_included_in_valuation", - {"root_type": "Expense", "account_type": "Expenses Included in Valuation"}], ["stock_received_but_not_billed", {"root_type": "Liability", "account_type": "Stock Received But Not Billed"}], ["service_received_but_not_billed", diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json index 38f1c0bcdb2..349c328c06c 100644 --- a/erpnext/setup/doctype/company/company.json +++ b/erpnext/setup/doctype/company/company.json @@ -79,12 +79,10 @@ "column_break_32", "stock_received_but_not_billed", "default_provisional_account", - "expenses_included_in_valuation", "fixed_asset_defaults", "accumulated_depreciation_account", "depreciation_expense_account", "series_for_depreciation_entry", - "expenses_included_in_asset_valuation", "column_break_40", "disposal_account", "depreciation_cost_center", @@ -466,14 +464,6 @@ "no_copy": 1, "options": "Account" }, - { - "fieldname": "expenses_included_in_valuation", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Expenses Included In Valuation", - "no_copy": 1, - "options": "Account" - }, { "fieldname": "accumulated_depreciation_account", "fieldtype": "Link", @@ -493,12 +483,6 @@ "fieldtype": "Data", "label": "Series for Asset Depreciation Entry (Journal Entry)" }, - { - "fieldname": "expenses_included_in_asset_valuation", - "fieldtype": "Link", - "label": "Expenses Included In Asset Valuation", - "options": "Account" - }, { "fieldname": "column_break_40", "fieldtype": "Column Break" @@ -728,7 +712,7 @@ "image_field": "company_logo", "is_tree": 1, "links": [], - "modified": "2023-07-07 05:41:41.537256", + "modified": "2023-10-23 10:19:24.322898", "modified_by": "Administrator", "module": "Setup", "name": "Company", diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py index fcdf245659b..bea76f19412 100644 --- a/erpnext/setup/doctype/company/company.py +++ b/erpnext/setup/doctype/company/company.py @@ -92,7 +92,6 @@ class Company(NestedSet): ["Default Income Account", "default_income_account"], ["Stock Received But Not Billed Account", "stock_received_but_not_billed"], ["Stock Adjustment Account", "stock_adjustment_account"], - ["Expense Included In Valuation Account", "expenses_included_in_valuation"], ] for account in accounts: @@ -384,7 +383,6 @@ class Company(NestedSet): "depreciation_expense_account": "Depreciation", "capital_work_in_progress_account": "Capital Work in Progress", "asset_received_but_not_billed": "Asset Received But Not Billed", - "expenses_included_in_asset_valuation": "Expenses Included In Asset Valuation", "default_expense_account": "Cost of Goods Sold", } @@ -394,7 +392,6 @@ class Company(NestedSet): "stock_received_but_not_billed": "Stock Received But Not Billed", "default_inventory_account": "Stock", "stock_adjustment_account": "Stock Adjustment", - "expenses_included_in_valuation": "Expenses Included In Valuation", } ) diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index da534b245c6..964ce0b7218 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -13,7 +13,6 @@ from pypika import functions as fn import erpnext from erpnext.accounts.utils import get_account_currency 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.buying_controller import BuyingController from erpnext.stock.doctype.delivery_note.delivery_note import make_inter_company_transaction @@ -146,8 +145,8 @@ class PurchaseReceipt(BuyingController): if item.is_fixed_asset and is_cwip_accounting_enabled(item.asset_category): # check cwip accounts before making auto assets # Improves UX by not giving messages of "Assets Created" before throwing error of not finding arbnb account - arbnb_account = self.get_company_default("asset_received_but_not_billed") - cwip_account = get_asset_account( + self.get_company_default("asset_received_but_not_billed") + get_asset_account( "capital_work_in_progress_account", asset_category=item.asset_category, company=self.company ) break @@ -315,7 +314,6 @@ class PurchaseReceipt(BuyingController): self.make_item_gl_entries(gl_entries, warehouse_account=warehouse_account) self.make_tax_gl_entries(gl_entries) - self.get_asset_gl_entry(gl_entries) return process_gl_map(gl_entries) @@ -324,14 +322,6 @@ class PurchaseReceipt(BuyingController): get_purchase_document_details, ) - stock_rbnb = None - if erpnext.is_perpetual_inventory_enabled(self.company): - stock_rbnb = self.get_company_default("stock_received_but_not_billed") - landed_cost_entries = get_item_account_wise_additional_cost(self.name) - expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation") - - warehouse_with_no_account = [] - stock_items = self.get_stock_items() provisional_accounting_for_non_stock_items = cint( frappe.db.get_value( "Company", self.company, "enable_provisional_accounting_for_non_stock_items" @@ -340,28 +330,256 @@ class PurchaseReceipt(BuyingController): exchange_rate_map, net_rate_map = get_purchase_document_details(self) + def validate_account(account_type): + frappe.throw(_("{0} account not found while submitting purchase receipt").format(account_type)) + + def make_item_asset_inward_gl_entry(item, stock_value_diff, stock_asset_account_name): + account_currency = get_account_currency(stock_asset_account_name) + + if not stock_asset_account_name: + validate_account("Asset or warehouse account") + + self.add_gl_entry( + gl_entries=gl_entries, + account=stock_asset_account_name, + cost_center=d.cost_center, + debit=stock_value_diff, + credit=0.0, + remarks=remarks, + against_account=stock_asset_rbnb, + account_currency=account_currency, + item=item, + ) + + def make_stock_received_but_not_billed_entry(item): + account = ( + warehouse_account[item.from_warehouse]["account"] if item.from_warehouse else stock_asset_rbnb + ) + account_currency = get_account_currency(account) + + # GL Entry for from warehouse or Stock Received but not billed + # Intentionally passed negative debit amount to avoid incorrect GL Entry validation + credit_amount = ( + flt(item.base_net_amount, item.precision("base_net_amount")) + if account_currency == self.company_currency + else flt(item.net_amount, item.precision("net_amount")) + ) + + outgoing_amount = item.base_net_amount + if self.is_internal_transfer() and item.valuation_rate: + outgoing_amount = abs(get_stock_value_difference(self.name, item.name, item.from_warehouse)) + credit_amount = outgoing_amount + + if credit_amount: + if not account: + validate_account("Stock or Asset Received But Not Billed") + + self.add_gl_entry( + gl_entries=gl_entries, + account=account, + cost_center=item.cost_center, + debit=-1 * flt(outgoing_amount, item.precision("base_net_amount")), + credit=0.0, + remarks=remarks, + against_account=stock_asset_account_name, + debit_in_account_currency=-1 * flt(outgoing_amount, item.precision("base_net_amount")), + account_currency=account_currency, + item=item, + ) + + # check if the exchange rate has changed + if d.get("purchase_invoice"): + if ( + exchange_rate_map[item.purchase_invoice] + and self.conversion_rate != exchange_rate_map[item.purchase_invoice] + and item.net_rate == net_rate_map[item.purchase_invoice_item] + ): + + discrepancy_caused_by_exchange_rate_difference = (item.qty * item.net_rate) * ( + exchange_rate_map[item.purchase_invoice] - self.conversion_rate + ) + + self.add_gl_entry( + gl_entries=gl_entries, + account=account, + cost_center=item.cost_center, + debit=0.0, + credit=discrepancy_caused_by_exchange_rate_difference, + remarks=remarks, + against_account=self.supplier, + debit_in_account_currency=-1 * discrepancy_caused_by_exchange_rate_difference, + account_currency=account_currency, + item=item, + ) + + self.add_gl_entry( + gl_entries=gl_entries, + account=self.get_company_default("exchange_gain_loss_account"), + cost_center=d.cost_center, + debit=discrepancy_caused_by_exchange_rate_difference, + credit=0.0, + remarks=remarks, + against_account=self.supplier, + debit_in_account_currency=-1 * discrepancy_caused_by_exchange_rate_difference, + account_currency=account_currency, + item=item, + ) + + return outgoing_amount + + def make_landed_cost_gl_entries(item): + # Amount added through landed-cost-voucher + if item.landed_cost_voucher_amount and landed_cost_entries: + if (item.item_code, item.name) in landed_cost_entries: + for account, amount in landed_cost_entries[(item.item_code, item.name)].items(): + account_currency = get_account_currency(account) + credit_amount = ( + flt(amount["base_amount"]) + if (amount["base_amount"] or account_currency != self.company_currency) + else flt(amount["amount"]) + ) + + if not account: + validate_account("Landed Cost Account") + + self.add_gl_entry( + gl_entries=gl_entries, + account=account, + cost_center=item.cost_center, + debit=0.0, + credit=credit_amount, + remarks=remarks, + against_account=stock_asset_account_name, + credit_in_account_currency=flt(amount["amount"]), + account_currency=account_currency, + project=item.project, + item=item, + ) + + def make_rate_difference_entry(item): + if item.rate_difference_with_purchase_invoice and stock_asset_rbnb: + account_currency = get_account_currency(stock_asset_rbnb) + self.add_gl_entry( + gl_entries=gl_entries, + account=stock_asset_rbnb, + cost_center=item.cost_center, + debit=0.0, + credit=flt(item.rate_difference_with_purchase_invoice), + remarks=_("Adjustment based on Purchase Invoice rate"), + against_account=stock_asset_account_name, + account_currency=account_currency, + project=item.project, + item=item, + ) + + def make_sub_contracting_gl_entries(item): + # sub-contracting warehouse + if flt(item.rm_supp_cost) and warehouse_account.get(self.supplier_warehouse): + self.add_gl_entry( + gl_entries=gl_entries, + account=supplier_warehouse_account, + cost_center=item.cost_center, + debit=0.0, + credit=flt(item.rm_supp_cost), + remarks=remarks, + against_account=stock_asset_account_name, + account_currency=supplier_warehouse_account_currency, + item=item, + ) + + def make_divisional_loss_gl_entry(item, outgoing_amount): + if item.is_fixed_asset: + return + + # divisional loss adjustment + valuation_amount_as_per_doc = ( + flt(outgoing_amount, d.precision("base_net_amount")) + + flt(item.landed_cost_voucher_amount) + + flt(item.rm_supp_cost) + + flt(item.item_tax_amount) + + flt(item.rate_difference_with_purchase_invoice) + ) + + divisional_loss = flt( + valuation_amount_as_per_doc - flt(stock_value_diff), item.precision("base_net_amount") + ) + + if divisional_loss: + loss_account = ( + self.get_company_default("default_expense_account", ignore_validation=True) + or stock_asset_rbnb + ) + + cost_center = item.cost_center or frappe.get_cached_value( + "Company", self.company, "cost_center" + ) + account_currency = get_account_currency(loss_account) + self.add_gl_entry( + gl_entries=gl_entries, + account=loss_account, + cost_center=cost_center, + debit=divisional_loss, + credit=0.0, + remarks=remarks, + against_account=stock_asset_account_name, + account_currency=account_currency, + project=item.project, + item=item, + ) + + stock_items = self.get_stock_items() + warehouse_with_no_account = [] + for d in self.get("items"): - if d.item_code in stock_items and flt(d.qty) and (flt(d.valuation_rate) or self.is_return): - if warehouse_account.get(d.warehouse): - stock_value_diff = frappe.db.get_value( - "Stock Ledger Entry", - { - "voucher_type": "Purchase Receipt", - "voucher_no": self.name, - "voucher_detail_no": d.name, - "warehouse": d.warehouse, - "is_cancelled": 0, - }, - "stock_value_difference", + if ( + provisional_accounting_for_non_stock_items + and d.item_code not in stock_items + and flt(d.qty) + and d.get("provisional_expense_account") + and not d.is_fixed_asset + ): + self.add_provisional_gl_entry( + d, gl_entries, self.posting_date, d.get("provisional_expense_account") + ) + elif flt(d.qty) and (flt(d.valuation_rate) or self.is_return): + is_asset_pr = any(d.is_fixed_asset for d in self.get("items")) + remarks = self.get("remarks") or _("Accounting Entry for {0}").format( + "Asset" if is_asset_pr else "Stock" + ) + + if not (erpnext.is_perpetual_inventory_enabled(self.company) or is_asset_pr): + return + + stock_asset_rbnb = ( + self.get_company_default("asset_received_but_not_billed") + if is_asset_pr + else self.get_company_default("stock_received_but_not_billed") + ) + landed_cost_entries = get_item_account_wise_additional_cost(self.name) + + if d.is_fixed_asset: + account_type = ( + "capital_work_in_progress_account" + if is_cwip_accounting_enabled(d.asset_category) + else "fixed_asset_account" ) - warehouse_account_name = warehouse_account[d.warehouse]["account"] - warehouse_account_currency = warehouse_account[d.warehouse]["account_currency"] + stock_asset_account_name = get_asset_account( + account_type, asset_category=d.asset_category, company=self.company + ) + + stock_value_diff = ( + flt(d.net_amount) + + flt(d.item_tax_amount / self.conversion_rate) + + flt(d.landed_cost_voucher_amount) + ) + elif warehouse_account.get(d.warehouse): + stock_value_diff = get_stock_value_difference(self.name, d.name, d.warehouse) + stock_asset_account_name = warehouse_account[d.warehouse]["account"] supplier_warehouse_account = warehouse_account.get(self.supplier_warehouse, {}).get("account") supplier_warehouse_account_currency = warehouse_account.get(self.supplier_warehouse, {}).get( "account_currency" ) - remarks = self.get("remarks") or _("Accounting Entry for Stock") # If PR is sub-contracted and fg item rate is zero # in that case if account for source and target warehouse are same, @@ -369,214 +587,25 @@ class PurchaseReceipt(BuyingController): if ( flt(stock_value_diff) == flt(d.rm_supp_cost) and warehouse_account.get(self.supplier_warehouse) - and warehouse_account_name == supplier_warehouse_account + and stock_asset_account_name == supplier_warehouse_account ): continue - self.add_gl_entry( - gl_entries=gl_entries, - account=warehouse_account_name, - cost_center=d.cost_center, - debit=stock_value_diff, - credit=0.0, - remarks=remarks, - against_account=stock_rbnb, - account_currency=warehouse_account_currency, - item=d, - ) - - # GL Entry for from warehouse or Stock Received but not billed - # Intentionally passed negative debit amount to avoid incorrect GL Entry validation - credit_currency = ( - get_account_currency(warehouse_account[d.from_warehouse]["account"]) - if d.from_warehouse - else get_account_currency(stock_rbnb) - ) - - credit_amount = ( - flt(d.base_net_amount, d.precision("base_net_amount")) - if credit_currency == self.company_currency - else flt(d.net_amount, d.precision("net_amount")) - ) - - outgoing_amount = d.base_net_amount - if self.is_internal_transfer() and d.valuation_rate: - outgoing_amount = abs( - frappe.db.get_value( - "Stock Ledger Entry", - { - "voucher_type": "Purchase Receipt", - "voucher_no": self.name, - "voucher_detail_no": d.name, - "warehouse": d.from_warehouse, - "is_cancelled": 0, - }, - "stock_value_difference", - ) - ) - credit_amount = outgoing_amount - - if credit_amount: - account = warehouse_account[d.from_warehouse]["account"] if d.from_warehouse else stock_rbnb - - self.add_gl_entry( - gl_entries=gl_entries, - account=account, - cost_center=d.cost_center, - debit=-1 * flt(outgoing_amount, d.precision("base_net_amount")), - credit=0.0, - remarks=remarks, - against_account=warehouse_account_name, - debit_in_account_currency=-1 * credit_amount, - account_currency=credit_currency, - item=d, - ) - - # check if the exchange rate has changed - if d.get("purchase_invoice"): - if ( - exchange_rate_map[d.purchase_invoice] - and self.conversion_rate != exchange_rate_map[d.purchase_invoice] - and d.net_rate == net_rate_map[d.purchase_invoice_item] - ): - - discrepancy_caused_by_exchange_rate_difference = (d.qty * d.net_rate) * ( - exchange_rate_map[d.purchase_invoice] - self.conversion_rate - ) - - self.add_gl_entry( - gl_entries=gl_entries, - account=account, - cost_center=d.cost_center, - debit=0.0, - credit=discrepancy_caused_by_exchange_rate_difference, - remarks=remarks, - against_account=self.supplier, - debit_in_account_currency=-1 * discrepancy_caused_by_exchange_rate_difference, - account_currency=credit_currency, - item=d, - ) - - self.add_gl_entry( - gl_entries=gl_entries, - account=self.get_company_default("exchange_gain_loss_account"), - cost_center=d.cost_center, - debit=discrepancy_caused_by_exchange_rate_difference, - credit=0.0, - remarks=remarks, - against_account=self.supplier, - debit_in_account_currency=-1 * discrepancy_caused_by_exchange_rate_difference, - account_currency=credit_currency, - item=d, - ) - - # Amount added through landed-cos-voucher - if d.landed_cost_voucher_amount and landed_cost_entries: - if (d.item_code, d.name) in landed_cost_entries: - for account, amount in landed_cost_entries[(d.item_code, d.name)].items(): - account_currency = get_account_currency(account) - credit_amount = ( - flt(amount["base_amount"]) - if (amount["base_amount"] or account_currency != self.company_currency) - else flt(amount["amount"]) - ) - - self.add_gl_entry( - gl_entries=gl_entries, - account=account, - cost_center=d.cost_center, - debit=0.0, - credit=credit_amount, - remarks=remarks, - against_account=warehouse_account_name, - credit_in_account_currency=flt(amount["amount"]), - account_currency=account_currency, - project=d.project, - item=d, - ) - - if d.rate_difference_with_purchase_invoice and stock_rbnb: - account_currency = get_account_currency(stock_rbnb) - self.add_gl_entry( - gl_entries=gl_entries, - account=stock_rbnb, - cost_center=d.cost_center, - debit=0.0, - credit=flt(d.rate_difference_with_purchase_invoice), - remarks=_("Adjustment based on Purchase Invoice rate"), - against_account=warehouse_account_name, - account_currency=account_currency, - project=d.project, - item=d, - ) - - # sub-contracting warehouse - if flt(d.rm_supp_cost) and warehouse_account.get(self.supplier_warehouse): - self.add_gl_entry( - gl_entries=gl_entries, - account=supplier_warehouse_account, - cost_center=d.cost_center, - debit=0.0, - credit=flt(d.rm_supp_cost), - remarks=remarks, - against_account=warehouse_account_name, - account_currency=supplier_warehouse_account_currency, - item=d, - ) - - # divisional loss adjustment - valuation_amount_as_per_doc = ( - flt(outgoing_amount, d.precision("base_net_amount")) - + flt(d.landed_cost_voucher_amount) - + flt(d.rm_supp_cost) - + flt(d.item_tax_amount) - + flt(d.rate_difference_with_purchase_invoice) - ) - - divisional_loss = flt( - valuation_amount_as_per_doc - flt(stock_value_diff), d.precision("base_net_amount") - ) - - if divisional_loss: - if self.is_return or flt(d.item_tax_amount): - loss_account = expenses_included_in_valuation - else: - loss_account = ( - self.get_company_default("default_expense_account", ignore_validation=True) or stock_rbnb - ) - - cost_center = d.cost_center or frappe.get_cached_value( - "Company", self.company, "cost_center" - ) - - self.add_gl_entry( - gl_entries=gl_entries, - account=loss_account, - cost_center=cost_center, - debit=divisional_loss, - credit=0.0, - remarks=remarks, - against_account=warehouse_account_name, - account_currency=credit_currency, - project=d.project, - item=d, - ) - - elif ( - d.warehouse not in warehouse_with_no_account - or d.rejected_warehouse not in warehouse_with_no_account - ): - warehouse_with_no_account.append(d.warehouse) + if (flt(d.valuation_rate) or self.is_return or d.is_fixed_asset) and flt(d.qty): + make_item_asset_inward_gl_entry(d, stock_value_diff, stock_asset_account_name) + outgoing_amount = make_stock_received_but_not_billed_entry(d) + make_landed_cost_gl_entries(d) + make_rate_difference_entry(d) + make_sub_contracting_gl_entries(d) + make_divisional_loss_gl_entry(d, outgoing_amount) elif ( - d.item_code not in stock_items - and not d.is_fixed_asset - and flt(d.qty) - and provisional_accounting_for_non_stock_items - and d.get("provisional_expense_account") + d.warehouse not in warehouse_with_no_account + or d.rejected_warehouse not in warehouse_with_no_account ): - self.add_provisional_gl_entry( - d, gl_entries, self.posting_date, d.get("provisional_expense_account") - ) + warehouse_with_no_account.append(d.warehouse) + + if d.is_fixed_asset: + self.update_assets(d, d.valuation_rate) if warehouse_with_no_account: frappe.msgprint( @@ -589,8 +618,8 @@ class PurchaseReceipt(BuyingController): self, item, gl_entries, posting_date, provisional_account, reverse=0 ): credit_currency = get_account_currency(provisional_account) - debit_currency = get_account_currency(item.expense_account) expense_account = item.expense_account + debit_currency = get_account_currency(item.expense_account) remarks = self.get("remarks") or _("Accounting Entry for Service") multiplication_factor = 1 @@ -631,11 +660,8 @@ class PurchaseReceipt(BuyingController): ) def make_tax_gl_entries(self, gl_entries): - - if erpnext.is_perpetual_inventory_enabled(self.company): - expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation") - negative_expense_to_be_booked = sum([flt(d.item_tax_amount) for d in self.get("items")]) + is_asset_pr = any(d.is_fixed_asset for d in self.get("items")) # Cost center-wise amount breakup for other charges included for valuation valuation_tax = {} for tax in self.get("taxes"): @@ -655,26 +681,26 @@ class PurchaseReceipt(BuyingController): if negative_expense_to_be_booked and valuation_tax: # Backward compatibility: - # If expenses_included_in_valuation account has been credited in against PI # and charges added via Landed Cost Voucher, # post valuation related charges on "Stock Received But Not Billed" - # introduced in 2014 for backward compatibility of expenses already booked in expenses_included_in_valuation account - - negative_expense_booked_in_pi = frappe.db.sql( - """select name from `tabPurchase Invoice Item` pi - where docstatus = 1 and purchase_receipt=%s - and exists(select name from `tabGL Entry` where voucher_type='Purchase Invoice' - and voucher_no=pi.parent and account=%s)""", - (self.name, expenses_included_in_valuation), - ) - against_account = ", ".join([d.account for d in gl_entries if flt(d.debit) > 0]) total_valuation_amount = sum(valuation_tax.values()) amount_including_divisional_loss = negative_expense_to_be_booked - stock_rbnb = self.get_company_default("stock_received_but_not_billed") + stock_rbnb = ( + self.get("asset_received_but_not_billed") + if is_asset_pr + else self.get_company_default("stock_received_but_not_billed") + ) i = 1 for tax in self.get("taxes"): if valuation_tax.get(tax.name): + negative_expense_booked_in_pi = frappe.db.sql( + """select name from `tabPurchase Invoice Item` pi + where docstatus = 1 and purchase_receipt=%s + and exists(select name from `tabGL Entry` where voucher_type='Purchase Invoice' + and voucher_no=pi.parent and account=%s)""", + (self.name, tax.account_head), + ) if negative_expense_booked_in_pi: account = stock_rbnb @@ -702,103 +728,6 @@ class PurchaseReceipt(BuyingController): i += 1 - def get_asset_gl_entry(self, gl_entries): - for item in self.get("items"): - if item.is_fixed_asset: - if is_cwip_accounting_enabled(item.asset_category): - self.add_asset_gl_entries(item, gl_entries) - if flt(item.landed_cost_voucher_amount): - self.add_lcv_gl_entries(item, gl_entries) - # update assets gross amount by its valuation rate - # valuation rate is total of net rate, raw mat supp cost, tax amount, lcv amount per item - self.update_assets(item, item.valuation_rate) - return gl_entries - - def add_asset_gl_entries(self, item, gl_entries): - arbnb_account = self.get_company_default("asset_received_but_not_billed") - # This returns category's cwip account if not then fallback to company's default cwip account - cwip_account = get_asset_account( - "capital_work_in_progress_account", asset_category=item.asset_category, company=self.company - ) - - asset_amount = flt(item.net_amount) + flt(item.item_tax_amount / self.conversion_rate) - base_asset_amount = flt(item.base_net_amount + item.item_tax_amount) - remarks = self.get("remarks") or _("Accounting Entry for Asset") - - cwip_account_currency = get_account_currency(cwip_account) - # debit cwip account - debit_in_account_currency = ( - base_asset_amount if cwip_account_currency == self.company_currency else asset_amount - ) - - self.add_gl_entry( - gl_entries=gl_entries, - account=cwip_account, - cost_center=item.cost_center, - debit=base_asset_amount, - credit=0.0, - remarks=remarks, - against_account=arbnb_account, - debit_in_account_currency=debit_in_account_currency, - item=item, - ) - - asset_rbnb_currency = get_account_currency(arbnb_account) - # credit arbnb account - credit_in_account_currency = ( - base_asset_amount if asset_rbnb_currency == self.company_currency else asset_amount - ) - - self.add_gl_entry( - gl_entries=gl_entries, - account=arbnb_account, - cost_center=item.cost_center, - debit=0.0, - credit=base_asset_amount, - remarks=remarks, - against_account=cwip_account, - credit_in_account_currency=credit_in_account_currency, - item=item, - ) - - def add_lcv_gl_entries(self, item, gl_entries): - expenses_included_in_asset_valuation = self.get_company_default( - "expenses_included_in_asset_valuation" - ) - if not is_cwip_accounting_enabled(item.asset_category): - asset_account = get_asset_category_account( - asset_category=item.asset_category, fieldname="fixed_asset_account", company=self.company - ) - else: - # This returns company's default cwip account - asset_account = get_asset_account("capital_work_in_progress_account", company=self.company) - - remarks = self.get("remarks") or _("Accounting Entry for Stock") - - self.add_gl_entry( - gl_entries=gl_entries, - account=expenses_included_in_asset_valuation, - cost_center=item.cost_center, - debit=0.0, - credit=flt(item.landed_cost_voucher_amount), - remarks=remarks, - against_account=asset_account, - project=item.project, - item=item, - ) - - self.add_gl_entry( - gl_entries=gl_entries, - account=asset_account, - cost_center=item.cost_center, - debit=flt(item.landed_cost_voucher_amount), - credit=0.0, - remarks=remarks, - against_account=expenses_included_in_asset_valuation, - project=item.project, - item=item, - ) - def update_assets(self, item, valuation_rate): assets = frappe.db.get_all( "Asset", filters={"purchase_receipt": self.name, "item_code": item.item_code} @@ -832,6 +761,20 @@ class PurchaseReceipt(BuyingController): self.load_from_db() +def get_stock_value_difference(voucher_no, voucher_detail_no, warehouse): + return frappe.db.get_value( + "Stock Ledger Entry", + { + "voucher_type": "Purchase Receipt", + "voucher_no": voucher_no, + "voucher_detail_no": voucher_detail_no, + "warehouse": warehouse, + "is_cancelled": 0, + }, + "stock_value_difference", + ) + + def update_billed_amount_based_on_po(po_details, update_modified=True): po_billed_amt_details = get_billed_amount_against_po(po_details) 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 4911523e7ed..d7b1660c55f 100644 --- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json +++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json @@ -118,7 +118,9 @@ "dimension_col_break", "cost_center", "section_break_80", - "page_break" + "page_break", + "sales_order", + "sales_order_item" ], "fields": [ { @@ -1025,12 +1027,32 @@ "fieldtype": "Link", "label": "WIP Composite Asset", "options": "Asset" + }, + { + "fieldname": "sales_order", + "fieldtype": "Link", + "label": "Sales Order", + "no_copy": 1, + "options": "Sales Order", + "print_hide": 1, + "read_only": 1, + "search_index": 1 + }, + { + "fieldname": "sales_order_item", + "fieldtype": "Data", + "hidden": 1, + "label": "Sales Order Item", + "no_copy": 1, + "print_hide": 1, + "read_only": 1, + "search_index": 1 } ], "idx": 1, "istable": 1, "links": [], - "modified": "2023-10-03 21:11:50.547261", + "modified": "2023-10-19 10:50:58.071735", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt Item", diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py index de74fda687d..72f6bcdddf2 100644 --- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py @@ -447,9 +447,7 @@ class TestStockEntry(FrappeTestCase): repack.posting_date = nowdate() repack.posting_time = nowtime() - expenses_included_in_valuation = frappe.get_value( - "Company", company, "expenses_included_in_valuation" - ) + default_expense_account = frappe.get_value("Company", company, "default_expense_account") items = get_multiple_items() repack.items = [] @@ -460,12 +458,12 @@ class TestStockEntry(FrappeTestCase): "additional_costs", [ { - "expense_account": expenses_included_in_valuation, + "expense_account": default_expense_account, "description": "Actual Operating Cost", "amount": 1000, }, { - "expense_account": expenses_included_in_valuation, + "expense_account": default_expense_account, "description": "Additional Operating Cost", "amount": 200, }, @@ -504,9 +502,7 @@ class TestStockEntry(FrappeTestCase): self.check_gl_entries( "Stock Entry", repack.name, - sorted( - [[stock_in_hand_account, 1200, 0.0], ["Expenses Included In Valuation - TCP1", 0.0, 1200.0]] - ), + sorted([[stock_in_hand_account, 1200, 0.0], ["Cost of Goods Sold - TCP1", 0.0, 1200.0]]), ) def check_stock_ledger_entries(self, voucher_type, voucher_no, expected_sle): diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py index 130f38fb809..a83068f73fd 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py @@ -299,7 +299,6 @@ class SubcontractingReceipt(SubcontractingController): def make_item_gl_entries(self, gl_entries, warehouse_account=None): stock_rbnb = self.get_company_default("stock_received_but_not_billed") - expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation") warehouse_with_no_account = [] @@ -371,10 +370,7 @@ class SubcontractingReceipt(SubcontractingController): divisional_loss = flt(item.amount - stock_value_diff, item.precision("amount")) if divisional_loss: - if self.is_return: - loss_account = expenses_included_in_valuation - else: - loss_account = item.expense_account + loss_account = item.expense_account self.add_gl_entry( gl_entries=gl_entries, From fa5780ca81ad59fd2769b5ca23935ca193d6c05d Mon Sep 17 00:00:00 2001 From: Raffael Meyer <14891507+barredterra@users.noreply.github.com> Date: Wed, 25 Oct 2023 05:43:06 +0200 Subject: [PATCH 033/151] fix: wrong german translation (#37658) --- erpnext/translations/de.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/translations/de.csv b/erpnext/translations/de.csv index 0c840f44ea8..4cd4f0edf15 100644 --- a/erpnext/translations/de.csv +++ b/erpnext/translations/de.csv @@ -369,7 +369,7 @@ Base,Basis, Base URL,Basis-URL, Based On,Basiert auf, Based On Payment Terms,Basierend auf Zahlungsbedingungen, -Basic,Grundeinkommen, +Basic,Basic, Batch,Charge, Batch Entries,Batch-Einträge, Batch ID is mandatory,Batch-ID ist obligatorisch, From 677d728e6732f801e2d602cc72cbafed31affff4 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sun, 22 Oct 2023 10:58:39 +0530 Subject: [PATCH 034/151] refactor: exc rate on foreign currency JE from Bank Reconciliation (cherry picked from commit 89f484282a90516c117c31624fb2cc5eab6fb840) # Conflicts: # erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py --- .../bank_reconciliation_tool.py | 93 +++++++++++++++---- 1 file changed, 73 insertions(+), 20 deletions(-) diff --git a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py index 0eef3e9a67b..2100c6103a1 100644 --- a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py +++ b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py @@ -16,7 +16,12 @@ from erpnext.accounts.report.bank_reconciliation_statement.bank_reconciliation_s get_amounts_not_reflected_in_system, get_entries, ) +<<<<<<< HEAD from erpnext.accounts.utils import get_balance_on +======= +from erpnext.accounts.utils import get_account_currency, get_balance_on +from erpnext.setup.utils import get_exchange_rate +>>>>>>> 89f484282a (refactor: exc rate on foreign currency JE from Bank Reconciliation) class BankReconciliationTool(Document): @@ -143,29 +148,74 @@ def create_journal_entry_bts( ) company = frappe.get_value("Account", company_account, "company") + company_default_currency = frappe.get_cached_value("Company", company, "default_currency") + company_account_currency = frappe.get_cached_value("Account", company_account, "account_currency") + second_account_currency = frappe.get_cached_value("Account", second_account, "account_currency") + + is_multi_currency = ( + True + if company_default_currency != company_account_currency + or company_default_currency != second_account_currency + else False + ) accounts = [] - # Multi Currency? - accounts.append( - { - "account": second_account, - "credit_in_account_currency": bank_transaction.deposit, - "debit_in_account_currency": bank_transaction.withdrawal, - "party_type": party_type, - "party": party, - "cost_center": get_default_cost_center(company), - } - ) + second_account_dict = { + "account": second_account, + "account_currency": second_account_currency, + "credit_in_account_currency": bank_transaction.deposit, + "debit_in_account_currency": bank_transaction.withdrawal, + "party_type": party_type, + "party": party, + "cost_center": get_default_cost_center(company), + } - accounts.append( - { - "account": company_account, - "bank_account": bank_transaction.bank_account, - "credit_in_account_currency": bank_transaction.withdrawal, - "debit_in_account_currency": bank_transaction.deposit, - "cost_center": get_default_cost_center(company), - } - ) + company_account_dict = { + "account": company_account, + "account_currency": company_account_currency, + "bank_account": bank_transaction.bank_account, + "credit_in_account_currency": bank_transaction.withdrawal, + "debit_in_account_currency": bank_transaction.deposit, + "cost_center": get_default_cost_center(company), + } + + if is_multi_currency: + exc_rate = get_exchange_rate(company_account_currency, company_default_currency, posting_date) + withdrawal_in_company_currency = flt(exc_rate * abs(bank_transaction.withdrawal)) + deposit_in_company_currency = flt(exc_rate * abs(bank_transaction.deposit)) + + if second_account_currency != company_default_currency: + exc_rate = get_exchange_rate(second_account_currency, company_default_currency, posting_date) + second_account_dict.update( + { + "exchange_rate": exc_rate, + "credit": deposit_in_company_currency, + "debit": withdrawal_in_company_currency, + } + ) + else: + second_account_dict.update( + { + "exchange_rate": 1, + "credit": deposit_in_company_currency, + "debit": withdrawal_in_company_currency, + "credit_in_account_currency": deposit_in_company_currency, + "debit_in_account_currency": withdrawal_in_company_currency, + } + ) + + if company_account_currency != company_default_currency: + exc_rate = get_exchange_rate(company_account_currency, company_default_currency, posting_date) + company_account_dict.update( + { + "exchange_rate": exc_rate, + "credit": withdrawal_in_company_currency, + "debit": deposit_in_company_currency, + } + ) + + accounts.append(second_account_dict) + accounts.append(company_account_dict) journal_entry_dict = { "voucher_type": entry_type, @@ -175,6 +225,9 @@ def create_journal_entry_bts( "cheque_no": reference_number, "mode_of_payment": mode_of_payment, } + if is_multi_currency: + journal_entry_dict.update({"multi_currency": True}) + journal_entry = frappe.new_doc("Journal Entry") journal_entry.update(journal_entry_dict) journal_entry.set("accounts", accounts) From 91130854d8cdfae0a72953473fec72abe992eda4 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 25 Oct 2023 09:45:05 +0530 Subject: [PATCH 035/151] refactor: handle bank transaction in foreign currency (cherry picked from commit 74a0d6408a2082a2a039cd55547e56206e7c70bd) --- .../bank_reconciliation_tool.py | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py index 2100c6103a1..98b49f296d4 100644 --- a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py +++ b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py @@ -134,7 +134,7 @@ def create_journal_entry_bts( bank_transaction = frappe.db.get_values( "Bank Transaction", bank_transaction_name, - fieldname=["name", "deposit", "withdrawal", "bank_account"], + fieldname=["name", "deposit", "withdrawal", "bank_account", "currency"], as_dict=True, )[0] company_account = frappe.get_value("Bank Account", bank_transaction.bank_account, "account") @@ -152,10 +152,12 @@ def create_journal_entry_bts( company_account_currency = frappe.get_cached_value("Account", company_account, "account_currency") second_account_currency = frappe.get_cached_value("Account", second_account, "account_currency") + # determine if multi-currency Journal or not is_multi_currency = ( True if company_default_currency != company_account_currency or company_default_currency != second_account_currency + or company_default_currency != bank_transaction.currency else False ) @@ -179,11 +181,16 @@ def create_journal_entry_bts( "cost_center": get_default_cost_center(company), } + # convert transaction amount to company currency if is_multi_currency: - exc_rate = get_exchange_rate(company_account_currency, company_default_currency, posting_date) + exc_rate = get_exchange_rate(bank_transaction.currency, company_default_currency, posting_date) withdrawal_in_company_currency = flt(exc_rate * abs(bank_transaction.withdrawal)) deposit_in_company_currency = flt(exc_rate * abs(bank_transaction.deposit)) + else: + withdrawal_in_company_currency = bank_transaction.withdrawal + deposit_in_company_currency = bank_transaction.deposit + # if second account is of foreign currency, convert and set debit and credit fields. if second_account_currency != company_default_currency: exc_rate = get_exchange_rate(second_account_currency, company_default_currency, posting_date) second_account_dict.update( @@ -191,6 +198,8 @@ def create_journal_entry_bts( "exchange_rate": exc_rate, "credit": deposit_in_company_currency, "debit": withdrawal_in_company_currency, + "credit_in_account_currency": flt(deposit_in_company_currency / exc_rate) or 0, + "debit_in_account_currency": flt(withdrawal_in_company_currency / exc_rate) or 0, } ) else: @@ -204,6 +213,7 @@ def create_journal_entry_bts( } ) + # if company account is of foreign currency, convert and set debit and credit fields. if company_account_currency != company_default_currency: exc_rate = get_exchange_rate(company_account_currency, company_default_currency, posting_date) company_account_dict.update( @@ -213,6 +223,16 @@ def create_journal_entry_bts( "debit": deposit_in_company_currency, } ) + else: + company_account_dict.update( + { + "exchange_rate": 1, + "credit": withdrawal_in_company_currency, + "debit": deposit_in_company_currency, + "credit_in_account_currency": withdrawal_in_company_currency, + "debit_in_account_currency": deposit_in_company_currency, + } + ) accounts.append(second_account_dict) accounts.append(company_account_dict) From 2317f6a0000843887d60b873d41de66efcbcc6c2 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 25 Oct 2023 11:27:51 +0530 Subject: [PATCH 036/151] chore: resolve conflict --- .../bank_reconciliation_tool/bank_reconciliation_tool.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py index 98b49f296d4..6017c5a491d 100644 --- a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py +++ b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py @@ -16,12 +16,8 @@ from erpnext.accounts.report.bank_reconciliation_statement.bank_reconciliation_s get_amounts_not_reflected_in_system, get_entries, ) -<<<<<<< HEAD from erpnext.accounts.utils import get_balance_on -======= -from erpnext.accounts.utils import get_account_currency, get_balance_on from erpnext.setup.utils import get_exchange_rate ->>>>>>> 89f484282a (refactor: exc rate on foreign currency JE from Bank Reconciliation) class BankReconciliationTool(Document): From f35a0c227dce5b2cf0164e8f81d79b8a65cf4985 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 25 Oct 2023 12:03:24 +0530 Subject: [PATCH 037/151] chore: release v14 (#37654) * fix(delivery): rename dt fetch stop action (backport #37605) (#37606) fix(delivery): rename dt fetch stop action (cherry picked from commit 79d51a0a0b685909371e9bda68d9702fb287c53e) Co-authored-by: David Arnold * fix: incorrect cost center in the purchase invoice (backport #37591) (#37609) * fix: incorrect cost center in the purchase invoice (#37591) (cherry picked from commit 14b009b09355f53b1dfcd05d0f7ba918b0b25210) # Conflicts: # erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py * chore: fix conflicts --------- Co-authored-by: rohitwaghchaure * fix(minor): filter bank accounts in bank statement import (#37525) fix(minor): filter bank accounts in bank statement import (#37525) fix: filter by company in bank account (cherry picked from commit 9d392970f02b510799baa7123e1eb64fbb62dcf5) Co-authored-by: Gursheen Kaur Anand <40693548+GursheenK@users.noreply.github.com> * fix: set empty value for tax template in item details (#37496) * fix: set empty value for tax template in item details (#37496) * fix: empty tax template for items with invalid templates * fix: test for empty tax template * fix: test for item tax template calculation * fix: test for pos inv tax template calculation (cherry picked from commit b0d440c34b9cb4d0e0d75153c279ccaa6206253d) # Conflicts: # erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py * chore: resolve conflicts --------- Co-authored-by: Gursheen Kaur Anand <40693548+GursheenK@users.noreply.github.com> * fix(minor): filter tax template based on company in subscription (#37562) fix: filter tax template based on company (cherry picked from commit 1a2f659de2a06bea513ced0a5b8ff007ebec6437) Co-authored-by: Gursheen Anand * fix: Cash flow mapping fix (#37522) Cash flow mapping fix * fix: remove from or target warehouse for non internal transfer entries (backport #37612) (#37628) fix: remove from or target warehouse for non internal transfer entries (#37612) (cherry picked from commit 5136fe196b4e3aab6bb18d2edf5effbfacd2b060) Co-authored-by: rohitwaghchaure * Revert "fix: set empty value for tax template in item details (#37496)" Revert "fix: set empty value for tax template in item details (#37496)" This reverts commit ec208b8df557649cfa4c70387ef5c3297c251866. * refactor: gain_loss posting date fields in the allocation table (cherry picked from commit 55dbcee36a2acf4aa41c66147c263a85ef606f81) * refactor: introduce fields in popup (cherry picked from commit 5323bb7beeb6526d16bcb19fc2f3acd3a95927e6) * refactor: pass gain loss posting date to controller (cherry picked from commit 7e600a6494d7f07c6fd2b8f1cc71857801a2573c) * test: varying posting date for gain loss journal (cherry picked from commit 514d5434a3ae24e2c7839fbd76a115d6c0841513) * fix: overallocation on Payment with PO/SO (cherry picked from commit 23df4205f8abfca6764d84665cb9877703c1a470) # Conflicts: # erpnext/accounts/utils.py * test: overalloction on reconciliation when PO is involved (cherry picked from commit 946228d783cb58cd2809f4b0bc0a854c49cc4060) * refactor(test): make use of utility methods (cherry picked from commit 547993f80103fa192563a82447c39fe122918767) * chore: fix flakiness `test_sales_order_partial_advance_payment` (cherry picked from commit 4dff2c7a0dad1de840a2b1f53d51e9fe1682fa7f) * chore: resolve conflict * fix: close employee loan on write off (#37638) * fix: exclude written off amount while calculating loan repayment * fix: revert exclude written off amount while calculating loan repayment * fix: close employee loan on write off * refactor: button on PE to filter associated Journals (cherry picked from commit 150728deaaaf0ceb13c567d22f9c0910015245d6) # Conflicts: # erpnext/accounts/doctype/payment_entry/payment_entry.js * chore: resolve conflict * fix: incorrect process loss validation for multiple finished items (backport #37576) (#37656) fix: incorrect process loss validation for multiple finished items (#37576) (cherry picked from commit 92cbe580e6fb6380a8d2c1e3420b09680826cb76) Co-authored-by: rohitwaghchaure * chore: fixed test cases related to Internal Transfer (#37659) * fix: GL Entries for receiving non CWIP assets using Purchase Receipt (#37660) * fix: GL Entries for receiving non CWIP assets using Purchase Receipt * fix: rearrange functions * chore: rearrange functions * chore: rearrange functions * fix: Purchase Invoice GL entires for assets * test: cwip accounting unit tests * chore: Attribute error * chore: Purchase Invoice tests * chore: Missing asset account * chore: Missing asset account * chore: update tests * fix: Internal transfer GL Entries * test: Deprecate tests * test: Depricate tests * test: Depricate tests * chore: make `Reserve Stock` checkbox visible in SO * refactor: rename field `Auto Reserve Stock for Sales Order` * feat: add fields to hold SO and SO Item ref in PR Item * test: Deprecate tests * test: Depricate tests * test: Depricate tests * refactor: Remove expense included in valuation accounts * chore: Add back default in transit warehousefield --------- Co-authored-by: s-aga-r * fix: wrong german translation (#37658) --------- Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> Co-authored-by: David Arnold Co-authored-by: rohitwaghchaure Co-authored-by: Gursheen Kaur Anand <40693548+GursheenK@users.noreply.github.com> Co-authored-by: Gursheen Anand Co-authored-by: saeedkola Co-authored-by: Deepesh Garg Co-authored-by: ruthra kumar Co-authored-by: Anand Baburajan Co-authored-by: s-aga-r Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com> --- .../bank_statement_import.js | 10 + .../doctype/payment_entry/payment_entry.js | 6 + .../payment_reconciliation.js | 12 + .../payment_reconciliation.py | 2 + .../test_payment_reconciliation.py | 262 +++++++- .../payment_reconciliation_allocation.json | 8 +- .../purchase_invoice/purchase_invoice.py | 239 +------ .../purchase_invoice/test_purchase_invoice.py | 24 + .../sales_invoice/test_sales_invoice.py | 23 +- .../doctype/subscription/subscription.js | 8 + erpnext/accounts/general_ledger.py | 2 +- .../report/cash_flow/custom_cash_flow.py | 2 +- erpnext/accounts/utils.py | 15 +- erpnext/assets/doctype/asset/test_asset.py | 9 +- .../doctype/purchase_order/purchase_order.py | 2 + erpnext/controllers/accounts_controller.py | 6 +- erpnext/controllers/stock_controller.py | 30 +- .../tests/test_accounts_controller.py | 69 +- erpnext/loan_management/doctype/loan/loan.py | 5 - .../doctype/loan_write_off/loan_write_off.py | 41 ++ erpnext/manufacturing/doctype/bom/bom.py | 8 +- ...se_account_in_landed_cost_voucher_taxes.py | 42 -- erpnext/public/js/utils/unreconcile.js | 6 +- .../doctype/sales_order/sales_order.json | 2 +- .../doctype/sales_order/test_sales_order.py | 8 +- erpnext/setup/doctype/company/company.js | 3 - erpnext/setup/doctype/company/company.json | 18 +- erpnext/setup/doctype/company/company.py | 3 - .../delivery_note/test_delivery_note.py | 15 + .../doctype/delivery_trip/delivery_trip.js | 2 +- erpnext/stock/doctype/item/test_item.py | 12 +- .../purchase_receipt/purchase_receipt.py | 631 ++++++++---------- .../purchase_receipt/test_purchase_receipt.py | 66 +- .../purchase_receipt_item.json | 26 +- .../stock/doctype/stock_entry/stock_entry.py | 42 +- .../doctype/stock_entry/test_stock_entry.py | 12 +- erpnext/stock/get_item_details.py | 6 + .../subcontracting_receipt.py | 6 +- erpnext/translations/de.csv | 2 +- 39 files changed, 939 insertions(+), 746 deletions(-) delete mode 100644 erpnext/patches/v12_0/set_expense_account_in_landed_cost_voucher_taxes.py diff --git a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.js b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.js index a70af7a90e3..db68dfad79e 100644 --- a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.js +++ b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.js @@ -2,6 +2,16 @@ // For license information, please see license.txt frappe.ui.form.on("Bank Statement Import", { + onload(frm) { + frm.set_query("bank_account", function (doc) { + return { + filters: { + company: doc.company, + }, + }; + }); + }, + setup(frm) { frappe.realtime.on("data_import_refresh", ({ data_import }) => { frm.import_in_progress = false; diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index d5b047389ed..d4ea816aa3b 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -152,6 +152,12 @@ frappe.ui.form.on('Payment Entry', { frm.events.hide_unhide_fields(frm); frm.events.set_dynamic_labels(frm); frm.events.show_general_ledger(frm); + if(frm.doc.references.find((elem) => {return elem.exchange_gain_loss != 0})) { + frm.add_custom_button(__("View Exchange Gain/Loss Journals"), function() { + frappe.set_route("List", "Journal Entry", {"voucher_type": "Exchange Gain Or Loss", "reference_name": frm.doc.name}); + }, __('Actions')); + + } erpnext.accounts.unreconcile_payments.add_unreconcile_btn(frm); }, diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js index 6f1f34bc9f3..5cdedb73c09 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js @@ -216,6 +216,7 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo this.data = []; const dialog = new frappe.ui.Dialog({ title: __("Select Difference Account"), + size: 'extra-large', fields: [ { fieldname: "allocation", @@ -239,6 +240,13 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo in_list_view: 1, read_only: 1 }, { + fieldtype:'Date', + fieldname:"gain_loss_posting_date", + label: __("Posting Date"), + in_list_view: 1, + reqd: 1, + }, { + fieldtype:'Link', options: 'Account', in_list_view: 1, @@ -272,6 +280,9 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo args.forEach(d => { frappe.model.set_value("Payment Reconciliation Allocation", d.docname, "difference_account", d.difference_account); + frappe.model.set_value("Payment Reconciliation Allocation", d.docname, + "gain_loss_posting_date", d.gain_loss_posting_date); + }); this.reconcile_payment_entries(); @@ -287,6 +298,7 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo 'reference_name': d.reference_name, 'difference_amount': d.difference_amount, 'difference_account': d.difference_account, + 'gain_loss_posting_date': d.gain_loss_posting_date }); } }); diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index 08923e74266..897bbee0beb 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -315,6 +315,7 @@ class PaymentReconciliation(Document): res.difference_amount = self.get_difference_amount(pay, inv, res["allocated_amount"]) res.difference_account = default_exchange_gain_loss_account res.exchange_rate = inv.get("exchange_rate") + res.update({"gain_loss_posting_date": pay.get("posting_date")}) if pay.get("amount") == 0: entries.append(res) @@ -421,6 +422,7 @@ class PaymentReconciliation(Document): "allocated_amount": flt(row.get("allocated_amount")), "difference_amount": flt(row.get("difference_amount")), "difference_account": row.get("difference_account"), + "difference_posting_date": row.get("gain_loss_posting_date"), "cost_center": row.get("cost_center"), } ) diff --git a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py index 1d843abde1d..71bc498b494 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py @@ -14,6 +14,7 @@ from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_pay from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice from erpnext.accounts.party import get_party_account +from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order from erpnext.stock.doctype.item.test_item import create_item test_dependencies = ["Item"] @@ -85,26 +86,44 @@ class TestPaymentReconciliation(FrappeTestCase): self.customer5 = make_customer("_Test PR Customer 5", "EUR") def create_account(self): - account_name = "Debtors EUR" - if not frappe.db.get_value( - "Account", filters={"account_name": account_name, "company": self.company} - ): - acc = frappe.new_doc("Account") - acc.account_name = account_name - acc.parent_account = "Accounts Receivable - _PR" - acc.company = self.company - acc.account_currency = "EUR" - acc.account_type = "Receivable" - acc.insert() - else: - name = frappe.db.get_value( - "Account", - filters={"account_name": account_name, "company": self.company}, - fieldname="name", - pluck=True, - ) - acc = frappe.get_doc("Account", name) - self.debtors_eur = acc.name + accounts = [ + { + "attribute": "debtors_eur", + "account_name": "Debtors EUR", + "parent_account": "Accounts Receivable - _PR", + "account_currency": "EUR", + "account_type": "Receivable", + }, + { + "attribute": "creditors_usd", + "account_name": "Payable USD", + "parent_account": "Accounts Payable - _PR", + "account_currency": "USD", + "account_type": "Payable", + }, + ] + + for x in accounts: + x = frappe._dict(x) + if not frappe.db.get_value( + "Account", filters={"account_name": x.account_name, "company": self.company} + ): + acc = frappe.new_doc("Account") + acc.account_name = x.account_name + acc.parent_account = x.parent_account + acc.company = self.company + acc.account_currency = x.account_currency + acc.account_type = x.account_type + acc.insert() + else: + name = frappe.db.get_value( + "Account", + filters={"account_name": x.account_name, "company": self.company}, + fieldname="name", + pluck=True, + ) + acc = frappe.get_doc("Account", name) + setattr(self, x.attribute, acc.name) def create_sales_invoice( self, qty=1, rate=100, posting_date=nowdate(), do_not_save=False, do_not_submit=False @@ -151,6 +170,64 @@ class TestPaymentReconciliation(FrappeTestCase): payment.posting_date = posting_date return payment + def create_purchase_invoice( + self, qty=1, rate=100, posting_date=nowdate(), do_not_save=False, do_not_submit=False + ): + """ + Helper function to populate default values in sales invoice + """ + pinv = make_purchase_invoice( + qty=qty, + rate=rate, + company=self.company, + customer=self.supplier, + item_code=self.item, + item_name=self.item, + cost_center=self.cost_center, + warehouse=self.warehouse, + debit_to=self.debit_to, + parent_cost_center=self.cost_center, + update_stock=0, + currency="INR", + is_pos=0, + is_return=0, + return_against=None, + income_account=self.income_account, + expense_account=self.expense_account, + do_not_save=do_not_save, + do_not_submit=do_not_submit, + ) + return pinv + + def create_purchase_order( + self, qty=1, rate=100, posting_date=nowdate(), do_not_save=False, do_not_submit=False + ): + """ + Helper function to populate default values in sales invoice + """ + pord = create_purchase_order( + qty=qty, + rate=rate, + company=self.company, + customer=self.supplier, + item_code=self.item, + item_name=self.item, + cost_center=self.cost_center, + warehouse=self.warehouse, + debit_to=self.debit_to, + parent_cost_center=self.cost_center, + update_stock=0, + currency="INR", + is_pos=0, + is_return=0, + return_against=None, + income_account=self.income_account, + expense_account=self.expense_account, + do_not_save=do_not_save, + do_not_submit=do_not_submit, + ) + return pord + def clear_old_entries(self): doctype_list = [ "GL Entry", @@ -163,13 +240,11 @@ class TestPaymentReconciliation(FrappeTestCase): for doctype in doctype_list: qb.from_(qb.DocType(doctype)).delete().where(qb.DocType(doctype).company == self.company).run() - def create_payment_reconciliation(self): + def create_payment_reconciliation(self, party_is_customer=True): pr = frappe.new_doc("Payment Reconciliation") pr.company = self.company - pr.party_type = ( - self.party_type if hasattr(self, "party_type") and self.party_type else "Customer" - ) - pr.party = self.customer + pr.party_type = "Customer" if party_is_customer else "Supplier" + pr.party = self.customer if party_is_customer else self.supplier pr.receivable_payable_account = get_party_account(pr.party_type, pr.party, pr.company) pr.from_invoice_date = pr.to_invoice_date = pr.from_payment_date = pr.to_payment_date = nowdate() return pr @@ -906,9 +981,13 @@ class TestPaymentReconciliation(FrappeTestCase): self.assertEqual(pr.allocation[0].difference_amount, 0) def test_reconciliation_purchase_invoice_against_return(self): - pi = make_purchase_invoice( - supplier="_Test Supplier USD", currency="USD", conversion_rate=50 - ).submit() + self.supplier = "_Test Supplier USD" + pi = self.create_purchase_invoice(qty=5, rate=50, do_not_submit=True) + pi.supplier = self.supplier + pi.currency = "USD" + pi.conversion_rate = 50 + pi.credit_to = self.creditors_usd + pi.save().submit() pi_return = frappe.get_doc(pi.as_dict()) pi_return.name = None @@ -918,11 +997,12 @@ class TestPaymentReconciliation(FrappeTestCase): pi_return.items[0].qty = -pi_return.items[0].qty pi_return.submit() - self.company = "_Test Company" - self.party_type = "Supplier" - self.customer = "_Test Supplier USD" - - pr = self.create_payment_reconciliation() + pr = frappe.get_doc("Payment Reconciliation") + pr.company = self.company + pr.party_type = "Supplier" + pr.party = self.supplier + pr.receivable_payable_account = self.creditors_usd + pr.from_invoice_date = pr.to_invoice_date = pr.from_payment_date = pr.to_payment_date = nowdate() pr.get_unreconciled_entries() invoices = [] @@ -931,6 +1011,7 @@ class TestPaymentReconciliation(FrappeTestCase): if invoice.invoice_number == pi.name: invoices.append(invoice.as_dict()) break + for payment in pr.payments: if payment.reference_name == pi_return.name: payments.append(payment.as_dict()) @@ -941,6 +1022,121 @@ class TestPaymentReconciliation(FrappeTestCase): # Should not raise frappe.exceptions.ValidationError: Total Debit must be equal to Total Credit. pr.reconcile() + def test_reconciliation_from_purchase_order_to_multiple_invoices(self): + """ + Reconciling advance payment from PO/SO to multiple invoices should not cause overallocation + """ + + self.supplier = "_Test Supplier" + + pi1 = self.create_purchase_invoice(qty=10, rate=100) + pi2 = self.create_purchase_invoice(qty=10, rate=100) + po = self.create_purchase_order(qty=20, rate=100) + pay = get_payment_entry(po.doctype, po.name) + # Overpay Puchase Order + pay.paid_amount = 3000 + pay.save().submit() + # assert total allocated and unallocated before reconciliation + self.assertEqual( + ( + pay.references[0].reference_doctype, + pay.references[0].reference_name, + pay.references[0].allocated_amount, + ), + (po.doctype, po.name, 2000), + ) + self.assertEqual(pay.total_allocated_amount, 2000) + self.assertEqual(pay.unallocated_amount, 1000) + self.assertEqual(pay.difference_amount, 0) + + pr = self.create_payment_reconciliation(party_is_customer=False) + pr.get_unreconciled_entries() + + self.assertEqual(len(pr.invoices), 2) + self.assertEqual(len(pr.payments), 2) + + for x in pr.payments: + self.assertEqual((x.reference_type, x.reference_name), (pay.doctype, pay.name)) + + invoices = [x.as_dict() for x in pr.invoices] + payments = [x.as_dict() for x in pr.payments] + pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments})) + # partial allocation on pi1 and full allocate on pi2 + pr.allocation[0].allocated_amount = 100 + pr.reconcile() + + # assert references and total allocated and unallocated amount + pay.reload() + self.assertEqual(len(pay.references), 3) + self.assertEqual( + ( + pay.references[0].reference_doctype, + pay.references[0].reference_name, + pay.references[0].allocated_amount, + ), + (po.doctype, po.name, 900), + ) + self.assertEqual( + ( + pay.references[1].reference_doctype, + pay.references[1].reference_name, + pay.references[1].allocated_amount, + ), + (pi1.doctype, pi1.name, 100), + ) + self.assertEqual( + ( + pay.references[2].reference_doctype, + pay.references[2].reference_name, + pay.references[2].allocated_amount, + ), + (pi2.doctype, pi2.name, 1000), + ) + self.assertEqual(pay.total_allocated_amount, 2000) + self.assertEqual(pay.unallocated_amount, 1000) + self.assertEqual(pay.difference_amount, 0) + + pr.get_unreconciled_entries() + self.assertEqual(len(pr.invoices), 1) + self.assertEqual(len(pr.payments), 2) + + invoices = [x.as_dict() for x in pr.invoices] + payments = [x.as_dict() for x in pr.payments] + pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments})) + pr.reconcile() + + # assert references and total allocated and unallocated amount + pay.reload() + self.assertEqual(len(pay.references), 3) + # PO references should be removed now + self.assertEqual( + ( + pay.references[0].reference_doctype, + pay.references[0].reference_name, + pay.references[0].allocated_amount, + ), + (pi1.doctype, pi1.name, 100), + ) + self.assertEqual( + ( + pay.references[1].reference_doctype, + pay.references[1].reference_name, + pay.references[1].allocated_amount, + ), + (pi2.doctype, pi2.name, 1000), + ) + self.assertEqual( + ( + pay.references[2].reference_doctype, + pay.references[2].reference_name, + pay.references[2].allocated_amount, + ), + (pi1.doctype, pi1.name, 900), + ) + self.assertEqual(pay.total_allocated_amount, 2000) + self.assertEqual(pay.unallocated_amount, 1000) + self.assertEqual(pay.difference_amount, 0) + def make_customer(customer_name, currency=None): if not frappe.db.exists("Customer", customer_name): diff --git a/erpnext/accounts/doctype/payment_reconciliation_allocation/payment_reconciliation_allocation.json b/erpnext/accounts/doctype/payment_reconciliation_allocation/payment_reconciliation_allocation.json index ec718aa70d3..5b8556e7c83 100644 --- a/erpnext/accounts/doctype/payment_reconciliation_allocation/payment_reconciliation_allocation.json +++ b/erpnext/accounts/doctype/payment_reconciliation_allocation/payment_reconciliation_allocation.json @@ -19,6 +19,7 @@ "is_advance", "section_break_5", "difference_amount", + "gain_loss_posting_date", "column_break_7", "difference_account", "exchange_rate", @@ -151,11 +152,16 @@ "fieldtype": "Link", "label": "Cost Center", "options": "Cost Center" + }, + { + "fieldname": "gain_loss_posting_date", + "fieldtype": "Date", + "label": "Difference Posting Date" } ], "istable": 1, "links": [], - "modified": "2023-09-03 07:52:33.684217", + "modified": "2023-10-23 10:44:56.066303", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Reconciliation Allocation", diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index c5e45d4f3a9..306208603c0 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -33,7 +33,7 @@ from erpnext.accounts.general_ledger import ( ) from erpnext.accounts.party import get_due_date, get_party_account 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.asset import 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 @@ -284,9 +284,6 @@ class PurchaseInvoice(BuyingController): # in case of auto inventory accounting, # expense account is always "Stock Received But Not Billed" for a stock item # except opening entry, drop-ship entry and fixed asset items - if item.item_code: - asset_category = frappe.get_cached_value("Item", item.item_code, "asset_category") - if ( auto_accounting_for_stock and item.item_code in stock_items @@ -353,22 +350,26 @@ class PurchaseInvoice(BuyingController): frappe.msgprint(msg, title=_("Expense Head Changed")) item.expense_account = stock_not_billed_account - - elif item.is_fixed_asset and not is_cwip_accounting_enabled(asset_category): + elif item.is_fixed_asset and item.pr_detail: + if not asset_received_but_not_billed: + asset_received_but_not_billed = self.get_company_default("asset_received_but_not_billed") + item.expense_account = asset_received_but_not_billed + elif item.is_fixed_asset: + account_type = ( + "capital_work_in_progress_account" + if is_cwip_accounting_enabled(item.asset_category) + else "fixed_asset_account" + ) asset_category_account = get_asset_category_account( - "fixed_asset_account", item=item.item_code, company=self.company + account_type, item=item.item_code, company=self.company ) if not asset_category_account: - form_link = get_link_to_form("Asset Category", asset_category) + form_link = get_link_to_form("Asset Category", item.asset_category) throw( _("Please set Fixed Asset Account in {} against {}.").format(form_link, self.company), title=_("Missing Account"), ) item.expense_account = asset_category_account - elif item.is_fixed_asset and item.pr_detail: - if not asset_received_but_not_billed: - asset_received_but_not_billed = self.get_company_default("asset_received_but_not_billed") - item.expense_account = asset_received_but_not_billed elif not item.expense_account and for_validate: throw(_("Expense account is mandatory for item {0}").format(item.item_code or item.item_name)) @@ -591,12 +592,12 @@ class PurchaseInvoice(BuyingController): def get_gl_entries(self, warehouse_account=None): self.auto_accounting_for_stock = erpnext.is_perpetual_inventory_enabled(self.company) + self.asset_received_but_not_billed = self.get_company_default("asset_received_but_not_billed") + if self.auto_accounting_for_stock: self.stock_received_but_not_billed = self.get_company_default("stock_received_but_not_billed") - self.expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation") else: self.stock_received_but_not_billed = None - self.expenses_included_in_valuation = None self.negative_expense_to_be_booked = 0.0 gl_entries = [] @@ -605,9 +606,6 @@ class PurchaseInvoice(BuyingController): self.make_item_gl_entries(gl_entries) self.make_precision_loss_gl_entry(gl_entries) - if self.check_asset_cwip_enabled(): - self.get_asset_gl_entry(gl_entries) - self.make_tax_gl_entries(gl_entries) self.make_internal_transfer_gl_entries(gl_entries) @@ -711,7 +709,11 @@ class PurchaseInvoice(BuyingController): if item.item_code: asset_category = frappe.get_cached_value("Item", item.item_code, "asset_category") - if self.update_stock and self.auto_accounting_for_stock and item.item_code in stock_items: + if ( + self.update_stock + and self.auto_accounting_for_stock + and (item.item_code in stock_items or item.is_fixed_asset) + ): # warehouse account warehouse_debit_amount = self.make_stock_adjustment_entry( gl_entries, item, voucher_wise_stock_value, account_currency @@ -826,9 +828,7 @@ class PurchaseInvoice(BuyingController): ) ) - elif not item.is_fixed_asset or ( - item.is_fixed_asset and not is_cwip_accounting_enabled(asset_category) - ): + else: expense_account = ( item.expense_account if (not item.enable_deferred_expense or self.is_return) @@ -921,40 +921,6 @@ class PurchaseInvoice(BuyingController): ) ) - # If asset is bought through this document and not linked to PR - if self.update_stock and item.landed_cost_voucher_amount: - expenses_included_in_asset_valuation = self.get_company_default( - "expenses_included_in_asset_valuation" - ) - # Amount added through landed-cost-voucher - gl_entries.append( - self.get_gl_dict( - { - "account": expenses_included_in_asset_valuation, - "against": expense_account, - "cost_center": item.cost_center, - "remarks": self.get("remarks") or _("Accounting Entry for Stock"), - "credit": flt(item.landed_cost_voucher_amount), - "project": item.project or self.project, - }, - item=item, - ) - ) - - gl_entries.append( - self.get_gl_dict( - { - "account": expense_account, - "against": expenses_included_in_asset_valuation, - "cost_center": item.cost_center, - "remarks": self.get("remarks") or _("Accounting Entry for Stock"), - "debit": flt(item.landed_cost_voucher_amount), - "project": item.project or self.project, - }, - item=item, - ) - ) - # update gross amount of asset bought through this document assets = frappe.db.get_all( "Asset", filters={"purchase_invoice": self.name, "item_code": item.item_code} @@ -979,11 +945,16 @@ class PurchaseInvoice(BuyingController): (item.purchase_receipt, valuation_tax_accounts), ) + stock_rbnb = ( + self.asset_received_but_not_billed + if item.is_fixed_asset + else self.stock_received_but_not_billed + ) if not negative_expense_booked_in_pr: gl_entries.append( self.get_gl_dict( { - "account": self.stock_received_but_not_billed, + "account": stock_rbnb, "against": self.supplier, "debit": flt(item.item_tax_amount, item.precision("item_tax_amount")), "remarks": self.remarks or _("Accounting Entry for Stock"), @@ -998,156 +969,12 @@ class PurchaseInvoice(BuyingController): item.item_tax_amount, item.precision("item_tax_amount") ) - def get_asset_gl_entry(self, gl_entries): - arbnb_account = None - eiiav_account = None - asset_eiiav_currency = None - - for item in self.get("items"): - if item.is_fixed_asset: - asset_amount = flt(item.net_amount) + flt(item.item_tax_amount / self.conversion_rate) - base_asset_amount = flt(item.base_net_amount + item.item_tax_amount) - - item_exp_acc_type = frappe.db.get_value("Account", item.expense_account, "account_type") - if not item.expense_account or item_exp_acc_type not in [ - "Asset Received But Not Billed", - "Fixed Asset", - ]: - if not arbnb_account: - arbnb_account = self.get_company_default("asset_received_but_not_billed") - item.expense_account = arbnb_account - - if not self.update_stock: - arbnb_currency = get_account_currency(item.expense_account) - gl_entries.append( - self.get_gl_dict( - { - "account": item.expense_account, - "against": self.supplier, - "remarks": self.get("remarks") or _("Accounting Entry for Asset"), - "debit": base_asset_amount, - "debit_in_account_currency": ( - base_asset_amount if arbnb_currency == self.company_currency else asset_amount - ), - "cost_center": item.cost_center, - "project": item.project or self.project, - }, - item=item, - ) - ) - - if item.item_tax_amount: - if not eiiav_account or not asset_eiiav_currency: - eiiav_account = self.get_company_default("expenses_included_in_asset_valuation") - asset_eiiav_currency = get_account_currency(eiiav_account) - - gl_entries.append( - self.get_gl_dict( - { - "account": eiiav_account, - "against": self.supplier, - "remarks": self.get("remarks") or _("Accounting Entry for Asset"), - "cost_center": item.cost_center, - "project": item.project or self.project, - "credit": item.item_tax_amount, - "credit_in_account_currency": ( - item.item_tax_amount - if asset_eiiav_currency == self.company_currency - else item.item_tax_amount / self.conversion_rate - ), - }, - item=item, - ) - ) - else: - cwip_account = get_asset_account( - "capital_work_in_progress_account", asset_category=item.asset_category, company=self.company - ) - - cwip_account_currency = get_account_currency(cwip_account) - gl_entries.append( - self.get_gl_dict( - { - "account": cwip_account, - "against": self.supplier, - "remarks": self.get("remarks") or _("Accounting Entry for Asset"), - "debit": base_asset_amount, - "debit_in_account_currency": ( - base_asset_amount if cwip_account_currency == self.company_currency else asset_amount - ), - "cost_center": self.cost_center, - "project": item.project or self.project, - }, - item=item, - ) - ) - - if item.item_tax_amount and not cint(erpnext.is_perpetual_inventory_enabled(self.company)): - if not eiiav_account or not asset_eiiav_currency: - eiiav_account = self.get_company_default("expenses_included_in_asset_valuation") - asset_eiiav_currency = get_account_currency(eiiav_account) - - gl_entries.append( - self.get_gl_dict( - { - "account": eiiav_account, - "against": self.supplier, - "remarks": self.get("remarks") or _("Accounting Entry for Asset"), - "cost_center": item.cost_center, - "credit": item.item_tax_amount, - "project": item.project or self.project, - "credit_in_account_currency": ( - item.item_tax_amount - if asset_eiiav_currency == self.company_currency - else item.item_tax_amount / self.conversion_rate - ), - }, - item=item, - ) - ) - - # Assets are bought through this document then it will be linked to this document - if flt(item.landed_cost_voucher_amount): - if not eiiav_account: - eiiav_account = self.get_company_default("expenses_included_in_asset_valuation") - - gl_entries.append( - self.get_gl_dict( - { - "account": eiiav_account, - "against": cwip_account, - "cost_center": item.cost_center, - "remarks": self.get("remarks") or _("Accounting Entry for Stock"), - "credit": flt(item.landed_cost_voucher_amount), - "project": item.project or self.project, - }, - item=item, - ) - ) - - gl_entries.append( - self.get_gl_dict( - { - "account": cwip_account, - "against": eiiav_account, - "cost_center": item.cost_center, - "remarks": self.get("remarks") or _("Accounting Entry for Stock"), - "debit": flt(item.landed_cost_voucher_amount), - "project": item.project or self.project, - }, - item=item, - ) - ) - - # update gross amount of assets bought through this document - assets = frappe.db.get_all( - "Asset", filters={"purchase_invoice": self.name, "item_code": item.item_code} - ) - for asset in assets: - frappe.db.set_value("Asset", asset.name, "gross_purchase_amount", flt(item.valuation_rate)) - frappe.db.set_value("Asset", asset.name, "purchase_receipt_amount", flt(item.valuation_rate)) - - return gl_entries + assets = frappe.db.get_all( + "Asset", filters={"purchase_invoice": self.name, "item_code": item.item_code} + ) + for asset in assets: + frappe.db.set_value("Asset", asset.name, "gross_purchase_amount", flt(item.valuation_rate)) + frappe.db.set_value("Asset", asset.name, "purchase_receipt_amount", flt(item.valuation_rate)) def make_stock_adjustment_entry( self, gl_entries, item, voucher_wise_stock_value, account_currency diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index 170a163b45f..1f60a11908c 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -1858,6 +1858,30 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin): pi.load_from_db() self.assertFalse(pi.repost_required) + def test_default_cost_center_for_purchase(self): + from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center + + for c_center in ["_Test Cost Center Selling", "_Test Cost Center Buying"]: + create_cost_center(cost_center_name=c_center) + + item = create_item( + "_Test Cost Center Item For Purchase", + is_stock_item=1, + buying_cost_center="_Test Cost Center Buying - _TC", + selling_cost_center="_Test Cost Center Selling - _TC", + ) + + pi = make_purchase_invoice( + item=item.name, qty=1, rate=1000, update_stock=True, do_not_submit=True, cost_center="" + ) + + pi.items[0].cost_center = "" + pi.set_missing_values() + pi.calculate_taxes_and_totals() + pi.save() + + self.assertEqual(pi.items[0].cost_center, "_Test Cost Center Buying - _TC") + def check_gl_entries( doc, diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 272382e8c18..79363dbeb1b 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -2497,12 +2497,6 @@ class TestSalesInvoice(FrappeTestCase): "stock_received_but_not_billed", "Stock Received But Not Billed - _TC1", ) - frappe.db.set_value( - "Company", - "_Test Company 1", - "expenses_included_in_valuation", - "Expenses Included In Valuation - _TC1", - ) # begin test si = create_sales_invoice( @@ -2540,7 +2534,7 @@ class TestSalesInvoice(FrappeTestCase): # tear down frappe.local.enable_perpetual_inventory["_Test Company 1"] = old_perpetual_inventory - frappe.db.set_value("Stock Settings", None, "allow_negative_stock", old_negative_stock) + frappe.db.set_single_value("Stock Settings", "allow_negative_stock", old_negative_stock) def test_sle_for_target_warehouse(self): se = make_stock_entry( @@ -2552,6 +2546,7 @@ class TestSalesInvoice(FrappeTestCase): ) si = frappe.copy_doc(test_records[0]) + si.customer = "_Test Internal Customer 3" si.update_stock = 1 si.set_warehouse = "Finished Goods - _TC" si.set_target_warehouse = "Stores - _TC" @@ -3579,6 +3574,20 @@ def create_internal_parties(): allowed_to_interact_with="_Test Company with perpetual inventory", ) + create_internal_customer( + customer_name="_Test Internal Customer 3", + represents_company="_Test Company", + allowed_to_interact_with="_Test Company", + ) + + account = create_account( + account_name="Unrealized Profit", + parent_account="Current Liabilities - _TC", + company="_Test Company", + ) + + frappe.db.set_value("Company", "_Test Company", "unrealized_profit_loss_account", account) + create_internal_supplier( supplier_name="_Test Internal Supplier", represents_company="Wind Power LLC", diff --git a/erpnext/accounts/doctype/subscription/subscription.js b/erpnext/accounts/doctype/subscription/subscription.js index 1a9066470a5..4b351f9d764 100644 --- a/erpnext/accounts/doctype/subscription/subscription.js +++ b/erpnext/accounts/doctype/subscription/subscription.js @@ -18,6 +18,14 @@ frappe.ui.form.on('Subscription', { } }; }); + + frm.set_query('sales_tax_template', function () { + return { + filters: { + company: frm.doc.company + } + }; + }); }, refresh: function(frm) { diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py index ed6d9dbe026..07cc5dd734f 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -41,7 +41,7 @@ def make_gl_entries( from_repost=from_repost, ) save_entries(gl_map, adv_adj, update_outstanding, from_repost) - # Post GL Map proccess there may no be any GL Entries + # Post GL Map process there may no be any GL Entries elif gl_map: frappe.throw( _( diff --git a/erpnext/accounts/report/cash_flow/custom_cash_flow.py b/erpnext/accounts/report/cash_flow/custom_cash_flow.py index b165c88c068..24e585e07f6 100644 --- a/erpnext/accounts/report/cash_flow/custom_cash_flow.py +++ b/erpnext/accounts/report/cash_flow/custom_cash_flow.py @@ -67,7 +67,7 @@ def setup_mappers(mappers): mapping["finance_costs"] = [] mapping["finance_costs_adjustments"] = [] doc = frappe.get_doc("Cash Flow Mapper", mapping["name"]) - mapping_names = [item.name for item in doc.accounts] + mapping_names = [item.mapping for item in doc.accounts] if not mapping_names: continue diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 76339713a22..53e1c1d76dc 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -624,7 +624,7 @@ def update_reference_in_payment_entry( "outstanding_amount": d.outstanding_amount, "allocated_amount": d.allocated_amount, "exchange_rate": d.exchange_rate if 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 + "exchange_gain_loss": d.exchange_gain_loss, } if d.voucher_detail_no: @@ -636,28 +636,29 @@ def update_reference_in_payment_entry( existing_row.reference_doctype, existing_row.reference_name ).set_total_advance_paid() - original_row = existing_row.as_dict().copy() - existing_row.update(reference_details) + if d.allocated_amount <= existing_row.allocated_amount: + existing_row.allocated_amount -= d.allocated_amount - if d.allocated_amount < original_row.allocated_amount: new_row = payment_entry.append("references") new_row.docstatus = 1 for field in list(reference_details): - new_row.set(field, original_row[field]) + new_row.set(field, reference_details[field]) - new_row.allocated_amount = original_row.allocated_amount - d.allocated_amount else: new_row = payment_entry.append("references") new_row.docstatus = 1 new_row.update(reference_details) payment_entry.flags.ignore_validate_update_after_submit = True + payment_entry.clear_unallocated_reference_document_rows() payment_entry.setup_party_account_field() payment_entry.set_missing_values() if not skip_ref_details_update_for_pe: payment_entry.set_missing_ref_details() payment_entry.set_amounts() - payment_entry.make_exchange_gain_loss_journal() + payment_entry.make_exchange_gain_loss_journal( + frappe._dict({"difference_posting_date": d.difference_posting_date}) + ) if not do_not_save: payment_entry.save(ignore_permissions=True) diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index fc36df8aec5..89553f2a83a 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -186,6 +186,7 @@ class TestAsset(AssetSetup): def test_is_fixed_asset_set(self): asset = create_asset(is_existing_asset=1) doc = frappe.new_doc("Purchase Invoice") + doc.company = "_Test Company" doc.supplier = "_Test Supplier" doc.append("items", {"item_code": "Macbook Pro", "qty": 1, "asset": asset.name}) @@ -487,7 +488,7 @@ class TestAsset(AssetSetup): self.assertEqual("Asset Received But Not Billed - _TC", doc.items[0].expense_account) - # CWIP: Capital Work In Progress + # Capital Work In Progress def test_cwip_accounting(self): pr = make_purchase_receipt( item_code="Macbook Pro", qty=1, rate=5000, do_not_submit=True, location="Test Location" @@ -520,7 +521,8 @@ class TestAsset(AssetSetup): pr.submit() expected_gle = ( - ("Asset Received But Not Billed - _TC", 0.0, 5250.0), + ("_Test Account Shipping Charges - _TC", 0.0, 250.0), + ("Asset Received But Not Billed - _TC", 0.0, 5000.0), ("CWIP Account - _TC", 5250.0, 0.0), ) @@ -539,9 +541,8 @@ class TestAsset(AssetSetup): expected_gle = ( ("_Test Account Service Tax - _TC", 250.0, 0.0), ("_Test Account Shipping Charges - _TC", 250.0, 0.0), - ("Asset Received But Not Billed - _TC", 5250.0, 0.0), + ("Asset Received But Not Billed - _TC", 5000.0, 0.0), ("Creditors - _TC", 0.0, 5500.0), - ("Expenses Included In Asset Valuation - _TC", 0.0, 250.0), ) pi_gle = frappe.db.sql( diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index 06b9d29e69c..d87a855febf 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -522,6 +522,8 @@ def make_purchase_receipt(source_name, target_doc=None): "bom": "bom", "material_request": "material_request", "material_request_item": "material_request_item", + "sales_order": "sales_order", + "sales_order_item": "sales_order_item", }, "postprocess": update_item, "condition": lambda doc: abs(doc.received_qty) < abs(doc.qty) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index c17866162b6..745c23a91c9 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1131,7 +1131,9 @@ class AccountsController(TransactionBase): self.name, arg.get("referenced_row"), ): - posting_date = frappe.db.get_value(arg.voucher_type, arg.voucher_no, "posting_date") + posting_date = arg.get("difference_posting_date") or frappe.db.get_value( + arg.voucher_type, arg.voucher_no, "posting_date" + ) je = create_gain_loss_journal( self.company, posting_date, @@ -1214,7 +1216,7 @@ class AccountsController(TransactionBase): je = create_gain_loss_journal( self.company, - self.posting_date, + args.get("difference_posting_date") if args else self.posting_date, self.party_type, self.party, party_account, diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index e24d8fb661f..a72cc667399 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -62,9 +62,12 @@ class StockController(AccountsController): ) ) + is_asset_pr = any(d.get("is_fixed_asset") for d in self.get("items")) + if ( cint(erpnext.is_perpetual_inventory_enabled(self.company)) or provisional_accounting_for_non_stock_items + or is_asset_pr ): warehouse_account = get_warehouse_account_map(self.company) @@ -73,11 +76,6 @@ class StockController(AccountsController): gl_entries = self.get_gl_entries(warehouse_account) make_gl_entries(gl_entries, from_repost=from_repost) - elif self.doctype in ["Purchase Receipt", "Purchase Invoice"] and self.docstatus == 1: - gl_entries = [] - gl_entries = self.get_asset_gl_entry(gl_entries) - make_gl_entries(gl_entries, from_repost=from_repost) - def validate_serialized_batch(self): from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos @@ -670,13 +668,21 @@ class StockController(AccountsController): d.stock_uom_rate = d.rate / (d.conversion_factor or 1) def validate_internal_transfer(self): - if ( - self.doctype in ("Sales Invoice", "Delivery Note", "Purchase Invoice", "Purchase Receipt") - and self.is_internal_transfer() - ): - self.validate_in_transit_warehouses() - self.validate_multi_currency() - self.validate_packed_items() + if self.doctype in ("Sales Invoice", "Delivery Note", "Purchase Invoice", "Purchase Receipt"): + if self.is_internal_transfer(): + self.validate_in_transit_warehouses() + self.validate_multi_currency() + self.validate_packed_items() + else: + self.validate_internal_transfer_warehouse() + + def validate_internal_transfer_warehouse(self): + for row in self.items: + if row.get("target_warehouse"): + row.target_warehouse = None + + if row.get("from_warehouse"): + row.from_warehouse = None def validate_in_transit_warehouses(self): if ( diff --git a/erpnext/controllers/tests/test_accounts_controller.py b/erpnext/controllers/tests/test_accounts_controller.py index 391258fde77..97d3c5c32de 100644 --- a/erpnext/controllers/tests/test_accounts_controller.py +++ b/erpnext/controllers/tests/test_accounts_controller.py @@ -7,7 +7,7 @@ import frappe from frappe import qb from frappe.query_builder.functions import Sum from frappe.tests.utils import FrappeTestCase, change_settings -from frappe.utils import add_days, flt, nowdate +from frappe.utils import add_days, flt, getdate, nowdate from erpnext import get_default_cost_center from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry @@ -614,6 +614,73 @@ class TestAccountsController(FrappeTestCase): self.assertEqual(exc_je_for_si, []) self.assertEqual(exc_je_for_pe, []) + def test_15_gain_loss_on_different_posting_date(self): + # Invoice in Foreign Currency + si = self.create_sales_invoice( + posting_date=add_days(nowdate(), -2), qty=2, conversion_rate=80, rate=1 + ) + # Payment + pe = ( + self.create_payment_entry(posting_date=add_days(nowdate(), -1), amount=2, source_exc_rate=75) + .save() + .submit() + ) + + # There should be outstanding in both currencies + si.reload() + self.assertEqual(si.outstanding_amount, 2) + self.assert_ledger_outstanding(si.doctype, si.name, 160.0, 2.0) + + # Reconcile the remaining amount + pr = frappe.get_doc("Payment Reconciliation") + pr.company = self.company + pr.party_type = "Customer" + pr.party = self.customer + pr.receivable_payable_account = self.debit_usd + pr.get_unreconciled_entries() + self.assertEqual(len(pr.invoices), 1) + self.assertEqual(len(pr.payments), 1) + invoices = [x.as_dict() for x in pr.invoices] + payments = [x.as_dict() for x in pr.payments] + pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments})) + pr.allocation[0].gain_loss_posting_date = add_days(nowdate(), 1) + pr.reconcile() + + # Exchange Gain/Loss Journal should've been created. + exc_je_for_si = self.get_journals_for(si.doctype, si.name) + exc_je_for_pe = self.get_journals_for(pe.doctype, pe.name) + self.assertNotEqual(exc_je_for_si, []) + self.assertEqual(len(exc_je_for_si), 1) + self.assertEqual(len(exc_je_for_pe), 1) + self.assertEqual(exc_je_for_si[0], exc_je_for_pe[0]) + + self.assertEqual( + frappe.db.get_value("Journal Entry", exc_je_for_si[0].parent, "posting_date"), + getdate(add_days(nowdate(), 1)), + ) + + self.assertEqual(len(pr.invoices), 0) + self.assertEqual(len(pr.payments), 0) + + # There should be no outstanding + si.reload() + self.assertEqual(si.outstanding_amount, 0) + self.assert_ledger_outstanding(si.doctype, si.name, 0.0, 0.0) + + # Cancel Payment + pe.reload() + pe.cancel() + + si.reload() + self.assertEqual(si.outstanding_amount, 2) + self.assert_ledger_outstanding(si.doctype, si.name, 160.0, 2.0) + + # Exchange Gain/Loss Journal should've been cancelled + exc_je_for_si = self.get_journals_for(si.doctype, si.name) + exc_je_for_pe = self.get_journals_for(pe.doctype, pe.name) + self.assertEqual(exc_je_for_si, []) + self.assertEqual(exc_je_for_pe, []) + def test_20_journal_against_sales_invoice(self): # Invoice in Foreign Currency si = self.create_sales_invoice(qty=1, conversion_rate=80, rate=1) diff --git a/erpnext/loan_management/doctype/loan/loan.py b/erpnext/loan_management/doctype/loan/loan.py index 0c9c97f60fd..4a333df5a59 100644 --- a/erpnext/loan_management/doctype/loan/loan.py +++ b/erpnext/loan_management/doctype/loan/loan.py @@ -411,11 +411,6 @@ def close_unsecured_term_loan(loan): frappe.throw(_("Cannot close this loan until full repayment")) -def close_loan(loan, total_amount_paid): - frappe.db.set_value("Loan", loan, "total_amount_paid", total_amount_paid) - frappe.db.set_value("Loan", loan, "status", "Closed") - - @frappe.whitelist() def make_loan_disbursement(loan, company, applicant_type, applicant, pending_amount=0, as_dict=0): disbursement_entry = frappe.new_doc("Loan Disbursement") diff --git a/erpnext/loan_management/doctype/loan_write_off/loan_write_off.py b/erpnext/loan_management/doctype/loan_write_off/loan_write_off.py index 25aecf673bb..e920f08c70f 100644 --- a/erpnext/loan_management/doctype/loan_write_off/loan_write_off.py +++ b/erpnext/loan_management/doctype/loan_write_off/loan_write_off.py @@ -9,6 +9,9 @@ from frappe.utils import cint, flt, getdate import erpnext from erpnext.accounts.general_ledger import make_gl_entries from erpnext.controllers.accounts_controller import AccountsController +from erpnext.loan_management.doctype.loan_repayment.loan_repayment import ( + get_pending_principal_amount, +) class LoanWriteOff(AccountsController): @@ -39,11 +42,13 @@ class LoanWriteOff(AccountsController): def on_submit(self): self.update_outstanding_amount() self.make_gl_entries() + self.close_employee_loan() def on_cancel(self): self.update_outstanding_amount(cancel=1) self.ignore_linked_doctypes = ["GL Entry", "Payment Ledger Entry"] self.make_gl_entries(cancel=1) + self.close_employee_loan(cancel=1) def update_outstanding_amount(self, cancel=0): written_off_amount = frappe.db.get_value("Loan", self.loan, "written_off_amount") @@ -94,3 +99,39 @@ class LoanWriteOff(AccountsController): ) make_gl_entries(gl_entries, cancel=cancel, merge_entries=False) + + def close_employee_loan(self, cancel=0): + if not frappe.db.has_column("Loan", "repay_from_salary"): + return + + loan = frappe.get_value( + "Loan", + self.loan, + [ + "total_payment", + "total_principal_paid", + "loan_amount", + "total_interest_payable", + "written_off_amount", + "disbursed_amount", + "status", + "is_secured_loan", + "repay_from_salary", + "name", + ], + as_dict=1, + ) + + if loan.is_secured_loan or not loan.repay_from_salary: + return + + if not cancel: + pending_principal_amount = get_pending_principal_amount(loan) + + precision = cint(frappe.db.get_default("currency_precision")) or 2 + + if flt(pending_principal_amount, precision) <= 0: + frappe.db.set_value("Loan", loan.name, "status", "Closed") + frappe.msgprint(_("Loan {0} closed").format(loan.name)) + else: + frappe.db.set_value("Loan", loan.loan, "status", "Disbursed") diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index 8058a5f8b75..8d593d12963 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -1172,12 +1172,12 @@ def get_children(parent=None, is_root=False, **filters): def add_additional_cost(stock_entry, work_order): # Add non stock items cost in the additional cost stock_entry.additional_costs = [] - expenses_included_in_valuation = frappe.get_cached_value( - "Company", work_order.company, "expenses_included_in_valuation" + default_expense_account = frappe.get_cached_value( + "Company", work_order.company, "default_expense_account" ) - add_non_stock_items_cost(stock_entry, work_order, expenses_included_in_valuation) - add_operations_cost(stock_entry, work_order, expenses_included_in_valuation) + add_non_stock_items_cost(stock_entry, work_order, default_expense_account) + add_operations_cost(stock_entry, work_order, default_expense_account) def add_non_stock_items_cost(stock_entry, work_order, expense_account): diff --git a/erpnext/patches/v12_0/set_expense_account_in_landed_cost_voucher_taxes.py b/erpnext/patches/v12_0/set_expense_account_in_landed_cost_voucher_taxes.py deleted file mode 100644 index 9588e026d34..00000000000 --- a/erpnext/patches/v12_0/set_expense_account_in_landed_cost_voucher_taxes.py +++ /dev/null @@ -1,42 +0,0 @@ -import frappe - - -def execute(): - frappe.reload_doctype("Landed Cost Taxes and Charges") - - company_account_map = frappe._dict( - frappe.db.sql( - """ - SELECT name, expenses_included_in_valuation from `tabCompany` - """ - ) - ) - - for company, account in company_account_map.items(): - frappe.db.sql( - """ - UPDATE - `tabLanded Cost Taxes and Charges` t, `tabLanded Cost Voucher` l - SET - t.expense_account = %s - WHERE - l.docstatus = 1 - AND l.company = %s - AND t.parent = l.name - """, - (account, company), - ) - - frappe.db.sql( - """ - UPDATE - `tabLanded Cost Taxes and Charges` t, `tabStock Entry` s - SET - t.expense_account = %s - WHERE - s.docstatus = 1 - AND s.company = %s - AND t.parent = s.name - """, - (account, company), - ) diff --git a/erpnext/public/js/utils/unreconcile.js b/erpnext/public/js/utils/unreconcile.js index bbdd51d6e54..fa00ed23620 100644 --- a/erpnext/public/js/utils/unreconcile.js +++ b/erpnext/public/js/utils/unreconcile.js @@ -17,7 +17,7 @@ erpnext.accounts.unreconcile_payments = { }, callback: function(r) { if (r.message) { - frm.add_custom_button(__("Un-Reconcile"), function() { + frm.add_custom_button(__("UnReconcile"), function() { erpnext.accounts.unreconcile_payments.build_unreconcile_dialog(frm); }, __('Actions')); } @@ -87,11 +87,11 @@ erpnext.accounts.unreconcile_payments = { unreconcile_dialog_fields[0].get_data = function(){ return r.message}; let d = new frappe.ui.Dialog({ - title: 'Un-Reconcile Allocations', + title: 'UnReconcile Allocations', fields: unreconcile_dialog_fields, size: 'large', cannot_add_rows: true, - primary_action_label: 'Un-Reconcile', + primary_action_label: 'UnReconcile', primary_action(values) { let selected_allocations = values.allocations.filter(x=>x.__checked); diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json index 4f498fb20d5..490bd7a9830 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.json +++ b/erpnext/selling/doctype/sales_order/sales_order.json @@ -1631,7 +1631,7 @@ "idx": 105, "is_submittable": 1, "links": [], - "modified": "2023-04-20 11:14:01.036202", + "modified": "2023-10-18 12:41:54.813462", "modified_by": "Administrator", "module": "Selling", "name": "Sales Order", diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index 799ad555a52..ec400161e3a 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -1893,10 +1893,10 @@ class TestSalesOrder(FrappeTestCase): si.submit() pe.load_from_db() - self.assertEqual(pe.references[0].reference_name, si.name) - self.assertEqual(pe.references[0].allocated_amount, 200) - self.assertEqual(pe.references[1].reference_name, so.name) - self.assertEqual(pe.references[1].allocated_amount, 300) + self.assertEqual(pe.references[0].reference_name, so.name) + self.assertEqual(pe.references[0].allocated_amount, 300) + self.assertEqual(pe.references[1].reference_name, si.name) + self.assertEqual(pe.references[1].allocated_amount, 200) def test_delivered_item_material_request(self): "SO -> MR (Manufacture) -> WO. Test if WO Qty is updated in SO." diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js index 6aa400a53c7..15f32b15c5e 100644 --- a/erpnext/setup/doctype/company/company.js +++ b/erpnext/setup/doctype/company/company.js @@ -223,7 +223,6 @@ erpnext.company.setup_queries = function(frm) { ["cost_center", {}], ["round_off_cost_center", {}], ["depreciation_cost_center", {}], - ["expenses_included_in_asset_valuation", {"account_type": "Expenses Included In Asset Valuation"}], ["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"]]}], @@ -236,8 +235,6 @@ erpnext.company.setup_queries = function(frm) { $.each([ ["stock_adjustment_account", {"root_type": "Expense", "account_type": "Stock Adjustment"}], - ["expenses_included_in_valuation", - {"root_type": "Expense", "account_type": "Expenses Included in Valuation"}], ["stock_received_but_not_billed", {"root_type": "Liability", "account_type": "Stock Received But Not Billed"}], ["service_received_but_not_billed", diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json index 38f1c0bcdb2..349c328c06c 100644 --- a/erpnext/setup/doctype/company/company.json +++ b/erpnext/setup/doctype/company/company.json @@ -79,12 +79,10 @@ "column_break_32", "stock_received_but_not_billed", "default_provisional_account", - "expenses_included_in_valuation", "fixed_asset_defaults", "accumulated_depreciation_account", "depreciation_expense_account", "series_for_depreciation_entry", - "expenses_included_in_asset_valuation", "column_break_40", "disposal_account", "depreciation_cost_center", @@ -466,14 +464,6 @@ "no_copy": 1, "options": "Account" }, - { - "fieldname": "expenses_included_in_valuation", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Expenses Included In Valuation", - "no_copy": 1, - "options": "Account" - }, { "fieldname": "accumulated_depreciation_account", "fieldtype": "Link", @@ -493,12 +483,6 @@ "fieldtype": "Data", "label": "Series for Asset Depreciation Entry (Journal Entry)" }, - { - "fieldname": "expenses_included_in_asset_valuation", - "fieldtype": "Link", - "label": "Expenses Included In Asset Valuation", - "options": "Account" - }, { "fieldname": "column_break_40", "fieldtype": "Column Break" @@ -728,7 +712,7 @@ "image_field": "company_logo", "is_tree": 1, "links": [], - "modified": "2023-07-07 05:41:41.537256", + "modified": "2023-10-23 10:19:24.322898", "modified_by": "Administrator", "module": "Setup", "name": "Company", diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py index fcdf245659b..bea76f19412 100644 --- a/erpnext/setup/doctype/company/company.py +++ b/erpnext/setup/doctype/company/company.py @@ -92,7 +92,6 @@ class Company(NestedSet): ["Default Income Account", "default_income_account"], ["Stock Received But Not Billed Account", "stock_received_but_not_billed"], ["Stock Adjustment Account", "stock_adjustment_account"], - ["Expense Included In Valuation Account", "expenses_included_in_valuation"], ] for account in accounts: @@ -384,7 +383,6 @@ class Company(NestedSet): "depreciation_expense_account": "Depreciation", "capital_work_in_progress_account": "Capital Work in Progress", "asset_received_but_not_billed": "Asset Received But Not Billed", - "expenses_included_in_asset_valuation": "Expenses Included In Asset Valuation", "default_expense_account": "Cost of Goods Sold", } @@ -394,7 +392,6 @@ class Company(NestedSet): "stock_received_but_not_billed": "Stock Received But Not Billed", "default_inventory_account": "Stock", "stock_adjustment_account": "Stock Adjustment", - "expenses_included_in_valuation": "Expenses Included In Valuation", } ) diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py index c6f3197a668..3ecfbad1d5d 100644 --- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py @@ -1315,6 +1315,21 @@ class TestDeliveryNote(FrappeTestCase): frappe.db.rollback() frappe.db.set_single_value("Selling Settings", "dont_reserve_sales_order_qty_on_sales_return", 0) + def non_internal_transfer_delivery_note(self): + from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse + + dn = create_delivery_note(do_not_submit=True) + warehouse = create_warehouse("Internal Transfer Warehouse", dn.company) + dn.items[0].db_set("target_warehouse", "warehouse") + + dn.reload() + + self.assertEqual(dn.items[0].target_warehouse, warehouse.name) + + dn.save() + dn.reload() + self.assertFalse(dn.items[0].target_warehouse) + def create_delivery_note(**args): dn = frappe.new_doc("Delivery Note") diff --git a/erpnext/stock/doctype/delivery_trip/delivery_trip.js b/erpnext/stock/doctype/delivery_trip/delivery_trip.js index a6fbb66aa2b..45064d8fcb9 100755 --- a/erpnext/stock/doctype/delivery_trip/delivery_trip.js +++ b/erpnext/stock/doctype/delivery_trip/delivery_trip.js @@ -62,7 +62,7 @@ frappe.ui.form.on('Delivery Trip', { company: frm.doc.company, } }) - }, __("Get customers from")); + }, __("Get stops from")); } }, diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py index 53f6b7f8f17..9aa66a9a1ec 100644 --- a/erpnext/stock/doctype/item/test_item.py +++ b/erpnext/stock/doctype/item/test_item.py @@ -893,6 +893,8 @@ def create_item( opening_stock=0, is_fixed_asset=0, asset_category=None, + buying_cost_center=None, + selling_cost_center=None, company="_Test Company", ): if not frappe.db.exists("Item", item_code): @@ -910,7 +912,15 @@ def create_item( item.is_purchase_item = is_purchase_item item.is_customer_provided_item = is_customer_provided_item item.customer = customer or "" - item.append("item_defaults", {"default_warehouse": warehouse, "company": company}) + item.append( + "item_defaults", + { + "default_warehouse": warehouse, + "company": company, + "selling_cost_center": selling_cost_center, + "buying_cost_center": buying_cost_center, + }, + ) item.save() else: item = frappe.get_doc("Item", item_code) diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index da534b245c6..964ce0b7218 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -13,7 +13,6 @@ from pypika import functions as fn import erpnext from erpnext.accounts.utils import get_account_currency 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.buying_controller import BuyingController from erpnext.stock.doctype.delivery_note.delivery_note import make_inter_company_transaction @@ -146,8 +145,8 @@ class PurchaseReceipt(BuyingController): if item.is_fixed_asset and is_cwip_accounting_enabled(item.asset_category): # check cwip accounts before making auto assets # Improves UX by not giving messages of "Assets Created" before throwing error of not finding arbnb account - arbnb_account = self.get_company_default("asset_received_but_not_billed") - cwip_account = get_asset_account( + self.get_company_default("asset_received_but_not_billed") + get_asset_account( "capital_work_in_progress_account", asset_category=item.asset_category, company=self.company ) break @@ -315,7 +314,6 @@ class PurchaseReceipt(BuyingController): self.make_item_gl_entries(gl_entries, warehouse_account=warehouse_account) self.make_tax_gl_entries(gl_entries) - self.get_asset_gl_entry(gl_entries) return process_gl_map(gl_entries) @@ -324,14 +322,6 @@ class PurchaseReceipt(BuyingController): get_purchase_document_details, ) - stock_rbnb = None - if erpnext.is_perpetual_inventory_enabled(self.company): - stock_rbnb = self.get_company_default("stock_received_but_not_billed") - landed_cost_entries = get_item_account_wise_additional_cost(self.name) - expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation") - - warehouse_with_no_account = [] - stock_items = self.get_stock_items() provisional_accounting_for_non_stock_items = cint( frappe.db.get_value( "Company", self.company, "enable_provisional_accounting_for_non_stock_items" @@ -340,28 +330,256 @@ class PurchaseReceipt(BuyingController): exchange_rate_map, net_rate_map = get_purchase_document_details(self) + def validate_account(account_type): + frappe.throw(_("{0} account not found while submitting purchase receipt").format(account_type)) + + def make_item_asset_inward_gl_entry(item, stock_value_diff, stock_asset_account_name): + account_currency = get_account_currency(stock_asset_account_name) + + if not stock_asset_account_name: + validate_account("Asset or warehouse account") + + self.add_gl_entry( + gl_entries=gl_entries, + account=stock_asset_account_name, + cost_center=d.cost_center, + debit=stock_value_diff, + credit=0.0, + remarks=remarks, + against_account=stock_asset_rbnb, + account_currency=account_currency, + item=item, + ) + + def make_stock_received_but_not_billed_entry(item): + account = ( + warehouse_account[item.from_warehouse]["account"] if item.from_warehouse else stock_asset_rbnb + ) + account_currency = get_account_currency(account) + + # GL Entry for from warehouse or Stock Received but not billed + # Intentionally passed negative debit amount to avoid incorrect GL Entry validation + credit_amount = ( + flt(item.base_net_amount, item.precision("base_net_amount")) + if account_currency == self.company_currency + else flt(item.net_amount, item.precision("net_amount")) + ) + + outgoing_amount = item.base_net_amount + if self.is_internal_transfer() and item.valuation_rate: + outgoing_amount = abs(get_stock_value_difference(self.name, item.name, item.from_warehouse)) + credit_amount = outgoing_amount + + if credit_amount: + if not account: + validate_account("Stock or Asset Received But Not Billed") + + self.add_gl_entry( + gl_entries=gl_entries, + account=account, + cost_center=item.cost_center, + debit=-1 * flt(outgoing_amount, item.precision("base_net_amount")), + credit=0.0, + remarks=remarks, + against_account=stock_asset_account_name, + debit_in_account_currency=-1 * flt(outgoing_amount, item.precision("base_net_amount")), + account_currency=account_currency, + item=item, + ) + + # check if the exchange rate has changed + if d.get("purchase_invoice"): + if ( + exchange_rate_map[item.purchase_invoice] + and self.conversion_rate != exchange_rate_map[item.purchase_invoice] + and item.net_rate == net_rate_map[item.purchase_invoice_item] + ): + + discrepancy_caused_by_exchange_rate_difference = (item.qty * item.net_rate) * ( + exchange_rate_map[item.purchase_invoice] - self.conversion_rate + ) + + self.add_gl_entry( + gl_entries=gl_entries, + account=account, + cost_center=item.cost_center, + debit=0.0, + credit=discrepancy_caused_by_exchange_rate_difference, + remarks=remarks, + against_account=self.supplier, + debit_in_account_currency=-1 * discrepancy_caused_by_exchange_rate_difference, + account_currency=account_currency, + item=item, + ) + + self.add_gl_entry( + gl_entries=gl_entries, + account=self.get_company_default("exchange_gain_loss_account"), + cost_center=d.cost_center, + debit=discrepancy_caused_by_exchange_rate_difference, + credit=0.0, + remarks=remarks, + against_account=self.supplier, + debit_in_account_currency=-1 * discrepancy_caused_by_exchange_rate_difference, + account_currency=account_currency, + item=item, + ) + + return outgoing_amount + + def make_landed_cost_gl_entries(item): + # Amount added through landed-cost-voucher + if item.landed_cost_voucher_amount and landed_cost_entries: + if (item.item_code, item.name) in landed_cost_entries: + for account, amount in landed_cost_entries[(item.item_code, item.name)].items(): + account_currency = get_account_currency(account) + credit_amount = ( + flt(amount["base_amount"]) + if (amount["base_amount"] or account_currency != self.company_currency) + else flt(amount["amount"]) + ) + + if not account: + validate_account("Landed Cost Account") + + self.add_gl_entry( + gl_entries=gl_entries, + account=account, + cost_center=item.cost_center, + debit=0.0, + credit=credit_amount, + remarks=remarks, + against_account=stock_asset_account_name, + credit_in_account_currency=flt(amount["amount"]), + account_currency=account_currency, + project=item.project, + item=item, + ) + + def make_rate_difference_entry(item): + if item.rate_difference_with_purchase_invoice and stock_asset_rbnb: + account_currency = get_account_currency(stock_asset_rbnb) + self.add_gl_entry( + gl_entries=gl_entries, + account=stock_asset_rbnb, + cost_center=item.cost_center, + debit=0.0, + credit=flt(item.rate_difference_with_purchase_invoice), + remarks=_("Adjustment based on Purchase Invoice rate"), + against_account=stock_asset_account_name, + account_currency=account_currency, + project=item.project, + item=item, + ) + + def make_sub_contracting_gl_entries(item): + # sub-contracting warehouse + if flt(item.rm_supp_cost) and warehouse_account.get(self.supplier_warehouse): + self.add_gl_entry( + gl_entries=gl_entries, + account=supplier_warehouse_account, + cost_center=item.cost_center, + debit=0.0, + credit=flt(item.rm_supp_cost), + remarks=remarks, + against_account=stock_asset_account_name, + account_currency=supplier_warehouse_account_currency, + item=item, + ) + + def make_divisional_loss_gl_entry(item, outgoing_amount): + if item.is_fixed_asset: + return + + # divisional loss adjustment + valuation_amount_as_per_doc = ( + flt(outgoing_amount, d.precision("base_net_amount")) + + flt(item.landed_cost_voucher_amount) + + flt(item.rm_supp_cost) + + flt(item.item_tax_amount) + + flt(item.rate_difference_with_purchase_invoice) + ) + + divisional_loss = flt( + valuation_amount_as_per_doc - flt(stock_value_diff), item.precision("base_net_amount") + ) + + if divisional_loss: + loss_account = ( + self.get_company_default("default_expense_account", ignore_validation=True) + or stock_asset_rbnb + ) + + cost_center = item.cost_center or frappe.get_cached_value( + "Company", self.company, "cost_center" + ) + account_currency = get_account_currency(loss_account) + self.add_gl_entry( + gl_entries=gl_entries, + account=loss_account, + cost_center=cost_center, + debit=divisional_loss, + credit=0.0, + remarks=remarks, + against_account=stock_asset_account_name, + account_currency=account_currency, + project=item.project, + item=item, + ) + + stock_items = self.get_stock_items() + warehouse_with_no_account = [] + for d in self.get("items"): - if d.item_code in stock_items and flt(d.qty) and (flt(d.valuation_rate) or self.is_return): - if warehouse_account.get(d.warehouse): - stock_value_diff = frappe.db.get_value( - "Stock Ledger Entry", - { - "voucher_type": "Purchase Receipt", - "voucher_no": self.name, - "voucher_detail_no": d.name, - "warehouse": d.warehouse, - "is_cancelled": 0, - }, - "stock_value_difference", + if ( + provisional_accounting_for_non_stock_items + and d.item_code not in stock_items + and flt(d.qty) + and d.get("provisional_expense_account") + and not d.is_fixed_asset + ): + self.add_provisional_gl_entry( + d, gl_entries, self.posting_date, d.get("provisional_expense_account") + ) + elif flt(d.qty) and (flt(d.valuation_rate) or self.is_return): + is_asset_pr = any(d.is_fixed_asset for d in self.get("items")) + remarks = self.get("remarks") or _("Accounting Entry for {0}").format( + "Asset" if is_asset_pr else "Stock" + ) + + if not (erpnext.is_perpetual_inventory_enabled(self.company) or is_asset_pr): + return + + stock_asset_rbnb = ( + self.get_company_default("asset_received_but_not_billed") + if is_asset_pr + else self.get_company_default("stock_received_but_not_billed") + ) + landed_cost_entries = get_item_account_wise_additional_cost(self.name) + + if d.is_fixed_asset: + account_type = ( + "capital_work_in_progress_account" + if is_cwip_accounting_enabled(d.asset_category) + else "fixed_asset_account" ) - warehouse_account_name = warehouse_account[d.warehouse]["account"] - warehouse_account_currency = warehouse_account[d.warehouse]["account_currency"] + stock_asset_account_name = get_asset_account( + account_type, asset_category=d.asset_category, company=self.company + ) + + stock_value_diff = ( + flt(d.net_amount) + + flt(d.item_tax_amount / self.conversion_rate) + + flt(d.landed_cost_voucher_amount) + ) + elif warehouse_account.get(d.warehouse): + stock_value_diff = get_stock_value_difference(self.name, d.name, d.warehouse) + stock_asset_account_name = warehouse_account[d.warehouse]["account"] supplier_warehouse_account = warehouse_account.get(self.supplier_warehouse, {}).get("account") supplier_warehouse_account_currency = warehouse_account.get(self.supplier_warehouse, {}).get( "account_currency" ) - remarks = self.get("remarks") or _("Accounting Entry for Stock") # If PR is sub-contracted and fg item rate is zero # in that case if account for source and target warehouse are same, @@ -369,214 +587,25 @@ class PurchaseReceipt(BuyingController): if ( flt(stock_value_diff) == flt(d.rm_supp_cost) and warehouse_account.get(self.supplier_warehouse) - and warehouse_account_name == supplier_warehouse_account + and stock_asset_account_name == supplier_warehouse_account ): continue - self.add_gl_entry( - gl_entries=gl_entries, - account=warehouse_account_name, - cost_center=d.cost_center, - debit=stock_value_diff, - credit=0.0, - remarks=remarks, - against_account=stock_rbnb, - account_currency=warehouse_account_currency, - item=d, - ) - - # GL Entry for from warehouse or Stock Received but not billed - # Intentionally passed negative debit amount to avoid incorrect GL Entry validation - credit_currency = ( - get_account_currency(warehouse_account[d.from_warehouse]["account"]) - if d.from_warehouse - else get_account_currency(stock_rbnb) - ) - - credit_amount = ( - flt(d.base_net_amount, d.precision("base_net_amount")) - if credit_currency == self.company_currency - else flt(d.net_amount, d.precision("net_amount")) - ) - - outgoing_amount = d.base_net_amount - if self.is_internal_transfer() and d.valuation_rate: - outgoing_amount = abs( - frappe.db.get_value( - "Stock Ledger Entry", - { - "voucher_type": "Purchase Receipt", - "voucher_no": self.name, - "voucher_detail_no": d.name, - "warehouse": d.from_warehouse, - "is_cancelled": 0, - }, - "stock_value_difference", - ) - ) - credit_amount = outgoing_amount - - if credit_amount: - account = warehouse_account[d.from_warehouse]["account"] if d.from_warehouse else stock_rbnb - - self.add_gl_entry( - gl_entries=gl_entries, - account=account, - cost_center=d.cost_center, - debit=-1 * flt(outgoing_amount, d.precision("base_net_amount")), - credit=0.0, - remarks=remarks, - against_account=warehouse_account_name, - debit_in_account_currency=-1 * credit_amount, - account_currency=credit_currency, - item=d, - ) - - # check if the exchange rate has changed - if d.get("purchase_invoice"): - if ( - exchange_rate_map[d.purchase_invoice] - and self.conversion_rate != exchange_rate_map[d.purchase_invoice] - and d.net_rate == net_rate_map[d.purchase_invoice_item] - ): - - discrepancy_caused_by_exchange_rate_difference = (d.qty * d.net_rate) * ( - exchange_rate_map[d.purchase_invoice] - self.conversion_rate - ) - - self.add_gl_entry( - gl_entries=gl_entries, - account=account, - cost_center=d.cost_center, - debit=0.0, - credit=discrepancy_caused_by_exchange_rate_difference, - remarks=remarks, - against_account=self.supplier, - debit_in_account_currency=-1 * discrepancy_caused_by_exchange_rate_difference, - account_currency=credit_currency, - item=d, - ) - - self.add_gl_entry( - gl_entries=gl_entries, - account=self.get_company_default("exchange_gain_loss_account"), - cost_center=d.cost_center, - debit=discrepancy_caused_by_exchange_rate_difference, - credit=0.0, - remarks=remarks, - against_account=self.supplier, - debit_in_account_currency=-1 * discrepancy_caused_by_exchange_rate_difference, - account_currency=credit_currency, - item=d, - ) - - # Amount added through landed-cos-voucher - if d.landed_cost_voucher_amount and landed_cost_entries: - if (d.item_code, d.name) in landed_cost_entries: - for account, amount in landed_cost_entries[(d.item_code, d.name)].items(): - account_currency = get_account_currency(account) - credit_amount = ( - flt(amount["base_amount"]) - if (amount["base_amount"] or account_currency != self.company_currency) - else flt(amount["amount"]) - ) - - self.add_gl_entry( - gl_entries=gl_entries, - account=account, - cost_center=d.cost_center, - debit=0.0, - credit=credit_amount, - remarks=remarks, - against_account=warehouse_account_name, - credit_in_account_currency=flt(amount["amount"]), - account_currency=account_currency, - project=d.project, - item=d, - ) - - if d.rate_difference_with_purchase_invoice and stock_rbnb: - account_currency = get_account_currency(stock_rbnb) - self.add_gl_entry( - gl_entries=gl_entries, - account=stock_rbnb, - cost_center=d.cost_center, - debit=0.0, - credit=flt(d.rate_difference_with_purchase_invoice), - remarks=_("Adjustment based on Purchase Invoice rate"), - against_account=warehouse_account_name, - account_currency=account_currency, - project=d.project, - item=d, - ) - - # sub-contracting warehouse - if flt(d.rm_supp_cost) and warehouse_account.get(self.supplier_warehouse): - self.add_gl_entry( - gl_entries=gl_entries, - account=supplier_warehouse_account, - cost_center=d.cost_center, - debit=0.0, - credit=flt(d.rm_supp_cost), - remarks=remarks, - against_account=warehouse_account_name, - account_currency=supplier_warehouse_account_currency, - item=d, - ) - - # divisional loss adjustment - valuation_amount_as_per_doc = ( - flt(outgoing_amount, d.precision("base_net_amount")) - + flt(d.landed_cost_voucher_amount) - + flt(d.rm_supp_cost) - + flt(d.item_tax_amount) - + flt(d.rate_difference_with_purchase_invoice) - ) - - divisional_loss = flt( - valuation_amount_as_per_doc - flt(stock_value_diff), d.precision("base_net_amount") - ) - - if divisional_loss: - if self.is_return or flt(d.item_tax_amount): - loss_account = expenses_included_in_valuation - else: - loss_account = ( - self.get_company_default("default_expense_account", ignore_validation=True) or stock_rbnb - ) - - cost_center = d.cost_center or frappe.get_cached_value( - "Company", self.company, "cost_center" - ) - - self.add_gl_entry( - gl_entries=gl_entries, - account=loss_account, - cost_center=cost_center, - debit=divisional_loss, - credit=0.0, - remarks=remarks, - against_account=warehouse_account_name, - account_currency=credit_currency, - project=d.project, - item=d, - ) - - elif ( - d.warehouse not in warehouse_with_no_account - or d.rejected_warehouse not in warehouse_with_no_account - ): - warehouse_with_no_account.append(d.warehouse) + if (flt(d.valuation_rate) or self.is_return or d.is_fixed_asset) and flt(d.qty): + make_item_asset_inward_gl_entry(d, stock_value_diff, stock_asset_account_name) + outgoing_amount = make_stock_received_but_not_billed_entry(d) + make_landed_cost_gl_entries(d) + make_rate_difference_entry(d) + make_sub_contracting_gl_entries(d) + make_divisional_loss_gl_entry(d, outgoing_amount) elif ( - d.item_code not in stock_items - and not d.is_fixed_asset - and flt(d.qty) - and provisional_accounting_for_non_stock_items - and d.get("provisional_expense_account") + d.warehouse not in warehouse_with_no_account + or d.rejected_warehouse not in warehouse_with_no_account ): - self.add_provisional_gl_entry( - d, gl_entries, self.posting_date, d.get("provisional_expense_account") - ) + warehouse_with_no_account.append(d.warehouse) + + if d.is_fixed_asset: + self.update_assets(d, d.valuation_rate) if warehouse_with_no_account: frappe.msgprint( @@ -589,8 +618,8 @@ class PurchaseReceipt(BuyingController): self, item, gl_entries, posting_date, provisional_account, reverse=0 ): credit_currency = get_account_currency(provisional_account) - debit_currency = get_account_currency(item.expense_account) expense_account = item.expense_account + debit_currency = get_account_currency(item.expense_account) remarks = self.get("remarks") or _("Accounting Entry for Service") multiplication_factor = 1 @@ -631,11 +660,8 @@ class PurchaseReceipt(BuyingController): ) def make_tax_gl_entries(self, gl_entries): - - if erpnext.is_perpetual_inventory_enabled(self.company): - expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation") - negative_expense_to_be_booked = sum([flt(d.item_tax_amount) for d in self.get("items")]) + is_asset_pr = any(d.is_fixed_asset for d in self.get("items")) # Cost center-wise amount breakup for other charges included for valuation valuation_tax = {} for tax in self.get("taxes"): @@ -655,26 +681,26 @@ class PurchaseReceipt(BuyingController): if negative_expense_to_be_booked and valuation_tax: # Backward compatibility: - # If expenses_included_in_valuation account has been credited in against PI # and charges added via Landed Cost Voucher, # post valuation related charges on "Stock Received But Not Billed" - # introduced in 2014 for backward compatibility of expenses already booked in expenses_included_in_valuation account - - negative_expense_booked_in_pi = frappe.db.sql( - """select name from `tabPurchase Invoice Item` pi - where docstatus = 1 and purchase_receipt=%s - and exists(select name from `tabGL Entry` where voucher_type='Purchase Invoice' - and voucher_no=pi.parent and account=%s)""", - (self.name, expenses_included_in_valuation), - ) - against_account = ", ".join([d.account for d in gl_entries if flt(d.debit) > 0]) total_valuation_amount = sum(valuation_tax.values()) amount_including_divisional_loss = negative_expense_to_be_booked - stock_rbnb = self.get_company_default("stock_received_but_not_billed") + stock_rbnb = ( + self.get("asset_received_but_not_billed") + if is_asset_pr + else self.get_company_default("stock_received_but_not_billed") + ) i = 1 for tax in self.get("taxes"): if valuation_tax.get(tax.name): + negative_expense_booked_in_pi = frappe.db.sql( + """select name from `tabPurchase Invoice Item` pi + where docstatus = 1 and purchase_receipt=%s + and exists(select name from `tabGL Entry` where voucher_type='Purchase Invoice' + and voucher_no=pi.parent and account=%s)""", + (self.name, tax.account_head), + ) if negative_expense_booked_in_pi: account = stock_rbnb @@ -702,103 +728,6 @@ class PurchaseReceipt(BuyingController): i += 1 - def get_asset_gl_entry(self, gl_entries): - for item in self.get("items"): - if item.is_fixed_asset: - if is_cwip_accounting_enabled(item.asset_category): - self.add_asset_gl_entries(item, gl_entries) - if flt(item.landed_cost_voucher_amount): - self.add_lcv_gl_entries(item, gl_entries) - # update assets gross amount by its valuation rate - # valuation rate is total of net rate, raw mat supp cost, tax amount, lcv amount per item - self.update_assets(item, item.valuation_rate) - return gl_entries - - def add_asset_gl_entries(self, item, gl_entries): - arbnb_account = self.get_company_default("asset_received_but_not_billed") - # This returns category's cwip account if not then fallback to company's default cwip account - cwip_account = get_asset_account( - "capital_work_in_progress_account", asset_category=item.asset_category, company=self.company - ) - - asset_amount = flt(item.net_amount) + flt(item.item_tax_amount / self.conversion_rate) - base_asset_amount = flt(item.base_net_amount + item.item_tax_amount) - remarks = self.get("remarks") or _("Accounting Entry for Asset") - - cwip_account_currency = get_account_currency(cwip_account) - # debit cwip account - debit_in_account_currency = ( - base_asset_amount if cwip_account_currency == self.company_currency else asset_amount - ) - - self.add_gl_entry( - gl_entries=gl_entries, - account=cwip_account, - cost_center=item.cost_center, - debit=base_asset_amount, - credit=0.0, - remarks=remarks, - against_account=arbnb_account, - debit_in_account_currency=debit_in_account_currency, - item=item, - ) - - asset_rbnb_currency = get_account_currency(arbnb_account) - # credit arbnb account - credit_in_account_currency = ( - base_asset_amount if asset_rbnb_currency == self.company_currency else asset_amount - ) - - self.add_gl_entry( - gl_entries=gl_entries, - account=arbnb_account, - cost_center=item.cost_center, - debit=0.0, - credit=base_asset_amount, - remarks=remarks, - against_account=cwip_account, - credit_in_account_currency=credit_in_account_currency, - item=item, - ) - - def add_lcv_gl_entries(self, item, gl_entries): - expenses_included_in_asset_valuation = self.get_company_default( - "expenses_included_in_asset_valuation" - ) - if not is_cwip_accounting_enabled(item.asset_category): - asset_account = get_asset_category_account( - asset_category=item.asset_category, fieldname="fixed_asset_account", company=self.company - ) - else: - # This returns company's default cwip account - asset_account = get_asset_account("capital_work_in_progress_account", company=self.company) - - remarks = self.get("remarks") or _("Accounting Entry for Stock") - - self.add_gl_entry( - gl_entries=gl_entries, - account=expenses_included_in_asset_valuation, - cost_center=item.cost_center, - debit=0.0, - credit=flt(item.landed_cost_voucher_amount), - remarks=remarks, - against_account=asset_account, - project=item.project, - item=item, - ) - - self.add_gl_entry( - gl_entries=gl_entries, - account=asset_account, - cost_center=item.cost_center, - debit=flt(item.landed_cost_voucher_amount), - credit=0.0, - remarks=remarks, - against_account=expenses_included_in_asset_valuation, - project=item.project, - item=item, - ) - def update_assets(self, item, valuation_rate): assets = frappe.db.get_all( "Asset", filters={"purchase_receipt": self.name, "item_code": item.item_code} @@ -832,6 +761,20 @@ class PurchaseReceipt(BuyingController): self.load_from_db() +def get_stock_value_difference(voucher_no, voucher_detail_no, warehouse): + return frappe.db.get_value( + "Stock Ledger Entry", + { + "voucher_type": "Purchase Receipt", + "voucher_no": voucher_no, + "voucher_detail_no": voucher_detail_no, + "warehouse": warehouse, + "is_cancelled": 0, + }, + "stock_value_difference", + ) + + def update_billed_amount_based_on_po(po_details, update_modified=True): po_billed_amt_details = get_billed_amount_against_po(po_details) diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index a93d5b1bbbe..404758ce94f 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -928,17 +928,33 @@ class TestPurchaseReceipt(FrappeTestCase): pr1.cancel() def test_stock_transfer_from_purchase_receipt(self): + from erpnext.stock.doctype.delivery_note.delivery_note import make_inter_company_purchase_receipt + from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note + + prepare_data_for_internal_transfer() + + customer = "_Test Internal Customer 2" + company = "_Test Company with perpetual inventory" + pr1 = make_purchase_receipt( - warehouse="Work In Progress - TCP1", company="_Test Company with perpetual inventory" + warehouse="Stores - TCP1", company="_Test Company with perpetual inventory" ) - pr = make_purchase_receipt( - company="_Test Company with perpetual inventory", warehouse="Stores - TCP1", do_not_save=1 + dn1 = create_delivery_note( + item_code=pr1.items[0].item_code, + company=company, + customer=customer, + cost_center="Main - TCP1", + expense_account="Cost of Goods Sold - TCP1", + qty=5, + rate=500, + warehouse="Stores - TCP1", + target_warehouse="Work In Progress - TCP1", ) - pr.supplier_warehouse = "" + pr = make_inter_company_purchase_receipt(dn1.name) pr.items[0].from_warehouse = "Work In Progress - TCP1" - + pr.items[0].warehouse = "Stores - TCP1" pr.submit() gl_entries = get_gl_entries("Purchase Receipt", pr.name) @@ -955,6 +971,11 @@ class TestPurchaseReceipt(FrappeTestCase): pr1.cancel() def test_stock_transfer_from_purchase_receipt_with_valuation(self): + from erpnext.stock.doctype.delivery_note.delivery_note import make_inter_company_purchase_receipt + from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note + + prepare_data_for_internal_transfer() + create_warehouse( "_Test Warehouse for Valuation", company="_Test Company with perpetual inventory", @@ -962,16 +983,28 @@ class TestPurchaseReceipt(FrappeTestCase): ) pr1 = make_purchase_receipt( - warehouse="_Test Warehouse for Valuation - TCP1", + warehouse="Stores - TCP1", company="_Test Company with perpetual inventory", ) - pr = make_purchase_receipt( - company="_Test Company with perpetual inventory", warehouse="Stores - TCP1", do_not_save=1 + customer = "_Test Internal Customer 2" + company = "_Test Company with perpetual inventory" + + dn1 = create_delivery_note( + item_code=pr1.items[0].item_code, + company=company, + customer=customer, + cost_center="Main - TCP1", + expense_account="Cost of Goods Sold - TCP1", + qty=5, + rate=50, + warehouse="Stores - TCP1", + target_warehouse="_Test Warehouse for Valuation - TCP1", ) + pr = make_inter_company_purchase_receipt(dn1.name) pr.items[0].from_warehouse = "_Test Warehouse for Valuation - TCP1" - pr.supplier_warehouse = "" + pr.items[0].warehouse = "Stores - TCP1" pr.append( "taxes", @@ -2203,6 +2236,21 @@ class TestPurchaseReceipt(FrappeTestCase): for entry in gl_entries: self.assertEqual(abs(entry.debit + entry.credit), abs(sl_entries[0].stock_value_difference)) + def non_internal_transfer_purchase_receipt(self): + from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse + + pr_doc = make_purchase_receipt(do_not_submit=True) + warehouse = create_warehouse("Internal Transfer Warehouse", pr_doc.company) + pr_doc.items[0].db_set("target_warehouse", "warehouse") + + pr_doc.reload() + + self.assertEqual(pr_doc.items[0].from_warehouse, warehouse.name) + + pr_doc.save() + pr_doc.reload() + self.assertFalse(pr_doc.items[0].from_warehouse) + def prepare_data_for_internal_transfer(): from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier 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 4911523e7ed..d7b1660c55f 100644 --- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json +++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json @@ -118,7 +118,9 @@ "dimension_col_break", "cost_center", "section_break_80", - "page_break" + "page_break", + "sales_order", + "sales_order_item" ], "fields": [ { @@ -1025,12 +1027,32 @@ "fieldtype": "Link", "label": "WIP Composite Asset", "options": "Asset" + }, + { + "fieldname": "sales_order", + "fieldtype": "Link", + "label": "Sales Order", + "no_copy": 1, + "options": "Sales Order", + "print_hide": 1, + "read_only": 1, + "search_index": 1 + }, + { + "fieldname": "sales_order_item", + "fieldtype": "Data", + "hidden": 1, + "label": "Sales Order Item", + "no_copy": 1, + "print_hide": 1, + "read_only": 1, + "search_index": 1 } ], "idx": 1, "istable": 1, "links": [], - "modified": "2023-10-03 21:11:50.547261", + "modified": "2023-10-19 10:50:58.071735", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt Item", diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 9560b52f59c..54978d8aa36 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -451,31 +451,37 @@ class StockEntry(StockController): item_code.append(item.item_code) def validate_fg_completed_qty(self): - item_wise_qty = {} - if self.purpose == "Manufacture" and self.work_order: - for d in self.items: - if d.is_finished_item: - if self.process_loss_qty: - d.qty = self.fg_completed_qty - self.process_loss_qty + if self.purpose != "Manufacture": + return - item_wise_qty.setdefault(d.item_code, []).append(d.qty) + fg_qty = defaultdict(float) + for d in self.items: + if d.is_finished_item: + fg_qty[d.item_code] += flt(d.qty) + + if not fg_qty: + return precision = frappe.get_precision("Stock Entry Detail", "qty") - for item_code, qty_list in item_wise_qty.items(): - total = flt(sum(qty_list), precision) + fg_item = list(fg_qty.keys())[0] + fg_item_qty = flt(fg_qty[fg_item], precision) + fg_completed_qty = flt(self.fg_completed_qty, precision) - if (self.fg_completed_qty - total) > 0 and not self.process_loss_qty: - self.process_loss_qty = flt(self.fg_completed_qty - total, precision) - self.process_loss_percentage = flt(self.process_loss_qty * 100 / self.fg_completed_qty) + for d in self.items: + if not fg_qty.get(d.item_code): + continue - if self.process_loss_qty: - total += flt(self.process_loss_qty, precision) + if (fg_completed_qty - fg_item_qty) > 0: + self.process_loss_qty = fg_completed_qty - fg_item_qty - if self.fg_completed_qty != total: + if not self.process_loss_qty: + continue + + if fg_completed_qty != (flt(fg_item_qty) + flt(self.process_loss_qty, precision)): frappe.throw( - _("The finished product {0} quantity {1} and For Quantity {2} cannot be different").format( - frappe.bold(item_code), frappe.bold(total), frappe.bold(self.fg_completed_qty) - ) + _( + "Since there is a process loss of {0} units for the finished good {1}, you should reduce the quantity by {0} units for the finished good {1} in the Items Table." + ).format(frappe.bold(self.process_loss_qty), frappe.bold(d.item_code)) ) def validate_difference_account(self): diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py index de74fda687d..72f6bcdddf2 100644 --- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py @@ -447,9 +447,7 @@ class TestStockEntry(FrappeTestCase): repack.posting_date = nowdate() repack.posting_time = nowtime() - expenses_included_in_valuation = frappe.get_value( - "Company", company, "expenses_included_in_valuation" - ) + default_expense_account = frappe.get_value("Company", company, "default_expense_account") items = get_multiple_items() repack.items = [] @@ -460,12 +458,12 @@ class TestStockEntry(FrappeTestCase): "additional_costs", [ { - "expense_account": expenses_included_in_valuation, + "expense_account": default_expense_account, "description": "Actual Operating Cost", "amount": 1000, }, { - "expense_account": expenses_included_in_valuation, + "expense_account": default_expense_account, "description": "Additional Operating Cost", "amount": 200, }, @@ -504,9 +502,7 @@ class TestStockEntry(FrappeTestCase): self.check_gl_entries( "Stock Entry", repack.name, - sorted( - [[stock_in_hand_account, 1200, 0.0], ["Expenses Included In Valuation - TCP1", 0.0, 1200.0]] - ), + sorted([[stock_in_hand_account, 1200, 0.0], ["Cost of Goods Sold - TCP1", 0.0, 1200.0]]), ) def check_stock_ledger_entries(self, voucher_type, voucher_no, expected_sle): diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 92c945b254b..4a6ad70d291 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -771,6 +771,12 @@ def get_default_cost_center(args, item=None, item_group=None, brand=None, compan data = frappe.get_attr(path)(args.get("item_code"), company) if data and (data.selling_cost_center or data.buying_cost_center): + if args.get("customer") and data.selling_cost_center: + return data.selling_cost_center + + elif args.get("supplier") and data.buying_cost_center: + return data.buying_cost_center + return data.selling_cost_center or data.buying_cost_center if not cost_center and args.get("cost_center"): diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py index 130f38fb809..a83068f73fd 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py @@ -299,7 +299,6 @@ class SubcontractingReceipt(SubcontractingController): def make_item_gl_entries(self, gl_entries, warehouse_account=None): stock_rbnb = self.get_company_default("stock_received_but_not_billed") - expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation") warehouse_with_no_account = [] @@ -371,10 +370,7 @@ class SubcontractingReceipt(SubcontractingController): divisional_loss = flt(item.amount - stock_value_diff, item.precision("amount")) if divisional_loss: - if self.is_return: - loss_account = expenses_included_in_valuation - else: - loss_account = item.expense_account + loss_account = item.expense_account self.add_gl_entry( gl_entries=gl_entries, diff --git a/erpnext/translations/de.csv b/erpnext/translations/de.csv index 0c840f44ea8..4cd4f0edf15 100644 --- a/erpnext/translations/de.csv +++ b/erpnext/translations/de.csv @@ -369,7 +369,7 @@ Base,Basis, Base URL,Basis-URL, Based On,Basiert auf, Based On Payment Terms,Basierend auf Zahlungsbedingungen, -Basic,Grundeinkommen, +Basic,Basic, Batch,Charge, Batch Entries,Batch-Einträge, Batch ID is mandatory,Batch-ID ist obligatorisch, From 6b951bd17a1f247e0ecbb29a8450ec59303f7aa9 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Wed, 25 Oct 2023 12:30:31 +0530 Subject: [PATCH 038/151] chore(release): Bumped to Version 14.45.0 --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 31f1fa066e4..a0730da2fe4 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "14.44.1" +__version__ = "14.45.0" def get_default_company(user=None): From a7b75a4cc1d2225acca959fd1e4fe86b2b3c6614 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 25 Oct 2023 13:50:01 +0530 Subject: [PATCH 039/151] chore: fixed test case non_internal_transfer_delivery_note (backport #37671) (#37675) chore: fixed test case non_internal_transfer_delivery_note (#37671) (cherry picked from commit 2bcff4c7f2264d2408a1f98bddc78041b167a632) Co-authored-by: rohitwaghchaure --- erpnext/stock/doctype/delivery_note/test_delivery_note.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py index 3ecfbad1d5d..b66647c97a5 100644 --- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py @@ -1315,16 +1315,16 @@ class TestDeliveryNote(FrappeTestCase): frappe.db.rollback() frappe.db.set_single_value("Selling Settings", "dont_reserve_sales_order_qty_on_sales_return", 0) - def non_internal_transfer_delivery_note(self): + def test_non_internal_transfer_delivery_note(self): from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse dn = create_delivery_note(do_not_submit=True) - warehouse = create_warehouse("Internal Transfer Warehouse", dn.company) - dn.items[0].db_set("target_warehouse", "warehouse") + warehouse = create_warehouse("Internal Transfer Warehouse", company=dn.company) + dn.items[0].db_set("target_warehouse", warehouse) dn.reload() - self.assertEqual(dn.items[0].target_warehouse, warehouse.name) + self.assertEqual(dn.items[0].target_warehouse, warehouse) dn.save() dn.reload() From f5e9913746e57ad7eb7b27532f430378e4bf1428 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 25 Oct 2023 09:55:24 +0000 Subject: [PATCH 040/151] refactor: rename field `Over Order Allowance` to `Blanket Order Allowance` (backport #37669) (#37681) * refactor: rename field `Over Order Allowance` to `Blanket Order Allowance` (cherry picked from commit 8ffa2bfe25d8fae317d77705aec82a92dc269874) * chore: patch to rename field `over_order_allowance` (cherry picked from commit fcfcf6957e07ad5afc281e5f43154ca1170e14c4) # Conflicts: # erpnext/patches.txt * chore: `conflicts` --------- Co-authored-by: s-aga-r --- .../buying_settings/buying_settings.json | 18 +++++++++--------- .../doctype/blanket_order/blanket_order.py | 2 +- .../blanket_order/test_blanket_order.py | 6 +++--- erpnext/patches.txt | 1 + .../v14_0/rename_over_order_allowance_field.py | 15 +++++++++++++++ .../selling_settings/selling_settings.json | 16 ++++++++-------- 6 files changed, 37 insertions(+), 21 deletions(-) create mode 100644 erpnext/patches/v14_0/rename_over_order_allowance_field.py diff --git a/erpnext/buying/doctype/buying_settings/buying_settings.json b/erpnext/buying/doctype/buying_settings/buying_settings.json index 71cb01b188f..059999245d1 100644 --- a/erpnext/buying/doctype/buying_settings/buying_settings.json +++ b/erpnext/buying/doctype/buying_settings/buying_settings.json @@ -16,7 +16,7 @@ "transaction_settings_section", "po_required", "pr_required", - "over_order_allowance", + "blanket_order_allowance", "column_break_12", "maintain_same_rate", "set_landed_cost_based_on_purchase_invoice_rate", @@ -159,19 +159,19 @@ "fieldtype": "Check", "label": "Set Landed Cost Based on Purchase Invoice Rate" }, - { - "default": "0", - "description": "Percentage you are allowed to order more against the Blanket Order Quantity. For example: If you have a Blanket Order of Quantity 100 units. and your Allowance is 10% then you are allowed to order 110 units.", - "fieldname": "over_order_allowance", - "fieldtype": "Float", - "label": "Over Order Allowance (%)" - }, { "default": "0", "description": "While making Purchase Invoice from Purchase Order, use Exchange Rate on Invoice's transaction date rather than inheriting it from Purchase Order. Only applies for Purchase Invoice.", "fieldname": "use_transaction_date_exchange_rate", "fieldtype": "Check", "label": "Use Transaction Date Exchange Rate" + }, + { + "default": "0", + "description": "Percentage you are allowed to order beyond the Blanket Order quantity.", + "fieldname": "blanket_order_allowance", + "fieldtype": "Float", + "label": "Blanket Order Allowance (%)" } ], "icon": "fa fa-cog", @@ -179,7 +179,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2023-10-16 16:22:03.201078", + "modified": "2023-10-25 14:03:32.520418", "modified_by": "Administrator", "module": "Buying", "name": "Buying Settings", diff --git a/erpnext/manufacturing/doctype/blanket_order/blanket_order.py b/erpnext/manufacturing/doctype/blanket_order/blanket_order.py index 32f1c365ade..0135a4f9712 100644 --- a/erpnext/manufacturing/doctype/blanket_order/blanket_order.py +++ b/erpnext/manufacturing/doctype/blanket_order/blanket_order.py @@ -107,7 +107,7 @@ def validate_against_blanket_order(order_doc): allowance = flt( frappe.db.get_single_value( "Selling Settings" if order_doc.doctype == "Sales Order" else "Buying Settings", - "over_order_allowance", + "blanket_order_allowance", ) ) for bo_name, item_data in order_data.items(): diff --git a/erpnext/manufacturing/doctype/blanket_order/test_blanket_order.py b/erpnext/manufacturing/doctype/blanket_order/test_blanket_order.py index 58f3c950598..e9fc25b5bcb 100644 --- a/erpnext/manufacturing/doctype/blanket_order/test_blanket_order.py +++ b/erpnext/manufacturing/doctype/blanket_order/test_blanket_order.py @@ -63,7 +63,7 @@ class TestBlanketOrder(FrappeTestCase): po1.currency = get_company_currency(po1.company) self.assertEqual(po1.items[0].qty, (bo.items[0].qty - bo.items[0].ordered_qty)) - def test_over_order_allowance(self): + def test_blanket_order_allowance(self): # Sales Order bo = make_blanket_order(blanket_order_type="Selling", quantity=100) @@ -74,7 +74,7 @@ class TestBlanketOrder(FrappeTestCase): so.items[0].qty = 110 self.assertRaises(frappe.ValidationError, so.submit) - frappe.db.set_single_value("Selling Settings", "over_order_allowance", 10) + frappe.db.set_single_value("Selling Settings", "blanket_order_allowance", 10) so.submit() # Purchase Order @@ -87,7 +87,7 @@ class TestBlanketOrder(FrappeTestCase): po.items[0].qty = 110 self.assertRaises(frappe.ValidationError, po.submit) - frappe.db.set_single_value("Buying Settings", "over_order_allowance", 10) + frappe.db.set_single_value("Buying Settings", "blanket_order_allowance", 10) po.submit() diff --git a/erpnext/patches.txt b/erpnext/patches.txt index abdd09383bb..0c69c00b731 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -342,5 +342,6 @@ execute:frappe.db.set_single_value('Selling Settings', 'allow_negative_rates_for erpnext.patches.v14_0.correct_asset_value_if_je_with_workflow erpnext.patches.v14_0.migrate_deferred_accounts_to_item_defaults erpnext.patches.v14_0.create_accounting_dimensions_in_sales_order_item +erpnext.patches.v14_0.rename_over_order_allowance_field # below migration patch should always run last erpnext.patches.v14_0.migrate_gl_to_payment_ledger diff --git a/erpnext/patches/v14_0/rename_over_order_allowance_field.py b/erpnext/patches/v14_0/rename_over_order_allowance_field.py new file mode 100644 index 00000000000..a81fe888c2a --- /dev/null +++ b/erpnext/patches/v14_0/rename_over_order_allowance_field.py @@ -0,0 +1,15 @@ +from frappe.model.utils.rename_field import rename_field + + +def execute(): + rename_field( + "Buying Settings", + "over_order_allowance", + "blanket_order_allowance", + ) + + rename_field( + "Selling Settings", + "over_order_allowance", + "blanket_order_allowance", + ) diff --git a/erpnext/selling/doctype/selling_settings/selling_settings.json b/erpnext/selling/doctype/selling_settings/selling_settings.json index 46bdcfa5f15..85ce8a0d022 100644 --- a/erpnext/selling/doctype/selling_settings/selling_settings.json +++ b/erpnext/selling/doctype/selling_settings/selling_settings.json @@ -25,7 +25,7 @@ "so_required", "dn_required", "sales_update_frequency", - "over_order_allowance", + "blanket_order_allowance", "column_break_5", "allow_multiple_items", "allow_against_multiple_purchase_orders", @@ -182,17 +182,17 @@ "fieldtype": "Check", "label": "Allow Sales Order Creation For Expired Quotation" }, - { - "description": "Percentage you are allowed to order more against the Blanket Order Quantity. For example: If you have a Blanket Order of Quantity 100 units. and your Allowance is 10% then you are allowed to order 110 units.", - "fieldname": "over_order_allowance", - "fieldtype": "Float", - "label": "Over Order Allowance (%)" - }, { "default": "0", "fieldname": "allow_negative_rates_for_items", "fieldtype": "Check", "label": "Allow Negative rates for Items" + }, + { + "description": "Percentage you are allowed to sell beyond the Blanket Order quantity.", + "fieldname": "blanket_order_allowance", + "fieldtype": "Float", + "label": "Blanket Order Allowance (%)" } ], "icon": "fa fa-cog", @@ -200,7 +200,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2023-08-14 20:33:05.693667", + "modified": "2023-10-25 14:03:03.966701", "modified_by": "Administrator", "module": "Selling", "name": "Selling Settings", From d02ebd621504ac04737cc283ee6d06e2c6eb92c2 Mon Sep 17 00:00:00 2001 From: Smit Vora Date: Thu, 19 Oct 2023 22:35:55 +0530 Subject: [PATCH 041/151] fix: add regional support to extend purchase gl entries (cherry picked from commit 77cc91d06b9ea1e5758546a78bbcbb2ef97f549b) # Conflicts: # erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py # erpnext/controllers/stock_controller.py # erpnext/stock/doctype/purchase_receipt/purchase_receipt.py --- .../purchase_invoice/purchase_invoice.py | 153 +++++++++++++++++ erpnext/controllers/stock_controller.py | 162 ++++++++++++++++++ .../purchase_receipt/purchase_receipt.py | 16 +- 3 files changed, 325 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 306208603c0..a20117f0ec2 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -969,12 +969,165 @@ class PurchaseInvoice(BuyingController): item.item_tax_amount, item.precision("item_tax_amount") ) +<<<<<<< HEAD assets = frappe.db.get_all( "Asset", filters={"purchase_invoice": self.name, "item_code": item.item_code} ) for asset in assets: frappe.db.set_value("Asset", asset.name, "gross_purchase_amount", flt(item.valuation_rate)) frappe.db.set_value("Asset", asset.name, "purchase_receipt_amount", flt(item.valuation_rate)) +======= + def get_asset_gl_entry(self, gl_entries): + arbnb_account = None + eiiav_account = None + asset_eiiav_currency = None + + for item in self.get("items"): + if item.is_fixed_asset: + asset_amount = flt(item.net_amount) + flt(item.item_tax_amount / self.conversion_rate) + base_asset_amount = flt(item.base_net_amount + item.item_tax_amount) + + item_exp_acc_type = frappe.get_cached_value("Account", item.expense_account, "account_type") + if not item.expense_account or item_exp_acc_type not in [ + "Asset Received But Not Billed", + "Fixed Asset", + ]: + if not arbnb_account: + arbnb_account = self.get_company_default("asset_received_but_not_billed") + item.expense_account = arbnb_account + + if not self.update_stock: + arbnb_currency = get_account_currency(item.expense_account) + gl_entries.append( + self.get_gl_dict( + { + "account": item.expense_account, + "against": self.supplier, + "remarks": self.get("remarks") or _("Accounting Entry for Asset"), + "debit": base_asset_amount, + "debit_in_account_currency": ( + base_asset_amount if arbnb_currency == self.company_currency else asset_amount + ), + "cost_center": item.cost_center, + "project": item.project or self.project, + }, + item=item, + ) + ) + + if item.item_tax_amount: + if not eiiav_account or not asset_eiiav_currency: + eiiav_account = self.get_company_default("expenses_included_in_asset_valuation") + asset_eiiav_currency = get_account_currency(eiiav_account) + + gl_entries.append( + self.get_gl_dict( + { + "account": eiiav_account, + "against": self.supplier, + "remarks": self.get("remarks") or _("Accounting Entry for Asset"), + "cost_center": item.cost_center, + "project": item.project or self.project, + "credit": item.item_tax_amount, + "credit_in_account_currency": ( + item.item_tax_amount + if asset_eiiav_currency == self.company_currency + else item.item_tax_amount / self.conversion_rate + ), + }, + item=item, + ) + ) + else: + cwip_account = get_asset_account( + "capital_work_in_progress_account", asset_category=item.asset_category, company=self.company + ) + + cwip_account_currency = get_account_currency(cwip_account) + gl_entries.append( + self.get_gl_dict( + { + "account": cwip_account, + "against": self.supplier, + "remarks": self.get("remarks") or _("Accounting Entry for Asset"), + "debit": base_asset_amount, + "debit_in_account_currency": ( + base_asset_amount if cwip_account_currency == self.company_currency else asset_amount + ), + "cost_center": item.cost_center or self.cost_center, + "project": item.project or self.project, + }, + item=item, + ) + ) + + if item.item_tax_amount and not cint(erpnext.is_perpetual_inventory_enabled(self.company)): + if not eiiav_account or not asset_eiiav_currency: + eiiav_account = self.get_company_default("expenses_included_in_asset_valuation") + asset_eiiav_currency = get_account_currency(eiiav_account) + + gl_entries.append( + self.get_gl_dict( + { + "account": eiiav_account, + "against": self.supplier, + "remarks": self.get("remarks") or _("Accounting Entry for Asset"), + "cost_center": item.cost_center, + "credit": item.item_tax_amount, + "project": item.project or self.project, + "credit_in_account_currency": ( + item.item_tax_amount + if asset_eiiav_currency == self.company_currency + else item.item_tax_amount / self.conversion_rate + ), + }, + item=item, + ) + ) + + # Assets are bought through this document then it will be linked to this document + if flt(item.landed_cost_voucher_amount): + if not eiiav_account: + eiiav_account = self.get_company_default("expenses_included_in_asset_valuation") + + gl_entries.append( + self.get_gl_dict( + { + "account": eiiav_account, + "against": cwip_account, + "cost_center": item.cost_center, + "remarks": self.get("remarks") or _("Accounting Entry for Stock"), + "credit": flt(item.landed_cost_voucher_amount), + "project": item.project or self.project, + }, + item=item, + ) + ) + + gl_entries.append( + self.get_gl_dict( + { + "account": cwip_account, + "against": eiiav_account, + "cost_center": item.cost_center, + "remarks": self.get("remarks") or _("Accounting Entry for Stock"), + "debit": flt(item.landed_cost_voucher_amount), + "project": item.project or self.project, + }, + item=item, + ) + ) + + # update gross amount of assets bought through this document + assets = frappe.db.get_all( + "Asset", filters={"purchase_invoice": self.name, "item_code": item.item_code} + ) + for asset in assets: + frappe.db.set_value("Asset", asset.name, "gross_purchase_amount", flt(item.valuation_rate)) + frappe.db.set_value("Asset", asset.name, "purchase_receipt_amount", flt(item.valuation_rate)) + + return gl_entries +>>>>>>> 77cc91d06b (fix: add regional support to extend purchase gl entries) def make_stock_adjustment_entry( self, gl_entries, item, voucher_wise_stock_value, account_currency diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index a72cc667399..3e766f4a7f1 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -76,6 +76,15 @@ class StockController(AccountsController): gl_entries = self.get_gl_entries(warehouse_account) make_gl_entries(gl_entries, from_repost=from_repost) +<<<<<<< HEAD +======= + elif self.doctype in ["Purchase Receipt", "Purchase Invoice"] and self.docstatus == 1: + gl_entries = [] + gl_entries = self.get_asset_gl_entry(gl_entries) + update_regional_gl_entries(gl_entries, self) + make_gl_entries(gl_entries, from_repost=from_repost) + +>>>>>>> 77cc91d06b (fix: add regional support to extend purchase gl entries) def validate_serialized_batch(self): from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos @@ -837,6 +846,154 @@ class StockController(AccountsController): gl_entries.append(self.get_gl_dict(gl_entry, item=item)) +<<<<<<< HEAD +======= +@frappe.whitelist() +def show_accounting_ledger_preview(company, doctype, docname): + filters = frappe._dict(company=company, include_dimensions=1) + doc = frappe.get_doc(doctype, docname) + doc.run_method("before_gl_preview") + + gl_columns, gl_data = get_accounting_ledger_preview(doc, filters) + + frappe.db.rollback() + + return {"gl_columns": gl_columns, "gl_data": gl_data} + + +@frappe.whitelist() +def show_stock_ledger_preview(company, doctype, docname): + filters = frappe._dict(company=company) + doc = frappe.get_doc(doctype, docname) + doc.run_method("before_sl_preview") + + sl_columns, sl_data = get_stock_ledger_preview(doc, filters) + + frappe.db.rollback() + + return { + "sl_columns": sl_columns, + "sl_data": sl_data, + } + + +def get_accounting_ledger_preview(doc, filters): + from erpnext.accounts.report.general_ledger.general_ledger import get_columns as get_gl_columns + + gl_columns, gl_data = [], [] + fields = [ + "posting_date", + "account", + "debit", + "credit", + "against", + "party", + "party_type", + "cost_center", + "against_voucher_type", + "against_voucher", + ] + + doc.docstatus = 1 + + if doc.get("update_stock") or doc.doctype in ("Purchase Receipt", "Delivery Note"): + doc.update_stock_ledger() + + doc.make_gl_entries() + columns = get_gl_columns(filters) + gl_entries = get_gl_entries_for_preview(doc.doctype, doc.name, fields) + + gl_columns = get_columns(columns, fields) + gl_data = get_data(fields, gl_entries) + + return gl_columns, gl_data + + +def get_stock_ledger_preview(doc, filters): + from erpnext.stock.report.stock_ledger.stock_ledger import get_columns as get_sl_columns + + sl_columns, sl_data = [], [] + fields = [ + "item_code", + "stock_uom", + "actual_qty", + "qty_after_transaction", + "warehouse", + "incoming_rate", + "valuation_rate", + "stock_value", + "stock_value_difference", + ] + columns_fields = [ + "item_code", + "stock_uom", + "in_qty", + "out_qty", + "qty_after_transaction", + "warehouse", + "incoming_rate", + "in_out_rate", + "stock_value", + "stock_value_difference", + ] + + if doc.get("update_stock") or doc.doctype in ("Purchase Receipt", "Delivery Note"): + doc.docstatus = 1 + doc.update_stock_ledger() + columns = get_sl_columns(filters) + sl_entries = get_sl_entries_for_preview(doc.doctype, doc.name, fields) + + sl_columns = get_columns(columns, columns_fields) + sl_data = get_data(columns_fields, sl_entries) + + return sl_columns, sl_data + + +def get_sl_entries_for_preview(doctype, docname, fields): + sl_entries = frappe.get_all( + "Stock Ledger Entry", filters={"voucher_type": doctype, "voucher_no": docname}, fields=fields + ) + + for entry in sl_entries: + if entry.actual_qty > 0: + entry["in_qty"] = entry.actual_qty + entry["out_qty"] = 0 + else: + entry["out_qty"] = abs(entry.actual_qty) + entry["in_qty"] = 0 + + entry["in_out_rate"] = entry["valuation_rate"] + + return sl_entries + + +def get_gl_entries_for_preview(doctype, docname, fields): + return frappe.get_all( + "GL Entry", filters={"voucher_type": doctype, "voucher_no": docname}, fields=fields + ) + + +def get_columns(raw_columns, fields): + return [ + {"name": d.get("label"), "editable": False, "width": 110} + for d in raw_columns + if not d.get("hidden") and d.get("fieldname") in fields + ] + + +def get_data(raw_columns, raw_data): + datatable_data = [] + for row in raw_data: + data_row = [] + for column in raw_columns: + data_row.append(row.get(column) or "") + + datatable_data.append(data_row) + + return datatable_data + + +>>>>>>> 77cc91d06b (fix: add regional support to extend purchase gl entries) def repost_required_for_queue(doc: StockController) -> bool: """check if stock document contains repeated item-warehouse with queue based valuation. @@ -1057,3 +1214,8 @@ def create_item_wise_repost_entries(voucher_type, voucher_no, allow_zero_rate=Fa repost_entries.append(repost_entry) return repost_entries + + +@erpnext.allow_regional +def update_regional_gl_entries(gl_list, doc): + return diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 964ce0b7218..1e4ccc8b6ec 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -314,6 +314,11 @@ class PurchaseReceipt(BuyingController): self.make_item_gl_entries(gl_entries, warehouse_account=warehouse_account) self.make_tax_gl_entries(gl_entries) +<<<<<<< HEAD +======= + self.get_asset_gl_entry(gl_entries) + update_regional_gl_entries(gl_entries, self) +>>>>>>> 77cc91d06b (fix: add regional support to extend purchase gl entries) return process_gl_map(gl_entries) @@ -758,8 +763,6 @@ class PurchaseReceipt(BuyingController): pr_doc = self if (pr == self.name) else frappe.get_doc("Purchase Receipt", pr) update_billing_percentage(pr_doc, update_modified=update_modified) - self.load_from_db() - def get_stock_value_difference(voucher_no, voucher_detail_no, warehouse): return frappe.db.get_value( @@ -886,9 +889,6 @@ def get_billed_amount_against_po(po_items): def update_billing_percentage(pr_doc, update_modified=True, adjust_incoming_rate=False): - # Reload as billed amount was set in db directly - pr_doc.load_from_db() - # Update Billing % based on pending accepted qty total_amount, total_billed_amount = 0, 0 item_wise_returned_qty = get_item_wise_returned_qty(pr_doc) @@ -914,7 +914,6 @@ def update_billing_percentage(pr_doc, update_modified=True, adjust_incoming_rate percent_billed = round(100 * (total_billed_amount / (total_amount or 1)), 6) pr_doc.db_set("per_billed", percent_billed) - pr_doc.load_from_db() if update_modified: pr_doc.set_status(update=True) @@ -1193,3 +1192,8 @@ def get_item_account_wise_additional_cost(purchase_document): def on_doctype_update(): frappe.db.add_index("Purchase Receipt", ["supplier", "is_return", "return_against"]) + + +@erpnext.allow_regional +def update_regional_gl_entries(gl_list, doc): + return From 89f07dcfac3740af81c59579152646f3c806113b Mon Sep 17 00:00:00 2001 From: Smit Vora Date: Fri, 20 Oct 2023 09:33:37 +0530 Subject: [PATCH 042/151] fix: update existing doc if possible (cherry picked from commit ff7108a3b139b2f019230be681147f1a1c90a681) # Conflicts: # erpnext/stock/doctype/purchase_receipt/purchase_receipt.py --- .../purchase_receipt/purchase_receipt.py | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 1e4ccc8b6ec..865b447dc49 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -757,13 +757,14 @@ class PurchaseReceipt(BuyingController): po_details.append(d.purchase_order_item) if po_details: - updated_pr += update_billed_amount_based_on_po(po_details, update_modified) + updated_pr += update_billed_amount_based_on_po(po_details, update_modified, self) for pr in set(updated_pr): pr_doc = self if (pr == self.name) else frappe.get_doc("Purchase Receipt", pr) update_billing_percentage(pr_doc, update_modified=update_modified) +<<<<<<< HEAD def get_stock_value_difference(voucher_no, voucher_detail_no, warehouse): return frappe.db.get_value( "Stock Ledger Entry", @@ -779,6 +780,9 @@ def get_stock_value_difference(voucher_no, voucher_detail_no, warehouse): def update_billed_amount_based_on_po(po_details, update_modified=True): +======= +def update_billed_amount_based_on_po(po_details, update_modified=True, pr_doc=None): +>>>>>>> ff7108a3b1 (fix: update existing doc if possible) po_billed_amt_details = get_billed_amount_against_po(po_details) # Get all Purchase Receipt Item rows against the Purchase Order Items @@ -807,13 +811,19 @@ def update_billed_amount_based_on_po(po_details, update_modified=True): po_billed_amt_details[pr_item.purchase_order_item] = billed_against_po if pr_item.billed_amt != billed_amt_agianst_pr: - frappe.db.set_value( - "Purchase Receipt Item", - pr_item.name, - "billed_amt", - billed_amt_agianst_pr, - update_modified=update_modified, - ) + # update existing doc if possible + if pr_doc and pr_item.parent == pr_doc.name: + pr_item = next((item for item in pr_doc.items if item.name == pr_item.name), None) + pr_item.db_set("billed_amt", billed_amt_agianst_pr, update_modified=update_modified) + + else: + frappe.db.set_value( + "Purchase Receipt Item", + pr_item.name, + "billed_amt", + billed_amt_agianst_pr, + update_modified=update_modified, + ) updated_pr.append(pr_item.parent) From e01e9ebc370cba980307a5f3ecbc4a15b379fbc6 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 25 Oct 2023 19:42:02 +0530 Subject: [PATCH 043/151] chore: resolve conflicts --- .../purchase_invoice/purchase_invoice.py | 153 ----------------- erpnext/controllers/stock_controller.py | 157 ------------------ .../purchase_receipt/purchase_receipt.py | 8 - 3 files changed, 318 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index a20117f0ec2..306208603c0 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -969,165 +969,12 @@ class PurchaseInvoice(BuyingController): item.item_tax_amount, item.precision("item_tax_amount") ) -<<<<<<< HEAD assets = frappe.db.get_all( "Asset", filters={"purchase_invoice": self.name, "item_code": item.item_code} ) for asset in assets: frappe.db.set_value("Asset", asset.name, "gross_purchase_amount", flt(item.valuation_rate)) frappe.db.set_value("Asset", asset.name, "purchase_receipt_amount", flt(item.valuation_rate)) -======= - def get_asset_gl_entry(self, gl_entries): - arbnb_account = None - eiiav_account = None - asset_eiiav_currency = None - - for item in self.get("items"): - if item.is_fixed_asset: - asset_amount = flt(item.net_amount) + flt(item.item_tax_amount / self.conversion_rate) - base_asset_amount = flt(item.base_net_amount + item.item_tax_amount) - - item_exp_acc_type = frappe.get_cached_value("Account", item.expense_account, "account_type") - if not item.expense_account or item_exp_acc_type not in [ - "Asset Received But Not Billed", - "Fixed Asset", - ]: - if not arbnb_account: - arbnb_account = self.get_company_default("asset_received_but_not_billed") - item.expense_account = arbnb_account - - if not self.update_stock: - arbnb_currency = get_account_currency(item.expense_account) - gl_entries.append( - self.get_gl_dict( - { - "account": item.expense_account, - "against": self.supplier, - "remarks": self.get("remarks") or _("Accounting Entry for Asset"), - "debit": base_asset_amount, - "debit_in_account_currency": ( - base_asset_amount if arbnb_currency == self.company_currency else asset_amount - ), - "cost_center": item.cost_center, - "project": item.project or self.project, - }, - item=item, - ) - ) - - if item.item_tax_amount: - if not eiiav_account or not asset_eiiav_currency: - eiiav_account = self.get_company_default("expenses_included_in_asset_valuation") - asset_eiiav_currency = get_account_currency(eiiav_account) - - gl_entries.append( - self.get_gl_dict( - { - "account": eiiav_account, - "against": self.supplier, - "remarks": self.get("remarks") or _("Accounting Entry for Asset"), - "cost_center": item.cost_center, - "project": item.project or self.project, - "credit": item.item_tax_amount, - "credit_in_account_currency": ( - item.item_tax_amount - if asset_eiiav_currency == self.company_currency - else item.item_tax_amount / self.conversion_rate - ), - }, - item=item, - ) - ) - else: - cwip_account = get_asset_account( - "capital_work_in_progress_account", asset_category=item.asset_category, company=self.company - ) - - cwip_account_currency = get_account_currency(cwip_account) - gl_entries.append( - self.get_gl_dict( - { - "account": cwip_account, - "against": self.supplier, - "remarks": self.get("remarks") or _("Accounting Entry for Asset"), - "debit": base_asset_amount, - "debit_in_account_currency": ( - base_asset_amount if cwip_account_currency == self.company_currency else asset_amount - ), - "cost_center": item.cost_center or self.cost_center, - "project": item.project or self.project, - }, - item=item, - ) - ) - - if item.item_tax_amount and not cint(erpnext.is_perpetual_inventory_enabled(self.company)): - if not eiiav_account or not asset_eiiav_currency: - eiiav_account = self.get_company_default("expenses_included_in_asset_valuation") - asset_eiiav_currency = get_account_currency(eiiav_account) - - gl_entries.append( - self.get_gl_dict( - { - "account": eiiav_account, - "against": self.supplier, - "remarks": self.get("remarks") or _("Accounting Entry for Asset"), - "cost_center": item.cost_center, - "credit": item.item_tax_amount, - "project": item.project or self.project, - "credit_in_account_currency": ( - item.item_tax_amount - if asset_eiiav_currency == self.company_currency - else item.item_tax_amount / self.conversion_rate - ), - }, - item=item, - ) - ) - - # Assets are bought through this document then it will be linked to this document - if flt(item.landed_cost_voucher_amount): - if not eiiav_account: - eiiav_account = self.get_company_default("expenses_included_in_asset_valuation") - - gl_entries.append( - self.get_gl_dict( - { - "account": eiiav_account, - "against": cwip_account, - "cost_center": item.cost_center, - "remarks": self.get("remarks") or _("Accounting Entry for Stock"), - "credit": flt(item.landed_cost_voucher_amount), - "project": item.project or self.project, - }, - item=item, - ) - ) - - gl_entries.append( - self.get_gl_dict( - { - "account": cwip_account, - "against": eiiav_account, - "cost_center": item.cost_center, - "remarks": self.get("remarks") or _("Accounting Entry for Stock"), - "debit": flt(item.landed_cost_voucher_amount), - "project": item.project or self.project, - }, - item=item, - ) - ) - - # update gross amount of assets bought through this document - assets = frappe.db.get_all( - "Asset", filters={"purchase_invoice": self.name, "item_code": item.item_code} - ) - for asset in assets: - frappe.db.set_value("Asset", asset.name, "gross_purchase_amount", flt(item.valuation_rate)) - frappe.db.set_value("Asset", asset.name, "purchase_receipt_amount", flt(item.valuation_rate)) - - return gl_entries ->>>>>>> 77cc91d06b (fix: add regional support to extend purchase gl entries) def make_stock_adjustment_entry( self, gl_entries, item, voucher_wise_stock_value, account_currency diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 3e766f4a7f1..261c7e990b7 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -76,15 +76,6 @@ class StockController(AccountsController): gl_entries = self.get_gl_entries(warehouse_account) make_gl_entries(gl_entries, from_repost=from_repost) -<<<<<<< HEAD -======= - elif self.doctype in ["Purchase Receipt", "Purchase Invoice"] and self.docstatus == 1: - gl_entries = [] - gl_entries = self.get_asset_gl_entry(gl_entries) - update_regional_gl_entries(gl_entries, self) - make_gl_entries(gl_entries, from_repost=from_repost) - ->>>>>>> 77cc91d06b (fix: add regional support to extend purchase gl entries) def validate_serialized_batch(self): from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos @@ -846,154 +837,6 @@ class StockController(AccountsController): gl_entries.append(self.get_gl_dict(gl_entry, item=item)) -<<<<<<< HEAD -======= -@frappe.whitelist() -def show_accounting_ledger_preview(company, doctype, docname): - filters = frappe._dict(company=company, include_dimensions=1) - doc = frappe.get_doc(doctype, docname) - doc.run_method("before_gl_preview") - - gl_columns, gl_data = get_accounting_ledger_preview(doc, filters) - - frappe.db.rollback() - - return {"gl_columns": gl_columns, "gl_data": gl_data} - - -@frappe.whitelist() -def show_stock_ledger_preview(company, doctype, docname): - filters = frappe._dict(company=company) - doc = frappe.get_doc(doctype, docname) - doc.run_method("before_sl_preview") - - sl_columns, sl_data = get_stock_ledger_preview(doc, filters) - - frappe.db.rollback() - - return { - "sl_columns": sl_columns, - "sl_data": sl_data, - } - - -def get_accounting_ledger_preview(doc, filters): - from erpnext.accounts.report.general_ledger.general_ledger import get_columns as get_gl_columns - - gl_columns, gl_data = [], [] - fields = [ - "posting_date", - "account", - "debit", - "credit", - "against", - "party", - "party_type", - "cost_center", - "against_voucher_type", - "against_voucher", - ] - - doc.docstatus = 1 - - if doc.get("update_stock") or doc.doctype in ("Purchase Receipt", "Delivery Note"): - doc.update_stock_ledger() - - doc.make_gl_entries() - columns = get_gl_columns(filters) - gl_entries = get_gl_entries_for_preview(doc.doctype, doc.name, fields) - - gl_columns = get_columns(columns, fields) - gl_data = get_data(fields, gl_entries) - - return gl_columns, gl_data - - -def get_stock_ledger_preview(doc, filters): - from erpnext.stock.report.stock_ledger.stock_ledger import get_columns as get_sl_columns - - sl_columns, sl_data = [], [] - fields = [ - "item_code", - "stock_uom", - "actual_qty", - "qty_after_transaction", - "warehouse", - "incoming_rate", - "valuation_rate", - "stock_value", - "stock_value_difference", - ] - columns_fields = [ - "item_code", - "stock_uom", - "in_qty", - "out_qty", - "qty_after_transaction", - "warehouse", - "incoming_rate", - "in_out_rate", - "stock_value", - "stock_value_difference", - ] - - if doc.get("update_stock") or doc.doctype in ("Purchase Receipt", "Delivery Note"): - doc.docstatus = 1 - doc.update_stock_ledger() - columns = get_sl_columns(filters) - sl_entries = get_sl_entries_for_preview(doc.doctype, doc.name, fields) - - sl_columns = get_columns(columns, columns_fields) - sl_data = get_data(columns_fields, sl_entries) - - return sl_columns, sl_data - - -def get_sl_entries_for_preview(doctype, docname, fields): - sl_entries = frappe.get_all( - "Stock Ledger Entry", filters={"voucher_type": doctype, "voucher_no": docname}, fields=fields - ) - - for entry in sl_entries: - if entry.actual_qty > 0: - entry["in_qty"] = entry.actual_qty - entry["out_qty"] = 0 - else: - entry["out_qty"] = abs(entry.actual_qty) - entry["in_qty"] = 0 - - entry["in_out_rate"] = entry["valuation_rate"] - - return sl_entries - - -def get_gl_entries_for_preview(doctype, docname, fields): - return frappe.get_all( - "GL Entry", filters={"voucher_type": doctype, "voucher_no": docname}, fields=fields - ) - - -def get_columns(raw_columns, fields): - return [ - {"name": d.get("label"), "editable": False, "width": 110} - for d in raw_columns - if not d.get("hidden") and d.get("fieldname") in fields - ] - - -def get_data(raw_columns, raw_data): - datatable_data = [] - for row in raw_data: - data_row = [] - for column in raw_columns: - data_row.append(row.get(column) or "") - - datatable_data.append(data_row) - - return datatable_data - - ->>>>>>> 77cc91d06b (fix: add regional support to extend purchase gl entries) def repost_required_for_queue(doc: StockController) -> bool: """check if stock document contains repeated item-warehouse with queue based valuation. diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 865b447dc49..dea8bceb3c6 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -314,11 +314,7 @@ class PurchaseReceipt(BuyingController): self.make_item_gl_entries(gl_entries, warehouse_account=warehouse_account) self.make_tax_gl_entries(gl_entries) -<<<<<<< HEAD -======= - self.get_asset_gl_entry(gl_entries) update_regional_gl_entries(gl_entries, self) ->>>>>>> 77cc91d06b (fix: add regional support to extend purchase gl entries) return process_gl_map(gl_entries) @@ -764,7 +760,6 @@ class PurchaseReceipt(BuyingController): update_billing_percentage(pr_doc, update_modified=update_modified) -<<<<<<< HEAD def get_stock_value_difference(voucher_no, voucher_detail_no, warehouse): return frappe.db.get_value( "Stock Ledger Entry", @@ -779,10 +774,7 @@ def get_stock_value_difference(voucher_no, voucher_detail_no, warehouse): ) -def update_billed_amount_based_on_po(po_details, update_modified=True): -======= def update_billed_amount_based_on_po(po_details, update_modified=True, pr_doc=None): ->>>>>>> ff7108a3b1 (fix: update existing doc if possible) po_billed_amt_details = get_billed_amount_against_po(po_details) # Get all Purchase Receipt Item rows against the Purchase Order Items From fa490ef2f0d86af007ed2ba197a768f916416216 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 25 Oct 2023 19:46:51 +0530 Subject: [PATCH 044/151] chore: resolve conflicts --- erpnext/controllers/stock_controller.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 261c7e990b7..a72cc667399 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -1057,8 +1057,3 @@ def create_item_wise_repost_entries(voucher_type, voucher_no, allow_zero_rate=Fa repost_entries.append(repost_entry) return repost_entries - - -@erpnext.allow_regional -def update_regional_gl_entries(gl_list, doc): - return From e96d5b314a96ab70a80e858890722f4a2c44e39a Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 26 Oct 2023 06:14:02 +0000 Subject: [PATCH 045/151] feat: allow return of components for SCO that don't have SCR created (backport #37686) (#37692) * feat: allow return of components for SCO that don't have SCR created (cherry picked from commit 8e3b9ec87977b54cfdee4b65283723d20e91f274) * fix: consider returned qty while calculating unsupplied qty (cherry picked from commit 3290df5593009a4e237e7903feb078961f15ae12) --------- Co-authored-by: s-aga-r --- .../stock/doctype/stock_entry/stock_entry.py | 28 ++++++++++++++++--- .../subcontracting_order.js | 4 +-- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 54978d8aa36..ebae337531c 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -988,14 +988,34 @@ class StockEntry(StockController): & (se.docstatus == 1) & (se_detail.item_code == se_item.item_code) & ( - (se.purchase_order == self.purchase_order) + ((se.purchase_order == self.purchase_order) & (se_detail.po_detail == se_item.po_detail)) if self.subcontract_data.order_doctype == "Purchase Order" - else (se.subcontracting_order == self.subcontracting_order) + else ( + (se.subcontracting_order == self.subcontracting_order) + & (se_detail.sco_rm_detail == se_item.sco_rm_detail) + ) ) ) - ).run()[0][0] + ).run()[0][0] or 0 - if flt(total_supplied, precision) > flt(total_allowed, precision): + total_returned = 0 + if self.subcontract_data.order_doctype == "Subcontracting Order": + total_returned = ( + frappe.qb.from_(se) + .inner_join(se_detail) + .on(se.name == se_detail.parent) + .select(Sum(se_detail.transfer_qty)) + .where( + (se.purpose == "Material Transfer") + & (se.docstatus == 1) + & (se.is_return == 1) + & (se_detail.item_code == se_item.item_code) + & (se_detail.sco_rm_detail == se_item.sco_rm_detail) + & (se.subcontracting_order == self.subcontracting_order) + ) + ).run()[0][0] or 0 + + if flt(total_supplied - total_returned, precision) > flt(total_allowed, precision): frappe.throw( _("Row {0}# Item {1} cannot be transferred more than {2} against {3} {4}").format( se_item.idx, diff --git a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js index 15a2ac90912..d9c503b3de8 100644 --- a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js +++ b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js @@ -107,7 +107,7 @@ frappe.ui.form.on('Subcontracting Order', { get_materials_from_supplier: function (frm) { let sco_rm_details = []; - if (frm.doc.status != "Closed" && frm.doc.supplied_items && frm.doc.per_received > 0) { + if (frm.doc.status != "Closed" && frm.doc.supplied_items) { frm.doc.supplied_items.forEach(d => { if (d.total_supplied_qty > 0 && d.total_supplied_qty != d.consumed_qty) { sco_rm_details.push(d.name); @@ -193,7 +193,7 @@ erpnext.buying.SubcontractingOrderController = class SubcontractingOrderControll } has_unsupplied_items() { - return this.frm.doc['supplied_items'].some(item => item.required_qty > item.supplied_qty); + return this.frm.doc['supplied_items'].some(item => item.required_qty > (item.supplied_qty - item.returned_qty)); } make_subcontracting_receipt() { From c58fefb35947fbd4be9b9e40e7321160155fbe23 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 26 Oct 2023 13:12:44 +0530 Subject: [PATCH 046/151] feat(delivery): link to delivery notes list view from delivery trip (backport #37604) (#37695) feat(delivery): link to delivery notes list view from delivery trip (cherry picked from commit 85488cd0dcac2efe02478ced44349a0cadb3b064) Co-authored-by: David Arnold --- erpnext/stock/doctype/delivery_trip/delivery_trip.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/erpnext/stock/doctype/delivery_trip/delivery_trip.js b/erpnext/stock/doctype/delivery_trip/delivery_trip.js index 45064d8fcb9..4ba5b32ca62 100755 --- a/erpnext/stock/doctype/delivery_trip/delivery_trip.js +++ b/erpnext/stock/doctype/delivery_trip/delivery_trip.js @@ -64,6 +64,11 @@ frappe.ui.form.on('Delivery Trip', { }) }, __("Get stops from")); } + frm.add_custom_button(__("Delivery Notes"), function () { + frappe.set_route("List", "Delivery Note", + {'name': ["in", frm.doc.delivery_stops.map((stop) => {return stop.delivery_note;})]} + ); + }, __("View")); }, calculate_arrival_time: function (frm) { From 7558b622a4cd9db193d9f87bec210d5d46a9a1c7 Mon Sep 17 00:00:00 2001 From: Smit Vora Date: Thu, 19 Oct 2023 22:35:55 +0530 Subject: [PATCH 047/151] fix: add regional support to extend purchase gl entries (cherry picked from commit 77cc91d06b9ea1e5758546a78bbcbb2ef97f549b) # Conflicts: # erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py # erpnext/controllers/stock_controller.py # erpnext/stock/doctype/purchase_receipt/purchase_receipt.py (cherry picked from commit d02ebd621504ac04737cc283ee6d06e2c6eb92c2) --- .../purchase_invoice/purchase_invoice.py | 153 +++++++++++++++++ erpnext/controllers/stock_controller.py | 162 ++++++++++++++++++ .../purchase_receipt/purchase_receipt.py | 16 +- 3 files changed, 325 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 306208603c0..a20117f0ec2 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -969,12 +969,165 @@ class PurchaseInvoice(BuyingController): item.item_tax_amount, item.precision("item_tax_amount") ) +<<<<<<< HEAD assets = frappe.db.get_all( "Asset", filters={"purchase_invoice": self.name, "item_code": item.item_code} ) for asset in assets: frappe.db.set_value("Asset", asset.name, "gross_purchase_amount", flt(item.valuation_rate)) frappe.db.set_value("Asset", asset.name, "purchase_receipt_amount", flt(item.valuation_rate)) +======= + def get_asset_gl_entry(self, gl_entries): + arbnb_account = None + eiiav_account = None + asset_eiiav_currency = None + + for item in self.get("items"): + if item.is_fixed_asset: + asset_amount = flt(item.net_amount) + flt(item.item_tax_amount / self.conversion_rate) + base_asset_amount = flt(item.base_net_amount + item.item_tax_amount) + + item_exp_acc_type = frappe.get_cached_value("Account", item.expense_account, "account_type") + if not item.expense_account or item_exp_acc_type not in [ + "Asset Received But Not Billed", + "Fixed Asset", + ]: + if not arbnb_account: + arbnb_account = self.get_company_default("asset_received_but_not_billed") + item.expense_account = arbnb_account + + if not self.update_stock: + arbnb_currency = get_account_currency(item.expense_account) + gl_entries.append( + self.get_gl_dict( + { + "account": item.expense_account, + "against": self.supplier, + "remarks": self.get("remarks") or _("Accounting Entry for Asset"), + "debit": base_asset_amount, + "debit_in_account_currency": ( + base_asset_amount if arbnb_currency == self.company_currency else asset_amount + ), + "cost_center": item.cost_center, + "project": item.project or self.project, + }, + item=item, + ) + ) + + if item.item_tax_amount: + if not eiiav_account or not asset_eiiav_currency: + eiiav_account = self.get_company_default("expenses_included_in_asset_valuation") + asset_eiiav_currency = get_account_currency(eiiav_account) + + gl_entries.append( + self.get_gl_dict( + { + "account": eiiav_account, + "against": self.supplier, + "remarks": self.get("remarks") or _("Accounting Entry for Asset"), + "cost_center": item.cost_center, + "project": item.project or self.project, + "credit": item.item_tax_amount, + "credit_in_account_currency": ( + item.item_tax_amount + if asset_eiiav_currency == self.company_currency + else item.item_tax_amount / self.conversion_rate + ), + }, + item=item, + ) + ) + else: + cwip_account = get_asset_account( + "capital_work_in_progress_account", asset_category=item.asset_category, company=self.company + ) + + cwip_account_currency = get_account_currency(cwip_account) + gl_entries.append( + self.get_gl_dict( + { + "account": cwip_account, + "against": self.supplier, + "remarks": self.get("remarks") or _("Accounting Entry for Asset"), + "debit": base_asset_amount, + "debit_in_account_currency": ( + base_asset_amount if cwip_account_currency == self.company_currency else asset_amount + ), + "cost_center": item.cost_center or self.cost_center, + "project": item.project or self.project, + }, + item=item, + ) + ) + + if item.item_tax_amount and not cint(erpnext.is_perpetual_inventory_enabled(self.company)): + if not eiiav_account or not asset_eiiav_currency: + eiiav_account = self.get_company_default("expenses_included_in_asset_valuation") + asset_eiiav_currency = get_account_currency(eiiav_account) + + gl_entries.append( + self.get_gl_dict( + { + "account": eiiav_account, + "against": self.supplier, + "remarks": self.get("remarks") or _("Accounting Entry for Asset"), + "cost_center": item.cost_center, + "credit": item.item_tax_amount, + "project": item.project or self.project, + "credit_in_account_currency": ( + item.item_tax_amount + if asset_eiiav_currency == self.company_currency + else item.item_tax_amount / self.conversion_rate + ), + }, + item=item, + ) + ) + + # Assets are bought through this document then it will be linked to this document + if flt(item.landed_cost_voucher_amount): + if not eiiav_account: + eiiav_account = self.get_company_default("expenses_included_in_asset_valuation") + + gl_entries.append( + self.get_gl_dict( + { + "account": eiiav_account, + "against": cwip_account, + "cost_center": item.cost_center, + "remarks": self.get("remarks") or _("Accounting Entry for Stock"), + "credit": flt(item.landed_cost_voucher_amount), + "project": item.project or self.project, + }, + item=item, + ) + ) + + gl_entries.append( + self.get_gl_dict( + { + "account": cwip_account, + "against": eiiav_account, + "cost_center": item.cost_center, + "remarks": self.get("remarks") or _("Accounting Entry for Stock"), + "debit": flt(item.landed_cost_voucher_amount), + "project": item.project or self.project, + }, + item=item, + ) + ) + + # update gross amount of assets bought through this document + assets = frappe.db.get_all( + "Asset", filters={"purchase_invoice": self.name, "item_code": item.item_code} + ) + for asset in assets: + frappe.db.set_value("Asset", asset.name, "gross_purchase_amount", flt(item.valuation_rate)) + frappe.db.set_value("Asset", asset.name, "purchase_receipt_amount", flt(item.valuation_rate)) + + return gl_entries +>>>>>>> 77cc91d06b (fix: add regional support to extend purchase gl entries) def make_stock_adjustment_entry( self, gl_entries, item, voucher_wise_stock_value, account_currency diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index a72cc667399..3e766f4a7f1 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -76,6 +76,15 @@ class StockController(AccountsController): gl_entries = self.get_gl_entries(warehouse_account) make_gl_entries(gl_entries, from_repost=from_repost) +<<<<<<< HEAD +======= + elif self.doctype in ["Purchase Receipt", "Purchase Invoice"] and self.docstatus == 1: + gl_entries = [] + gl_entries = self.get_asset_gl_entry(gl_entries) + update_regional_gl_entries(gl_entries, self) + make_gl_entries(gl_entries, from_repost=from_repost) + +>>>>>>> 77cc91d06b (fix: add regional support to extend purchase gl entries) def validate_serialized_batch(self): from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos @@ -837,6 +846,154 @@ class StockController(AccountsController): gl_entries.append(self.get_gl_dict(gl_entry, item=item)) +<<<<<<< HEAD +======= +@frappe.whitelist() +def show_accounting_ledger_preview(company, doctype, docname): + filters = frappe._dict(company=company, include_dimensions=1) + doc = frappe.get_doc(doctype, docname) + doc.run_method("before_gl_preview") + + gl_columns, gl_data = get_accounting_ledger_preview(doc, filters) + + frappe.db.rollback() + + return {"gl_columns": gl_columns, "gl_data": gl_data} + + +@frappe.whitelist() +def show_stock_ledger_preview(company, doctype, docname): + filters = frappe._dict(company=company) + doc = frappe.get_doc(doctype, docname) + doc.run_method("before_sl_preview") + + sl_columns, sl_data = get_stock_ledger_preview(doc, filters) + + frappe.db.rollback() + + return { + "sl_columns": sl_columns, + "sl_data": sl_data, + } + + +def get_accounting_ledger_preview(doc, filters): + from erpnext.accounts.report.general_ledger.general_ledger import get_columns as get_gl_columns + + gl_columns, gl_data = [], [] + fields = [ + "posting_date", + "account", + "debit", + "credit", + "against", + "party", + "party_type", + "cost_center", + "against_voucher_type", + "against_voucher", + ] + + doc.docstatus = 1 + + if doc.get("update_stock") or doc.doctype in ("Purchase Receipt", "Delivery Note"): + doc.update_stock_ledger() + + doc.make_gl_entries() + columns = get_gl_columns(filters) + gl_entries = get_gl_entries_for_preview(doc.doctype, doc.name, fields) + + gl_columns = get_columns(columns, fields) + gl_data = get_data(fields, gl_entries) + + return gl_columns, gl_data + + +def get_stock_ledger_preview(doc, filters): + from erpnext.stock.report.stock_ledger.stock_ledger import get_columns as get_sl_columns + + sl_columns, sl_data = [], [] + fields = [ + "item_code", + "stock_uom", + "actual_qty", + "qty_after_transaction", + "warehouse", + "incoming_rate", + "valuation_rate", + "stock_value", + "stock_value_difference", + ] + columns_fields = [ + "item_code", + "stock_uom", + "in_qty", + "out_qty", + "qty_after_transaction", + "warehouse", + "incoming_rate", + "in_out_rate", + "stock_value", + "stock_value_difference", + ] + + if doc.get("update_stock") or doc.doctype in ("Purchase Receipt", "Delivery Note"): + doc.docstatus = 1 + doc.update_stock_ledger() + columns = get_sl_columns(filters) + sl_entries = get_sl_entries_for_preview(doc.doctype, doc.name, fields) + + sl_columns = get_columns(columns, columns_fields) + sl_data = get_data(columns_fields, sl_entries) + + return sl_columns, sl_data + + +def get_sl_entries_for_preview(doctype, docname, fields): + sl_entries = frappe.get_all( + "Stock Ledger Entry", filters={"voucher_type": doctype, "voucher_no": docname}, fields=fields + ) + + for entry in sl_entries: + if entry.actual_qty > 0: + entry["in_qty"] = entry.actual_qty + entry["out_qty"] = 0 + else: + entry["out_qty"] = abs(entry.actual_qty) + entry["in_qty"] = 0 + + entry["in_out_rate"] = entry["valuation_rate"] + + return sl_entries + + +def get_gl_entries_for_preview(doctype, docname, fields): + return frappe.get_all( + "GL Entry", filters={"voucher_type": doctype, "voucher_no": docname}, fields=fields + ) + + +def get_columns(raw_columns, fields): + return [ + {"name": d.get("label"), "editable": False, "width": 110} + for d in raw_columns + if not d.get("hidden") and d.get("fieldname") in fields + ] + + +def get_data(raw_columns, raw_data): + datatable_data = [] + for row in raw_data: + data_row = [] + for column in raw_columns: + data_row.append(row.get(column) or "") + + datatable_data.append(data_row) + + return datatable_data + + +>>>>>>> 77cc91d06b (fix: add regional support to extend purchase gl entries) def repost_required_for_queue(doc: StockController) -> bool: """check if stock document contains repeated item-warehouse with queue based valuation. @@ -1057,3 +1214,8 @@ def create_item_wise_repost_entries(voucher_type, voucher_no, allow_zero_rate=Fa repost_entries.append(repost_entry) return repost_entries + + +@erpnext.allow_regional +def update_regional_gl_entries(gl_list, doc): + return diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 964ce0b7218..1e4ccc8b6ec 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -314,6 +314,11 @@ class PurchaseReceipt(BuyingController): self.make_item_gl_entries(gl_entries, warehouse_account=warehouse_account) self.make_tax_gl_entries(gl_entries) +<<<<<<< HEAD +======= + self.get_asset_gl_entry(gl_entries) + update_regional_gl_entries(gl_entries, self) +>>>>>>> 77cc91d06b (fix: add regional support to extend purchase gl entries) return process_gl_map(gl_entries) @@ -758,8 +763,6 @@ class PurchaseReceipt(BuyingController): pr_doc = self if (pr == self.name) else frappe.get_doc("Purchase Receipt", pr) update_billing_percentage(pr_doc, update_modified=update_modified) - self.load_from_db() - def get_stock_value_difference(voucher_no, voucher_detail_no, warehouse): return frappe.db.get_value( @@ -886,9 +889,6 @@ def get_billed_amount_against_po(po_items): def update_billing_percentage(pr_doc, update_modified=True, adjust_incoming_rate=False): - # Reload as billed amount was set in db directly - pr_doc.load_from_db() - # Update Billing % based on pending accepted qty total_amount, total_billed_amount = 0, 0 item_wise_returned_qty = get_item_wise_returned_qty(pr_doc) @@ -914,7 +914,6 @@ def update_billing_percentage(pr_doc, update_modified=True, adjust_incoming_rate percent_billed = round(100 * (total_billed_amount / (total_amount or 1)), 6) pr_doc.db_set("per_billed", percent_billed) - pr_doc.load_from_db() if update_modified: pr_doc.set_status(update=True) @@ -1193,3 +1192,8 @@ def get_item_account_wise_additional_cost(purchase_document): def on_doctype_update(): frappe.db.add_index("Purchase Receipt", ["supplier", "is_return", "return_against"]) + + +@erpnext.allow_regional +def update_regional_gl_entries(gl_list, doc): + return From e457d39b5dce2d57849d6aa2cf87929725499ef7 Mon Sep 17 00:00:00 2001 From: Smit Vora Date: Fri, 20 Oct 2023 09:33:37 +0530 Subject: [PATCH 048/151] fix: update existing doc if possible (cherry picked from commit ff7108a3b139b2f019230be681147f1a1c90a681) # Conflicts: # erpnext/stock/doctype/purchase_receipt/purchase_receipt.py (cherry picked from commit 89f07dcfac3740af81c59579152646f3c806113b) --- .../purchase_receipt/purchase_receipt.py | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 1e4ccc8b6ec..865b447dc49 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -757,13 +757,14 @@ class PurchaseReceipt(BuyingController): po_details.append(d.purchase_order_item) if po_details: - updated_pr += update_billed_amount_based_on_po(po_details, update_modified) + updated_pr += update_billed_amount_based_on_po(po_details, update_modified, self) for pr in set(updated_pr): pr_doc = self if (pr == self.name) else frappe.get_doc("Purchase Receipt", pr) update_billing_percentage(pr_doc, update_modified=update_modified) +<<<<<<< HEAD def get_stock_value_difference(voucher_no, voucher_detail_no, warehouse): return frappe.db.get_value( "Stock Ledger Entry", @@ -779,6 +780,9 @@ def get_stock_value_difference(voucher_no, voucher_detail_no, warehouse): def update_billed_amount_based_on_po(po_details, update_modified=True): +======= +def update_billed_amount_based_on_po(po_details, update_modified=True, pr_doc=None): +>>>>>>> ff7108a3b1 (fix: update existing doc if possible) po_billed_amt_details = get_billed_amount_against_po(po_details) # Get all Purchase Receipt Item rows against the Purchase Order Items @@ -807,13 +811,19 @@ def update_billed_amount_based_on_po(po_details, update_modified=True): po_billed_amt_details[pr_item.purchase_order_item] = billed_against_po if pr_item.billed_amt != billed_amt_agianst_pr: - frappe.db.set_value( - "Purchase Receipt Item", - pr_item.name, - "billed_amt", - billed_amt_agianst_pr, - update_modified=update_modified, - ) + # update existing doc if possible + if pr_doc and pr_item.parent == pr_doc.name: + pr_item = next((item for item in pr_doc.items if item.name == pr_item.name), None) + pr_item.db_set("billed_amt", billed_amt_agianst_pr, update_modified=update_modified) + + else: + frappe.db.set_value( + "Purchase Receipt Item", + pr_item.name, + "billed_amt", + billed_amt_agianst_pr, + update_modified=update_modified, + ) updated_pr.append(pr_item.parent) From cef2231d6a7059ff657a6fbc97e5ecd0f5ed4644 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 25 Oct 2023 19:42:02 +0530 Subject: [PATCH 049/151] chore: resolve conflicts (cherry picked from commit e01e9ebc370cba980307a5f3ecbc4a15b379fbc6) --- .../purchase_invoice/purchase_invoice.py | 153 ----------------- erpnext/controllers/stock_controller.py | 157 ------------------ .../purchase_receipt/purchase_receipt.py | 8 - 3 files changed, 318 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index a20117f0ec2..306208603c0 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -969,165 +969,12 @@ class PurchaseInvoice(BuyingController): item.item_tax_amount, item.precision("item_tax_amount") ) -<<<<<<< HEAD assets = frappe.db.get_all( "Asset", filters={"purchase_invoice": self.name, "item_code": item.item_code} ) for asset in assets: frappe.db.set_value("Asset", asset.name, "gross_purchase_amount", flt(item.valuation_rate)) frappe.db.set_value("Asset", asset.name, "purchase_receipt_amount", flt(item.valuation_rate)) -======= - def get_asset_gl_entry(self, gl_entries): - arbnb_account = None - eiiav_account = None - asset_eiiav_currency = None - - for item in self.get("items"): - if item.is_fixed_asset: - asset_amount = flt(item.net_amount) + flt(item.item_tax_amount / self.conversion_rate) - base_asset_amount = flt(item.base_net_amount + item.item_tax_amount) - - item_exp_acc_type = frappe.get_cached_value("Account", item.expense_account, "account_type") - if not item.expense_account or item_exp_acc_type not in [ - "Asset Received But Not Billed", - "Fixed Asset", - ]: - if not arbnb_account: - arbnb_account = self.get_company_default("asset_received_but_not_billed") - item.expense_account = arbnb_account - - if not self.update_stock: - arbnb_currency = get_account_currency(item.expense_account) - gl_entries.append( - self.get_gl_dict( - { - "account": item.expense_account, - "against": self.supplier, - "remarks": self.get("remarks") or _("Accounting Entry for Asset"), - "debit": base_asset_amount, - "debit_in_account_currency": ( - base_asset_amount if arbnb_currency == self.company_currency else asset_amount - ), - "cost_center": item.cost_center, - "project": item.project or self.project, - }, - item=item, - ) - ) - - if item.item_tax_amount: - if not eiiav_account or not asset_eiiav_currency: - eiiav_account = self.get_company_default("expenses_included_in_asset_valuation") - asset_eiiav_currency = get_account_currency(eiiav_account) - - gl_entries.append( - self.get_gl_dict( - { - "account": eiiav_account, - "against": self.supplier, - "remarks": self.get("remarks") or _("Accounting Entry for Asset"), - "cost_center": item.cost_center, - "project": item.project or self.project, - "credit": item.item_tax_amount, - "credit_in_account_currency": ( - item.item_tax_amount - if asset_eiiav_currency == self.company_currency - else item.item_tax_amount / self.conversion_rate - ), - }, - item=item, - ) - ) - else: - cwip_account = get_asset_account( - "capital_work_in_progress_account", asset_category=item.asset_category, company=self.company - ) - - cwip_account_currency = get_account_currency(cwip_account) - gl_entries.append( - self.get_gl_dict( - { - "account": cwip_account, - "against": self.supplier, - "remarks": self.get("remarks") or _("Accounting Entry for Asset"), - "debit": base_asset_amount, - "debit_in_account_currency": ( - base_asset_amount if cwip_account_currency == self.company_currency else asset_amount - ), - "cost_center": item.cost_center or self.cost_center, - "project": item.project or self.project, - }, - item=item, - ) - ) - - if item.item_tax_amount and not cint(erpnext.is_perpetual_inventory_enabled(self.company)): - if not eiiav_account or not asset_eiiav_currency: - eiiav_account = self.get_company_default("expenses_included_in_asset_valuation") - asset_eiiav_currency = get_account_currency(eiiav_account) - - gl_entries.append( - self.get_gl_dict( - { - "account": eiiav_account, - "against": self.supplier, - "remarks": self.get("remarks") or _("Accounting Entry for Asset"), - "cost_center": item.cost_center, - "credit": item.item_tax_amount, - "project": item.project or self.project, - "credit_in_account_currency": ( - item.item_tax_amount - if asset_eiiav_currency == self.company_currency - else item.item_tax_amount / self.conversion_rate - ), - }, - item=item, - ) - ) - - # Assets are bought through this document then it will be linked to this document - if flt(item.landed_cost_voucher_amount): - if not eiiav_account: - eiiav_account = self.get_company_default("expenses_included_in_asset_valuation") - - gl_entries.append( - self.get_gl_dict( - { - "account": eiiav_account, - "against": cwip_account, - "cost_center": item.cost_center, - "remarks": self.get("remarks") or _("Accounting Entry for Stock"), - "credit": flt(item.landed_cost_voucher_amount), - "project": item.project or self.project, - }, - item=item, - ) - ) - - gl_entries.append( - self.get_gl_dict( - { - "account": cwip_account, - "against": eiiav_account, - "cost_center": item.cost_center, - "remarks": self.get("remarks") or _("Accounting Entry for Stock"), - "debit": flt(item.landed_cost_voucher_amount), - "project": item.project or self.project, - }, - item=item, - ) - ) - - # update gross amount of assets bought through this document - assets = frappe.db.get_all( - "Asset", filters={"purchase_invoice": self.name, "item_code": item.item_code} - ) - for asset in assets: - frappe.db.set_value("Asset", asset.name, "gross_purchase_amount", flt(item.valuation_rate)) - frappe.db.set_value("Asset", asset.name, "purchase_receipt_amount", flt(item.valuation_rate)) - - return gl_entries ->>>>>>> 77cc91d06b (fix: add regional support to extend purchase gl entries) def make_stock_adjustment_entry( self, gl_entries, item, voucher_wise_stock_value, account_currency diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 3e766f4a7f1..261c7e990b7 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -76,15 +76,6 @@ class StockController(AccountsController): gl_entries = self.get_gl_entries(warehouse_account) make_gl_entries(gl_entries, from_repost=from_repost) -<<<<<<< HEAD -======= - elif self.doctype in ["Purchase Receipt", "Purchase Invoice"] and self.docstatus == 1: - gl_entries = [] - gl_entries = self.get_asset_gl_entry(gl_entries) - update_regional_gl_entries(gl_entries, self) - make_gl_entries(gl_entries, from_repost=from_repost) - ->>>>>>> 77cc91d06b (fix: add regional support to extend purchase gl entries) def validate_serialized_batch(self): from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos @@ -846,154 +837,6 @@ class StockController(AccountsController): gl_entries.append(self.get_gl_dict(gl_entry, item=item)) -<<<<<<< HEAD -======= -@frappe.whitelist() -def show_accounting_ledger_preview(company, doctype, docname): - filters = frappe._dict(company=company, include_dimensions=1) - doc = frappe.get_doc(doctype, docname) - doc.run_method("before_gl_preview") - - gl_columns, gl_data = get_accounting_ledger_preview(doc, filters) - - frappe.db.rollback() - - return {"gl_columns": gl_columns, "gl_data": gl_data} - - -@frappe.whitelist() -def show_stock_ledger_preview(company, doctype, docname): - filters = frappe._dict(company=company) - doc = frappe.get_doc(doctype, docname) - doc.run_method("before_sl_preview") - - sl_columns, sl_data = get_stock_ledger_preview(doc, filters) - - frappe.db.rollback() - - return { - "sl_columns": sl_columns, - "sl_data": sl_data, - } - - -def get_accounting_ledger_preview(doc, filters): - from erpnext.accounts.report.general_ledger.general_ledger import get_columns as get_gl_columns - - gl_columns, gl_data = [], [] - fields = [ - "posting_date", - "account", - "debit", - "credit", - "against", - "party", - "party_type", - "cost_center", - "against_voucher_type", - "against_voucher", - ] - - doc.docstatus = 1 - - if doc.get("update_stock") or doc.doctype in ("Purchase Receipt", "Delivery Note"): - doc.update_stock_ledger() - - doc.make_gl_entries() - columns = get_gl_columns(filters) - gl_entries = get_gl_entries_for_preview(doc.doctype, doc.name, fields) - - gl_columns = get_columns(columns, fields) - gl_data = get_data(fields, gl_entries) - - return gl_columns, gl_data - - -def get_stock_ledger_preview(doc, filters): - from erpnext.stock.report.stock_ledger.stock_ledger import get_columns as get_sl_columns - - sl_columns, sl_data = [], [] - fields = [ - "item_code", - "stock_uom", - "actual_qty", - "qty_after_transaction", - "warehouse", - "incoming_rate", - "valuation_rate", - "stock_value", - "stock_value_difference", - ] - columns_fields = [ - "item_code", - "stock_uom", - "in_qty", - "out_qty", - "qty_after_transaction", - "warehouse", - "incoming_rate", - "in_out_rate", - "stock_value", - "stock_value_difference", - ] - - if doc.get("update_stock") or doc.doctype in ("Purchase Receipt", "Delivery Note"): - doc.docstatus = 1 - doc.update_stock_ledger() - columns = get_sl_columns(filters) - sl_entries = get_sl_entries_for_preview(doc.doctype, doc.name, fields) - - sl_columns = get_columns(columns, columns_fields) - sl_data = get_data(columns_fields, sl_entries) - - return sl_columns, sl_data - - -def get_sl_entries_for_preview(doctype, docname, fields): - sl_entries = frappe.get_all( - "Stock Ledger Entry", filters={"voucher_type": doctype, "voucher_no": docname}, fields=fields - ) - - for entry in sl_entries: - if entry.actual_qty > 0: - entry["in_qty"] = entry.actual_qty - entry["out_qty"] = 0 - else: - entry["out_qty"] = abs(entry.actual_qty) - entry["in_qty"] = 0 - - entry["in_out_rate"] = entry["valuation_rate"] - - return sl_entries - - -def get_gl_entries_for_preview(doctype, docname, fields): - return frappe.get_all( - "GL Entry", filters={"voucher_type": doctype, "voucher_no": docname}, fields=fields - ) - - -def get_columns(raw_columns, fields): - return [ - {"name": d.get("label"), "editable": False, "width": 110} - for d in raw_columns - if not d.get("hidden") and d.get("fieldname") in fields - ] - - -def get_data(raw_columns, raw_data): - datatable_data = [] - for row in raw_data: - data_row = [] - for column in raw_columns: - data_row.append(row.get(column) or "") - - datatable_data.append(data_row) - - return datatable_data - - ->>>>>>> 77cc91d06b (fix: add regional support to extend purchase gl entries) def repost_required_for_queue(doc: StockController) -> bool: """check if stock document contains repeated item-warehouse with queue based valuation. diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 865b447dc49..dea8bceb3c6 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -314,11 +314,7 @@ class PurchaseReceipt(BuyingController): self.make_item_gl_entries(gl_entries, warehouse_account=warehouse_account) self.make_tax_gl_entries(gl_entries) -<<<<<<< HEAD -======= - self.get_asset_gl_entry(gl_entries) update_regional_gl_entries(gl_entries, self) ->>>>>>> 77cc91d06b (fix: add regional support to extend purchase gl entries) return process_gl_map(gl_entries) @@ -764,7 +760,6 @@ class PurchaseReceipt(BuyingController): update_billing_percentage(pr_doc, update_modified=update_modified) -<<<<<<< HEAD def get_stock_value_difference(voucher_no, voucher_detail_no, warehouse): return frappe.db.get_value( "Stock Ledger Entry", @@ -779,10 +774,7 @@ def get_stock_value_difference(voucher_no, voucher_detail_no, warehouse): ) -def update_billed_amount_based_on_po(po_details, update_modified=True): -======= def update_billed_amount_based_on_po(po_details, update_modified=True, pr_doc=None): ->>>>>>> ff7108a3b1 (fix: update existing doc if possible) po_billed_amt_details = get_billed_amount_against_po(po_details) # Get all Purchase Receipt Item rows against the Purchase Order Items From 519bf3d377656e59f01a2e048d8407a6c1f3f273 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 25 Oct 2023 19:46:51 +0530 Subject: [PATCH 050/151] chore: resolve conflicts (cherry picked from commit fa490ef2f0d86af007ed2ba197a768f916416216) --- erpnext/controllers/stock_controller.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 261c7e990b7..a72cc667399 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -1057,8 +1057,3 @@ def create_item_wise_repost_entries(voucher_type, voucher_no, allow_zero_rate=Fa repost_entries.append(repost_entry) return repost_entries - - -@erpnext.allow_regional -def update_regional_gl_entries(gl_list, doc): - return From 38665760cdc50ffdc9aa53477d3f047b98b94ebd Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Thu, 26 Oct 2023 08:52:24 +0000 Subject: [PATCH 051/151] chore(release): Bumped to Version 14.45.1 ## [14.45.1](https://github.com/frappe/erpnext/compare/v14.45.0...v14.45.1) (2023-10-26) ### Bug Fixes * add regional support to extend purchase gl entries ([7558b62](https://github.com/frappe/erpnext/commit/7558b622a4cd9db193d9f87bec210d5d46a9a1c7)) * update existing doc if possible ([e457d39](https://github.com/frappe/erpnext/commit/e457d39b5dce2d57849d6aa2cf87929725499ef7)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index a0730da2fe4..c91c877c1c2 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "14.45.0" +__version__ = "14.45.1" def get_default_company(user=None): From 375be8cd9336af81cc599b70a9fc9c061b046e2f Mon Sep 17 00:00:00 2001 From: Smit Vora Date: Thu, 26 Oct 2023 17:59:42 +0530 Subject: [PATCH 052/151] fix: purchase receipt with stock and asset items --- erpnext/assets/doctype/asset/test_asset.py | 1 + .../doctype/purchase_receipt/purchase_receipt.py | 12 +++++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index 89553f2a83a..e8ffae3015b 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -1732,6 +1732,7 @@ def create_asset_category(): "fixed_asset_account": "_Test Fixed Asset - _TC", "accumulated_depreciation_account": "_Test Accumulated Depreciations - _TC", "depreciation_expense_account": "_Test Depreciations - _TC", + "capital_work_in_progress_account": "CWIP Account - _TC", }, ) asset_category.append( diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index dea8bceb3c6..ec448b619a7 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -543,17 +543,19 @@ class PurchaseReceipt(BuyingController): d, gl_entries, self.posting_date, d.get("provisional_expense_account") ) elif flt(d.qty) and (flt(d.valuation_rate) or self.is_return): - is_asset_pr = any(d.is_fixed_asset for d in self.get("items")) remarks = self.get("remarks") or _("Accounting Entry for {0}").format( - "Asset" if is_asset_pr else "Stock" + "Asset" if d.is_fixed_asset else "Stock" ) - if not (erpnext.is_perpetual_inventory_enabled(self.company) or is_asset_pr): - return + if not ( + (erpnext.is_perpetual_inventory_enabled(self.company) and d.item_code in stock_items) + or d.is_fixed_asset + ): + continue stock_asset_rbnb = ( self.get_company_default("asset_received_but_not_billed") - if is_asset_pr + if d.is_fixed_asset else self.get_company_default("stock_received_but_not_billed") ) landed_cost_entries = get_item_account_wise_additional_cost(self.name) From 556095daaa5dd0e1c7778af9c2a8b213c857cd37 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 26 Oct 2023 18:12:01 +0530 Subject: [PATCH 053/151] fix: avoid name clash in delivery stop (backport #37306) (#37701) fix: avoid name clash in delivery stop (#37306) * fix(stock): avoid name clash in delivery stop with Document.lock() * chore(stock): format delivery stop json according to doctype builder (cherry picked from commit 681782121cd0d723f725e4c1c4c30167eb1622ec) Co-authored-by: David Arnold --- erpnext/patches.txt | 1 + .../v14_0/migrate_delivery_stop_lock_field.py | 7 + .../doctype/delivery_stop/delivery_stop.json | 956 ++++-------------- .../doctype/delivery_trip/delivery_trip.py | 2 +- .../delivery_trip/test_delivery_trip.py | 4 +- 5 files changed, 180 insertions(+), 790 deletions(-) create mode 100644 erpnext/patches/v14_0/migrate_delivery_stop_lock_field.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 0c69c00b731..81b14e89b67 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -343,5 +343,6 @@ erpnext.patches.v14_0.correct_asset_value_if_je_with_workflow erpnext.patches.v14_0.migrate_deferred_accounts_to_item_defaults erpnext.patches.v14_0.create_accounting_dimensions_in_sales_order_item erpnext.patches.v14_0.rename_over_order_allowance_field +erpnext.patches.v14_0.migrate_delivery_stop_lock_field # below migration patch should always run last erpnext.patches.v14_0.migrate_gl_to_payment_ledger diff --git a/erpnext/patches/v14_0/migrate_delivery_stop_lock_field.py b/erpnext/patches/v14_0/migrate_delivery_stop_lock_field.py new file mode 100644 index 00000000000..c9ec1e113dc --- /dev/null +++ b/erpnext/patches/v14_0/migrate_delivery_stop_lock_field.py @@ -0,0 +1,7 @@ +import frappe +from frappe.model.utils.rename_field import rename_field + + +def execute(): + if frappe.db.has_column("Delivery Stop", "lock"): + rename_field("Delivery Stop", "lock", "locked") diff --git a/erpnext/stock/doctype/delivery_stop/delivery_stop.json b/erpnext/stock/doctype/delivery_stop/delivery_stop.json index 5610a8108ab..42560e612ef 100644 --- a/erpnext/stock/doctype/delivery_stop/delivery_stop.json +++ b/erpnext/stock/doctype/delivery_stop/delivery_stop.json @@ -1,815 +1,197 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2017-10-16 16:46:28.166950", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "creation": "2017-10-16 16:46:28.166950", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "customer", + "address", + "locked", + "column_break_6", + "customer_address", + "visited", + "order_information_section", + "delivery_note", + "cb_order", + "grand_total", + "section_break_7", + "contact", + "email_sent_to", + "column_break_7", + "customer_contact", + "section_break_9", + "distance", + "estimated_arrival", + "lat", + "column_break_19", + "uom", + "lng", + "more_information_section", + "details" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 2, - "fieldname": "customer", - "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": "Customer", - "length": 0, - "no_copy": 0, - "options": "Customer", - "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 - }, + "columns": 2, + "fieldname": "customer", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Customer", + "options": "Customer" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "address", - "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": "Address Name", - "length": 0, - "no_copy": 0, - "options": "Address", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "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": "address", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Address Name", + "options": "Address", + "print_hide": 1, + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "lock", - "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": "Lock", - "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", + "fieldname": "locked", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Locked" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_6", - "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_6", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "customer_address", - "fieldtype": "Small Text", - "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": "Customer Address", - "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 - }, + "fieldname": "customer_address", + "fieldtype": "Small Text", + "label": "Customer Address", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.docstatus==1", - "fieldname": "visited", - "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": "Visited", - "length": 0, - "no_copy": 1, - "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 - }, + "allow_on_submit": 1, + "default": "0", + "depends_on": "eval:doc.docstatus==1", + "fieldname": "visited", + "fieldtype": "Check", + "label": "Visited", + "no_copy": 1, + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "order_information_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": "Order Information", - "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": "order_information_section", + "fieldtype": "Section Break", + "label": "Order Information" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "delivery_note", - "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": "Delivery Note", - "length": 0, - "no_copy": 1, - "options": "Delivery Note", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "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 - }, + "fieldname": "delivery_note", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Delivery Note", + "no_copy": 1, + "options": "Delivery Note", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "cb_order", - "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": "cb_order", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "grand_total", - "fieldtype": "Currency", - "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": "Grand Total", - "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 - }, + "fieldname": "grand_total", + "fieldtype": "Currency", + "label": "Grand Total", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_7", - "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": "Contact Information", - "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": "section_break_7", + "fieldtype": "Section Break", + "label": "Contact Information" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "contact", - "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": "Contact Name", - "length": 0, - "no_copy": 0, - "options": "Contact", - "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 - }, + "fieldname": "contact", + "fieldtype": "Link", + "label": "Contact Name", + "options": "Contact", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "email_sent_to", - "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": "Email sent to", - "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 - }, + "fieldname": "email_sent_to", + "fieldtype": "Data", + "label": "Email sent to", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_7", - "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_7", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "customer_contact", - "fieldtype": "Small Text", - "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": "Customer Contact", - "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 - }, + "fieldname": "customer_contact", + "fieldtype": "Small Text", + "label": "Customer Contact", + "read_only": 1 + }, { - "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, - "label": "Dispatch Information", - "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": "section_break_9", + "fieldtype": "Section Break", + "label": "Dispatch Information" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "distance", - "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": "Distance", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "2", - "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 - }, + "fieldname": "distance", + "fieldtype": "Float", + "label": "Distance", + "precision": "2", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "estimated_arrival", - "fieldtype": "Datetime", - "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": "Estimated Arrival", - "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": "estimated_arrival", + "fieldtype": "Datetime", + "in_list_view": 1, + "label": "Estimated Arrival" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "lat", - "fieldtype": "Float", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Latitude", - "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": "lat", + "fieldtype": "Float", + "hidden": 1, + "label": "Latitude" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_19", - "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_19", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "depends_on": "eval:doc.distance", - "fieldname": "uom", - "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": "UOM", - "length": 0, - "no_copy": 0, - "options": "UOM", - "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 - }, + "depends_on": "eval:doc.distance", + "fieldname": "uom", + "fieldtype": "Link", + "label": "UOM", + "options": "UOM", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "lng", - "fieldtype": "Float", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Longitude", - "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": "lng", + "fieldtype": "Float", + "hidden": 1, + "label": "Longitude" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "more_information_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": "More Information", - "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": "more_information_section", + "fieldtype": "Section Break", + "label": "More Information" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "details", - "fieldtype": "Text Editor", - "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": "Details", - "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": "details", + "fieldtype": "Text Editor", + "label": "Details" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2018-10-16 05:23:25.661542", - "modified_by": "Administrator", - "module": "Stock", - "name": "Delivery Stop", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 + ], + "istable": 1, + "links": [], + "modified": "2023-09-29 09:22:53.435161", + "modified_by": "Administrator", + "module": "Stock", + "name": "Delivery Stop", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/stock/doctype/delivery_trip/delivery_trip.py b/erpnext/stock/doctype/delivery_trip/delivery_trip.py index af2f4113e1e..c531a8769c0 100644 --- a/erpnext/stock/doctype/delivery_trip/delivery_trip.py +++ b/erpnext/stock/doctype/delivery_trip/delivery_trip.py @@ -170,7 +170,7 @@ class DeliveryTrip(Document): for stop in self.delivery_stops: leg.append(stop.customer_address) - if optimize and stop.lock: + if optimize and stop.locked: route_list.append(leg) leg = [stop.customer_address] diff --git a/erpnext/stock/doctype/delivery_trip/test_delivery_trip.py b/erpnext/stock/doctype/delivery_trip/test_delivery_trip.py index ed699e37b88..9b8b46e6e0a 100644 --- a/erpnext/stock/doctype/delivery_trip/test_delivery_trip.py +++ b/erpnext/stock/doctype/delivery_trip/test_delivery_trip.py @@ -46,7 +46,7 @@ class TestDeliveryTrip(FrappeTestCase): self.assertEqual(len(route_list[0]), 4) def test_unoptimized_route_list_with_locks(self): - self.delivery_trip.delivery_stops[0].lock = 1 + self.delivery_trip.delivery_stops[0].locked = 1 self.delivery_trip.save() route_list = self.delivery_trip.form_route_list(optimize=False) @@ -65,7 +65,7 @@ class TestDeliveryTrip(FrappeTestCase): self.assertEqual(len(route_list[0]), 4) def test_optimized_route_list_with_locks(self): - self.delivery_trip.delivery_stops[0].lock = 1 + self.delivery_trip.delivery_stops[0].locked = 1 self.delivery_trip.save() route_list = self.delivery_trip.form_route_list(optimize=True) From a0893ddf96f0ffcc997ce21a9e5832e0c0beddc3 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 26 Oct 2023 18:14:23 +0530 Subject: [PATCH 054/151] fix(defaults): apply discount and provisonal defaults from item group and brand if available (backport #37466) (#37703) fix(defaults): apply discount and provisonal defaults from item group and brand if available (#37466) (cherry picked from commit 1612d7ba3f353e23ad6ae9ba12b995106cddcb9e) Co-authored-by: David Arnold --- erpnext/stock/get_item_details.py | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 4a6ad70d291..e3fa5a0e7b1 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -364,8 +364,12 @@ def get_basic_details(args, item, overwrite_warehouse=True): ), "expense_account": expense_account or get_default_expense_account(args, item_defaults, item_group_defaults, brand_defaults), - "discount_account": get_default_discount_account(args, item_defaults), - "provisional_expense_account": get_provisional_account(args, item_defaults), + "discount_account": get_default_discount_account( + args, item_defaults, item_group_defaults, brand_defaults + ), + "provisional_expense_account": get_provisional_account( + args, item_defaults, item_group_defaults, brand_defaults + ), "cost_center": get_default_cost_center( args, item_defaults, item_group_defaults, brand_defaults ), @@ -719,12 +723,22 @@ def get_default_expense_account(args, item, item_group, brand): ) -def get_provisional_account(args, item): - return item.get("default_provisional_account") or args.default_provisional_account +def get_provisional_account(args, item, item_group, brand): + return ( + item.get("default_provisional_account") + or item_group.get("default_provisional_account") + or brand.get("default_provisional_account") + or args.default_provisional_account + ) -def get_default_discount_account(args, item): - return item.get("default_discount_account") or args.discount_account +def get_default_discount_account(args, item, item_group, brand): + return ( + item.get("default_discount_account") + or item_group.get("default_discount_account") + or brand.get("default_discount_account") + or args.discount_account + ) def get_default_deferred_account(args, item, fieldname=None): From 848928e7d1a03261a0e44855bc6cced015b1c756 Mon Sep 17 00:00:00 2001 From: Smit Vora Date: Thu, 26 Oct 2023 17:59:42 +0530 Subject: [PATCH 055/151] fix: purchase receipt with stock and asset items (cherry picked from commit 375be8cd9336af81cc599b70a9fc9c061b046e2f) --- erpnext/assets/doctype/asset/test_asset.py | 1 + .../doctype/purchase_receipt/purchase_receipt.py | 12 +++++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index 89553f2a83a..e8ffae3015b 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -1732,6 +1732,7 @@ def create_asset_category(): "fixed_asset_account": "_Test Fixed Asset - _TC", "accumulated_depreciation_account": "_Test Accumulated Depreciations - _TC", "depreciation_expense_account": "_Test Depreciations - _TC", + "capital_work_in_progress_account": "CWIP Account - _TC", }, ) asset_category.append( diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index dea8bceb3c6..ec448b619a7 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -543,17 +543,19 @@ class PurchaseReceipt(BuyingController): d, gl_entries, self.posting_date, d.get("provisional_expense_account") ) elif flt(d.qty) and (flt(d.valuation_rate) or self.is_return): - is_asset_pr = any(d.is_fixed_asset for d in self.get("items")) remarks = self.get("remarks") or _("Accounting Entry for {0}").format( - "Asset" if is_asset_pr else "Stock" + "Asset" if d.is_fixed_asset else "Stock" ) - if not (erpnext.is_perpetual_inventory_enabled(self.company) or is_asset_pr): - return + if not ( + (erpnext.is_perpetual_inventory_enabled(self.company) and d.item_code in stock_items) + or d.is_fixed_asset + ): + continue stock_asset_rbnb = ( self.get_company_default("asset_received_but_not_billed") - if is_asset_pr + if d.is_fixed_asset else self.get_company_default("stock_received_but_not_billed") ) landed_cost_entries = get_item_account_wise_additional_cost(self.name) From fea27d5e2efb6c6fcc70cfdce60f645a8f160ba0 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 27 Oct 2023 07:41:30 +0530 Subject: [PATCH 056/151] fix: typeerror on tds payable monthly report --- .../accounts/report/tds_payable_monthly/tds_payable_monthly.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py index f2ec31c70e1..eac5426b8b5 100644 --- a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py +++ b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py @@ -68,7 +68,7 @@ def get_result( tax_amount += entry.credit - entry.debit if net_total_map.get(name): - if voucher_type == "Journal Entry": + if voucher_type == "Journal Entry" and tax_amount and rate: # back calcalute total amount from rate and tax_amount total_amount = grand_total = base_total = tax_amount / (rate / 100) else: From 52cfe3f612b8297f433b34d41a45ec68721f5865 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 27 Oct 2023 07:41:30 +0530 Subject: [PATCH 057/151] fix: typeerror on tds payable monthly report (cherry picked from commit fea27d5e2efb6c6fcc70cfdce60f645a8f160ba0) --- .../accounts/report/tds_payable_monthly/tds_payable_monthly.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py index f2ec31c70e1..eac5426b8b5 100644 --- a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py +++ b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py @@ -68,7 +68,7 @@ def get_result( tax_amount += entry.credit - entry.debit if net_total_map.get(name): - if voucher_type == "Journal Entry": + if voucher_type == "Journal Entry" and tax_amount and rate: # back calcalute total amount from rate and tax_amount total_amount = grand_total = base_total = tax_amount / (rate / 100) else: From 83b378520259c2b8c20811d5b2a798a15d0294a8 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Fri, 27 Oct 2023 03:44:35 +0000 Subject: [PATCH 058/151] chore(release): Bumped to Version 14.45.2 ## [14.45.2](https://github.com/frappe/erpnext/compare/v14.45.1...v14.45.2) (2023-10-27) ### Bug Fixes * purchase receipt with stock and asset items ([848928e](https://github.com/frappe/erpnext/commit/848928e7d1a03261a0e44855bc6cced015b1c756)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index c91c877c1c2..23e5114413e 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "14.45.1" +__version__ = "14.45.2" def get_default_company(user=None): From 8e4e4a9fb4842fafc804b9626d86b746b0646879 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Fri, 27 Oct 2023 03:54:13 +0000 Subject: [PATCH 059/151] chore(release): Bumped to Version 14.45.3 ## [14.45.3](https://github.com/frappe/erpnext/compare/v14.45.2...v14.45.3) (2023-10-27) ### Bug Fixes * typeerror on tds payable monthly report ([52cfe3f](https://github.com/frappe/erpnext/commit/52cfe3f612b8297f433b34d41a45ec68721f5865)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 23e5114413e..2e1746c3c80 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "14.45.2" +__version__ = "14.45.3" def get_default_company(user=None): From ffdc11bbf6cd26e3280ee01fe9fbef8e3eb779e8 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 25 Oct 2023 22:58:24 +0530 Subject: [PATCH 060/151] fix(plaid): Do not sync pending transactions (cherry picked from commit 46ea8685590e81266beb1cccbe72925f28bc42ba) # Conflicts: # erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py --- .../doctype/bank_transaction/bank_transaction.py | 1 - .../doctype/plaid_settings/plaid_settings.py | 10 +++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py index f82337fbd77..256bde5c719 100644 --- a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py +++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py @@ -89,7 +89,6 @@ class BankTransaction(StatusUpdater): - 0 > a: Error: already over-allocated - clear means: set the latest transaction date as clearance date """ - gl_bank_account = frappe.db.get_value("Bank Account", self.bank_account, "account") remaining_amount = self.unallocated_amount for payment_entry in self.payment_entries: if payment_entry.allocated_amount == 0.0: diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py index e57a30a88e1..d37868ab681 100644 --- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py +++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py @@ -7,7 +7,7 @@ import frappe from frappe import _ from frappe.desk.doctype.tag.tag import add_tag from frappe.model.document import Document -from frappe.utils import add_months, formatdate, getdate, today +from frappe.utils import add_months, formatdate, getdate, sbool, today from plaid.errors import ItemError from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account @@ -238,8 +238,11 @@ def new_bank_transaction(transaction): deposit = abs(amount) withdrawal = 0.0 +<<<<<<< HEAD status = "Pending" if transaction["pending"] == "True" else "Settled" +======= +>>>>>>> 46ea868559 (fix(plaid): Do not sync pending transactions) tags = [] try: tags += transaction["category"] @@ -247,13 +250,14 @@ def new_bank_transaction(transaction): except KeyError: pass - if not frappe.db.exists("Bank Transaction", dict(transaction_id=transaction["transaction_id"])): + if not frappe.db.exists( + "Bank Transaction", dict(transaction_id=transaction["transaction_id"]) + ) and not sbool(transaction["pending"]): try: new_transaction = frappe.get_doc( { "doctype": "Bank Transaction", "date": getdate(transaction["date"]), - "status": status, "bank_account": bank_account, "deposit": deposit, "withdrawal": withdrawal, From 3f296eea3a549dbf54faa41797b6b1522106beee Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 27 Oct 2023 18:54:47 +0530 Subject: [PATCH 061/151] chore: allow wip_composite_asset in the MR PO PR PI flow (copy #37723) (#37725) * chore: allow wip_composite_asset in the MR PO PR PI flow (cherry picked from commit 0e5bea33a320497d2de3253ca0e1bb061a07130f) # Conflicts: # erpnext/buying/doctype/purchase_order/purchase_order.py * chore: resolve conflict --------- Co-authored-by: anandbaburajan --- .../purchase_invoice/purchase_invoice.py | 1 + erpnext/assets/doctype/asset/asset.json | 14 ++++++++--- .../asset_capitalization.py | 6 +---- .../doctype/purchase_order/purchase_order.py | 2 ++ .../purchase_order_item.json | 14 ++++++++++- .../material_request/material_request.py | 1 + .../material_request_item.json | 24 ++++++++++++++++--- .../purchase_receipt/purchase_receipt.py | 1 + 8 files changed, 51 insertions(+), 12 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 306208603c0..5f7de3d98a5 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -1676,6 +1676,7 @@ def make_purchase_receipt(source_name, target_doc=None): "po_detail": "purchase_order_item", "material_request": "material_request", "material_request_item": "material_request_item", + "wip_composite_asset": "wip_composite_asset", }, "postprocess": update_item, "condition": lambda doc: abs(doc.received_qty) < abs(doc.qty), diff --git a/erpnext/assets/doctype/asset/asset.json b/erpnext/assets/doctype/asset/asset.json index 1da3edcc60e..963e4b8297a 100644 --- a/erpnext/assets/doctype/asset/asset.json +++ b/erpnext/assets/doctype/asset/asset.json @@ -221,11 +221,11 @@ "read_only": 1 }, { + "depends_on": "eval:!(doc.is_composite_asset && !doc.capitalized_in)", "fieldname": "gross_purchase_amount", "fieldtype": "Currency", "label": "Gross Purchase Amount", "options": "Company:company:default_currency", - "read_only": 1, "read_only_depends_on": "eval:!doc.is_existing_asset", "reqd": 1 }, @@ -413,6 +413,7 @@ "fieldtype": "Column Break" }, { + "depends_on": "eval:!doc.is_composite_asset && !doc.is_existing_asset", "fieldname": "purchase_receipt", "fieldtype": "Link", "label": "Purchase Receipt", @@ -430,6 +431,7 @@ "read_only": 1 }, { + "depends_on": "eval:!doc.is_composite_asset && !doc.is_existing_asset", "fieldname": "purchase_invoice", "fieldtype": "Link", "label": "Purchase Invoice", @@ -493,10 +495,11 @@ "read_only": 1 }, { + "depends_on": "eval.doc.asset_quantity", "fieldname": "asset_quantity", "fieldtype": "Int", "label": "Asset Quantity", - "read_only_depends_on": "eval:!doc.is_existing_asset && !doc.is_composite_asset" + "read_only": 1 }, { "fieldname": "depr_entry_posting_status", @@ -555,9 +558,14 @@ "link_doctype": "Journal Entry", "link_fieldname": "reference_name", "table_fieldname": "accounts" + }, + { + "group": "Asset Capitalization", + "link_doctype": "Asset Capitalization", + "link_fieldname": "target_asset" } ], - "modified": "2023-10-03 23:28:26.732269", + "modified": "2023-10-27 17:03:46.629617", "modified_by": "Administrator", "module": "Assets", "name": "Asset", diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py index 04654104c7a..92cb85d1b7c 100644 --- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py +++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py @@ -832,12 +832,8 @@ def get_items_tagged_to_wip_composite_asset(asset): "amount", ] - pi_items = frappe.get_all( - "Purchase Invoice Item", filters={"wip_composite_asset": asset}, fields=fields - ) - pr_items = frappe.get_all( "Purchase Receipt Item", filters={"wip_composite_asset": asset}, fields=fields ) - return pi_items + pr_items + return pr_items diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index d87a855febf..69f34c33b69 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -524,6 +524,7 @@ def make_purchase_receipt(source_name, target_doc=None): "material_request_item": "material_request_item", "sales_order": "sales_order", "sales_order_item": "sales_order_item", + "wip_composite_asset": "wip_composite_asset", }, "postprocess": update_item, "condition": lambda doc: abs(doc.received_qty) < abs(doc.qty) @@ -600,6 +601,7 @@ def get_mapped_purchase_invoice(source_name, target_doc=None, ignore_permissions "field_map": { "name": "po_detail", "parent": "purchase_order", + "wip_composite_asset": "wip_composite_asset", }, "postprocess": update_item, "condition": lambda doc: (doc.base_amount == 0 or abs(doc.billed_amt) < abs(doc.amount)), diff --git a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json index fe1b9702539..ec14768aff0 100644 --- a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json +++ b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json @@ -86,6 +86,8 @@ "billed_amt", "accounting_details", "expense_account", + "column_break_fyqr", + "wip_composite_asset", "manufacture_details", "manufacturer", "manufacturer_part_no", @@ -897,13 +899,23 @@ "fieldname": "apply_tds", "fieldtype": "Check", "label": "Apply TDS" + }, + { + "fieldname": "wip_composite_asset", + "fieldtype": "Link", + "label": "WIP Composite Asset", + "options": "Asset" + }, + { + "fieldname": "column_break_fyqr", + "fieldtype": "Column Break" } ], "idx": 1, "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2023-09-13 16:22:40.825092", + "modified": "2023-10-27 15:50:42.655573", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order Item", diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py index 311b0ed8ce9..659bc42f0a6 100644 --- a/erpnext/stock/doctype/material_request/material_request.py +++ b/erpnext/stock/doctype/material_request/material_request.py @@ -405,6 +405,7 @@ def make_purchase_order(source_name, target_doc=None, args=None): ["uom", "uom"], ["sales_order", "sales_order"], ["sales_order_item", "sales_order_item"], + ["wip_composite_asset", "wip_composite_asset"], ], "postprocess": update_item, "condition": select_item, diff --git a/erpnext/stock/doctype/material_request_item/material_request_item.json b/erpnext/stock/doctype/material_request_item/material_request_item.json index 770dacdd19c..f0e117ef48d 100644 --- a/erpnext/stock/doctype/material_request_item/material_request_item.json +++ b/erpnext/stock/doctype/material_request_item/material_request_item.json @@ -37,6 +37,10 @@ "rate", "col_break3", "amount", + "accounting_details_section", + "expense_account", + "column_break_glru", + "wip_composite_asset", "manufacture_details", "manufacturer", "manufacturer_part_no", @@ -50,11 +54,10 @@ "lead_time_date", "sales_order", "sales_order_item", + "col_break4", "production_plan", "material_request_plan_item", "job_card_item", - "col_break4", - "expense_account", "section_break_46", "page_break" ], @@ -455,13 +458,28 @@ "label": "Job Card Item", "no_copy": 1, "print_hide": 1 + }, + { + "fieldname": "accounting_details_section", + "fieldtype": "Section Break", + "label": "Accounting Details" + }, + { + "fieldname": "column_break_glru", + "fieldtype": "Column Break" + }, + { + "fieldname": "wip_composite_asset", + "fieldtype": "Link", + "label": "WIP Composite Asset", + "options": "Asset" } ], "idx": 1, "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2023-05-07 20:23:31.250252", + "modified": "2023-10-27 15:53:41.444236", "modified_by": "Administrator", "module": "Stock", "name": "Material Request Item", diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index ec448b619a7..cde3dd6d412 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -1039,6 +1039,7 @@ def make_purchase_invoice(source_name, target_doc=None): "is_fixed_asset": "is_fixed_asset", "asset_location": "asset_location", "asset_category": "asset_category", + "wip_composite_asset": "wip_composite_asset", }, "postprocess": update_item, "filter": lambda d: get_pending_qty(d)[0] <= 0 From 6a6a5b0a75692b7cfa76fceb008f057be0bcbcb3 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 27 Oct 2023 18:54:47 +0530 Subject: [PATCH 062/151] chore: allow wip_composite_asset in the MR PO PR PI flow (copy #37723) (#37725) * chore: allow wip_composite_asset in the MR PO PR PI flow (cherry picked from commit 0e5bea33a320497d2de3253ca0e1bb061a07130f) # Conflicts: # erpnext/buying/doctype/purchase_order/purchase_order.py * chore: resolve conflict --------- Co-authored-by: anandbaburajan (cherry picked from commit 3f296eea3a549dbf54faa41797b6b1522106beee) --- .../purchase_invoice/purchase_invoice.py | 1 + erpnext/assets/doctype/asset/asset.json | 14 ++++++++--- .../asset_capitalization.py | 6 +---- .../doctype/purchase_order/purchase_order.py | 2 ++ .../purchase_order_item.json | 14 ++++++++++- .../material_request/material_request.py | 1 + .../material_request_item.json | 24 ++++++++++++++++--- .../purchase_receipt/purchase_receipt.py | 1 + 8 files changed, 51 insertions(+), 12 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 306208603c0..5f7de3d98a5 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -1676,6 +1676,7 @@ def make_purchase_receipt(source_name, target_doc=None): "po_detail": "purchase_order_item", "material_request": "material_request", "material_request_item": "material_request_item", + "wip_composite_asset": "wip_composite_asset", }, "postprocess": update_item, "condition": lambda doc: abs(doc.received_qty) < abs(doc.qty), diff --git a/erpnext/assets/doctype/asset/asset.json b/erpnext/assets/doctype/asset/asset.json index 1da3edcc60e..963e4b8297a 100644 --- a/erpnext/assets/doctype/asset/asset.json +++ b/erpnext/assets/doctype/asset/asset.json @@ -221,11 +221,11 @@ "read_only": 1 }, { + "depends_on": "eval:!(doc.is_composite_asset && !doc.capitalized_in)", "fieldname": "gross_purchase_amount", "fieldtype": "Currency", "label": "Gross Purchase Amount", "options": "Company:company:default_currency", - "read_only": 1, "read_only_depends_on": "eval:!doc.is_existing_asset", "reqd": 1 }, @@ -413,6 +413,7 @@ "fieldtype": "Column Break" }, { + "depends_on": "eval:!doc.is_composite_asset && !doc.is_existing_asset", "fieldname": "purchase_receipt", "fieldtype": "Link", "label": "Purchase Receipt", @@ -430,6 +431,7 @@ "read_only": 1 }, { + "depends_on": "eval:!doc.is_composite_asset && !doc.is_existing_asset", "fieldname": "purchase_invoice", "fieldtype": "Link", "label": "Purchase Invoice", @@ -493,10 +495,11 @@ "read_only": 1 }, { + "depends_on": "eval.doc.asset_quantity", "fieldname": "asset_quantity", "fieldtype": "Int", "label": "Asset Quantity", - "read_only_depends_on": "eval:!doc.is_existing_asset && !doc.is_composite_asset" + "read_only": 1 }, { "fieldname": "depr_entry_posting_status", @@ -555,9 +558,14 @@ "link_doctype": "Journal Entry", "link_fieldname": "reference_name", "table_fieldname": "accounts" + }, + { + "group": "Asset Capitalization", + "link_doctype": "Asset Capitalization", + "link_fieldname": "target_asset" } ], - "modified": "2023-10-03 23:28:26.732269", + "modified": "2023-10-27 17:03:46.629617", "modified_by": "Administrator", "module": "Assets", "name": "Asset", diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py index 04654104c7a..92cb85d1b7c 100644 --- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py +++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py @@ -832,12 +832,8 @@ def get_items_tagged_to_wip_composite_asset(asset): "amount", ] - pi_items = frappe.get_all( - "Purchase Invoice Item", filters={"wip_composite_asset": asset}, fields=fields - ) - pr_items = frappe.get_all( "Purchase Receipt Item", filters={"wip_composite_asset": asset}, fields=fields ) - return pi_items + pr_items + return pr_items diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index d87a855febf..69f34c33b69 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -524,6 +524,7 @@ def make_purchase_receipt(source_name, target_doc=None): "material_request_item": "material_request_item", "sales_order": "sales_order", "sales_order_item": "sales_order_item", + "wip_composite_asset": "wip_composite_asset", }, "postprocess": update_item, "condition": lambda doc: abs(doc.received_qty) < abs(doc.qty) @@ -600,6 +601,7 @@ def get_mapped_purchase_invoice(source_name, target_doc=None, ignore_permissions "field_map": { "name": "po_detail", "parent": "purchase_order", + "wip_composite_asset": "wip_composite_asset", }, "postprocess": update_item, "condition": lambda doc: (doc.base_amount == 0 or abs(doc.billed_amt) < abs(doc.amount)), diff --git a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json index fe1b9702539..ec14768aff0 100644 --- a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json +++ b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json @@ -86,6 +86,8 @@ "billed_amt", "accounting_details", "expense_account", + "column_break_fyqr", + "wip_composite_asset", "manufacture_details", "manufacturer", "manufacturer_part_no", @@ -897,13 +899,23 @@ "fieldname": "apply_tds", "fieldtype": "Check", "label": "Apply TDS" + }, + { + "fieldname": "wip_composite_asset", + "fieldtype": "Link", + "label": "WIP Composite Asset", + "options": "Asset" + }, + { + "fieldname": "column_break_fyqr", + "fieldtype": "Column Break" } ], "idx": 1, "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2023-09-13 16:22:40.825092", + "modified": "2023-10-27 15:50:42.655573", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order Item", diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py index 311b0ed8ce9..659bc42f0a6 100644 --- a/erpnext/stock/doctype/material_request/material_request.py +++ b/erpnext/stock/doctype/material_request/material_request.py @@ -405,6 +405,7 @@ def make_purchase_order(source_name, target_doc=None, args=None): ["uom", "uom"], ["sales_order", "sales_order"], ["sales_order_item", "sales_order_item"], + ["wip_composite_asset", "wip_composite_asset"], ], "postprocess": update_item, "condition": select_item, diff --git a/erpnext/stock/doctype/material_request_item/material_request_item.json b/erpnext/stock/doctype/material_request_item/material_request_item.json index 770dacdd19c..f0e117ef48d 100644 --- a/erpnext/stock/doctype/material_request_item/material_request_item.json +++ b/erpnext/stock/doctype/material_request_item/material_request_item.json @@ -37,6 +37,10 @@ "rate", "col_break3", "amount", + "accounting_details_section", + "expense_account", + "column_break_glru", + "wip_composite_asset", "manufacture_details", "manufacturer", "manufacturer_part_no", @@ -50,11 +54,10 @@ "lead_time_date", "sales_order", "sales_order_item", + "col_break4", "production_plan", "material_request_plan_item", "job_card_item", - "col_break4", - "expense_account", "section_break_46", "page_break" ], @@ -455,13 +458,28 @@ "label": "Job Card Item", "no_copy": 1, "print_hide": 1 + }, + { + "fieldname": "accounting_details_section", + "fieldtype": "Section Break", + "label": "Accounting Details" + }, + { + "fieldname": "column_break_glru", + "fieldtype": "Column Break" + }, + { + "fieldname": "wip_composite_asset", + "fieldtype": "Link", + "label": "WIP Composite Asset", + "options": "Asset" } ], "idx": 1, "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2023-05-07 20:23:31.250252", + "modified": "2023-10-27 15:53:41.444236", "modified_by": "Administrator", "module": "Stock", "name": "Material Request Item", diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index ec448b619a7..cde3dd6d412 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -1039,6 +1039,7 @@ def make_purchase_invoice(source_name, target_doc=None): "is_fixed_asset": "is_fixed_asset", "asset_location": "asset_location", "asset_category": "asset_category", + "wip_composite_asset": "wip_composite_asset", }, "postprocess": update_item, "filter": lambda d: get_pending_qty(d)[0] <= 0 From fabcfc1fce9231a8c15d1aee96227697ce92c9b8 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sun, 29 Oct 2023 12:16:34 +0530 Subject: [PATCH 063/151] fix(minor): set tax values for item variants (backport #37674) (#37738) * fix: copy all child fields to item variant (cherry picked from commit 5deba1b6f9b03ce5d078d624e339f6b0209a1555) * fix: only update if variant table empty (cherry picked from commit d436a407390c0e0d89c66445539bbb95784be7eb) --------- Co-authored-by: Gursheen Anand --- erpnext/stock/doctype/item/item.py | 2 +- erpnext/stock/get_item_details.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index 693d33ffb71..e1dd481b075 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -280,7 +280,7 @@ class Item(Document): # add item taxes from template for d in template.get("taxes"): - self.append("taxes", {"item_tax_template": d.item_tax_template}) + self.append("taxes", d) # copy re-order table if empty if not self.get("reorder_levels"): diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index e3fa5a0e7b1..990e03daa1b 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -302,7 +302,7 @@ def get_basic_details(args, item, overwrite_warehouse=True): if not item: item = frappe.get_doc("Item", args.get("item_code")) - if item.variant_of: + if item.variant_of and not item.taxes: item.update_template_tables() item_defaults = get_item_defaults(item.name, args.company) From b605b08ec1e6abb4db68a27b93ee42e4cb49b04f Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 30 Oct 2023 10:10:16 +0530 Subject: [PATCH 064/151] refactor: remove extraneous disabled filters (backport #37732) (#37748) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: remove extraneous disabled filters (cherry picked from commit f276fbba4f84979e12b8091492be7eddbf0caa56) # Conflicts: # erpnext/accounts/report/profitability_analysis/profitability_analysis.js # erpnext/public/js/controllers/accounts.js * chore: `conflicts` --------- Co-authored-by: Bernd Oliver Sünderhauf <46800703+bosue@users.noreply.github.com> Co-authored-by: s-aga-r --- .../profitability_analysis/profitability_analysis.js | 7 ------- erpnext/assets/doctype/asset/asset.js | 1 - .../supplier_quotation_comparison.js | 5 ----- .../report/bom_operations_time/bom_operations_time.js | 2 +- erpnext/public/js/controllers/accounts.js | 1 - erpnext/stock/doctype/item_price/item_price.js | 1 - .../doctype/stock_reconciliation/stock_reconciliation.js | 7 ------- erpnext/support/doctype/issue/issue.js | 7 ------- 8 files changed, 1 insertion(+), 30 deletions(-) diff --git a/erpnext/accounts/report/profitability_analysis/profitability_analysis.js b/erpnext/accounts/report/profitability_analysis/profitability_analysis.js index 27b29baa40a..c4054a977a8 100644 --- a/erpnext/accounts/report/profitability_analysis/profitability_analysis.js +++ b/erpnext/accounts/report/profitability_analysis/profitability_analysis.js @@ -33,13 +33,6 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { "label": __("Accounting Dimension"), "fieldtype": "Link", "options": "Accounting Dimension", - "get_query": () =>{ - return { - filters: { - "disabled": 0 - } - } - } }, { "fieldname": "fiscal_year", diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js index 5d7794e1bcc..951e612bea0 100644 --- a/erpnext/assets/doctype/asset/asset.js +++ b/erpnext/assets/doctype/asset/asset.js @@ -9,7 +9,6 @@ frappe.ui.form.on('Asset', { frm.set_query("item_code", function() { return { "filters": { - "disabled": 0, "is_fixed_asset": 1, "is_stock_item": 0 } diff --git a/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.js b/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.js index fd73b870c59..579c0a65ad9 100644 --- a/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.js +++ b/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.js @@ -44,11 +44,6 @@ frappe.query_reports["Supplier Quotation Comparison"] = { } } } - else { - return { - filters: { "disabled": 0 } - } - } } }, { diff --git a/erpnext/manufacturing/report/bom_operations_time/bom_operations_time.js b/erpnext/manufacturing/report/bom_operations_time/bom_operations_time.js index 0eb22a22f73..30974f170c4 100644 --- a/erpnext/manufacturing/report/bom_operations_time/bom_operations_time.js +++ b/erpnext/manufacturing/report/bom_operations_time/bom_operations_time.js @@ -12,7 +12,7 @@ frappe.query_reports["BOM Operations Time"] = { "options": "Item", "get_query": () =>{ return { - filters: { "disabled": 0, "is_stock_item": 1 } + filters: { "is_stock_item": 1 } } } }, diff --git a/erpnext/public/js/controllers/accounts.js b/erpnext/public/js/controllers/accounts.js index 47b88a002bc..72845b20742 100644 --- a/erpnext/public/js/controllers/accounts.js +++ b/erpnext/public/js/controllers/accounts.js @@ -28,7 +28,6 @@ frappe.ui.form.on(cur_frm.doctype, { filters: { "account_type": account_type, "company": doc.company, - "disabled": 0 } } }); diff --git a/erpnext/stock/doctype/item_price/item_price.js b/erpnext/stock/doctype/item_price/item_price.js index ce489ff52b4..8a4b4eef0ae 100644 --- a/erpnext/stock/doctype/item_price/item_price.js +++ b/erpnext/stock/doctype/item_price/item_price.js @@ -6,7 +6,6 @@ frappe.ui.form.on("Item Price", { frm.set_query("item_code", function() { return { filters: { - "disabled": 0, "has_variants": 0 } }; diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js index 05dd105d99d..1415aa36164 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js @@ -95,13 +95,6 @@ frappe.ui.form.on("Stock Reconciliation", { fieldname: "item_code", fieldtype: "Link", options: "Item", - "get_query": function() { - return { - "filters": { - "disabled": 0, - } - }; - } }, { label: __("Ignore Empty Stock"), diff --git a/erpnext/support/doctype/issue/issue.js b/erpnext/support/doctype/issue/issue.js index d4daacd4ea4..f96823b2908 100644 --- a/erpnext/support/doctype/issue/issue.js +++ b/erpnext/support/doctype/issue/issue.js @@ -1,13 +1,6 @@ frappe.ui.form.on("Issue", { onload: function(frm) { frm.email_field = "raised_by"; - frm.set_query("customer", function () { - return { - filters: { - "disabled": 0 - } - }; - }); frappe.db.get_value("Support Settings", {name: "Support Settings"}, ["allow_resetting_service_level_agreement", "track_service_level_agreement"], (r) => { From f1407bcfd2b93af9daa2d8e9befa71f7f1004090 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Mon, 30 Oct 2023 13:53:48 +0530 Subject: [PATCH 065/151] fix: negative current qty causing recursion issue (#37752) --- .../stock/doctype/stock_reconciliation/stock_reconciliation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index e469291eac9..d37b2a67e11 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -647,7 +647,7 @@ class StockReconciliation(StockController): {"voucher_detail_no": row.name, "actual_qty": ("<", 0), "is_cancelled": 0}, "name", ) - and current_qty + and current_qty > 0 ): new_sle = self.get_sle_for_items(row) new_sle.actual_qty = current_qty * -1 From 20ca948e6b4acc4fdc056593ee9769139ca6d302 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 30 Oct 2023 14:23:39 +0530 Subject: [PATCH 066/151] fix: negative current qty causing recursion issue (backport #37752) (#37753) fix: negative current qty causing recursion issue (#37752) (cherry picked from commit f1407bcfd2b93af9daa2d8e9befa71f7f1004090) Co-authored-by: rohitwaghchaure --- .../stock/doctype/stock_reconciliation/stock_reconciliation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index e469291eac9..d37b2a67e11 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -647,7 +647,7 @@ class StockReconciliation(StockController): {"voucher_detail_no": row.name, "actual_qty": ("<", 0), "is_cancelled": 0}, "name", ) - and current_qty + and current_qty > 0 ): new_sle = self.get_sle_for_items(row) new_sle.actual_qty = current_qty * -1 From 78a9edf6c906171c3392a0fccd3682ddb8955e99 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Mon, 30 Oct 2023 08:55:38 +0000 Subject: [PATCH 067/151] chore(release): Bumped to Version 14.45.4 ## [14.45.4](https://github.com/frappe/erpnext/compare/v14.45.3...v14.45.4) (2023-10-30) ### Bug Fixes * negative current qty causing recursion issue (backport [#37752](https://github.com/frappe/erpnext/issues/37752)) ([#37753](https://github.com/frappe/erpnext/issues/37753)) ([20ca948](https://github.com/frappe/erpnext/commit/20ca948e6b4acc4fdc056593ee9769139ca6d302)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 2e1746c3c80..63d7c201a14 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "14.45.3" +__version__ = "14.45.4" def get_default_company(user=None): From 712ddb75becd71c6554da69c18e5b794c54f4faf Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 30 Oct 2023 15:20:26 +0530 Subject: [PATCH 068/151] fix: make changes that enable gantt view for job cards (backport #37661) (#37756) fix: make changes that enable gantt view for job cards (#37661) * fix: make changes that enable gantt view for job cards * fix: add fields on listview and remove from json file * fix: undo modified date --------- Co-authored-by: Dietmar Fischer (cherry picked from commit 500435b856a028bdab7fdbe12647ec0f11287eab) Co-authored-by: Didiman1998 <118364772+Didiman1998@users.noreply.github.com> --- erpnext/manufacturing/doctype/job_card/job_card_calendar.js | 4 ++-- erpnext/manufacturing/doctype/job_card/job_card_list.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/manufacturing/doctype/job_card/job_card_calendar.js b/erpnext/manufacturing/doctype/job_card/job_card_calendar.js index f4877fdca0b..9e320853514 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card_calendar.js +++ b/erpnext/manufacturing/doctype/job_card/job_card_calendar.js @@ -10,8 +10,8 @@ frappe.views.calendar["Job Card"] = { }, gantt: { field_map: { - "start": "started_time", - "end": "started_time", + "start": "expected_start_date", + "end": "expected_end_date", "id": "name", "title": "subject", "color": "color", diff --git a/erpnext/manufacturing/doctype/job_card/job_card_list.js b/erpnext/manufacturing/doctype/job_card/job_card_list.js index 5d883bf9fa7..99fca9570f7 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card_list.js +++ b/erpnext/manufacturing/doctype/job_card/job_card_list.js @@ -1,6 +1,6 @@ frappe.listview_settings['Job Card'] = { has_indicator_for_draft: true, - + add_fields: ["expected_start_date", "expected_end_date"], get_indicator: function(doc) { const status_colors = { "Work In Progress": "orange", From 4c01128827a6a51ff570bce54369b23a01ae7da6 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 27 Oct 2023 12:37:37 +0530 Subject: [PATCH 069/151] refactor: ignore cancelled GLE's while looking for currency (cherry picked from commit 8d9b90f3f53083e631444a0d39d86bda0b08f479) --- erpnext/accounts/party.py | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index 150b56742e4..7903beec7cf 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -5,7 +5,7 @@ from typing import Optional import frappe -from frappe import _, msgprint, scrub +from frappe import _, msgprint, qb, scrub from frappe.contacts.doctype.address.address import get_company_address, get_default_address from frappe.core.doctype.user_permission.user_permission import get_permitted_documents from frappe.model.utils import get_fetch_values @@ -459,11 +459,19 @@ def get_party_account_currency(party_type, party, company): def get_party_gle_currency(party_type, party, company): def generator(): - existing_gle_currency = frappe.db.sql( - """select account_currency from `tabGL Entry` - where docstatus=1 and company=%(company)s and party_type=%(party_type)s and party=%(party)s - limit 1""", - {"company": company, "party_type": party_type, "party": party}, + gl = qb.DocType("GL Entry") + existing_gle_currency = ( + qb.from_(gl) + .select(gl.account_currency) + .where( + (gl.docstatus == 1) + & (gl.company == company) + & (gl.party_type == party_type) + & (gl.party == party) + & (gl.is_cancelled == 0) + ) + .limit(1) + .run() ) return existing_gle_currency[0][0] if existing_gle_currency else None From 69e83ff6ab47c8d592a2ea25b2190c1f923af5e8 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 30 Oct 2023 16:15:05 +0530 Subject: [PATCH 070/151] chore: add index to posting_date in PLE (cherry picked from commit ca698452382eba85bd940dfb6344ff19d1eab4e4) --- .../doctype/payment_ledger_entry/payment_ledger_entry.json | 5 +++-- 1 file changed, 3 insertions(+), 2 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 22842cec0fe..6da2be986bb 100644 --- a/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.json +++ b/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.json @@ -29,7 +29,8 @@ { "fieldname": "posting_date", "fieldtype": "Date", - "label": "Posting Date" + "label": "Posting Date", + "search_index": 1 }, { "fieldname": "account_type", @@ -147,7 +148,7 @@ "in_create": 1, "index_web_pages_for_search": 1, "links": [], - "modified": "2022-08-22 15:32:56.629430", + "modified": "2023-10-30 16:15:00.470283", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Ledger Entry", From 25448fcdbc497a5ac3eff58bb4b897b71a19fda0 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 30 Oct 2023 16:15:05 +0530 Subject: [PATCH 071/151] chore: add index to posting_date in PLE (cherry picked from commit ca698452382eba85bd940dfb6344ff19d1eab4e4) --- .../doctype/payment_ledger_entry/payment_ledger_entry.json | 5 +++-- 1 file changed, 3 insertions(+), 2 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 22842cec0fe..6da2be986bb 100644 --- a/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.json +++ b/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.json @@ -29,7 +29,8 @@ { "fieldname": "posting_date", "fieldtype": "Date", - "label": "Posting Date" + "label": "Posting Date", + "search_index": 1 }, { "fieldname": "account_type", @@ -147,7 +148,7 @@ "in_create": 1, "index_web_pages_for_search": 1, "links": [], - "modified": "2022-08-22 15:32:56.629430", + "modified": "2023-10-30 16:15:00.470283", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Ledger Entry", From 9b66a06c8602964cab2440788b9b130219536517 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 31 Oct 2023 14:31:38 +0530 Subject: [PATCH 072/151] fix: indexing on Delivery Note Item (backport #37766) (#37777) fix: indexing on Delivery Note Item (#37766) fix: added indexing on Delivery Note Item (cherry picked from commit 056b74b162ed58dd979cd9748129752fb8cab242) Co-authored-by: rohitwaghchaure --- .../doctype/purchase_receipt_item/purchase_receipt_item.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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 d7b1660c55f..47883bbf8a7 100644 --- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json +++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json @@ -923,7 +923,8 @@ "label": "Delivery Note Item", "no_copy": 1, "print_hide": 1, - "read_only": 1 + "read_only": 1, + "search_index": 1 }, { "collapsible": 1, @@ -1052,7 +1053,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2023-10-19 10:50:58.071735", + "modified": "2023-10-30 17:32:24.560337", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt Item", From 25718d9f1b7cda3b87263c2cf885958cbd283947 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Tue, 31 Oct 2023 15:12:35 +0530 Subject: [PATCH 073/151] fix: incorrect material request quantity in production plan (#37785) --- .../production_plan/production_plan.py | 5 +++- .../production_plan/test_production_plan.py | 27 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index 5fc764fb6f7..70bdcccdb8a 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -1734,7 +1734,10 @@ def get_raw_materials_of_sub_assembly_items( if not item.conversion_factor and item.purchase_uom: item.conversion_factor = get_uom_conversion_factor(item.item_code, item.purchase_uom) - item_details.setdefault(item.get("item_code"), item) + if details := item_details.get(item.get("item_code")): + details.qty += item.get("qty") + else: + item_details.setdefault(item.get("item_code"), item) return item_details diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py index dbd3083ab58..5042ae9b5fc 100644 --- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py @@ -1321,6 +1321,33 @@ class TestProductionPlan(FrappeTestCase): self.assertTrue(row.warehouse == mrp_warhouse) self.assertEqual(row.quantity, 12) + def test_mr_qty_for_same_rm_with_different_sub_assemblies(self): + from erpnext.manufacturing.doctype.bom.test_bom import create_nested_bom + + bom_tree = { + "Fininshed Goods2 For SUB Test": { + "SubAssembly2 For SUB Test": {"ChildPart2 For SUB Test": {}}, + "SubAssembly3 For SUB Test": {"ChildPart2 For SUB Test": {}}, + } + } + + parent_bom = create_nested_bom(bom_tree, prefix="") + plan = create_production_plan( + item_code=parent_bom.item, + planned_qty=1, + ignore_existing_ordered_qty=1, + do_not_submit=1, + skip_available_sub_assembly_item=1, + warehouse="_Test Warehouse - _TC", + ) + + plan.get_sub_assembly_items() + plan.make_material_request() + + for row in plan.mr_items: + if row.item_code == "ChildPart2 For SUB Test": + self.assertEqual(row.quantity, 2) + def create_production_plan(**args): """ From 86cf156968de8e6514c71bfdfda99fb81c8c05b8 Mon Sep 17 00:00:00 2001 From: Samuel Danieli <23150094+scdanieli@users.noreply.github.com> Date: Tue, 31 Oct 2023 11:37:10 +0100 Subject: [PATCH 074/151] fix: set correct `purchase_sle` in `get_last_sle()` (#37708) sle_dict may look like this: { 'incoming': [ {... Stock Entry ...}, {... Purchase Receipt ...} ], 'outgoing': [ {... Stock Entry ...} ] } --- erpnext/stock/doctype/serial_no/serial_no.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py index a9989956ebb..71b2faa41de 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.py +++ b/erpnext/stock/doctype/serial_no/serial_no.py @@ -191,7 +191,7 @@ class SerialNo(StockController): sle_dict = self.get_stock_ledger_entries(serial_no) if sle_dict: if sle_dict.get("incoming", []): - entries["purchase_sle"] = sle_dict["incoming"][0] + entries["purchase_sle"] = sle_dict["incoming"][-1] if len(sle_dict.get("incoming", [])) - len(sle_dict.get("outgoing", [])) > 0: entries["last_sle"] = sle_dict["incoming"][0] From 0b1da08d7821c213461068b38f5cfe3ea8c1b653 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 31 Oct 2023 18:53:37 +0530 Subject: [PATCH 075/151] fix: In-Transit Warehouse company filter (backport #37796) (#37797) fix: In-Transit Warehouse company filter (#37796) (cherry picked from commit daf2ec063c4b0313e32e247459d0cb8dc6958833) Co-authored-by: hyaray --- erpnext/setup/doctype/company/company.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js index 15f32b15c5e..589b3fe8b98 100644 --- a/erpnext/setup/doctype/company/company.js +++ b/erpnext/setup/doctype/company/company.js @@ -40,7 +40,7 @@ frappe.ui.form.on("Company", { filters:{ 'warehouse_type' : 'Transit', 'is_group': 0, - 'company': frm.doc.company + 'company': frm.doc.company_name } }; }); From 3276f39740ebec834084f3f8be4cec3df678dabb Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 31 Oct 2023 17:30:42 +0530 Subject: [PATCH 076/151] refactor: pull remarks only if needed on AR/AP report (cherry picked from commit eb73017798eed964e8f019db4cef3513162855f6) --- .../report/accounts_receivable/accounts_receivable.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index b9c7a0bfb87..20444f94964 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -718,6 +718,7 @@ class ReceivablePayableReport(object): query = ( qb.from_(ple) .select( + ple.name, ple.account, ple.voucher_type, ple.voucher_no, @@ -731,13 +732,15 @@ class ReceivablePayableReport(object): ple.account_currency, ple.amount, ple.amount_in_account_currency, - ple.remarks, ) .where(ple.delinked == 0) .where(Criterion.all(self.qb_selection_filter)) .where(Criterion.any(self.or_filters)) ) + if self.filters.get("show_remarks"): + query = query.select(ple.remarks) + if self.filters.get("group_by_party"): query = query.orderby(self.ple.party, self.ple.posting_date) else: From fdf2977ce26bab03602f1b6f60a1a09f3e85b210 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 31 Oct 2023 17:47:02 +0530 Subject: [PATCH 077/151] chore: update default limit values in reconciliation tool (cherry picked from commit 1fd888175f2f224bcebba3e9a958d672ab09bcd5) --- erpnext/patches.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 81b14e89b67..d7e3d5af4b7 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -344,5 +344,7 @@ erpnext.patches.v14_0.migrate_deferred_accounts_to_item_defaults erpnext.patches.v14_0.create_accounting_dimensions_in_sales_order_item erpnext.patches.v14_0.rename_over_order_allowance_field erpnext.patches.v14_0.migrate_delivery_stop_lock_field +execute:frappe.db.set_single_value("Payment Reconciliation", "invoice_limit", 50) +execute:frappe.db.set_single_value("Payment Reconciliation", "payment_limit", 50) # below migration patch should always run last erpnext.patches.v14_0.migrate_gl_to_payment_ledger From 7df4009dd37de91fe093ea39ef8a642120716a44 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 29 Oct 2023 10:18:47 +0530 Subject: [PATCH 078/151] fix: fetch asset received but not billed account only when needed (cherry picked from commit 3a8736374cfd73d828cd98936900cef1ec78fdb7) --- erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 5f7de3d98a5..aad1eeabd79 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -592,7 +592,6 @@ class PurchaseInvoice(BuyingController): def get_gl_entries(self, warehouse_account=None): self.auto_accounting_for_stock = erpnext.is_perpetual_inventory_enabled(self.company) - self.asset_received_but_not_billed = self.get_company_default("asset_received_but_not_billed") if self.auto_accounting_for_stock: self.stock_received_but_not_billed = self.get_company_default("stock_received_but_not_billed") @@ -946,10 +945,11 @@ class PurchaseInvoice(BuyingController): ) stock_rbnb = ( - self.asset_received_but_not_billed + self.get_company_default("asset_received_but_not_billed") if item.is_fixed_asset else self.stock_received_but_not_billed ) + if not negative_expense_booked_in_pr: gl_entries.append( self.get_gl_dict( From d8e743013f95157bf5a90d65ba5fcf4c5f8c6156 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Wed, 25 Oct 2023 13:59:37 +0530 Subject: [PATCH 079/151] fix: status for over delivery or billing (cherry picked from commit d69b0d76dd2d89fe0c1b9a634923cf8716a37c11) --- erpnext/controllers/status_updater.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py index 73a248fb531..d09001c8fc1 100644 --- a/erpnext/controllers/status_updater.py +++ b/erpnext/controllers/status_updater.py @@ -47,15 +47,15 @@ status_map = { ], [ "To Bill", - "eval:(self.per_delivered == 100 or self.skip_delivery_note) and self.per_billed < 100 and self.docstatus == 1", + "eval:(self.per_delivered >= 100 or self.skip_delivery_note) and self.per_billed < 100 and self.docstatus == 1", ], [ "To Deliver", - "eval:self.per_delivered < 100 and self.per_billed == 100 and self.docstatus == 1 and not self.skip_delivery_note", + "eval:self.per_delivered < 100 and self.per_billed >= 100 and self.docstatus == 1 and not self.skip_delivery_note", ], [ "Completed", - "eval:(self.per_delivered == 100 or self.skip_delivery_note) and self.per_billed == 100 and self.docstatus == 1", + "eval:(self.per_delivered >= 100 or self.skip_delivery_note) and self.per_billed >= 100 and self.docstatus == 1", ], ["Cancelled", "eval:self.docstatus==2"], ["Closed", "eval:self.status=='Closed' and self.docstatus != 2"], From 4a225b069bbbb45541749479fcf3717b20053122 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Mon, 23 Oct 2023 11:56:28 +0530 Subject: [PATCH 080/151] fix: validate so item with qtn (cherry picked from commit 17ebc1ea8096a83e882bedd00f358a4bd799eff4) --- erpnext/selling/doctype/sales_order/sales_order.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 25ea9189553..914adbb7906 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -204,7 +204,15 @@ class SalesOrder(SellingController): def validate_with_previous_doc(self): super(SalesOrder, self).validate_with_previous_doc( - {"Quotation": {"ref_dn_field": "prevdoc_docname", "compare_fields": [["company", "="]]}} + { + "Quotation": {"ref_dn_field": "prevdoc_docname", "compare_fields": [["company", "="]]}, + "Quotation Item": { + "ref_dn_field": "quotation_item", + "compare_fields": [["item_code", "="], ["uom", "="], ["conversion_factor", "="]], + "is_child_table": True, + "allow_duplicate_prev_row_id": True, + }, + } ) if cint(frappe.db.get_single_value("Selling Settings", "maintain_same_sales_rate")): From 357697702be3126b6508e6755d67ac4aa58f668b Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 1 Nov 2023 14:11:36 +0530 Subject: [PATCH 081/151] chore: resolve conflicts --- .../doctype/plaid_settings/plaid_settings.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py index d37868ab681..a462141a86b 100644 --- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py +++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py @@ -238,11 +238,6 @@ def new_bank_transaction(transaction): deposit = abs(amount) withdrawal = 0.0 -<<<<<<< HEAD - status = "Pending" if transaction["pending"] == "True" else "Settled" - -======= ->>>>>>> 46ea868559 (fix(plaid): Do not sync pending transactions) tags = [] try: tags += transaction["category"] From b75aae99caa5175f16f90304f8b5b56a46ae4773 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Wed, 27 Sep 2023 18:51:09 +0200 Subject: [PATCH 082/151] fix: german translations of Accounts Settings (cherry picked from commit b3486b43c45df03697b13a4f0fc655d70bc52407) # Conflicts: # erpnext/translations/de.csv --- erpnext/translations/de.csv | 80 ++++++++++++++++++++++++++++++++++--- 1 file changed, 74 insertions(+), 6 deletions(-) diff --git a/erpnext/translations/de.csv b/erpnext/translations/de.csv index 4cd4f0edf15..44adc555df4 100644 --- a/erpnext/translations/de.csv +++ b/erpnext/translations/de.csv @@ -3671,7 +3671,7 @@ Cannot Optimize Route as Driver Address is Missing.,"Route kann nicht optimiert Cannot complete task {0} as its dependant task {1} are not ccompleted / cancelled.,"Aufgabe {0} kann nicht abgeschlossen werden, da die abhängige Aufgabe {1} nicht abgeschlossen / abgebrochen wurde.", Cannot create loan until application is approved,"Darlehen kann erst erstellt werden, wenn der Antrag genehmigt wurde", Cannot find a matching Item. Please select some other value for {0}.,Ein passender Artikel kann nicht gefunden werden. Bitte einen anderen Wert für {0} auswählen., -"Cannot overbill for Item {0} in row {1} more than {2}. To allow over-billing, please set allowance in Accounts Settings","Artikel {0} in Zeile {1} kann nicht mehr als {2} in Rechnung gestellt werden. Um eine Überberechnung zuzulassen, legen Sie die Überberechnung in den Kontoeinstellungen fest", +"Cannot overbill for Item {0} in row {1} more than {2}. To allow over-billing, please set allowance in Accounts Settings","Artikel {0} in Zeile {1} kann nicht mehr als {2} in Rechnung gestellt werden. Um eine Überberechnung zuzulassen, legen Sie die Überberechnung in den Buchhaltungseinstellungen fest", "Capacity Planning Error, planned start time can not be same as end time","Kapazitätsplanungsfehler, die geplante Startzeit darf nicht mit der Endzeit übereinstimmen", Categories,Kategorien, Changes in {0},Änderungen in {0}, @@ -4125,7 +4125,7 @@ This page keeps track of your items in which buyers have showed some interest.," Thursday,Donnerstag, Timing,Zeitliche Koordinierung, Title,Bezeichnung, -"To allow over billing, update ""Over Billing Allowance"" in Accounts Settings or the Item.","Aktualisieren Sie "Over Billing Allowance" in den Kontoeinstellungen oder im Artikel, um eine Überberechnung zuzulassen.", +"To allow over billing, update ""Over Billing Allowance"" in Accounts Settings or the Item.","Aktualisieren Sie "Over Billing Allowance" in den Buchhaltungseinstellungen oder im Artikel, um eine Überberechnung zuzulassen.", "To allow over receipt / delivery, update ""Over Receipt/Delivery Allowance"" in Stock Settings or the Item.","Um eine Überbestätigung / Überlieferung zu ermöglichen, aktualisieren Sie "Überbestätigung / Überlieferung" in den Lagereinstellungen oder im Artikel.", To date needs to be before from date,Das Datum muss vor dem Datum liegen, Total,Summe, @@ -4533,8 +4533,8 @@ Mandatory For Profit and Loss Account,Obligatorisch für Gewinn- und Verlustrech Accounting Period,Abrechnungszeitraum, Period Name,Zeitraumname, Closed Documents,Geschlossene Dokumente, -Accounts Settings,Konteneinstellungen, -Settings for Accounts,Konteneinstellungen, +Accounts Settings,Buchhaltungseinstellungen, +Settings for Accounts,Einstellungen für das Buchhaltungsmodul, Make Accounting Entry For Every Stock Movement,Eine Buchung für jede Lagerbewegung erstellen, Users with this role are allowed to set frozen accounts and create / modify accounting entries against frozen accounts,Benutzer mit dieser Rolle sind berechtigt Konten zu sperren und Buchungen zu gesperrten Konten zu erstellen/verändern, Determine Address Tax Category From,Adresssteuerkategorie bestimmen von, @@ -8622,7 +8622,7 @@ Template Title,Vorlagentitel, 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, +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 Buchhaltungseinstellungen und versuchen Sie es erneut, End date cannot be before start date,Das Enddatum darf nicht vor dem Startdatum liegen, Total Counts Targeted,Gesamtzahl der anvisierten Zählungen, Total Counts Completed,Gesamtzahl der abgeschlossenen Zählungen, @@ -8924,7 +8924,7 @@ Checking this will automatically create a Sales Invoice whenever an appointment Healthcare Service Items,Artikel im Gesundheitswesen, "You can create a service item for Inpatient Visit Charge and set it here. Similarly, you can set up other Healthcare Service Items for billing in this section. Click ",Sie können ein Serviceelement für die Gebühr für stationäre Besuche erstellen und hier festlegen. Ebenso können Sie in diesem Abschnitt andere Gesundheitsposten für die Abrechnung einrichten. Klicken, Set up default Accounts for the Healthcare Facility,Richten Sie Standardkonten für die Gesundheitseinrichtung ein, -"If you wish to override default accounts settings and configure the Income and Receivable accounts for Healthcare, you can do so here.","Wenn Sie die Standardkonteneinstellungen überschreiben und die Einkommens- und Debitorenkonten für das Gesundheitswesen konfigurieren möchten, können Sie dies hier tun.", +"If you wish to override default accounts settings and configure the Income and Receivable accounts for Healthcare, you can do so here.","Wenn Sie die Buchhaltungseinstellungen überschreiben und spezielle Einkommens- und Debitorenkonten für das Gesundheitswesen konfigurieren möchten, können Sie dies hier tun.", Out Patient SMS alerts,Out Patient SMS-Benachrichtigungen, "If you want to send SMS alert on Patient Registration, you can enable this option. Similary, you can set up Out Patient SMS alerts for other functionalities in this section. Click ","Wenn Sie bei der Patientenregistrierung eine SMS-Benachrichtigung senden möchten, können Sie diese Option aktivieren. Ebenso können Sie in diesem Abschnitt SMS-Benachrichtigungen für andere Patienten einrichten. Klicken", Admission Order Details,Details zur Zulassungsbestellung, @@ -9931,3 +9931,71 @@ Select Alternative Items for Sales Order,Alternativpositionen für Auftragsbest Select an item from each set to be used in the Sales Order.,"Wählen Sie aus den Alternativen jeweils einen Artikel aus, der in die Auftragsbestätigung übernommen werden soll.", Is Alternative,Ist Alternative, Alternative Items,Alternativpositionen, +<<<<<<< HEAD +======= +Add Template,Vorlage einfügen, +Add Comment,Kommentar hinzufügen, +More...,Mehr..., +Notes,Hinweise, +Payment Gateway,Zahlungs-Gateways, +Payment Gateway Name,Name des Zahlungsgateways, +Payments,Zahlungen, +Plan Name,Planname, +Portal,Portal, +Scan Barcode,Barcode scannen, +Some information is missing,Einige Informationen fehlen, +Successful,Erfolgreich, +Tools,Werkzeuge, +Use Sandbox,Sandkastenmodus verwenden, +Busy,Beschäftigt, +Completed By,Vervollständigt von, +Is Template, Ist Vorlage, +Payment Failed,Bezahlung fehlgeschlagen, +Column {0},Spalte {0}, +Field Mapping,Feldzuordnung, +Not Specified,Keine Angabe, +Update Type,Aktualisierungsart, +Dr,Soll, +End Time,Endzeit, +Fetching...,Abrufen ..., +"It seems that there is an issue with the server's stripe configuration. In case of failure, the amount will get refunded to your account.","Es scheint, dass ein Problem mit der Stripe-Konfiguration des Servers vorliegt. Im Falle eines Fehlers wird der Betrag Ihrem Konto gutgeschrieben.", +Looks like someone sent you to an incomplete URL. Please ask them to look into it.,"Sieht aus wie jemand, den Sie zu einer unvollständigen URL gesendet. Bitte fragen Sie sie, sich in sie.", +Master,Vorlage, +Pay,Zahlen, +You can also copy-paste this link in your browser,Sie können diese Verknüpfung in Ihren Browser kopieren, +Verified By,Überprüft von, +Invalid naming series (. missing) for {0},Ungültige Namensreihe (. Fehlt) für {0}, +Phone Number,Telefonnummer, +Account SID,Konto-SID, +Global Defaults,Allgemeine Voreinstellungen, +Is Mandatory,Ist obligatorisch, +WhatsApp,WhatsApp, +Make a call,Einen Anruf tätigen, +Enable Automatic Party Matching,Automatisches Zuordnen von Parteien aktivieren, +Auto match and set the Party in Bank Transactions,"Partei automatisch anhand der Kontonummer bzw. IBAN zuordnen", +Enable Fuzzy Matching,Unscharfe Zuordnung aktivieren, +Approximately match the description/party name against parties,"Partei automatisch anhand grober Übereinstimmung des Names zuordnen" +Accounts Closing,Kontoabschluss, +Period Closing Settings,Periodenabschlusseinstellungen, +Ignore Account Closing Balance,Saldo des Kontos zum Periodenabschluss ignorieren, +Financial reports will be generated using GL Entry doctypes (should be enabled if Period Closing Voucher is not posted for all years sequentially or missing),"Finanzberichte werden anhand des Hauptbuchs erstellt (sollte aktiviert sein, wenn Periodenabschlüsse fehlen oder nicht sequentiell für alle Jahre gebucht werden)", +Asset Settings,Vermögenswerteinstellungen, +POS Setting,POS-Einstellungen, +Create Ledger Entries for Change Amount,Buchungssätze für Wechselgeld erstellen, +"If enabled, ledger entries will be posted for change amount in POS transactions","Wenn aktiviert, werden Buchungssätze für Wechselgeld in POS-Transaktionen erstellt", +Credit Limit Settings,Kreditlimit-Einstellungen, +Role Allowed to Over Bill, +Users with this role are allowed to over bill above the allowance percentage,"Roll, die mehr als den erlaubten Prozentsatz zusätzlich abrechnen darf", +Role allowed to bypass Credit Limit,"Rolle, die das Kreditlimit umgehen darf", +Invoice Cancellation,Rechnungsstornierung, +Delete Accounting and Stock Ledger Entries on deletion of Transaction,Beim Löschen einer Transaktion auch die entsprechenden Buchungs- und Lagerbuchungssätze löschen, +Enable Common Party Accounting,Verknüpfung von Kunden und Liefeanten erlauben, +Allow multi-currency invoices against single party account,Rechnungsbeträge in Fremdwährungen dürfen umgerechnet und in der Hauptwährung gebucht werden, +Enabling this will allow creation of multi-currency invoices against single party account in company currency,Bei Aktivierung können Rechnungen in Fremdwährungen gegen ein Konto in der Hauptwährung gebucht werden, +Payment Terms from orders will be fetched into the invoices as is,Die Zahlungsbedingungen aus einem Auftragen werden eins zu eins in die Rechnungen übernommen, +Automatically Fetch Payment Terms from Order,Zahlungsbedingungen aus Auftrag in die Rechnung übernehmen, +Enable Custom Cash Flow Format,Individuelles Cashflow-Format aktivieren, +Tax Settings,Umsatzsteuer-Einstellungen, +Book Tax Loss on Early Payment Discount,Umsatzsteueranteil bei Skonto berücksichtigen, +Split Early Payment Discount Loss into Income and Tax Loss,"Skontobetrag in Aufwand und Umsatzsteuerkorrektur aufteilen", +>>>>>>> b3486b43c4 (fix: german translations of Accounts Settings) From 4d2169e48e3738b21e16cb1418b3127df1b05883 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Tue, 3 Oct 2023 23:06:48 +0200 Subject: [PATCH 083/151] fix(translations): suggestions from review (cherry picked from commit 115f0242600a519209a7acd0e59fba434fd8c1c5) --- erpnext/translations/de.csv | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/erpnext/translations/de.csv b/erpnext/translations/de.csv index 44adc555df4..99630c2391a 100644 --- a/erpnext/translations/de.csv +++ b/erpnext/translations/de.csv @@ -3671,7 +3671,7 @@ Cannot Optimize Route as Driver Address is Missing.,"Route kann nicht optimiert Cannot complete task {0} as its dependant task {1} are not ccompleted / cancelled.,"Aufgabe {0} kann nicht abgeschlossen werden, da die abhängige Aufgabe {1} nicht abgeschlossen / abgebrochen wurde.", Cannot create loan until application is approved,"Darlehen kann erst erstellt werden, wenn der Antrag genehmigt wurde", Cannot find a matching Item. Please select some other value for {0}.,Ein passender Artikel kann nicht gefunden werden. Bitte einen anderen Wert für {0} auswählen., -"Cannot overbill for Item {0} in row {1} more than {2}. To allow over-billing, please set allowance in Accounts Settings","Artikel {0} in Zeile {1} kann nicht mehr als {2} in Rechnung gestellt werden. Um eine Überberechnung zuzulassen, legen Sie die Überberechnung in den Buchhaltungseinstellungen fest", +"Cannot overbill for Item {0} in row {1} more than {2}. To allow over-billing, please set allowance in Accounts Settings","Für Artikel {0} in Zeile {1} kann nicht mehr als {2} zusätzlich in Rechnung gestellt werden. Um diese Überfakturierung zuzulassen, passen Sie bitte die Grenzwerte in den Buchhaltungseinstellungen an.", "Capacity Planning Error, planned start time can not be same as end time","Kapazitätsplanungsfehler, die geplante Startzeit darf nicht mit der Endzeit übereinstimmen", Categories,Kategorien, Changes in {0},Änderungen in {0}, @@ -4534,7 +4534,7 @@ Accounting Period,Abrechnungszeitraum, Period Name,Zeitraumname, Closed Documents,Geschlossene Dokumente, Accounts Settings,Buchhaltungseinstellungen, -Settings for Accounts,Einstellungen für das Buchhaltungsmodul, +Settings for Accounts,Einstellungen für die Buchhaltung, Make Accounting Entry For Every Stock Movement,Eine Buchung für jede Lagerbewegung erstellen, Users with this role are allowed to set frozen accounts and create / modify accounting entries against frozen accounts,Benutzer mit dieser Rolle sind berechtigt Konten zu sperren und Buchungen zu gesperrten Konten zu erstellen/verändern, Determine Address Tax Category From,Adresssteuerkategorie bestimmen von, @@ -8924,7 +8924,7 @@ Checking this will automatically create a Sales Invoice whenever an appointment Healthcare Service Items,Artikel im Gesundheitswesen, "You can create a service item for Inpatient Visit Charge and set it here. Similarly, you can set up other Healthcare Service Items for billing in this section. Click ",Sie können ein Serviceelement für die Gebühr für stationäre Besuche erstellen und hier festlegen. Ebenso können Sie in diesem Abschnitt andere Gesundheitsposten für die Abrechnung einrichten. Klicken, Set up default Accounts for the Healthcare Facility,Richten Sie Standardkonten für die Gesundheitseinrichtung ein, -"If you wish to override default accounts settings and configure the Income and Receivable accounts for Healthcare, you can do so here.","Wenn Sie die Buchhaltungseinstellungen überschreiben und spezielle Einkommens- und Debitorenkonten für das Gesundheitswesen konfigurieren möchten, können Sie dies hier tun.", +"If you wish to override default accounts settings and configure the Income and Receivable accounts for Healthcare, you can do so here.","Wenn Sie die Standardkonteneinstellungen überschreiben und die Einkommens- und Debitorenkonten für das Gesundheitswesen konfigurieren möchten, können Sie dies hier tun.", Out Patient SMS alerts,Out Patient SMS-Benachrichtigungen, "If you want to send SMS alert on Patient Registration, you can enable this option. Similary, you can set up Out Patient SMS alerts for other functionalities in this section. Click ","Wenn Sie bei der Patientenregistrierung eine SMS-Benachrichtigung senden möchten, können Sie diese Option aktivieren. Ebenso können Sie in diesem Abschnitt SMS-Benachrichtigungen für andere Patienten einrichten. Klicken", Admission Order Details,Details zur Zulassungsbestellung, @@ -9973,9 +9973,9 @@ WhatsApp,WhatsApp, Make a call,Einen Anruf tätigen, Enable Automatic Party Matching,Automatisches Zuordnen von Parteien aktivieren, Auto match and set the Party in Bank Transactions,"Partei automatisch anhand der Kontonummer bzw. IBAN zuordnen", -Enable Fuzzy Matching,Unscharfe Zuordnung aktivieren, -Approximately match the description/party name against parties,"Partei automatisch anhand grober Übereinstimmung des Names zuordnen" -Accounts Closing,Kontoabschluss, +Enable Fuzzy Matching,Fuzzy Matching aktivieren, +Approximately match the description/party name against parties,"Partei automatisch anhand grober Übereinstimmung des Namens zuordnen" +Accounts Closing,Kontenabschluss, Period Closing Settings,Periodenabschlusseinstellungen, Ignore Account Closing Balance,Saldo des Kontos zum Periodenabschluss ignorieren, Financial reports will be generated using GL Entry doctypes (should be enabled if Period Closing Voucher is not posted for all years sequentially or missing),"Finanzberichte werden anhand des Hauptbuchs erstellt (sollte aktiviert sein, wenn Periodenabschlüsse fehlen oder nicht sequentiell für alle Jahre gebucht werden)", @@ -9992,7 +9992,7 @@ Delete Accounting and Stock Ledger Entries on deletion of Transaction,Beim Lösc Enable Common Party Accounting,Verknüpfung von Kunden und Liefeanten erlauben, Allow multi-currency invoices against single party account,Rechnungsbeträge in Fremdwährungen dürfen umgerechnet und in der Hauptwährung gebucht werden, Enabling this will allow creation of multi-currency invoices against single party account in company currency,Bei Aktivierung können Rechnungen in Fremdwährungen gegen ein Konto in der Hauptwährung gebucht werden, -Payment Terms from orders will be fetched into the invoices as is,Die Zahlungsbedingungen aus einem Auftragen werden eins zu eins in die Rechnungen übernommen, +Payment Terms from orders will be fetched into the invoices as is,Zahlungsbedingungen aus Aufträgen werden eins zu eins in Rechnungen übernommen, Automatically Fetch Payment Terms from Order,Zahlungsbedingungen aus Auftrag in die Rechnung übernehmen, Enable Custom Cash Flow Format,Individuelles Cashflow-Format aktivieren, Tax Settings,Umsatzsteuer-Einstellungen, From 2c1fdecc913301845da735c1e20051d29903a116 Mon Sep 17 00:00:00 2001 From: Raffael Meyer <14891507+barredterra@users.noreply.github.com> Date: Wed, 1 Nov 2023 11:54:29 +0100 Subject: [PATCH 084/151] chore: resolve merge conflicts --- erpnext/translations/de.csv | 68 ------------------------------------- 1 file changed, 68 deletions(-) diff --git a/erpnext/translations/de.csv b/erpnext/translations/de.csv index 99630c2391a..63d78adc6a4 100644 --- a/erpnext/translations/de.csv +++ b/erpnext/translations/de.csv @@ -9931,71 +9931,3 @@ Select Alternative Items for Sales Order,Alternativpositionen für Auftragsbest Select an item from each set to be used in the Sales Order.,"Wählen Sie aus den Alternativen jeweils einen Artikel aus, der in die Auftragsbestätigung übernommen werden soll.", Is Alternative,Ist Alternative, Alternative Items,Alternativpositionen, -<<<<<<< HEAD -======= -Add Template,Vorlage einfügen, -Add Comment,Kommentar hinzufügen, -More...,Mehr..., -Notes,Hinweise, -Payment Gateway,Zahlungs-Gateways, -Payment Gateway Name,Name des Zahlungsgateways, -Payments,Zahlungen, -Plan Name,Planname, -Portal,Portal, -Scan Barcode,Barcode scannen, -Some information is missing,Einige Informationen fehlen, -Successful,Erfolgreich, -Tools,Werkzeuge, -Use Sandbox,Sandkastenmodus verwenden, -Busy,Beschäftigt, -Completed By,Vervollständigt von, -Is Template, Ist Vorlage, -Payment Failed,Bezahlung fehlgeschlagen, -Column {0},Spalte {0}, -Field Mapping,Feldzuordnung, -Not Specified,Keine Angabe, -Update Type,Aktualisierungsart, -Dr,Soll, -End Time,Endzeit, -Fetching...,Abrufen ..., -"It seems that there is an issue with the server's stripe configuration. In case of failure, the amount will get refunded to your account.","Es scheint, dass ein Problem mit der Stripe-Konfiguration des Servers vorliegt. Im Falle eines Fehlers wird der Betrag Ihrem Konto gutgeschrieben.", -Looks like someone sent you to an incomplete URL. Please ask them to look into it.,"Sieht aus wie jemand, den Sie zu einer unvollständigen URL gesendet. Bitte fragen Sie sie, sich in sie.", -Master,Vorlage, -Pay,Zahlen, -You can also copy-paste this link in your browser,Sie können diese Verknüpfung in Ihren Browser kopieren, -Verified By,Überprüft von, -Invalid naming series (. missing) for {0},Ungültige Namensreihe (. Fehlt) für {0}, -Phone Number,Telefonnummer, -Account SID,Konto-SID, -Global Defaults,Allgemeine Voreinstellungen, -Is Mandatory,Ist obligatorisch, -WhatsApp,WhatsApp, -Make a call,Einen Anruf tätigen, -Enable Automatic Party Matching,Automatisches Zuordnen von Parteien aktivieren, -Auto match and set the Party in Bank Transactions,"Partei automatisch anhand der Kontonummer bzw. IBAN zuordnen", -Enable Fuzzy Matching,Fuzzy Matching aktivieren, -Approximately match the description/party name against parties,"Partei automatisch anhand grober Übereinstimmung des Namens zuordnen" -Accounts Closing,Kontenabschluss, -Period Closing Settings,Periodenabschlusseinstellungen, -Ignore Account Closing Balance,Saldo des Kontos zum Periodenabschluss ignorieren, -Financial reports will be generated using GL Entry doctypes (should be enabled if Period Closing Voucher is not posted for all years sequentially or missing),"Finanzberichte werden anhand des Hauptbuchs erstellt (sollte aktiviert sein, wenn Periodenabschlüsse fehlen oder nicht sequentiell für alle Jahre gebucht werden)", -Asset Settings,Vermögenswerteinstellungen, -POS Setting,POS-Einstellungen, -Create Ledger Entries for Change Amount,Buchungssätze für Wechselgeld erstellen, -"If enabled, ledger entries will be posted for change amount in POS transactions","Wenn aktiviert, werden Buchungssätze für Wechselgeld in POS-Transaktionen erstellt", -Credit Limit Settings,Kreditlimit-Einstellungen, -Role Allowed to Over Bill, -Users with this role are allowed to over bill above the allowance percentage,"Roll, die mehr als den erlaubten Prozentsatz zusätzlich abrechnen darf", -Role allowed to bypass Credit Limit,"Rolle, die das Kreditlimit umgehen darf", -Invoice Cancellation,Rechnungsstornierung, -Delete Accounting and Stock Ledger Entries on deletion of Transaction,Beim Löschen einer Transaktion auch die entsprechenden Buchungs- und Lagerbuchungssätze löschen, -Enable Common Party Accounting,Verknüpfung von Kunden und Liefeanten erlauben, -Allow multi-currency invoices against single party account,Rechnungsbeträge in Fremdwährungen dürfen umgerechnet und in der Hauptwährung gebucht werden, -Enabling this will allow creation of multi-currency invoices against single party account in company currency,Bei Aktivierung können Rechnungen in Fremdwährungen gegen ein Konto in der Hauptwährung gebucht werden, -Payment Terms from orders will be fetched into the invoices as is,Zahlungsbedingungen aus Aufträgen werden eins zu eins in Rechnungen übernommen, -Automatically Fetch Payment Terms from Order,Zahlungsbedingungen aus Auftrag in die Rechnung übernehmen, -Enable Custom Cash Flow Format,Individuelles Cashflow-Format aktivieren, -Tax Settings,Umsatzsteuer-Einstellungen, -Book Tax Loss on Early Payment Discount,Umsatzsteueranteil bei Skonto berücksichtigen, -Split Early Payment Discount Loss into Income and Tax Loss,"Skontobetrag in Aufwand und Umsatzsteuerkorrektur aufteilen", ->>>>>>> b3486b43c4 (fix: german translations of Accounts Settings) From 875d7f8b7256c282d43ce7292a276ad9e1ec0796 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 1 Nov 2023 16:41:33 +0530 Subject: [PATCH 085/151] refactor: update fields label and remove unused fields from BIN (backport #37827) (#37829) * refactor: rearrange fields and update label (cherry picked from commit ec1a7869f82c9bb3d5e12e8a9dc695823e502a4a) * refactor: remove unused fields `fcfs_rate` and `ma_rate` from Bin (cherry picked from commit f0a1f4ac7cb9362e8dfe05179533fe4ad00d061e) --------- Co-authored-by: s-aga-r --- erpnext/stock/doctype/bin/bin.json | 71 +++++++++++++++--------------- 1 file changed, 36 insertions(+), 35 deletions(-) diff --git a/erpnext/stock/doctype/bin/bin.json b/erpnext/stock/doctype/bin/bin.json index a11572776a2..02684a72419 100644 --- a/erpnext/stock/doctype/bin/bin.json +++ b/erpnext/stock/doctype/bin/bin.json @@ -5,20 +5,23 @@ "doctype": "DocType", "engine": "InnoDB", "field_order": [ - "warehouse", "item_code", - "reserved_qty", + "column_break_yreo", + "warehouse", + "section_break_stag", "actual_qty", - "ordered_qty", - "indented_qty", "planned_qty", - "projected_qty", + "indented_qty", + "ordered_qty", + "column_break_xn5j", + "reserved_qty", "reserved_qty_for_production", "reserved_qty_for_sub_contract", "reserved_qty_for_production_plan", - "ma_rate", + "projected_qty", + "section_break_pmrs", "stock_uom", - "fcfs_rate", + "column_break_0slj", "valuation_rate", "stock_value" ], @@ -56,7 +59,7 @@ "fieldname": "reserved_qty", "fieldtype": "Float", "in_list_view": 1, - "label": "Reserved Quantity", + "label": "Reserved Qty", "oldfieldname": "reserved_qty", "oldfieldtype": "Currency", "read_only": 1 @@ -67,7 +70,7 @@ "fieldtype": "Float", "in_filter": 1, "in_list_view": 1, - "label": "Actual Quantity", + "label": "Actual Qty", "oldfieldname": "actual_qty", "oldfieldtype": "Currency", "read_only": 1 @@ -77,7 +80,7 @@ "fieldname": "ordered_qty", "fieldtype": "Float", "in_list_view": 1, - "label": "Ordered Quantity", + "label": "Ordered Qty", "oldfieldname": "ordered_qty", "oldfieldtype": "Currency", "read_only": 1 @@ -86,7 +89,7 @@ "default": "0.00", "fieldname": "indented_qty", "fieldtype": "Float", - "label": "Requested Quantity", + "label": "Requested Qty", "oldfieldname": "indented_qty", "oldfieldtype": "Currency", "read_only": 1 @@ -116,20 +119,9 @@ { "fieldname": "reserved_qty_for_sub_contract", "fieldtype": "Float", - "label": "Reserved Qty for sub contract", + "label": "Reserved Qty for Subcontract", "read_only": 1 }, - { - "fieldname": "ma_rate", - "fieldtype": "Float", - "hidden": 1, - "label": "Moving Average Rate", - "oldfieldname": "ma_rate", - "oldfieldtype": "Currency", - "print_hide": 1, - "read_only": 1, - "report_hide": 1 - }, { "fieldname": "stock_uom", "fieldtype": "Link", @@ -140,17 +132,6 @@ "options": "UOM", "read_only": 1 }, - { - "fieldname": "fcfs_rate", - "fieldtype": "Float", - "hidden": 1, - "label": "FCFS Rate", - "oldfieldname": "fcfs_rate", - "oldfieldtype": "Currency", - "print_hide": 1, - "read_only": 1, - "report_hide": 1 - }, { "fieldname": "valuation_rate", "fieldtype": "Float", @@ -172,13 +153,33 @@ "fieldtype": "Float", "label": "Reserved Qty for Production Plan", "read_only": 1 + }, + { + "fieldname": "section_break_stag", + "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_yreo", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_xn5j", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_pmrs", + "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_0slj", + "fieldtype": "Column Break" } ], "hide_toolbar": 1, "idx": 1, "in_create": 1, "links": [], - "modified": "2023-05-02 23:26:21.806965", + "modified": "2023-11-01 15:35:51.722534", "modified_by": "Administrator", "module": "Stock", "name": "Bin", From 174b76f6ffb5395e1fc47d6d4a9de7d08ff49077 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 1 Nov 2023 20:30:16 +0530 Subject: [PATCH 086/151] refactor: checkbox to toggle remarks in General Ledger (cherry picked from commit 8fa677b8e8c6e4e65193ad0be7c0a6d5b4f1769e) --- erpnext/accounts/report/general_ledger/general_ledger.js | 6 ++++++ erpnext/accounts/report/general_ledger/general_ledger.py | 9 +++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/general_ledger/general_ledger.js b/erpnext/accounts/report/general_ledger/general_ledger.js index 57a9091cf9b..3bc2ced32fb 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.js +++ b/erpnext/accounts/report/general_ledger/general_ledger.js @@ -188,7 +188,13 @@ frappe.query_reports["General Ledger"] = { "fieldname": "show_net_values_in_party_account", "label": __("Show Net Values in Party Account"), "fieldtype": "Check" + }, + { + "fieldname": "show_remarks", + "label": __("Show Remarks"), + "fieldtype": "Check" } + ] } diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py index d670a356975..8b938d3d83c 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.py +++ b/erpnext/accounts/report/general_ledger/general_ledger.py @@ -163,6 +163,9 @@ def get_gl_entries(filters, accounting_dimensions): select_fields = """, debit, credit, debit_in_account_currency, credit_in_account_currency """ + if filters.get("show_remarks"): + select_fields += """,remarks""" + order_by_statement = "order by posting_date, account, creation" if filters.get("include_dimensions"): @@ -189,7 +192,7 @@ def get_gl_entries(filters, accounting_dimensions): voucher_type, voucher_no, {dimension_fields} cost_center, project, against_voucher_type, against_voucher, account_currency, - remarks, against, is_opening, creation {select_fields} + against, is_opening, creation {select_fields} from `tabGL Entry` where company=%(company)s {conditions} {order_by_statement} @@ -593,8 +596,10 @@ def get_columns(filters): "width": 100, }, {"label": _("Supplier Invoice No"), "fieldname": "bill_no", "fieldtype": "Data", "width": 100}, - {"label": _("Remarks"), "fieldname": "remarks", "width": 400}, ] ) + if filters.get("show_remarks"): + columns.extend([{"label": _("Remarks"), "fieldname": "remarks", "width": 400}]) + return columns From ef4471f8c0e2e5018770276d4941e1cbb6d67a50 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Wed, 1 Nov 2023 11:48:27 +0530 Subject: [PATCH 087/151] fix: `TypeError` in PR for non-stock item (cherry picked from commit 028b3e2fbf52cdb7561e8d63b54d5918b5bc8af4) --- erpnext/stock/doctype/purchase_receipt/purchase_receipt.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index cde3dd6d412..10bd335468d 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -601,11 +601,10 @@ class PurchaseReceipt(BuyingController): make_rate_difference_entry(d) make_sub_contracting_gl_entries(d) make_divisional_loss_gl_entry(d, outgoing_amount) - elif ( - d.warehouse not in warehouse_with_no_account - or d.rejected_warehouse not in warehouse_with_no_account + elif (d.warehouse and d.warehouse not in warehouse_with_no_account) or ( + d.rejected_warehouse and d.rejected_warehouse not in warehouse_with_no_account ): - warehouse_with_no_account.append(d.warehouse) + warehouse_with_no_account.append(d.warehouse or d.rejected_warehouse) if d.is_fixed_asset: self.update_assets(d, d.valuation_rate) From 289236d065024787fe753fb75caee85916a9db8c Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 2 Nov 2023 09:16:26 +0530 Subject: [PATCH 088/151] refactor: pass limits to JE and PE queries in reconciliation tool (cherry picked from commit 54e8ce1ac5a0d14905050fbb630a1602a560088a) --- .../payment_reconciliation/payment_reconciliation.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index 897bbee0beb..d2c75111edd 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -93,6 +93,8 @@ class PaymentReconciliation(Document): "t2.against_account like %(bank_cash_account)s" if self.bank_cash_account else "1=1" ) + limit = f"limit {self.payment_limit}" if self.payment_limit else " " + # nosemgrep journal_entries = frappe.db.sql( """ @@ -116,11 +118,13 @@ class PaymentReconciliation(Document): ELSE {bank_account_condition} END) order by t1.posting_date + {limit} """.format( **{ "dr_or_cr": dr_or_cr, "bank_account_condition": bank_account_condition, "condition": condition, + "limit": limit, } ), { @@ -146,7 +150,7 @@ class PaymentReconciliation(Document): if self.payment_name: conditions.append(doc.name.like(f"%{self.payment_name}%")) - self.return_invoices = ( + self.return_invoices_query = ( qb.from_(doc) .select( ConstantColumn(voucher_type).as_("voucher_type"), @@ -154,8 +158,11 @@ class PaymentReconciliation(Document): doc.return_against, ) .where(Criterion.all(conditions)) - .run(as_dict=True) ) + if self.payment_limit: + self.return_invoices_query = self.return_invoices_query.limit(self.payment_limit) + + self.return_invoices = self.return_invoices_query.run(as_dict=True) def get_dr_or_cr_notes(self): From e8a706a5eaa2965078d5594feb961db58c9f526e Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 2 Nov 2023 10:46:42 +0530 Subject: [PATCH 089/151] feat(Stock Balance): add filters from route (backport #37836) (#37839) feat(Stock Balance): add filters from route (cherry picked from commit 38e5e4a8930714c08471308ba47b9f063802a4b3) Co-authored-by: barredterra <14891507+barredterra@users.noreply.github.com> --- erpnext/stock/page/stock_balance/stock_balance.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/stock/page/stock_balance/stock_balance.js b/erpnext/stock/page/stock_balance/stock_balance.js index f00dd3e7912..90b8d453420 100644 --- a/erpnext/stock/page/stock_balance/stock_balance.js +++ b/erpnext/stock/page/stock_balance/stock_balance.js @@ -11,6 +11,7 @@ frappe.pages['stock-balance'].on_page_load = function(wrapper) { label: __('Warehouse'), fieldtype:'Link', options:'Warehouse', + default: frappe.route_options && frappe.route_options.warehouse, change: function() { page.item_dashboard.start = 0; page.item_dashboard.refresh(); @@ -22,6 +23,7 @@ frappe.pages['stock-balance'].on_page_load = function(wrapper) { label: __('Item'), fieldtype:'Link', options:'Item', + default: frappe.route_options && frappe.route_options.item_code, change: function() { page.item_dashboard.start = 0; page.item_dashboard.refresh(); @@ -33,6 +35,7 @@ frappe.pages['stock-balance'].on_page_load = function(wrapper) { label: __('Item Group'), fieldtype:'Link', options:'Item Group', + default: frappe.route_options && frappe.route_options.item_group, change: function() { page.item_dashboard.start = 0; page.item_dashboard.refresh(); From 9b0a46853fa48899094f227a2ebd0fd4dff6d31f Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 2 Nov 2023 11:32:24 +0530 Subject: [PATCH 090/151] chore: std permissions for Process Payment Reconciilation log (cherry picked from commit a9fceeb00ff266b181791dc0ca4ca334810fff0a) --- .../process_payment_reconciliation_log.json | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/process_payment_reconciliation_log/process_payment_reconciliation_log.json b/erpnext/accounts/doctype/process_payment_reconciliation_log/process_payment_reconciliation_log.json index 1131a0fca69..b4ac9812cbf 100644 --- a/erpnext/accounts/doctype/process_payment_reconciliation_log/process_payment_reconciliation_log.json +++ b/erpnext/accounts/doctype/process_payment_reconciliation_log/process_payment_reconciliation_log.json @@ -110,7 +110,7 @@ "in_create": 1, "index_web_pages_for_search": 1, "links": [], - "modified": "2023-04-21 17:36:26.642617", + "modified": "2023-11-02 11:32:12.254018", "modified_by": "Administrator", "module": "Accounts", "name": "Process Payment Reconciliation Log", @@ -125,7 +125,19 @@ "print": 1, "read": 1, "report": 1, - "role": "System Manager", + "role": "Accounts Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts User", "share": 1, "write": 1 } From 69b569df465412605a1483139f49745245755188 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 2 Nov 2023 14:33:33 +0530 Subject: [PATCH 091/151] fix: standard submit perm in repost ledger for editable invoices (backport #37826) (#37854) fix: standard submit perm in repost ledger for editable invoices (#37826) * fix: ignore perm while reposting ledger * fix: use flag in save * fix: remove unnecessary save (cherry picked from commit 1b808e1d7c2c2d1ad51bd18acf6a45e9b5cfc605) Co-authored-by: Gursheen Kaur Anand <40693548+GursheenK@users.noreply.github.com> --- erpnext/controllers/accounts_controller.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 745c23a91c9..9a34af2c66c 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -2249,6 +2249,7 @@ class AccountsController(TransactionBase): repost_ledger = frappe.new_doc("Repost Accounting Ledger") repost_ledger.company = self.company repost_ledger.append("vouchers", {"voucher_type": self.doctype, "voucher_no": self.name}) + repost_ledger.flags.ignore_permissions = True repost_ledger.insert() repost_ledger.submit() self.db_set("repost_required", 0) From 273cc35b9147b85f33587776165b4cabdc23db61 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 2 Nov 2023 14:34:23 +0530 Subject: [PATCH 092/151] fix: gov compliance for tax withholding report (#37590) * refactor: avoid relying only on against in tds docs query (cherry picked from commit 705dadae8e3b49e751184b14323ba2686e5342cc) * chore: change column order (cherry picked from commit 7ecc0d5a04b6c8cd8b97256d8c0e4bdb12494d5d) * fix: sort by section code (cherry picked from commit 4471ad581e1c3d220468f42170a09739a46dcc21) * feat: proprietorship & partnership options in entity type (cherry picked from commit ed2457bddfaedc83301a96f9d77b093b36d15a72) # Conflicts: # erpnext/buying/doctype/supplier/supplier.json # erpnext/selling/doctype/customer/customer.json * chore: linting issues (cherry picked from commit 75441017c6629f81104409b892db56bb9c1bf1dd) * feat: add cols for supplier inv details (cherry picked from commit 6d5ccde864e373b787d781c583b85a26ed2d40e9) * chore: resolve conflicts * chore: resolve conflicts * chore: linting issues --------- Co-authored-by: Gursheen Anand Co-authored-by: Gursheen Kaur Anand <40693548+GursheenK@users.noreply.github.com> --- .../tds_payable_monthly.py | 174 ++++++++++++------ erpnext/buying/doctype/supplier/supplier.json | 6 +- .../selling/doctype/customer/customer.json | 6 +- 3 files changed, 119 insertions(+), 67 deletions(-) diff --git a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py index eac5426b8b5..06c9e44b455 100644 --- a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py +++ b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py @@ -47,6 +47,7 @@ def get_result( out = [] for name, details in gle_map.items(): tax_amount, total_amount, grand_total, base_total = 0, 0, 0, 0 + bill_no, bill_date = "", "" tax_withholding_category = tax_category_map.get(name) rate = tax_rate_map.get(tax_withholding_category) @@ -70,7 +71,10 @@ def get_result( if net_total_map.get(name): if voucher_type == "Journal Entry" and tax_amount and rate: # back calcalute total amount from rate and tax_amount - total_amount = grand_total = base_total = tax_amount / (rate / 100) + if rate: + total_amount = grand_total = base_total = tax_amount / (rate / 100) + elif voucher_type == "Purchase Invoice": + total_amount, grand_total, base_total, bill_no, bill_date = net_total_map.get(name) else: total_amount, grand_total, base_total = net_total_map.get(name) else: @@ -96,7 +100,7 @@ def get_result( row.update( { - "section_code": tax_withholding_category, + "section_code": tax_withholding_category or "", "entity_type": party_map.get(party, {}).get(party_type), "rate": rate, "total_amount": total_amount, @@ -106,10 +110,14 @@ def get_result( "transaction_date": posting_date, "transaction_type": voucher_type, "ref_no": name, + "supplier_invoice_no": bill_no, + "supplier_invoice_date": bill_date, } ) out.append(row) + out.sort(key=lambda x: x["section_code"]) + return out @@ -157,14 +165,14 @@ def get_gle_map(documents): def get_columns(filters): pan = "pan" if frappe.db.has_column(filters.party_type, "pan") else "tax_id" columns = [ - {"label": _(frappe.unscrub(pan)), "fieldname": pan, "fieldtype": "Data", "width": 60}, { - "label": _(filters.get("party_type")), - "fieldname": "party", - "fieldtype": "Dynamic Link", - "options": "party_type", - "width": 180, + "label": _("Section Code"), + "options": "Tax Withholding Category", + "fieldname": "section_code", + "fieldtype": "Link", + "width": 90, }, + {"label": _(frappe.unscrub(pan)), "fieldname": pan, "fieldtype": "Data", "width": 60}, ] if filters.naming_series == "Naming Series": @@ -179,51 +187,60 @@ def get_columns(filters): columns.extend( [ - { - "label": _("Date of Transaction"), - "fieldname": "transaction_date", - "fieldtype": "Date", - "width": 100, - }, - { - "label": _("Section Code"), - "options": "Tax Withholding Category", - "fieldname": "section_code", - "fieldtype": "Link", - "width": 90, - }, {"label": _("Entity Type"), "fieldname": "entity_type", "fieldtype": "Data", "width": 100}, - { - "label": _("Total Amount"), - "fieldname": "total_amount", - "fieldtype": "Float", - "width": 90, - }, + ] + ) + if filters.party_type == "Supplier": + columns.extend( + [ + { + "label": _("Supplier Invoice No"), + "fieldname": "supplier_invoice_no", + "fieldtype": "Data", + "width": 120, + }, + { + "label": _("Supplier Invoice Date"), + "fieldname": "supplier_invoice_date", + "fieldtype": "Date", + "width": 120, + }, + ] + ) + + columns.extend( + [ { "label": _("TDS Rate %") if filters.get("party_type") == "Supplier" else _("TCS Rate %"), "fieldname": "rate", "fieldtype": "Percent", - "width": 90, + "width": 60, }, { - "label": _("Tax Amount"), - "fieldname": "tax_amount", + "label": _("Total Amount"), + "fieldname": "total_amount", "fieldtype": "Float", - "width": 90, - }, - { - "label": _("Grand Total"), - "fieldname": "grand_total", - "fieldtype": "Float", - "width": 90, + "width": 120, }, { "label": _("Base Total"), "fieldname": "base_total", "fieldtype": "Float", - "width": 90, + "width": 120, }, - {"label": _("Transaction Type"), "fieldname": "transaction_type", "width": 100}, + { + "label": _("Tax Amount"), + "fieldname": "tax_amount", + "fieldtype": "Float", + "width": 120, + }, + { + "label": _("Grand Total"), + "fieldname": "grand_total", + "fieldtype": "Float", + "width": 120, + }, + {"label": _("Transaction Type"), "fieldname": "transaction_type", "width": 130}, { "label": _("Reference No."), "fieldname": "ref_no", @@ -231,6 +248,12 @@ def get_columns(filters): "options": "transaction_type", "width": 180, }, + { + "label": _("Date of Transaction"), + "fieldname": "transaction_date", + "fieldtype": "Date", + "width": 100, + }, ] ) @@ -253,27 +276,7 @@ def get_tds_docs(filters): "Tax Withholding Account", {"company": filters.get("company")}, pluck="account" ) - query_filters = { - "account": ("in", tds_accounts), - "posting_date": ("between", [filters.get("from_date"), filters.get("to_date")]), - "is_cancelled": 0, - "against": ("not in", bank_accounts), - } - - party = frappe.get_all(filters.get("party_type"), pluck="name") - or_filters.update({"against": ("in", party), "voucher_type": "Journal Entry"}) - - if filters.get("party"): - del query_filters["account"] - del query_filters["against"] - or_filters = {"against": filters.get("party"), "party": filters.get("party")} - - tds_docs = frappe.get_all( - "GL Entry", - filters=query_filters, - or_filters=or_filters, - fields=["voucher_no", "voucher_type", "against", "party"], - ) + tds_docs = get_tds_docs_query(filters, bank_accounts, tds_accounts).run(as_dict=True) for d in tds_docs: if d.voucher_type == "Purchase Invoice": @@ -309,6 +312,47 @@ def get_tds_docs(filters): ) +def get_tds_docs_query(filters, bank_accounts, tds_accounts): + if not tds_accounts: + frappe.throw( + _("No {0} Accounts found for this company.").format(frappe.bold("Tax Withholding")), + title=_("Accounts Missing Error"), + ) + gle = frappe.qb.DocType("GL Entry") + query = ( + frappe.qb.from_(gle) + .select("voucher_no", "voucher_type", "against", "party") + .where((gle.is_cancelled == 0)) + ) + + if filters.get("from_date"): + query = query.where(gle.posting_date >= filters.get("from_date")) + if filters.get("to_date"): + query = query.where(gle.posting_date <= filters.get("to_date")) + + if bank_accounts: + query = query.where(gle.against.notin(bank_accounts)) + + if filters.get("party"): + party = [filters.get("party")] + query = query.where( + ((gle.account.isin(tds_accounts) & gle.against.isin(party))) + | ((gle.voucher_type == "Journal Entry") & (gle.party == filters.get("party"))) + | gle.party.isin(party) + ) + else: + party = frappe.get_all(filters.get("party_type"), pluck="name") + query = query.where( + ((gle.account.isin(tds_accounts) & gle.against.isin(party))) + | ( + (gle.voucher_type == "Journal Entry") + & ((gle.party_type == filters.get("party_type")) | (gle.party_type == "")) + ) + | gle.party.isin(party) + ) + return query + + def get_journal_entry_party_map(journal_entries): journal_entry_party_map = {} for d in frappe.db.get_all( @@ -335,6 +379,8 @@ def get_doc_info(vouchers, doctype, tax_category_map, net_total_map=None): "base_tax_withholding_net_total", "grand_total", "base_total", + "bill_no", + "bill_date", ], "Sales Invoice": ["base_net_total", "grand_total", "base_total"], "Payment Entry": [ @@ -353,7 +399,13 @@ def get_doc_info(vouchers, doctype, tax_category_map, net_total_map=None): for entry in entries: tax_category_map.update({entry.name: entry.tax_withholding_category}) if doctype == "Purchase Invoice": - value = [entry.base_tax_withholding_net_total, entry.grand_total, entry.base_total] + value = [ + entry.base_tax_withholding_net_total, + entry.grand_total, + entry.base_total, + entry.bill_no, + entry.bill_date, + ] elif doctype == "Sales Invoice": value = [entry.base_net_total, entry.grand_total, entry.base_total] elif doctype == "Payment Entry": diff --git a/erpnext/buying/doctype/supplier/supplier.json b/erpnext/buying/doctype/supplier/supplier.json index 3aca21ed6a6..5ac8cde50fe 100644 --- a/erpnext/buying/doctype/supplier/supplier.json +++ b/erpnext/buying/doctype/supplier/supplier.json @@ -170,7 +170,7 @@ "fieldname": "supplier_type", "fieldtype": "Select", "label": "Supplier Type", - "options": "Company\nIndividual", + "options": "Company\nIndividual\nProprietorship\nPartnership", "reqd": 1 }, { @@ -457,7 +457,7 @@ "link_fieldname": "party" } ], - "modified": "2023-05-09 15:34:13.408932", + "modified": "2023-10-19 16:55:15.148325", "modified_by": "Administrator", "module": "Buying", "name": "Supplier", @@ -525,4 +525,4 @@ "states": [], "title_field": "supplier_name", "track_changes": 1 -} \ No newline at end of file +} diff --git a/erpnext/selling/doctype/customer/customer.json b/erpnext/selling/doctype/customer/customer.json index 18bb4b9bb46..87e5c5d0eec 100644 --- a/erpnext/selling/doctype/customer/customer.json +++ b/erpnext/selling/doctype/customer/customer.json @@ -131,7 +131,7 @@ "label": "Customer Type", "oldfieldname": "customer_type", "oldfieldtype": "Select", - "options": "Company\nIndividual", + "options": "Company\nIndividual\nProprietorship\nPartnership", "reqd": 1 }, { @@ -568,7 +568,7 @@ "link_fieldname": "party" } ], - "modified": "2023-05-09 15:38:40.255193", + "modified": "2023-10-19 16:56:27.327035", "modified_by": "Administrator", "module": "Selling", "name": "Customer", @@ -655,4 +655,4 @@ "states": [], "title_field": "customer_name", "track_changes": 1 -} \ No newline at end of file +} From e19cade12d59ec439e2e1141e929c4d846935013 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 2 Nov 2023 15:15:53 +0530 Subject: [PATCH 093/151] fix: remove voucher type and no for Item and Warehouse based reposting (backport #37849) (#37850) * fix: remove voucher type and no for Item and Warehouse based reposting (cherry picked from commit 0104897d693ca98796536eac7a9edadc58c64fff) * chore: fix test cases --------- Co-authored-by: Rohit Waghchaure --- erpnext/controllers/stock_controller.py | 2 -- .../repost_item_valuation/test_repost_item_valuation.py | 4 +++- erpnext/stock/doctype/stock_entry/test_stock_entry.py | 1 + .../doctype/stock_reconciliation/test_stock_reconciliation.py | 1 + .../stock_reposting_settings/stock_reposting_settings.json | 4 ++-- 5 files changed, 7 insertions(+), 5 deletions(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index a72cc667399..403f71be5eb 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -1043,8 +1043,6 @@ def create_item_wise_repost_entries(voucher_type, voucher_no, allow_zero_rate=Fa repost_entry = frappe.new_doc("Repost Item Valuation") repost_entry.based_on = "Item and Warehouse" - repost_entry.voucher_type = voucher_type - repost_entry.voucher_no = voucher_no repost_entry.item_code = sle.item_code repost_entry.warehouse = sle.warehouse diff --git a/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py b/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py index 1853f45f583..aa90ff03a82 100644 --- a/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py +++ b/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py @@ -5,7 +5,7 @@ from unittest.mock import MagicMock, call import frappe -from frappe.tests.utils import FrappeTestCase +from frappe.tests.utils import FrappeTestCase, change_settings from frappe.utils import add_days, add_to_date, now, nowdate, today from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice @@ -200,6 +200,7 @@ class TestRepostItemValuation(FrappeTestCase, StockTestMixin): riv.set_status("Skipped") + @change_settings("Stock Reposting Settings", {"item_based_reposting": 0}) def test_prevention_of_cancelled_transaction_riv(self): frappe.flags.dont_execute_stock_reposts = True @@ -377,6 +378,7 @@ class TestRepostItemValuation(FrappeTestCase, StockTestMixin): accounts_settings.acc_frozen_upto = "" accounts_settings.save() + @change_settings("Stock Reposting Settings", {"item_based_reposting": 0}) def test_create_repost_entry_for_cancelled_document(self): pr = make_purchase_receipt( company="_Test Company with perpetual inventory", diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py index 72f6bcdddf2..c4f26c4baaf 100644 --- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py @@ -1504,6 +1504,7 @@ class TestStockEntry(FrappeTestCase): self.assertEqual(se.items[0].item_name, item.item_name) self.assertEqual(se.items[0].stock_uom, item.stock_uom) + @change_settings("Stock Reposting Settings", {"item_based_reposting": 0}) def test_reposting_for_depedent_warehouse(self): from erpnext.stock.doctype.repost_item_valuation.repost_item_valuation import repost_sl_entries from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py index c913af3301a..dd1220a0ddb 100644 --- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py @@ -702,6 +702,7 @@ class TestStockReconciliation(FrappeTestCase, StockTestMixin): self.assertEqual(flt(sl_entry.actual_qty), 1.0) self.assertEqual(flt(sl_entry.qty_after_transaction), 1.0) + @change_settings("Stock Reposting Settings", {"item_based_reposting": 0}) def test_backdated_stock_reco_entry(self): from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry diff --git a/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.json b/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.json index 7c712ce2258..68afd996b49 100644 --- a/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.json +++ b/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.json @@ -50,7 +50,7 @@ "label": "Limit timeslot for Stock Reposting" }, { - "default": "0", + "default": "1", "fieldname": "item_based_reposting", "fieldtype": "Check", "label": "Use Item based reposting" @@ -70,7 +70,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2023-05-04 16:14:29.080697", + "modified": "2023-11-01 16:14:29.080697", "modified_by": "Administrator", "module": "Stock", "name": "Stock Reposting Settings", From 2c93f81d59ed259a584bec9b5585df2e8c7de387 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 3 Nov 2023 11:52:07 +0530 Subject: [PATCH 094/151] perf: Add index to supplier invoice field (backport #37861) (#37862) fix: Add index to supplier invoice field (#37861) * fix: Add index to supplier invoice field * chore: remove unintetional changes (cherry picked from commit c37e374fdd322c0f0423469ce95faecac15b18e4) Co-authored-by: Deepesh Garg --- .../accounts/doctype/purchase_invoice/purchase_invoice.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index 02a5114f345..bded7d38ccb 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -383,7 +383,8 @@ "label": "Supplier Invoice No", "oldfieldname": "bill_no", "oldfieldtype": "Data", - "print_hide": 1 + "print_hide": 1, + "search_index": 1 }, { "fieldname": "column_break_15", @@ -1592,7 +1593,7 @@ "idx": 204, "is_submittable": 1, "links": [], - "modified": "2023-10-16 16:24:51.886231", + "modified": "2023-11-02 18:46:16.810187", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice", From ee5898a7739e262a76ee33aaac29a8f000fc7ac7 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 3 Nov 2023 11:03:54 +0530 Subject: [PATCH 095/151] refactor: group only by voucher flag in AR/AP report (cherry picked from commit 23beb46d15a2b96601e1f20a68dee06d7e5f0c49) --- .../accounts_payable/accounts_payable.js | 8 ++++++- .../accounts_receivable.js | 8 ++++++- .../accounts_receivable.py | 22 +++++++++++++++---- 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/report/accounts_payable/accounts_payable.js b/erpnext/accounts/report/accounts_payable/accounts_payable.js index 9c73cbb344f..eff705dafac 100644 --- a/erpnext/accounts/report/accounts_payable/accounts_payable.js +++ b/erpnext/accounts/report/accounts_payable/accounts_payable.js @@ -143,7 +143,13 @@ frappe.query_reports["Accounts Payable"] = { "fieldname": "show_future_payments", "label": __("Show Future Payments"), "fieldtype": "Check", + }, + { + "fieldname": "ignore_accounts", + "label": __("Group by Voucher"), + "fieldtype": "Check", } + ], "formatter": function(value, row, column, data, default_formatter) { @@ -175,4 +181,4 @@ function get_party_type_options() { }); }); return options; -} \ No newline at end of file +} diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js index 1073be0bdc4..786aad601ba 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js @@ -172,7 +172,13 @@ frappe.query_reports["Accounts Receivable"] = { "fieldname": "show_remarks", "label": __("Show Remarks"), "fieldtype": "Check", + }, + { + "fieldname": "ignore_accounts", + "label": __("Group by Voucher"), + "fieldtype": "Check", } + ], "formatter": function(value, row, column, data, default_formatter) { @@ -205,4 +211,4 @@ function get_party_type_options() { }); }); return options; -} \ No newline at end of file +} diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index 20444f94964..f24a24e42ef 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -116,7 +116,12 @@ class ReceivablePayableReport(object): # build all keys, since we want to exclude vouchers beyond the report date for ple in self.ple_entries: # get the balance object for voucher_type - key = (ple.account, ple.voucher_type, ple.voucher_no, ple.party) + + if self.filters.get("ingore_accounts"): + key = (ple.voucher_type, ple.voucher_no, ple.party) + else: + key = (ple.account, ple.voucher_type, ple.voucher_no, ple.party) + if not key in self.voucher_balance: self.voucher_balance[key] = frappe._dict( voucher_type=ple.voucher_type, @@ -183,7 +188,10 @@ class ReceivablePayableReport(object): ): return - key = (ple.account, ple.against_voucher_type, ple.against_voucher_no, ple.party) + if self.filters.get("ingore_accounts"): + key = (ple.against_voucher_type, ple.against_voucher_no, ple.party) + else: + key = (ple.account, ple.against_voucher_type, ple.against_voucher_no, ple.party) # If payment is made against credit note # and credit note is made against a Sales Invoice @@ -192,13 +200,19 @@ class ReceivablePayableReport(object): if ple.against_voucher_no in self.return_entries: return_against = self.return_entries.get(ple.against_voucher_no) if return_against: - key = (ple.account, ple.against_voucher_type, return_against, ple.party) + if self.filters.get("ingore_accounts"): + key = (ple.against_voucher_type, return_against, ple.party) + else: + key = (ple.account, ple.against_voucher_type, return_against, ple.party) row = self.voucher_balance.get(key) if not row: # no invoice, this is an invoice / stand-alone payment / credit note - row = self.voucher_balance.get((ple.account, ple.voucher_type, ple.voucher_no, ple.party)) + if self.filters.get("ingore_accounts"): + row = self.voucher_balance.get((ple.voucher_type, ple.voucher_no, ple.party)) + else: + row = self.voucher_balance.get((ple.account, ple.voucher_type, ple.voucher_no, ple.party)) row.party_type = ple.party_type return row From 3d2ff7eea323b29390a9134b7d62485a760abc33 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 2 Nov 2023 13:17:21 +0530 Subject: [PATCH 096/151] refactor: better output on gl and pl comparison report (cherry picked from commit 539f0251d95d80055112d72e54af9571bea987dc) --- .../general_and_payment_ledger_comparison.py | 82 ++++++++++++++++--- 1 file changed, 69 insertions(+), 13 deletions(-) diff --git a/erpnext/accounts/report/general_and_payment_ledger_comparison/general_and_payment_ledger_comparison.py b/erpnext/accounts/report/general_and_payment_ledger_comparison/general_and_payment_ledger_comparison.py index 099884a48ec..696a03b0a70 100644 --- a/erpnext/accounts/report/general_and_payment_ledger_comparison/general_and_payment_ledger_comparison.py +++ b/erpnext/accounts/report/general_and_payment_ledger_comparison/general_and_payment_ledger_comparison.py @@ -79,7 +79,9 @@ class General_Payment_Ledger_Comparison(object): .select( gle.company, gle.account, + gle.voucher_type, gle.voucher_no, + gle.party_type, gle.party, outstanding, ) @@ -89,7 +91,9 @@ class General_Payment_Ledger_Comparison(object): & (gle.account.isin(val.accounts)) ) .where(Criterion.all(filter_criterion)) - .groupby(gle.company, gle.account, gle.voucher_no, gle.party) + .groupby( + gle.company, gle.account, gle.voucher_type, gle.voucher_no, gle.party_type, gle.party + ) .run() ) @@ -112,7 +116,13 @@ class General_Payment_Ledger_Comparison(object): self.account_types[acc_type].ple = ( qb.from_(ple) .select( - ple.company, ple.account, ple.voucher_no, ple.party, Sum(ple.amount).as_("outstanding") + ple.company, + ple.account, + ple.voucher_type, + ple.voucher_no, + ple.party_type, + ple.party, + Sum(ple.amount).as_("outstanding"), ) .where( (ple.company == self.filters.company) @@ -120,7 +130,9 @@ class General_Payment_Ledger_Comparison(object): & (ple.account.isin(val.accounts)) ) .where(Criterion.all(filter_criterion)) - .groupby(ple.company, ple.account, ple.voucher_no, ple.party) + .groupby( + ple.company, ple.account, ple.voucher_type, ple.voucher_no, ple.party_type, ple.party + ) .run() ) @@ -138,12 +150,12 @@ class General_Payment_Ledger_Comparison(object): self.diff = frappe._dict({}) for x in self.variation_in_payment_ledger: - self.diff[(x[0], x[1], x[2], x[3])] = frappe._dict({"gl_balance": x[4]}) + self.diff[(x[0], x[1], x[2], x[3], x[4], x[5])] = frappe._dict({"gl_balance": x[6]}) for x in self.variation_in_general_ledger: - self.diff.setdefault((x[0], x[1], x[2], x[3]), frappe._dict({"gl_balance": 0.0})).update( - frappe._dict({"pl_balance": x[4]}) - ) + self.diff.setdefault( + (x[0], x[1], x[2], x[3], x[4], x[5]), frappe._dict({"gl_balance": 0.0}) + ).update(frappe._dict({"pl_balance": x[6]})) def generate_data(self): self.data = [] @@ -151,8 +163,12 @@ class General_Payment_Ledger_Comparison(object): self.data.append( frappe._dict( { - "voucher_no": key[2], - "party": key[3], + "company": key[0], + "account": key[1], + "voucher_type": key[2], + "voucher_no": key[3], + "party_type": key[4], + "party": key[5], "gl_balance": val.gl_balance, "pl_balance": val.pl_balance, } @@ -162,12 +178,52 @@ class General_Payment_Ledger_Comparison(object): def get_columns(self): self.columns = [] options = None + self.columns.append( + dict( + label=_("Company"), + fieldname="company", + fieldtype="Link", + options="Company", + width="100", + ) + ) + + self.columns.append( + dict( + label=_("Account"), + fieldname="account", + fieldtype="Link", + options="Account", + width="100", + ) + ) + + self.columns.append( + dict( + label=_("Voucher Type"), + fieldname="voucher_type", + fieldtype="Link", + options="DocType", + width="100", + ) + ) + self.columns.append( dict( label=_("Voucher No"), fieldname="voucher_no", - fieldtype="Data", - options=options, + fieldtype="Dynamic Link", + options="voucher_type", + width="100", + ) + ) + + self.columns.append( + dict( + label=_("Party Type"), + fieldname="party_type", + fieldtype="Link", + options="DocType", width="100", ) ) @@ -176,8 +232,8 @@ class General_Payment_Ledger_Comparison(object): dict( label=_("Party"), fieldname="party", - fieldtype="Data", - options=options, + fieldtype="Dynamic Link", + options="party_type", width="100", ) ) From afa6ed9e225c1b1a9b9a4a30e5337aa537374106 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 2 Nov 2023 13:40:40 +0530 Subject: [PATCH 097/151] refactor(test): for ledger comparision report (cherry picked from commit 639f427d6d31c7d018033c2110c478f8e6ea8ce4) --- .../test_general_and_payment_ledger_comparison.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/accounts/report/general_and_payment_ledger_comparison/test_general_and_payment_ledger_comparison.py b/erpnext/accounts/report/general_and_payment_ledger_comparison/test_general_and_payment_ledger_comparison.py index 4b0e99d7125..59e906ba332 100644 --- a/erpnext/accounts/report/general_and_payment_ledger_comparison/test_general_and_payment_ledger_comparison.py +++ b/erpnext/accounts/report/general_and_payment_ledger_comparison/test_general_and_payment_ledger_comparison.py @@ -50,7 +50,11 @@ class TestGeneralAndPaymentLedger(FrappeTestCase, AccountsTestMixin): self.assertEqual(len(data), 1) expected = { + "company": sinv.company, + "account": sinv.debit_to, + "voucher_type": sinv.doctype, "voucher_no": sinv.name, + "party_type": "Customer", "party": sinv.customer, "gl_balance": sinv.grand_total, "pl_balance": sinv.grand_total - 1, From 1a3dae0f9ae53e58ce7a88e5216e426a1e6b1d6e Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 3 Nov 2023 16:24:36 +0530 Subject: [PATCH 098/151] fix: permission error while creating Supplier Quotation from Portal (backport #37864) (#37870) * fix: permission error while creating Supplier Quotation from Portal (cherry picked from commit e019d43d0b3fa6faa241a7b9885f65f6fccc9459) # Conflicts: # erpnext/controllers/buying_controller.py * chore: `conflicts` --------- Co-authored-by: s-aga-r --- erpnext/controllers/buying_controller.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index a38905c7e2b..d0955cf5c9e 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -4,7 +4,7 @@ import frappe from frappe import ValidationError, _, msgprint -from frappe.contacts.doctype.address.address import get_address_display +from frappe.contacts.doctype.address.address import render_address from frappe.utils import cint, cstr, flt, getdate from frappe.utils.data import nowtime @@ -219,7 +219,9 @@ class BuyingController(SubcontractingController): for address_field, address_display_field in address_dict.items(): if self.get(address_field): - self.set(address_display_field, get_address_display(self.get(address_field))) + self.set( + address_display_field, render_address(self.get(address_field), check_permissions=False) + ) def set_total_in_words(self): from frappe.utils import money_in_words From ad64065ec6d0b6606c37843dd4ce41a96dc8e969 Mon Sep 17 00:00:00 2001 From: anandbaburajan Date: Fri, 3 Nov 2023 20:57:52 +0530 Subject: [PATCH 099/151] fix: add missing disbursement account in update_old_loans patch --- erpnext/patches/v13_0/update_old_loans.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/patches/v13_0/update_old_loans.py b/erpnext/patches/v13_0/update_old_loans.py index 0bd3fcdec4c..30db996e736 100644 --- a/erpnext/patches/v13_0/update_old_loans.py +++ b/erpnext/patches/v13_0/update_old_loans.py @@ -125,6 +125,7 @@ def execute(): loan_type_doc.company = loan.company loan_type_doc.mode_of_payment = loan.mode_of_payment loan_type_doc.payment_account = loan.payment_account + loan_type_doc.disbursement_account = loan.payment_account loan_type_doc.loan_account = loan.loan_account loan_type_doc.interest_income_account = loan.interest_income_account loan_type_doc.penalty_income_account = penalty_account From fed25fac7624b6fd9c7c131e8463274cda14f64d Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 3 Nov 2023 21:59:46 +0530 Subject: [PATCH 100/151] perf: index return against for purchase invoice (backport #37881) (#37882) perf: index return against for purchase invoice (#37881) (cherry picked from commit 469ae2c7f1edb6dbd2d770b65c48bad5a0293ffe) Co-authored-by: Ankush Menat --- .../accounts/doctype/purchase_invoice/purchase_invoice.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index bded7d38ccb..643047e982d 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -407,7 +407,8 @@ "no_copy": 1, "options": "Purchase Invoice", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "search_index": 1 }, { "fieldname": "section_addresses", @@ -1593,7 +1594,7 @@ "idx": 204, "is_submittable": 1, "links": [], - "modified": "2023-11-02 18:46:16.810187", + "modified": "2023-11-03 15:47:30.319200", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice", From 86c6ae6275d6c7dafd7aa3cf6e9516bb6f05cee1 Mon Sep 17 00:00:00 2001 From: Anand Baburajan Date: Fri, 3 Nov 2023 23:26:34 +0530 Subject: [PATCH 101/151] chore: rename daily_depreciation in asset to depreciation_amount_based_on_num_days_in_month [v14] (#37895) chore: rename daily_depreciation to depreciation_amount_based_on_num_days_in_month --- erpnext/assets/doctype/asset/asset.py | 6 +++--- erpnext/assets/doctype/asset/test_asset.py | 9 ++++++--- .../asset_finance_book/asset_finance_book.json | 18 +++++++++--------- erpnext/patches.txt | 1 + ...iation_amount_based_on_num_days_in_month.py | 16 ++++++++++++++++ 5 files changed, 35 insertions(+), 15 deletions(-) create mode 100644 erpnext/patches/v14_0/rename_daily_depreciation_to_depreciation_amount_based_on_num_days_in_month.py diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index d54d15afaf6..cb9dc62da5c 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -1216,7 +1216,7 @@ def get_item_details(item_code, asset_category, gross_purchase_amount): "depreciation_method": d.depreciation_method, "total_number_of_depreciations": d.total_number_of_depreciations, "frequency_of_depreciation": d.frequency_of_depreciation, - "daily_depreciation": d.daily_depreciation, + "depreciation_amount_based_on_num_days_in_month": d.depreciation_amount_based_on_num_days_in_month, "salvage_value_percentage": d.salvage_value_percentage, "expected_value_after_useful_life": flt(gross_purchase_amount) * flt(d.salvage_value_percentage / 100), @@ -1396,7 +1396,7 @@ def get_straight_line_or_manual_depr_amount( ) # if the Depreciation Schedule is being modified after Asset Value Adjustment due to decrease in asset value elif asset.flags.decrease_in_asset_value_due_to_value_adjustment: - if row.daily_depreciation: + if row.depreciation_amount_based_on_num_days_in_month: daily_depr_amount = ( flt(row.value_after_depreciation) - flt(row.expected_value_after_useful_life) ) / date_diff( @@ -1441,7 +1441,7 @@ def get_straight_line_or_manual_depr_amount( ) / number_of_pending_depreciations # if the Depreciation Schedule is being prepared for the first time else: - if row.daily_depreciation: + if row.depreciation_amount_based_on_num_days_in_month: daily_depr_amount = ( flt(asset.gross_purchase_amount) - flt(asset.opening_accumulated_depreciation) diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index e8ffae3015b..1272b2eff33 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -731,7 +731,9 @@ class TestDepreciationMethods(AssetSetup): self.assertEqual(schedules, expected_schedules) - def test_schedule_for_straight_line_method_with_daily_depreciation(self): + def test_schedule_for_straight_line_method_with_depreciation_amount_based_on_num_days_in_month( + self, + ): asset = create_asset( calculate_depreciation=1, available_for_use_date="2023-01-01", @@ -740,7 +742,7 @@ class TestDepreciationMethods(AssetSetup): depreciation_start_date="2023-01-31", total_number_of_depreciations=12, frequency_of_depreciation=1, - daily_depreciation=1, + depreciation_amount_based_on_num_days_in_month=1, ) expected_schedules = [ @@ -1703,7 +1705,8 @@ def create_asset(**args): "total_number_of_depreciations": args.total_number_of_depreciations or 5, "expected_value_after_useful_life": args.expected_value_after_useful_life or 0, "depreciation_start_date": args.depreciation_start_date, - "daily_depreciation": args.daily_depreciation or 0, + "depreciation_amount_based_on_num_days_in_month": args.depreciation_amount_based_on_num_days_in_month + or 0, }, ) diff --git a/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json b/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json index ea1a8112843..cb7bc123686 100644 --- a/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json +++ b/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json @@ -8,7 +8,7 @@ "finance_book", "depreciation_method", "total_number_of_depreciations", - "daily_depreciation", + "depreciation_amount_based_on_num_days_in_month", "column_break_5", "frequency_of_depreciation", "depreciation_start_date", @@ -82,23 +82,23 @@ "fieldtype": "Percent", "label": "Rate of Depreciation" }, - { - "default": "0", - "depends_on": "eval:doc.depreciation_method == \"Straight Line\" || doc.depreciation_method == \"Manual\"", - "fieldname": "daily_depreciation", - "fieldtype": "Check", - "label": "Daily Depreciation" - }, { "fieldname": "salvage_value_percentage", "fieldtype": "Percent", "label": "Salvage Value Percentage" + }, + { + "default": "0", + "depends_on": "eval:doc.depreciation_method == \"Straight Line\" || doc.depreciation_method == \"Manual\"", + "fieldname": "depreciation_amount_based_on_num_days_in_month", + "fieldtype": "Check", + "label": "Depreciation amount based on number of days in the month" } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2023-09-29 15:39:52.740594", + "modified": "2023-11-03 22:21:52.090191", "modified_by": "Administrator", "module": "Assets", "name": "Asset Finance Book", diff --git a/erpnext/patches.txt b/erpnext/patches.txt index d7e3d5af4b7..b11af4b46e4 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -346,5 +346,6 @@ erpnext.patches.v14_0.rename_over_order_allowance_field erpnext.patches.v14_0.migrate_delivery_stop_lock_field execute:frappe.db.set_single_value("Payment Reconciliation", "invoice_limit", 50) execute:frappe.db.set_single_value("Payment Reconciliation", "payment_limit", 50) +erpnext.patches.v14_0.rename_daily_depreciation_to_depreciation_amount_based_on_num_days_in_month # below migration patch should always run last erpnext.patches.v14_0.migrate_gl_to_payment_ledger diff --git a/erpnext/patches/v14_0/rename_daily_depreciation_to_depreciation_amount_based_on_num_days_in_month.py b/erpnext/patches/v14_0/rename_daily_depreciation_to_depreciation_amount_based_on_num_days_in_month.py new file mode 100644 index 00000000000..9666c1734bb --- /dev/null +++ b/erpnext/patches/v14_0/rename_daily_depreciation_to_depreciation_amount_based_on_num_days_in_month.py @@ -0,0 +1,16 @@ +# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + + +from frappe.model.utils.rename_field import rename_field + + +def execute(): + try: + rename_field( + "Asset Finance Book", "daily_depreciation", "depreciation_amount_based_on_num_days_in_month" + ) + + except Exception as e: + if e.args[0] != 1054: + raise From 65dfef021a29e1ba6dfcec75293a8f33dbbf56fa Mon Sep 17 00:00:00 2001 From: Anand Baburajan Date: Sat, 4 Nov 2023 01:48:20 +0530 Subject: [PATCH 102/151] chore: rename depreciation_amount_based_on_num_days_in_month to daily_prorata_based [v14] (#37898) chore: rename depreciation_amount_based_on_num_days_in_month to daily_prorata_based --- erpnext/assets/doctype/asset/asset.py | 6 +++--- erpnext/assets/doctype/asset/test_asset.py | 7 +++---- .../asset_finance_book/asset_finance_book.json | 6 +++--- erpnext/patches.txt | 1 + ...n_num_days_in_month_to_daily_prorata_based.py | 16 ++++++++++++++++ 5 files changed, 26 insertions(+), 10 deletions(-) create mode 100644 erpnext/patches/v14_0/rename_depreciation_amount_based_on_num_days_in_month_to_daily_prorata_based.py diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index cb9dc62da5c..902a28b286c 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -1216,7 +1216,7 @@ def get_item_details(item_code, asset_category, gross_purchase_amount): "depreciation_method": d.depreciation_method, "total_number_of_depreciations": d.total_number_of_depreciations, "frequency_of_depreciation": d.frequency_of_depreciation, - "depreciation_amount_based_on_num_days_in_month": d.depreciation_amount_based_on_num_days_in_month, + "daily_prorata_based": d.daily_prorata_based, "salvage_value_percentage": d.salvage_value_percentage, "expected_value_after_useful_life": flt(gross_purchase_amount) * flt(d.salvage_value_percentage / 100), @@ -1396,7 +1396,7 @@ def get_straight_line_or_manual_depr_amount( ) # if the Depreciation Schedule is being modified after Asset Value Adjustment due to decrease in asset value elif asset.flags.decrease_in_asset_value_due_to_value_adjustment: - if row.depreciation_amount_based_on_num_days_in_month: + if row.daily_prorata_based: daily_depr_amount = ( flt(row.value_after_depreciation) - flt(row.expected_value_after_useful_life) ) / date_diff( @@ -1441,7 +1441,7 @@ def get_straight_line_or_manual_depr_amount( ) / number_of_pending_depreciations # if the Depreciation Schedule is being prepared for the first time else: - if row.depreciation_amount_based_on_num_days_in_month: + if row.daily_prorata_based: daily_depr_amount = ( flt(asset.gross_purchase_amount) - flt(asset.opening_accumulated_depreciation) diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index 1272b2eff33..3ab53e57ae3 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -731,7 +731,7 @@ class TestDepreciationMethods(AssetSetup): self.assertEqual(schedules, expected_schedules) - def test_schedule_for_straight_line_method_with_depreciation_amount_based_on_num_days_in_month( + def test_schedule_for_straight_line_method_with_daily_prorata_based( self, ): asset = create_asset( @@ -742,7 +742,7 @@ class TestDepreciationMethods(AssetSetup): depreciation_start_date="2023-01-31", total_number_of_depreciations=12, frequency_of_depreciation=1, - depreciation_amount_based_on_num_days_in_month=1, + daily_prorata_based=1, ) expected_schedules = [ @@ -1705,8 +1705,7 @@ def create_asset(**args): "total_number_of_depreciations": args.total_number_of_depreciations or 5, "expected_value_after_useful_life": args.expected_value_after_useful_life or 0, "depreciation_start_date": args.depreciation_start_date, - "depreciation_amount_based_on_num_days_in_month": args.depreciation_amount_based_on_num_days_in_month - or 0, + "daily_prorata_based": args.daily_prorata_based or 0, }, ) diff --git a/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json b/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json index cb7bc123686..7c059fde2df 100644 --- a/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json +++ b/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json @@ -8,7 +8,7 @@ "finance_book", "depreciation_method", "total_number_of_depreciations", - "depreciation_amount_based_on_num_days_in_month", + "daily_prorata_based", "column_break_5", "frequency_of_depreciation", "depreciation_start_date", @@ -90,9 +90,9 @@ { "default": "0", "depends_on": "eval:doc.depreciation_method == \"Straight Line\" || doc.depreciation_method == \"Manual\"", - "fieldname": "depreciation_amount_based_on_num_days_in_month", + "fieldname": "daily_prorata_based", "fieldtype": "Check", - "label": "Depreciation amount based on number of days in the month" + "label": "Depreciate based on daily pro-rata" } ], "index_web_pages_for_search": 1, diff --git a/erpnext/patches.txt b/erpnext/patches.txt index b11af4b46e4..578130f1b88 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -347,5 +347,6 @@ erpnext.patches.v14_0.migrate_delivery_stop_lock_field execute:frappe.db.set_single_value("Payment Reconciliation", "invoice_limit", 50) execute:frappe.db.set_single_value("Payment Reconciliation", "payment_limit", 50) erpnext.patches.v14_0.rename_daily_depreciation_to_depreciation_amount_based_on_num_days_in_month +erpnext.patches.v14_0.rename_depreciation_amount_based_on_num_days_in_month_to_daily_prorata_based # below migration patch should always run last erpnext.patches.v14_0.migrate_gl_to_payment_ledger diff --git a/erpnext/patches/v14_0/rename_depreciation_amount_based_on_num_days_in_month_to_daily_prorata_based.py b/erpnext/patches/v14_0/rename_depreciation_amount_based_on_num_days_in_month_to_daily_prorata_based.py new file mode 100644 index 00000000000..96a16ebf2b5 --- /dev/null +++ b/erpnext/patches/v14_0/rename_depreciation_amount_based_on_num_days_in_month_to_daily_prorata_based.py @@ -0,0 +1,16 @@ +# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + + +from frappe.model.utils.rename_field import rename_field + + +def execute(): + try: + rename_field( + "Asset Finance Book", "depreciation_amount_based_on_num_days_in_month", "daily_prorata_based" + ) + + except Exception as e: + if e.args[0] != 1054: + raise From 21b430a575143a258726386c430be4207ed5979d Mon Sep 17 00:00:00 2001 From: marination Date: Fri, 29 Sep 2023 15:19:09 +0530 Subject: [PATCH 103/151] fix: Set right party name in bank transaction - If party name and docname are different, set the docname in Bank Transaction (cherry picked from commit ff1dc72d740f32d75deb7fa6294e70b488f146b2) --- .../doctype/bank_transaction/auto_match_party.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/bank_transaction/auto_match_party.py b/erpnext/accounts/doctype/bank_transaction/auto_match_party.py index 5d94a08f2f0..671cde58a92 100644 --- a/erpnext/accounts/doctype/bank_transaction/auto_match_party.py +++ b/erpnext/accounts/doctype/bank_transaction/auto_match_party.py @@ -1,6 +1,7 @@ from typing import Tuple, Union import frappe +from frappe.core.utils import find from frappe.utils import flt from rapidfuzz import fuzz, process @@ -112,7 +113,8 @@ class AutoMatchbyPartyNameDescription: for party in parties: filters = {"status": "Active"} if party == "Employee" else {"disabled": 0} - names = frappe.get_all(party, filters=filters, pluck=party.lower() + "_name") + field = party.lower() + "_name" + names = frappe.get_all(party, filters=filters, fields=[f"{field} as party_name", "name"]) for field in ["bank_party_name", "description"]: if not self.get(field): @@ -131,12 +133,18 @@ class AutoMatchbyPartyNameDescription: def fuzzy_search_and_return_result(self, party, names, field) -> Union[Tuple, None]: skip = False - result = process.extract(query=self.get(field), choices=names, scorer=fuzz.token_set_ratio) + result = process.extract( + query=self.get(field), + choices=[name.get("party_name") for name in names], + scorer=fuzz.token_set_ratio, + ) party_name, skip = self.process_fuzzy_result(result) if not party_name: return None, skip + # Get Party Docname from the list of dicts + party_name = find(names, lambda x: x["party_name"] == party_name).get("name") return ( party, party_name, From b0a184e937553195862607b945f90b66148120d7 Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 31 Oct 2023 16:53:08 +0100 Subject: [PATCH 104/151] fix: Use `process.extract` to get the corresponding party doc name of the result - rapidfuzz accepts an iterable or a dict. dict input gives the dict key and value in the result (cherry picked from commit e0a03789aef1891273ed6fe5a83b182d3f76e1b7) --- .../doctype/bank_transaction/auto_match_party.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/erpnext/accounts/doctype/bank_transaction/auto_match_party.py b/erpnext/accounts/doctype/bank_transaction/auto_match_party.py index 671cde58a92..04dab4c28a0 100644 --- a/erpnext/accounts/doctype/bank_transaction/auto_match_party.py +++ b/erpnext/accounts/doctype/bank_transaction/auto_match_party.py @@ -1,7 +1,6 @@ from typing import Tuple, Union import frappe -from frappe.core.utils import find from frappe.utils import flt from rapidfuzz import fuzz, process @@ -135,7 +134,7 @@ class AutoMatchbyPartyNameDescription: skip = False result = process.extract( query=self.get(field), - choices=[name.get("party_name") for name in names], + choices={row.get("name"): row.get("party_name") for row in names}, scorer=fuzz.token_set_ratio, ) party_name, skip = self.process_fuzzy_result(result) @@ -143,8 +142,6 @@ class AutoMatchbyPartyNameDescription: if not party_name: return None, skip - # Get Party Docname from the list of dicts - party_name = find(names, lambda x: x["party_name"] == party_name).get("name") return ( party, party_name, @@ -157,14 +154,14 @@ class AutoMatchbyPartyNameDescription: Returns: Result, Skip (whether or not to discontinue matching) """ - PARTY, SCORE, CUTOFF = 0, 1, 80 + SCORE, PARTY_ID, CUTOFF = 1, 2, 80 if not result or not len(result): return None, False first_result = result[0] if len(result) == 1: - return (first_result[PARTY] if first_result[SCORE] > CUTOFF else None), True + return (first_result[PARTY_ID] if first_result[SCORE] > CUTOFF else None), True second_result = result[1] if first_result[SCORE] > CUTOFF: @@ -173,7 +170,7 @@ class AutoMatchbyPartyNameDescription: if first_result[SCORE] == second_result[SCORE]: return None, True - return first_result[PARTY], True + return first_result[PARTY_ID], True else: return None, False From be925d2d33370907291b17fc303696d92486d9b6 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sun, 5 Nov 2023 05:22:09 +0530 Subject: [PATCH 105/151] refactor: avoid precision based validation error on reconciliation --- erpnext/accounts/utils.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 53e1c1d76dc..01724ff55ca 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -511,7 +511,7 @@ def check_if_advance_entry_modified(args): party_account_field = ( "paid_from" if erpnext.get_party_account_type(args.party_type) == "Receivable" else "paid_to" ) - + precision = frappe.get_precision("Payment Entry", "unallocated_amount") if args.voucher_detail_no: ret = frappe.db.sql( """select t1.name @@ -533,9 +533,9 @@ def check_if_advance_entry_modified(args): 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 + and round(unallocated_amount, {1}) = %(unreconciled_amount)s """.format( - party_account_field + party_account_field, precision ), args, ) From eb3630bcf8b16d51d5a36a4ba9556eb572c2acb7 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sun, 5 Nov 2023 06:16:23 +0000 Subject: [PATCH 106/151] fix: Quality Inspection Parameter migration - DuplicateEntryError due to case sensitivity (backport #37499) (#37916) fix: Quality Inspection Parameter migration - DuplicateEntryError due to case sensitivity (#37499) * fix: account for case-insensitive database primary key for parameter names * chore: linting (cherry picked from commit b099590b2c1dcd041b833af50e99eb3e7988c595) Co-authored-by: Richard Case <110036763+casesolved-co-uk@users.noreply.github.com> --- .../convert_qi_parameter_to_link_field.py | 31 ++++++++++--------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/erpnext/patches/v13_0/convert_qi_parameter_to_link_field.py b/erpnext/patches/v13_0/convert_qi_parameter_to_link_field.py index efbb96c100a..e53bdf8f19e 100644 --- a/erpnext/patches/v13_0/convert_qi_parameter_to_link_field.py +++ b/erpnext/patches/v13_0/convert_qi_parameter_to_link_field.py @@ -3,23 +3,24 @@ import frappe def execute(): frappe.reload_doc("stock", "doctype", "quality_inspection_parameter") + params = set() - # get all distinct parameters from QI readigs table - reading_params = frappe.db.get_all( - "Quality Inspection Reading", fields=["distinct specification"] - ) - reading_params = [d.specification for d in reading_params] + # get all parameters from QI readings table + for (p,) in frappe.db.get_all( + "Quality Inspection Reading", fields=["specification"], as_list=True + ): + params.add(p.strip()) - # get all distinct parameters from QI Template as some may be unused in QI - template_params = frappe.db.get_all( - "Item Quality Inspection Parameter", fields=["distinct specification"] - ) - template_params = [d.specification for d in template_params] + # get all parameters from QI Template as some may be unused in QI + for (p,) in frappe.db.get_all( + "Item Quality Inspection Parameter", fields=["specification"], as_list=True + ): + params.add(p.strip()) - params = list(set(reading_params + template_params)) + # because db primary keys are case insensitive, so duplicates will cause an exception + params = set({x.casefold(): x for x in params}.values()) for parameter in params: - if not frappe.db.exists("Quality Inspection Parameter", parameter): - frappe.get_doc( - {"doctype": "Quality Inspection Parameter", "parameter": parameter, "description": parameter} - ).insert(ignore_permissions=True) + frappe.get_doc( + {"doctype": "Quality Inspection Parameter", "parameter": parameter, "description": parameter} + ).insert(ignore_permissions=True) From 05fed9e352f0f1d4f81821d64547412cd93ed90a Mon Sep 17 00:00:00 2001 From: viralkansodiya15 <98073516+viralpatel15@users.noreply.github.com> Date: Sun, 5 Nov 2023 11:54:39 +0530 Subject: [PATCH 107/151] fix: list index out of range (#37880) * fix: list index out of range * fix: changes in error message * fix: change error message and date comparision * fix: change a return a value when disposal date is equal to available for use date * fix: changes to fix linter test failing --- erpnext/assets/doctype/asset/depreciation.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/erpnext/assets/doctype/asset/depreciation.py b/erpnext/assets/doctype/asset/depreciation.py index f5fd5d60221..8e5658e318e 100644 --- a/erpnext/assets/doctype/asset/depreciation.py +++ b/erpnext/assets/doctype/asset/depreciation.py @@ -740,6 +740,15 @@ def get_disposal_account_and_cost_center(company): def get_value_after_depreciation_on_disposal_date(asset, disposal_date, finance_book=None): asset_doc = frappe.get_doc("Asset", asset) + if asset_doc.available_for_use_date > getdate(disposal_date): + frappe.throw( + "Disposal date {0} cannot be before available for use date {1} of the asset.".format( + disposal_date, asset_doc.available_for_use_date + ) + ) + elif asset_doc.available_for_use_date == getdate(disposal_date): + return flt(asset_doc.gross_purchase_amount - asset_doc.opening_accumulated_depreciation) + if asset_doc.calculate_depreciation: asset_doc.prepare_depreciation_data(getdate(disposal_date)) From 5e1a89b3f919251a85fe6e1ec21edace48148efe Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sun, 5 Nov 2023 17:56:19 +0530 Subject: [PATCH 108/151] fix: POS change amount gl entry with no amount (#37799) fix: POS change amount gl entry with no amount (#37799) (cherry picked from commit 2b02ef00664b12812a99059bd9a29827231fdd94) Co-authored-by: Deepesh Garg --- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 7f124f541f3..c6d1ec31019 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1036,7 +1036,7 @@ class SalesInvoice(SellingController): def make_customer_gl_entry(self, gl_entries): # Checked both rounding_adjustment and rounded_total - # because rounded_total had value even before introcution of posting GLE based on rounded total + # because rounded_total had value even before introduction of posting GLE based on rounded total grand_total = ( self.rounded_total if (self.rounding_adjustment and self.rounded_total) else self.grand_total ) @@ -1271,7 +1271,7 @@ class SalesInvoice(SellingController): if skip_change_gl_entries and payment_mode.account == self.account_for_change_amount: payment_mode.base_amount -= flt(self.change_amount) - if payment_mode.amount: + if payment_mode.base_amount: # POS, make payment entries gl_entries.append( self.get_gl_dict( From 6eb867a1e41197fa267a753f2ad1e9729aff5aee Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 3 Nov 2023 14:37:23 +0530 Subject: [PATCH 109/151] refactor: flag to toggle billed amy update in DN for Credit Note (cherry picked from commit a3191f1c8c08c5723d35f4cf05d44f123ec4ca2e) # Conflicts: # erpnext/accounts/doctype/sales_invoice/sales_invoice.json --- .../doctype/sales_invoice/sales_invoice.json | 12 ++++++++++++ .../accounts/doctype/sales_invoice/sales_invoice.py | 3 +++ 2 files changed, 15 insertions(+) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index 5fc711d893b..f51482f6fc9 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -26,6 +26,7 @@ "is_return", "return_against", "update_billed_amount_in_sales_order", + "update_billed_amount_in_delivery_note", "is_debit_note", "amended_from", "accounting_dimensions_section", @@ -2144,6 +2145,13 @@ "fieldname": "use_company_roundoff_cost_center", "fieldtype": "Check", "label": "Use Company default Cost Center for Round off" + }, + { + "default": "0", + "depends_on": "eval: doc.is_return", + "fieldname": "update_billed_amount_in_delivery_note", + "fieldtype": "Check", + "label": "Update Billed Amount in Delivery Note" } ], "icon": "fa fa-file-text", @@ -2156,7 +2164,11 @@ "link_fieldname": "consolidated_invoice" } ], +<<<<<<< HEAD "modified": "2023-06-19 16:02:05.309332", +======= + "modified": "2023-11-03 14:39:38.012346", +>>>>>>> a3191f1c8c (refactor: flag to toggle billed amy update in DN for Credit Note) "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice", diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index c6d1ec31019..339c5c2778b 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -261,6 +261,7 @@ class SalesInvoice(SellingController): self.update_status_updater_args() self.update_prevdoc_status() + self.update_billing_status_in_dn() self.clear_unallocated_mode_of_payments() @@ -1433,6 +1434,8 @@ class SalesInvoice(SellingController): ) def update_billing_status_in_dn(self, update_modified=True): + if self.is_return and not self.update_billed_amount_in_delivery_note: + return updated_delivery_notes = [] for d in self.get("items"): if d.dn_detail: From 71ad448281f18d6ebecfaed325dceee79df25e17 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sun, 5 Nov 2023 08:32:27 +0530 Subject: [PATCH 110/151] refactor(test): enable billed amt update on Sales Return(Cr Note) (cherry picked from commit 0c5bdbdcf3fd408b793c755900ba7e3bf958d482) --- erpnext/accounts/doctype/sales_invoice/sales_invoice.json | 4 ---- erpnext/stock/doctype/delivery_note/test_delivery_note.py | 2 ++ 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index f51482f6fc9..5ec612f6b0f 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -2164,11 +2164,7 @@ "link_fieldname": "consolidated_invoice" } ], -<<<<<<< HEAD - "modified": "2023-06-19 16:02:05.309332", -======= "modified": "2023-11-03 14:39:38.012346", ->>>>>>> a3191f1c8c (refactor: flag to toggle billed amy update in DN for Credit Note) "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice", diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py index b66647c97a5..65828f3a4ac 100644 --- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py @@ -1056,6 +1056,7 @@ class TestDeliveryNote(FrappeTestCase): dn1 = create_delivery_note(is_return=1, return_against=dn.name, qty=-3) si1 = make_sales_invoice(dn1.name) + si1.update_billed_amount_in_delivery_note = True si1.insert() si1.submit() dn1.reload() @@ -1064,6 +1065,7 @@ class TestDeliveryNote(FrappeTestCase): dn2 = create_delivery_note(is_return=1, return_against=dn.name, qty=-4) si2 = make_sales_invoice(dn2.name) + si2.update_billed_amount_in_delivery_note = True si2.insert() si2.submit() dn2.reload() From 4de97a4fcb4e38d1e49a92cb528f815bdde1a35f Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 6 Nov 2023 11:40:21 +0530 Subject: [PATCH 111/151] feat: reserved production plan sub assembly items (backport #37884) (#37926) feat: reserved production plan sub assembly items (#37884) (cherry picked from commit 34d3eb88b39ffb437fc111ed03527f2c3c4baf9b) Co-authored-by: rohitwaghchaure --- .../production_plan/production_plan.json | 9 +- .../production_plan/production_plan.py | 44 ++++++++- .../production_plan/test_production_plan.py | 91 ++++++++++++++++++- .../production_plan_sub_assembly_item.json | 27 ++---- .../doctype/work_order/work_order.py | 36 +++++++- erpnext/stock/doctype/bin/bin.py | 30 +++++- 6 files changed, 212 insertions(+), 25 deletions(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.json b/erpnext/manufacturing/doctype/production_plan/production_plan.json index 4a0041662bc..49386c4ebc4 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.json +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.json @@ -36,6 +36,7 @@ "prod_plan_references", "section_break_24", "combine_sub_items", + "sub_assembly_warehouse", "section_break_ucc4", "skip_available_sub_assembly_item", "column_break_igxl", @@ -416,13 +417,19 @@ { "fieldname": "column_break_igxl", "fieldtype": "Column Break" + }, + { + "fieldname": "sub_assembly_warehouse", + "fieldtype": "Link", + "label": "Sub Assembly Warehouse", + "options": "Warehouse" } ], "icon": "fa fa-calendar", "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2023-09-29 11:41:03.246059", + "modified": "2023-11-03 14:08:11.928027", "modified_by": "Administrator", "module": "Manufacturing", "name": "Production Plan", diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index 70bdcccdb8a..2d202386263 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -490,6 +490,12 @@ class ProductionPlan(Document): bin = frappe.get_doc("Bin", bin_name, for_update=True) bin.update_reserved_qty_for_production_plan() + for d in self.sub_assembly_items: + if d.fg_warehouse and d.type_of_manufacturing == "In House": + bin_name = get_or_make_bin(d.production_item, d.fg_warehouse) + bin = frappe.get_doc("Bin", bin_name, for_update=True) + bin.update_reserved_qty_for_for_sub_assembly() + def delete_draft_work_order(self): for d in frappe.get_all( "Work Order", fields=["name"], filters={"docstatus": 0, "production_plan": ("=", self.name)} @@ -808,7 +814,11 @@ class ProductionPlan(Document): bom_data = [] - warehouse = row.warehouse if self.skip_available_sub_assembly_item else None + warehouse = ( + (self.sub_assembly_warehouse or row.warehouse) + if self.skip_available_sub_assembly_item + else None + ) get_sub_assembly_items(row.bom_no, bom_data, row.planned_qty, self.company, warehouse=warehouse) self.set_sub_assembly_items_based_on_level(row, bom_data, manufacturing_type) sub_assembly_items_store.extend(bom_data) @@ -830,7 +840,7 @@ class ProductionPlan(Document): for data in bom_data: data.qty = data.stock_qty data.production_plan_item = row.name - data.fg_warehouse = row.warehouse + data.fg_warehouse = self.sub_assembly_warehouse or row.warehouse data.schedule_date = row.planned_start_date data.type_of_manufacturing = manufacturing_type or ( "Subcontract" if data.is_sub_contracted_item else "In House" @@ -1636,8 +1646,8 @@ def get_reserved_qty_for_production_plan(item_code, warehouse): query = query.run() - if not query: - return 0.0 + if not query or query[0][0] is None: + return None reserved_qty_for_production_plan = flt(query[0][0]) @@ -1779,3 +1789,29 @@ def sales_order_query( query = query.offset(start) return query.run() + + +def get_reserved_qty_for_sub_assembly(item_code, warehouse): + table = frappe.qb.DocType("Production Plan") + child = frappe.qb.DocType("Production Plan Sub Assembly Item") + + query = ( + frappe.qb.from_(table) + .inner_join(child) + .on(table.name == child.parent) + .select(Sum(child.qty - IfNull(child.wo_produced_qty, 0))) + .where( + (table.docstatus == 1) + & (child.production_item == item_code) + & (child.fg_warehouse == warehouse) + & (table.status.notin(["Completed", "Closed"])) + ) + ) + + query = query.run() + + if not query or query[0][0] is None: + return None + + qty = flt(query[0][0]) + return qty if qty > 0 else 0.0 diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py index 5042ae9b5fc..749c33f01bb 100644 --- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py @@ -1031,13 +1031,14 @@ class TestProductionPlan(FrappeTestCase): after_qty = flt(frappe.db.get_value("Bin", bin_name, "reserved_qty_for_production_plan")) self.assertEqual(after_qty - before_qty, 1) - pln = frappe.get_doc("Production Plan", pln.name) pln.cancel() bin_name = get_or_make_bin("Raw Material Item 1", "_Test Warehouse - _TC") after_qty = flt(frappe.db.get_value("Bin", bin_name, "reserved_qty_for_production_plan")) + pln.reload() + self.assertEqual(pln.docstatus, 2) self.assertEqual(after_qty, before_qty) def test_resered_qty_for_production_plan_for_work_order(self): @@ -1348,6 +1349,93 @@ class TestProductionPlan(FrappeTestCase): if row.item_code == "ChildPart2 For SUB Test": self.assertEqual(row.quantity, 2) + def test_reserve_sub_assembly_items(self): + from erpnext.manufacturing.doctype.bom.test_bom import create_nested_bom + from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse + + bom_tree = { + "Fininshed Goods Bicycle": { + "Frame Assembly": {"Frame": {}}, + "Chain Assembly": {"Chain": {}}, + } + } + parent_bom = create_nested_bom(bom_tree, prefix="") + + warehouse = "_Test Warehouse - _TC" + company = "_Test Company" + + sub_assembly_warehouse = create_warehouse("SUB ASSEMBLY WH", company=company) + + for item_code in ["Frame", "Chain"]: + make_stock_entry(item_code=item_code, target=warehouse, qty=2, basic_rate=100) + + before_qty = flt( + frappe.db.get_value( + "Bin", + {"item_code": "Frame Assembly", "warehouse": sub_assembly_warehouse}, + "reserved_qty_for_production_plan", + ) + ) + + plan = create_production_plan( + item_code=parent_bom.item, + planned_qty=2, + ignore_existing_ordered_qty=1, + do_not_submit=1, + skip_available_sub_assembly_item=1, + warehouse=warehouse, + sub_assembly_warehouse=sub_assembly_warehouse, + ) + + plan.get_sub_assembly_items() + plan.submit() + + after_qty = flt( + frappe.db.get_value( + "Bin", + {"item_code": "Frame Assembly", "warehouse": sub_assembly_warehouse}, + "reserved_qty_for_production_plan", + ) + ) + + self.assertEqual(after_qty, before_qty + 2) + + plan.make_work_order() + work_orders = frappe.get_all( + "Work Order", + fields=["name", "production_item"], + filters={"production_plan": plan.name}, + order_by="creation desc", + ) + + for d in work_orders: + wo_doc = frappe.get_doc("Work Order", d.name) + wo_doc.skip_transfer = 1 + wo_doc.from_wip_warehouse = 1 + + wo_doc.wip_warehouse = ( + warehouse + if d.production_item in ["Frame Assembly", "Chain Assembly"] + else sub_assembly_warehouse + ) + + wo_doc.submit() + + if d.production_item == "Frame Assembly": + self.assertEqual(wo_doc.fg_warehouse, sub_assembly_warehouse) + se_doc = frappe.get_doc(make_se_from_wo(wo_doc.name, "Manufacture", 2)) + se_doc.submit() + + after_qty = flt( + frappe.db.get_value( + "Bin", + {"item_code": "Frame Assembly", "warehouse": sub_assembly_warehouse}, + "reserved_qty_for_production_plan", + ) + ) + + self.assertEqual(after_qty, before_qty) + def create_production_plan(**args): """ @@ -1368,6 +1456,7 @@ def create_production_plan(**args): "ignore_existing_ordered_qty": args.ignore_existing_ordered_qty or 0, "get_items_from": "Sales Order", "skip_available_sub_assembly_item": args.skip_available_sub_assembly_item or 0, + "sub_assembly_warehouse": args.sub_assembly_warehouse, } ) diff --git a/erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.json b/erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.json index fde0404c019..aff740b732e 100644 --- a/erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.json +++ b/erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.json @@ -17,11 +17,10 @@ "type_of_manufacturing", "supplier", "work_order_details_section", - "work_order", + "wo_produced_qty", "purchase_order", "production_plan_item", "column_break_7", - "produced_qty", "received_qty", "indent", "section_break_19", @@ -52,13 +51,6 @@ "fieldtype": "Section Break", "label": "Reference" }, - { - "fieldname": "work_order", - "fieldtype": "Link", - "label": "Work Order", - "options": "Work Order", - "read_only": 1 - }, { "fieldname": "column_break_7", "fieldtype": "Column Break" @@ -81,7 +73,8 @@ { "fieldname": "received_qty", "fieldtype": "Float", - "label": "Received Qty" + "label": "Received Qty", + "read_only": 1 }, { "fieldname": "bom_no", @@ -161,12 +154,6 @@ "label": "Target Warehouse", "options": "Warehouse" }, - { - "fieldname": "produced_qty", - "fieldtype": "Data", - "label": "Produced Quantity", - "read_only": 1 - }, { "default": "In House", "fieldname": "type_of_manufacturing", @@ -209,12 +196,18 @@ "label": "Projected Qty", "no_copy": 1, "read_only": 1 + }, + { + "fieldname": "wo_produced_qty", + "fieldtype": "Float", + "label": "Produced Qty", + "read_only": 1 } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2023-05-22 17:52:34.708879", + "modified": "2023-11-03 13:33:42.959387", "modified_by": "Administrator", "module": "Manufacturing", "name": "Production Plan Sub Assembly Item", diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index 93d015dc93b..f95b4f66a33 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -297,6 +297,7 @@ class WorkOrder(Document): update_produced_qty_in_so_item(self.sales_order, self.sales_order_item) if self.production_plan: + self.set_produced_qty_for_sub_assembly_item() self.update_production_plan_status() def get_transferred_or_manufactured_qty(self, purpose): @@ -544,16 +545,49 @@ class WorkOrder(Document): ) def update_planned_qty(self): + from erpnext.manufacturing.doctype.production_plan.production_plan import ( + get_reserved_qty_for_sub_assembly, + ) + + qty_dict = {"planned_qty": get_planned_qty(self.production_item, self.fg_warehouse)} + + if self.production_plan_sub_assembly_item and self.production_plan: + qty_dict["reserved_qty_for_production_plan"] = get_reserved_qty_for_sub_assembly( + self.production_item, self.fg_warehouse + ) + update_bin_qty( self.production_item, self.fg_warehouse, - {"planned_qty": get_planned_qty(self.production_item, self.fg_warehouse)}, + qty_dict, ) if self.material_request: mr_obj = frappe.get_doc("Material Request", self.material_request) mr_obj.update_requested_qty([self.material_request_item]) + def set_produced_qty_for_sub_assembly_item(self): + table = frappe.qb.DocType("Work Order") + + query = ( + frappe.qb.from_(table) + .select(Sum(table.produced_qty)) + .where( + (table.production_plan == self.production_plan) + & (table.production_plan_sub_assembly_item == self.production_plan_sub_assembly_item) + & (table.docstatus == 1) + ) + ).run() + + produced_qty = flt(query[0][0]) if query else 0 + + frappe.db.set_value( + "Production Plan Sub Assembly Item", + self.production_plan_sub_assembly_item, + "wo_produced_qty", + produced_qty, + ) + def update_ordered_qty(self): if ( self.production_plan diff --git a/erpnext/stock/doctype/bin/bin.py b/erpnext/stock/doctype/bin/bin.py index 5abea9e69fe..bd6d7241c2b 100644 --- a/erpnext/stock/doctype/bin/bin.py +++ b/erpnext/stock/doctype/bin/bin.py @@ -34,10 +34,15 @@ class Bin(Document): get_reserved_qty_for_production_plan, ) - self.reserved_qty_for_production_plan = get_reserved_qty_for_production_plan( + reserved_qty_for_production_plan = get_reserved_qty_for_production_plan( self.item_code, self.warehouse ) + if reserved_qty_for_production_plan is None and not self.reserved_qty_for_production_plan: + return + + self.reserved_qty_for_production_plan = flt(reserved_qty_for_production_plan) + self.db_set( "reserved_qty_for_production_plan", flt(self.reserved_qty_for_production_plan), @@ -48,6 +53,29 @@ class Bin(Document): self.set_projected_qty() self.db_set("projected_qty", self.projected_qty, update_modified=True) + def update_reserved_qty_for_for_sub_assembly(self): + from erpnext.manufacturing.doctype.production_plan.production_plan import ( + get_reserved_qty_for_sub_assembly, + ) + + reserved_qty_for_production_plan = get_reserved_qty_for_sub_assembly( + self.item_code, self.warehouse + ) + + if reserved_qty_for_production_plan is None and not self.reserved_qty_for_production_plan: + return + + self.reserved_qty_for_production_plan = flt(reserved_qty_for_production_plan) + self.set_projected_qty() + + self.db_set( + { + "projected_qty": self.projected_qty, + "reserved_qty_for_production_plan": flt(self.reserved_qty_for_production_plan), + }, + update_modified=True, + ) + def update_reserved_qty_for_production(self): """Update qty reserved for production from Production Item tables in open work orders""" From c87012046dd90bba4134351e0cb42decad54d3ad Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 6 Nov 2023 17:26:37 +0530 Subject: [PATCH 112/151] fix: make `Material Request Item` required if `Material Request` is set in PO (backport #37928) (#37936) fix: make `Material Request Item` required if `Material Request` is set in PO (cherry picked from commit a9d91189b0e23ccb860e9f954e882ee0ebebbc2c) Co-authored-by: s-aga-r --- .../doctype/purchase_order_item/purchase_order_item.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json index ec14768aff0..bd7532e9554 100644 --- a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json +++ b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json @@ -471,6 +471,7 @@ "fieldname": "material_request", "fieldtype": "Link", "label": "Material Request", + "mandatory_depends_on": "eval: doc.material_request_item", "no_copy": 1, "oldfieldname": "prevdoc_docname", "oldfieldtype": "Link", @@ -486,6 +487,7 @@ "fieldtype": "Data", "hidden": 1, "label": "Material Request Item", + "mandatory_depends_on": "eval: doc.material_request", "no_copy": 1, "oldfieldname": "prevdoc_detail_docname", "oldfieldtype": "Data", @@ -915,7 +917,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2023-10-27 15:50:42.655573", + "modified": "2023-11-06 11:00:53.596417", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order Item", From 64658fc790a8bdbf24c0dc59cb2b0de0da52b174 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 6 Nov 2023 19:27:28 +0530 Subject: [PATCH 113/151] fix: don't reset rate if greater than zero in standalone debit note (backport #37935) (#37940) * fix: don't reset rate if greater than zero in standalone debit note (cherry picked from commit 5cce522ecdb7f13a001d4abbf6c3682088edb1b6) * fix(test): `test_gl_entries_for_standalone_debit_note` (cherry picked from commit f9fc6c9c9d5faef90f345df6e04ea4d2b8b8b69b) --------- Co-authored-by: s-aga-r --- .../purchase_invoice/test_purchase_invoice.py | 11 ++++-- erpnext/controllers/buying_controller.py | 38 +++++++++---------- 2 files changed, 27 insertions(+), 22 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index 1f60a11908c..153f62dccad 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -1718,9 +1718,14 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin): self.assertTrue(return_pi.docstatus == 1) def test_gl_entries_for_standalone_debit_note(self): - make_purchase_invoice(qty=5, rate=500, update_stock=True) + from erpnext.stock.doctype.item.test_item import make_item - returned_inv = make_purchase_invoice(qty=-5, rate=5, update_stock=True, is_return=True) + item_code = make_item(properties={"is_stock_item": 1}) + make_purchase_invoice(item_code=item_code, qty=5, rate=500, update_stock=True) + + returned_inv = make_purchase_invoice( + item_code=item_code, qty=-5, rate=5, update_stock=True, is_return=True + ) # override the rate with valuation rate sle = frappe.get_all( @@ -1730,7 +1735,7 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin): )[0] rate = flt(sle.stock_value_difference) / flt(sle.actual_qty) - self.assertAlmostEqual(returned_inv.items[0].rate, rate) + self.assertAlmostEqual(rate, 500) def test_payment_allocation_for_payment_terms(self): from erpnext.buying.doctype.purchase_order.test_purchase_order import ( diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index d0955cf5c9e..2a5f0b48228 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -78,26 +78,26 @@ class BuyingController(SubcontractingController): def set_rate_for_standalone_debit_note(self): if self.get("is_return") and self.get("update_stock") and not self.return_against: for row in self.items: + if row.rate <= 0: + # override the rate with valuation rate + row.rate = get_incoming_rate( + { + "item_code": row.item_code, + "warehouse": row.warehouse, + "posting_date": self.get("posting_date"), + "posting_time": self.get("posting_time"), + "qty": row.qty, + "serial_and_batch_bundle": row.get("serial_and_batch_bundle"), + "company": self.company, + "voucher_type": self.doctype, + "voucher_no": self.name, + }, + raise_error_if_no_rate=False, + ) - # override the rate with valuation rate - row.rate = get_incoming_rate( - { - "item_code": row.item_code, - "warehouse": row.warehouse, - "posting_date": self.get("posting_date"), - "posting_time": self.get("posting_time"), - "qty": row.qty, - "serial_and_batch_bundle": row.get("serial_and_batch_bundle"), - "company": self.company, - "voucher_type": self.doctype, - "voucher_no": self.name, - }, - raise_error_if_no_rate=False, - ) - - row.discount_percentage = 0.0 - row.discount_amount = 0.0 - row.margin_rate_or_amount = 0.0 + row.discount_percentage = 0.0 + row.discount_amount = 0.0 + row.margin_rate_or_amount = 0.0 def set_missing_values(self, for_validate=False): super(BuyingController, self).set_missing_values(for_validate) From 24da29a817674d1485090e1541354c3aaa6e8ef1 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 6 Nov 2023 19:28:19 +0530 Subject: [PATCH 114/151] fix: link between parent and child procedure (backport #37903) (#37943) * fix: link between parent and child procedure (cherry picked from commit 05f24ede96a30d194db9334a55706689f63462cb) * chore: add missing filters for `Parent Procedure` (cherry picked from commit 8fbd4cea5b374ddbd611f1c0ada26d9998d82b27) * test: add test case for Quality Procedure` (cherry picked from commit 30c6b83a103b262e7bd80601076fdb78a358c068) --------- Co-authored-by: s-aga-r --- .../quality_procedure/quality_procedure.js | 7 +- .../quality_procedure/quality_procedure.py | 62 ++++++-- .../test_quality_procedure.py | 143 ++++++++++++------ 3 files changed, 147 insertions(+), 65 deletions(-) diff --git a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.js b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.js index fd2b6a4eaa0..79fd2ebdbe9 100644 --- a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.js +++ b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.js @@ -3,10 +3,10 @@ frappe.ui.form.on('Quality Procedure', { refresh: function(frm) { - frm.set_query("procedure","processes", (frm) =>{ + frm.set_query('procedure', 'processes', (frm) =>{ return { filters: { - name: ["not in", [frm.parent_quality_procedure, frm.name]] + name: ['not in', [frm.parent_quality_procedure, frm.name]] } }; }); @@ -14,7 +14,8 @@ frappe.ui.form.on('Quality Procedure', { frm.set_query('parent_quality_procedure', function(){ return { filters: { - is_group: 1 + is_group: 1, + name: ['!=', frm.doc.name] } }; }); diff --git a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py index e8604080fbf..6834abc9d41 100644 --- a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py +++ b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py @@ -16,16 +16,13 @@ class QualityProcedure(NestedSet): def on_update(self): NestedSet.on_update(self) self.set_parent() + self.remove_parent_from_old_child() + self.add_child_to_parent() + self.remove_child_from_old_parent() def after_insert(self): self.set_parent() - - # add child to parent if missing - if self.parent_quality_procedure: - parent = frappe.get_doc("Quality Procedure", self.parent_quality_procedure) - if not [d for d in parent.processes if d.procedure == self.name]: - parent.append("processes", {"procedure": self.name, "process_description": self.name}) - parent.save() + self.add_child_to_parent() def on_trash(self): # clear from child table (sub procedures) @@ -36,15 +33,6 @@ class QualityProcedure(NestedSet): ) NestedSet.on_trash(self, allow_root_deletion=True) - def set_parent(self): - for process in self.processes: - # Set parent for only those children who don't have a parent - has_parent = frappe.db.get_value( - "Quality Procedure", process.procedure, "parent_quality_procedure" - ) - if not has_parent and process.procedure: - frappe.db.set_value(self.doctype, process.procedure, "parent_quality_procedure", self.name) - def check_for_incorrect_child(self): for process in self.processes: if process.procedure: @@ -61,6 +49,48 @@ class QualityProcedure(NestedSet): title=_("Invalid Child Procedure"), ) + def set_parent(self): + """Set `Parent Procedure` in `Child Procedures`""" + + for process in self.processes: + if process.procedure: + if not frappe.db.get_value("Quality Procedure", process.procedure, "parent_quality_procedure"): + frappe.db.set_value( + "Quality Procedure", process.procedure, "parent_quality_procedure", self.name + ) + + def remove_parent_from_old_child(self): + """Remove `Parent Procedure` from `Old Child Procedures`""" + + if old_doc := self.get_doc_before_save(): + if old_child_procedures := set([d.procedure for d in old_doc.processes if d.procedure]): + current_child_procedures = set([d.procedure for d in self.processes if d.procedure]) + + if removed_child_procedures := list(old_child_procedures.difference(current_child_procedures)): + for child_procedure in removed_child_procedures: + frappe.db.set_value("Quality Procedure", child_procedure, "parent_quality_procedure", None) + + def add_child_to_parent(self): + """Add `Child Procedure` to `Parent Procedure`""" + + if self.parent_quality_procedure: + parent = frappe.get_doc("Quality Procedure", self.parent_quality_procedure) + if not [d for d in parent.processes if d.procedure == self.name]: + parent.append("processes", {"procedure": self.name, "process_description": self.name}) + parent.save() + + def remove_child_from_old_parent(self): + """Remove `Child Procedure` from `Old Parent Procedure`""" + + if old_doc := self.get_doc_before_save(): + if old_parent := old_doc.parent_quality_procedure: + if self.parent_quality_procedure != old_parent: + parent = frappe.get_doc("Quality Procedure", old_parent) + for process in parent.processes: + if process.procedure == self.name: + parent.remove(process) + parent.save() + @frappe.whitelist() def get_children(doctype, parent=None, parent_quality_procedure=None, is_root=False): diff --git a/erpnext/quality_management/doctype/quality_procedure/test_quality_procedure.py b/erpnext/quality_management/doctype/quality_procedure/test_quality_procedure.py index 04e82112142..467186debd9 100644 --- a/erpnext/quality_management/doctype/quality_procedure/test_quality_procedure.py +++ b/erpnext/quality_management/doctype/quality_procedure/test_quality_procedure.py @@ -1,56 +1,107 @@ # Copyright (c) 2018, Frappe and Contributors # See license.txt -import unittest - import frappe +from frappe.tests.utils import FrappeTestCase from .quality_procedure import add_node -class TestQualityProcedure(unittest.TestCase): +class TestQualityProcedure(FrappeTestCase): def test_add_node(self): - try: - procedure = frappe.get_doc( - dict( - doctype="Quality Procedure", - quality_procedure_name="Test Procedure 1", - processes=[dict(process_description="Test Step 1")], - ) - ).insert() - - frappe.local.form_dict = frappe._dict( - doctype="Quality Procedure", - quality_procedure_name="Test Child 1", - parent_quality_procedure=procedure.name, - cmd="test", - is_root="false", - ) - node = add_node() - - procedure.reload() - - self.assertEqual(procedure.is_group, 1) - - # child row created - self.assertTrue([d for d in procedure.processes if d.procedure == node.name]) - - node.delete() - procedure.reload() - - # child unset - self.assertFalse([d for d in procedure.processes if d.name == node.name]) - - finally: - procedure.delete() - - -def create_procedure(): - return frappe.get_doc( - dict( - doctype="Quality Procedure", - quality_procedure_name="Test Procedure 1", - is_group=1, - processes=[dict(process_description="Test Step 1")], + procedure = create_procedure( + { + "quality_procedure_name": "Test Procedure 1", + "is_group": 1, + "processes": [dict(process_description="Test Step 1")], + } ) - ).insert() + + frappe.local.form_dict = frappe._dict( + doctype="Quality Procedure", + quality_procedure_name="Test Child 1", + parent_quality_procedure=procedure.name, + cmd="test", + is_root="false", + ) + node = add_node() + + procedure.reload() + + self.assertEqual(procedure.is_group, 1) + + # child row created + self.assertTrue([d for d in procedure.processes if d.procedure == node.name]) + + node.delete() + procedure.reload() + + # child unset + self.assertFalse([d for d in procedure.processes if d.name == node.name]) + + def test_remove_parent_from_old_child(self): + child_qp = create_procedure( + { + "quality_procedure_name": "Test Child 1", + "is_group": 0, + } + ) + group_qp = create_procedure( + { + "quality_procedure_name": "Test Group", + "is_group": 1, + "processes": [dict(procedure=child_qp.name)], + } + ) + + child_qp.reload() + self.assertEqual(child_qp.parent_quality_procedure, group_qp.name) + + group_qp.reload() + del group_qp.processes[0] + group_qp.save() + + child_qp.reload() + self.assertEqual(child_qp.parent_quality_procedure, None) + + def remove_child_from_old_parent(self): + child_qp = create_procedure( + { + "quality_procedure_name": "Test Child 1", + "is_group": 0, + } + ) + group_qp = create_procedure( + { + "quality_procedure_name": "Test Group", + "is_group": 1, + "processes": [dict(procedure=child_qp.name)], + } + ) + + group_qp.reload() + self.assertTrue([d for d in group_qp.processes if d.procedure == child_qp.name]) + + child_qp.reload() + self.assertEqual(child_qp.parent_quality_procedure, group_qp.name) + + child_qp.parent_quality_procedure = None + child_qp.save() + + group_qp.reload() + self.assertFalse([d for d in group_qp.processes if d.procedure == child_qp.name]) + + +def create_procedure(kwargs=None): + kwargs = frappe._dict(kwargs or {}) + + doc = frappe.new_doc("Quality Procedure") + doc.quality_procedure_name = kwargs.quality_procedure_name or "_Test Procedure" + doc.is_group = kwargs.is_group or 0 + + for process in kwargs.processes or []: + doc.append("processes", process) + + doc.insert() + + return doc From c53d281b9e6a5731da10bb110d4ddd2e85931707 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 6 Nov 2023 19:52:11 +0530 Subject: [PATCH 115/151] fix: add translation wrapper (backport #37911) (#37946) fix: add translation wrapper (cherry picked from commit 8722318081e30544b4cc76c97bdd1fe3362373d6) Co-authored-by: hyaray --- erpnext/manufacturing/doctype/work_order/work_order.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/work_order/work_order.js b/erpnext/manufacturing/doctype/work_order/work_order.js index c1a078d65e0..88747b8f7a6 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.js +++ b/erpnext/manufacturing/doctype/work_order/work_order.js @@ -710,7 +710,7 @@ erpnext.work_order = { return new Promise((resolve, reject) => { frappe.prompt({ fieldtype: 'Float', - label: __('Qty for {0}', [purpose]), + label: __('Qty for {0}', [__(purpose)]), fieldname: 'qty', description: __('Max: {0}', [max]), default: max From 3d72ec7ca484b70b4163f0194ccac6e53be76471 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 6 Nov 2023 20:23:26 +0530 Subject: [PATCH 116/151] fix: typo in AR report (cherry picked from commit 67e74d03edb5bd3190a44df811a30b2b130eeb39) --- .../report/accounts_receivable/accounts_receivable.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index f24a24e42ef..12e40037f1e 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -117,7 +117,7 @@ class ReceivablePayableReport(object): for ple in self.ple_entries: # get the balance object for voucher_type - if self.filters.get("ingore_accounts"): + if self.filters.get("ignore_accounts"): key = (ple.voucher_type, ple.voucher_no, ple.party) else: key = (ple.account, ple.voucher_type, ple.voucher_no, ple.party) @@ -188,7 +188,7 @@ class ReceivablePayableReport(object): ): return - if self.filters.get("ingore_accounts"): + if self.filters.get("ignore_accounts"): key = (ple.against_voucher_type, ple.against_voucher_no, ple.party) else: key = (ple.account, ple.against_voucher_type, ple.against_voucher_no, ple.party) @@ -200,7 +200,7 @@ class ReceivablePayableReport(object): if ple.against_voucher_no in self.return_entries: return_against = self.return_entries.get(ple.against_voucher_no) if return_against: - if self.filters.get("ingore_accounts"): + if self.filters.get("ignore_accounts"): key = (ple.against_voucher_type, return_against, ple.party) else: key = (ple.account, ple.against_voucher_type, return_against, ple.party) @@ -209,7 +209,7 @@ class ReceivablePayableReport(object): if not row: # no invoice, this is an invoice / stand-alone payment / credit note - if self.filters.get("ingore_accounts"): + if self.filters.get("ignore_accounts"): row = self.voucher_balance.get((ple.voucher_type, ple.voucher_no, ple.party)) else: row = self.voucher_balance.get((ple.account, ple.voucher_type, ple.voucher_no, ple.party)) From 38184f77c7d47b621c1ced07041a2f28f887aa3f Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 7 Nov 2023 12:12:27 +0530 Subject: [PATCH 117/151] fix: type error on new payment entry (cherry picked from commit adff2871606c8f0ed51971cd65db355d38032fb9) --- erpnext/accounts/doctype/payment_entry/payment_entry.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index d4ea816aa3b..d23ae877827 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -152,7 +152,7 @@ frappe.ui.form.on('Payment Entry', { frm.events.hide_unhide_fields(frm); frm.events.set_dynamic_labels(frm); frm.events.show_general_ledger(frm); - if(frm.doc.references.find((elem) => {return elem.exchange_gain_loss != 0})) { + if((frm.doc.references) && (frm.doc.references.find((elem) => {return elem.exchange_gain_loss != 0}))) { frm.add_custom_button(__("View Exchange Gain/Loss Journals"), function() { frappe.set_route("List", "Journal Entry", {"voucher_type": "Exchange Gain Or Loss", "reference_name": frm.doc.name}); }, __('Actions')); From 09cde5da08133cfd60d18983b01b1ea77e6a5ee1 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 7 Nov 2023 10:02:08 +0530 Subject: [PATCH 118/151] feat: settings page for repost (cherry picked from commit d582a7379580367e7d81bed58ed02773bd15d00c) --- .../__init__.py | 0 .../repost_accounting_ledger_settings.js | 8 ++++ .../repost_accounting_ledger_settings.json | 41 +++++++++++++++++ .../repost_accounting_ledger_settings.py | 9 ++++ .../test_repost_accounting_ledger_settings.py | 9 ++++ .../doctype/repost_allowed_types/__init__.py | 0 .../repost_allowed_types.json | 45 +++++++++++++++++++ .../repost_allowed_types.py | 9 ++++ 8 files changed, 121 insertions(+) create mode 100644 erpnext/accounts/doctype/repost_accounting_ledger_settings/__init__.py create mode 100644 erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.js create mode 100644 erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.json create mode 100644 erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.py create mode 100644 erpnext/accounts/doctype/repost_accounting_ledger_settings/test_repost_accounting_ledger_settings.py create mode 100644 erpnext/accounts/doctype/repost_allowed_types/__init__.py create mode 100644 erpnext/accounts/doctype/repost_allowed_types/repost_allowed_types.json create mode 100644 erpnext/accounts/doctype/repost_allowed_types/repost_allowed_types.py diff --git a/erpnext/accounts/doctype/repost_accounting_ledger_settings/__init__.py b/erpnext/accounts/doctype/repost_accounting_ledger_settings/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.js b/erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.js new file mode 100644 index 00000000000..8c83ca50431 --- /dev/null +++ b/erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.js @@ -0,0 +1,8 @@ +// Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +// frappe.ui.form.on("Repost Accounting Ledger Settings", { +// refresh(frm) { + +// }, +// }); diff --git a/erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.json b/erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.json new file mode 100644 index 00000000000..8aaa6e8f0a8 --- /dev/null +++ b/erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.json @@ -0,0 +1,41 @@ +{ + "actions": [], + "allow_rename": 1, + "creation": "2023-11-07 09:57:20.619939", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "allowed_types" + ], + "fields": [ + { + "fieldname": "allowed_types", + "fieldtype": "Table", + "label": "Allowed Doctypes", + "options": "Repost Allowed Types" + } + ], + "in_create": 1, + "issingle": 1, + "links": [], + "modified": "2023-11-07 10:12:07.519155", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Repost Accounting Ledger Settings", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.py b/erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.py new file mode 100644 index 00000000000..2b8230df86f --- /dev/null +++ b/erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.py @@ -0,0 +1,9 @@ +# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class RepostAccountingLedgerSettings(Document): + pass diff --git a/erpnext/accounts/doctype/repost_accounting_ledger_settings/test_repost_accounting_ledger_settings.py b/erpnext/accounts/doctype/repost_accounting_ledger_settings/test_repost_accounting_ledger_settings.py new file mode 100644 index 00000000000..ec4e87ffc00 --- /dev/null +++ b/erpnext/accounts/doctype/repost_accounting_ledger_settings/test_repost_accounting_ledger_settings.py @@ -0,0 +1,9 @@ +# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +# import frappe +from frappe.tests.utils import FrappeTestCase + + +class TestRepostAccountingLedgerSettings(FrappeTestCase): + pass diff --git a/erpnext/accounts/doctype/repost_allowed_types/__init__.py b/erpnext/accounts/doctype/repost_allowed_types/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/accounts/doctype/repost_allowed_types/repost_allowed_types.json b/erpnext/accounts/doctype/repost_allowed_types/repost_allowed_types.json new file mode 100644 index 00000000000..ede12fbc185 --- /dev/null +++ b/erpnext/accounts/doctype/repost_allowed_types/repost_allowed_types.json @@ -0,0 +1,45 @@ +{ + "actions": [], + "allow_rename": 1, + "creation": "2023-11-07 09:58:03.595382", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "document_type", + "column_break_sfzb", + "allowed" + ], + "fields": [ + { + "fieldname": "document_type", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Doctype", + "options": "DocType" + }, + { + "default": "0", + "fieldname": "allowed", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Allowed" + }, + { + "fieldname": "column_break_sfzb", + "fieldtype": "Column Break" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2023-11-07 10:01:39.217861", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Repost Allowed Types", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/repost_allowed_types/repost_allowed_types.py b/erpnext/accounts/doctype/repost_allowed_types/repost_allowed_types.py new file mode 100644 index 00000000000..0e4883b0c93 --- /dev/null +++ b/erpnext/accounts/doctype/repost_allowed_types/repost_allowed_types.py @@ -0,0 +1,9 @@ +# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class RepostAllowedTypes(Document): + pass From 560a45b3436299d062b21487f4f78c8b3f5fcfe3 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 7 Nov 2023 10:12:18 +0530 Subject: [PATCH 119/151] chore: patch to update default repost settings value (cherry picked from commit ebb186c8df6cd713800475d490962e75ea6ab6a6) # Conflicts: # erpnext/patches.txt --- erpnext/patches.txt | 7 +++++++ .../patches/v14_0/add_default_for_repost_settings.py | 12 ++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 erpnext/patches/v14_0/add_default_for_repost_settings.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 578130f1b88..bad878e3c54 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -346,7 +346,14 @@ erpnext.patches.v14_0.rename_over_order_allowance_field erpnext.patches.v14_0.migrate_delivery_stop_lock_field execute:frappe.db.set_single_value("Payment Reconciliation", "invoice_limit", 50) execute:frappe.db.set_single_value("Payment Reconciliation", "payment_limit", 50) +<<<<<<< HEAD erpnext.patches.v14_0.rename_daily_depreciation_to_depreciation_amount_based_on_num_days_in_month erpnext.patches.v14_0.rename_depreciation_amount_based_on_num_days_in_month_to_daily_prorata_based +======= +erpnext.patches.v14_0.add_default_for_repost_settings +erpnext.patches.v15_0.rename_daily_depreciation_to_depreciation_amount_based_on_num_days_in_month +erpnext.patches.v15_0.rename_depreciation_amount_based_on_num_days_in_month_to_daily_prorata_based +erpnext.patches.v15_0.set_reserved_stock_in_bin +>>>>>>> ebb186c8df (chore: patch to update default repost settings value) # below migration patch should always run last erpnext.patches.v14_0.migrate_gl_to_payment_ledger diff --git a/erpnext/patches/v14_0/add_default_for_repost_settings.py b/erpnext/patches/v14_0/add_default_for_repost_settings.py new file mode 100644 index 00000000000..6cafc66aab8 --- /dev/null +++ b/erpnext/patches/v14_0/add_default_for_repost_settings.py @@ -0,0 +1,12 @@ +import frappe + + +def execute(): + """ + Update Repost Accounting Ledger Settings with default values + """ + allowed_types = ["Sales Invoice", "Purchase Invoice", "Payment Entry", "Journal Entry"] + repost_settings = frappe.get_doc("Repost Accounting Ledger Settings") + for x in allowed_types: + repost_settings.append("allowed_types", {"document_type": x, "allowed": True}) + repost_settings.save() From 18e6931d787bdd8d87a9c68d7debc79f1da33fcb Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 7 Nov 2023 11:03:03 +0530 Subject: [PATCH 120/151] refactor: configurable repost settings (cherry picked from commit 5a068410c6ff4bc4ac100098c712d09bb70854df) --- .../repost_accounting_ledger.js | 4 +--- .../repost_accounting_ledger.py | 24 ++++++++++++++++--- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.js b/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.js index 3a87a380d19..c7b7a148cfb 100644 --- a/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.js +++ b/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.js @@ -5,9 +5,7 @@ frappe.ui.form.on("Repost Accounting Ledger", { setup: function(frm) { frm.fields_dict['vouchers'].grid.get_field('voucher_type').get_query = function(doc) { return { - filters: { - name: ['in', ['Purchase Invoice', 'Sales Invoice', 'Payment Entry', 'Journal Entry']], - } + query: "erpnext.accounts.doctype.repost_accounting_ledger.repost_accounting_ledger.get_repost_allowed_types" } } diff --git a/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py b/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py index dbb0971fdea..1d17a1c20e5 100644 --- a/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py +++ b/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py @@ -10,9 +10,12 @@ from frappe.utils.data import comma_and class RepostAccountingLedger(Document): def __init__(self, *args, **kwargs): super(RepostAccountingLedger, self).__init__(*args, **kwargs) - self._allowed_types = set( - ["Purchase Invoice", "Sales Invoice", "Payment Entry", "Journal Entry"] - ) + self._allowed_types = [ + x.document_type + for x in frappe.db.get_all( + "Repost Allowed Types", filters={"allowed": True}, fields=["document_type"] + ) + ] def validate(self): self.validate_vouchers() @@ -186,3 +189,18 @@ def validate_docs_for_deferred_accounting(sales_docs, purchase_docs): frappe.bold(comma_and([x[0] for x in docs_with_deferred_expense + docs_with_deferred_revenue])) ) ) + + +@frappe.whitelist() +@frappe.validate_and_sanitize_search_inputs +def get_repost_allowed_types(doctype, txt, searchfield, start, page_len, filters): + filters = {"allowed": True} + + if txt: + filters.update({"document_type": ("like", f"%{txt}%")}) + + if allowed_types := frappe.db.get_all( + "Repost Allowed Types", filters=filters, fields=["document_type"], as_list=1 + ): + return allowed_types + return [] From ed557274d2f070e598121fde109210d4bd860381 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 7 Nov 2023 11:10:42 +0530 Subject: [PATCH 121/151] refactor: support for expense claim repost (cherry picked from commit b651b36fff496a92713f634868a63a4ebe24f8c9) --- .../repost_accounting_ledger/repost_accounting_ledger.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py b/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py index 1d17a1c20e5..8aaafd0b16b 100644 --- a/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py +++ b/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py @@ -160,7 +160,7 @@ def start_repost(account_repost_doc=str) -> None: doc.docstatus = 1 doc.make_gl_entries() - elif doc.doctype in ["Payment Entry", "Journal Entry"]: + elif doc.doctype in ["Payment Entry", "Journal Entry", "Expense Claim"]: if not repost_doc.delete_cancelled_entries: doc.make_gl_entries(1) doc.make_gl_entries() From 35f54989132925a2cc9b30a126618bf053e58d79 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 7 Nov 2023 12:46:06 +0530 Subject: [PATCH 122/151] refactor(test): update repost settings for test cases (cherry picked from commit ac79b8483f0769ab7fd3dc4975b7be34e15b32b2) --- .../test_repost_accounting_ledger.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/erpnext/accounts/doctype/repost_accounting_ledger/test_repost_accounting_ledger.py b/erpnext/accounts/doctype/repost_accounting_ledger/test_repost_accounting_ledger.py index 0e75dd2e3e1..dda0ec778f6 100644 --- a/erpnext/accounts/doctype/repost_accounting_ledger/test_repost_accounting_ledger.py +++ b/erpnext/accounts/doctype/repost_accounting_ledger/test_repost_accounting_ledger.py @@ -20,10 +20,18 @@ class TestRepostAccountingLedger(AccountsTestMixin, FrappeTestCase): self.create_company() self.create_customer() self.create_item() + self.update_repost_settings() def teadDown(self): frappe.db.rollback() + def update_repost_settings(self): + allowed_types = ["Sales Invoice", "Purchase Invoice", "Payment Entry", "Journal Entry"] + repost_settings = frappe.get_doc("Repost Accounting Ledger Settings") + for x in allowed_types: + repost_settings.append("allowed_types", {"document_type": x, "allowed": True}) + repost_settings.save() + def test_01_basic_functions(self): si = create_sales_invoice( item=self.item, From 7d4dc67b6181d29c7f0e2f2133a6ed163eb8195d Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 7 Nov 2023 13:42:09 +0530 Subject: [PATCH 123/151] refactor: select distinct types (cherry picked from commit 61705047b037f91a735207d18635de6263af804d) --- .../repost_accounting_ledger/repost_accounting_ledger.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py b/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py index 8aaafd0b16b..69cfe9fcd74 100644 --- a/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py +++ b/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py @@ -13,7 +13,7 @@ class RepostAccountingLedger(Document): self._allowed_types = [ x.document_type for x in frappe.db.get_all( - "Repost Allowed Types", filters={"allowed": True}, fields=["document_type"] + "Repost Allowed Types", filters={"allowed": True}, fields=["distinct(document_type)"] ) ] @@ -200,7 +200,7 @@ def get_repost_allowed_types(doctype, txt, searchfield, start, page_len, filters filters.update({"document_type": ("like", f"%{txt}%")}) if allowed_types := frappe.db.get_all( - "Repost Allowed Types", filters=filters, fields=["document_type"], as_list=1 + "Repost Allowed Types", filters=filters, fields=["distinct(document_type)"], as_list=1 ): return allowed_types return [] From 82bf726b8352d8c916603578a72a33d85748cb92 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 7 Nov 2023 14:18:07 +0530 Subject: [PATCH 124/151] refactor(test): repost test case for purchase invoice (cherry picked from commit 11c8d9fcf1e7eef1890b719b37c823822b6a108d) --- .../doctype/purchase_invoice/test_purchase_invoice.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index 153f62dccad..dc2b37291e8 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -1838,6 +1838,12 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin): disable_dimension() def test_repost_accounting_entries(self): + # update repost settings + settings = frappe.get_doc("Repost Accounting Ledger Settings") + if not [x for x in settings.allowed_types if x.document_type == "Purchase Invoice"]: + settings.append("allowed_types", {"document_type": "Purchase Invoice", "allowed": True}) + settings.save() + pi = make_purchase_invoice( rate=1000, price_list_rate=1000, From 3da2597703a8f0883efb76462763b5a673ee52f8 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 7 Nov 2023 14:30:06 +0530 Subject: [PATCH 125/151] refactor: update permissions for repost settings (cherry picked from commit 10b9570429e16906f3f879228c065b668f63882b) --- .../repost_accounting_ledger_settings.json | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.json b/erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.json index 8aaa6e8f0a8..8aa0a840c7e 100644 --- a/erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.json +++ b/erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.json @@ -1,6 +1,5 @@ { "actions": [], - "allow_rename": 1, "creation": "2023-11-07 09:57:20.619939", "doctype": "DocType", "engine": "InnoDB", @@ -18,7 +17,7 @@ "in_create": 1, "issingle": 1, "links": [], - "modified": "2023-11-07 10:12:07.519155", + "modified": "2023-11-07 14:24:13.321522", "modified_by": "Administrator", "module": "Accounts", "name": "Repost Accounting Ledger Settings", @@ -30,12 +29,18 @@ "email": 1, "print": 1, "read": 1, - "role": "System Manager", + "role": "Administrator", "share": 1, "write": 1 + }, + { + "read": 1, + "role": "System Manager", + "select": 1 } ], "sort_field": "modified", "sort_order": "DESC", - "states": [] + "states": [], + "track_changes": 1 } \ No newline at end of file From 8f0cf1b7dc762bf73cb19fa73d90405f5be7c761 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 7 Nov 2023 15:56:35 +0530 Subject: [PATCH 126/151] chore: resolve conflict --- erpnext/patches.txt | 6 ------ 1 file changed, 6 deletions(-) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index bad878e3c54..86984819816 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -346,14 +346,8 @@ erpnext.patches.v14_0.rename_over_order_allowance_field erpnext.patches.v14_0.migrate_delivery_stop_lock_field execute:frappe.db.set_single_value("Payment Reconciliation", "invoice_limit", 50) execute:frappe.db.set_single_value("Payment Reconciliation", "payment_limit", 50) -<<<<<<< HEAD erpnext.patches.v14_0.rename_daily_depreciation_to_depreciation_amount_based_on_num_days_in_month erpnext.patches.v14_0.rename_depreciation_amount_based_on_num_days_in_month_to_daily_prorata_based -======= erpnext.patches.v14_0.add_default_for_repost_settings -erpnext.patches.v15_0.rename_daily_depreciation_to_depreciation_amount_based_on_num_days_in_month -erpnext.patches.v15_0.rename_depreciation_amount_based_on_num_days_in_month_to_daily_prorata_based -erpnext.patches.v15_0.set_reserved_stock_in_bin ->>>>>>> ebb186c8df (chore: patch to update default repost settings value) # below migration patch should always run last erpnext.patches.v14_0.migrate_gl_to_payment_ledger From e895bb3a6358447295f1398492de196d350b591f Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 7 Nov 2023 15:57:20 +0000 Subject: [PATCH 127/151] chore: typo in `Stock Entry` enqueue msg (backport #37970) (#37972) chore: typo in `Stock Entry` enqueue msg (cherry picked from commit ee60fa940cdcd6c2bd6c47d875e055275535d216) Co-authored-by: s-aga-r --- erpnext/stock/doctype/stock_entry/stock_entry.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index ebae337531c..5031625a6b8 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -168,7 +168,7 @@ class StockEntry(StockController): if self.is_enqueue_action(): frappe.msgprint( _( - "The task has been enqueued as a background job. In case there is any issue on processing in background, the system will add a comment about the error on this Stock Reconciliation and revert to the Draft stage" + "The task has been enqueued as a background job. In case there is any issue on processing in background, the system will add a comment about the error on this Stock Entry and revert to the Draft stage" ) ) self.queue_action("submit", timeout=2000) @@ -179,7 +179,7 @@ class StockEntry(StockController): if self.is_enqueue_action(): frappe.msgprint( _( - "The task has been enqueued as a background job. In case there is any issue on processing in background, the system will add a comment about the error on this Stock Reconciliation and revert to the Submitted stage" + "The task has been enqueued as a background job. In case there is any issue on processing in background, the system will add a comment about the error on this Stock Entry and revert to the Submitted stage" ) ) self.queue_action("cancel", timeout=2000) From e58604a90f101a02f71d7ae4ee835374bcc5ab3e Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 7 Nov 2023 17:38:07 +0530 Subject: [PATCH 128/151] refactor: optimize for speed (cherry picked from commit 416bd400bb94c1fe115f6929e9252c4ce9f3cd40) --- erpnext/utilities/bulk_transaction.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/erpnext/utilities/bulk_transaction.py b/erpnext/utilities/bulk_transaction.py index 5e57b317937..fcee2656445 100644 --- a/erpnext/utilities/bulk_transaction.py +++ b/erpnext/utilities/bulk_transaction.py @@ -192,9 +192,7 @@ def mark_retrired_transaction(log_doc, doc_name): record = 0 for d in log_doc.get("logger_data"): if d.transaction_name == doc_name and d.transaction_status == "Failed": - d.retried = 1 + frappe.db.set_value("Bulk Transaction Log Detail", d.name, "retried", 1) record = record + 1 - log_doc.save() - return record From 0b601d8cae64d217306794fdac5703f1fb4d34e9 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 3 Nov 2023 17:56:40 +0530 Subject: [PATCH 129/151] chore: performance optimization on payment ledger entry doctype (cherry picked from commit f14d1eb8716e1a816af4ed69f9dd1f6a4d30f035) --- .../payment_ledger_entry/payment_ledger_entry.json | 14 +++++++++----- 1 file changed, 9 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 6da2be986bb..90d922fb77b 100644 --- a/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.json +++ b/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.json @@ -64,7 +64,8 @@ "fieldtype": "Link", "in_standard_filter": 1, "label": "Voucher Type", - "options": "DocType" + "options": "DocType", + "search_index": 1 }, { "fieldname": "voucher_no", @@ -72,14 +73,16 @@ "in_list_view": 1, "in_standard_filter": 1, "label": "Voucher No", - "options": "voucher_type" + "options": "voucher_type", + "search_index": 1 }, { "fieldname": "against_voucher_type", "fieldtype": "Link", "in_standard_filter": 1, "label": "Against Voucher Type", - "options": "DocType" + "options": "DocType", + "search_index": 1 }, { "fieldname": "against_voucher_no", @@ -87,7 +90,8 @@ "in_list_view": 1, "in_standard_filter": 1, "label": "Against Voucher No", - "options": "against_voucher_type" + "options": "against_voucher_type", + "search_index": 1 }, { "fieldname": "amount", @@ -148,7 +152,7 @@ "in_create": 1, "index_web_pages_for_search": 1, "links": [], - "modified": "2023-10-30 16:15:00.470283", + "modified": "2023-11-03 16:39:58.904113", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Ledger Entry", From 3f8d58c9c6f8ddbc2071ae15523124f2ee16be40 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 8 Nov 2023 10:53:16 +0530 Subject: [PATCH 130/151] refactor: add voucher_detail_no to PLE doctype --- .../payment_ledger_entry/payment_ledger_entry.json | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) 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 90d922fb77b..3537abf81ad 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", @@ -147,12 +148,18 @@ "fieldname": "remarks", "fieldtype": "Text", "label": "Remarks" + }, + { + "fieldname": "voucher_detail_no", + "fieldtype": "Data", + "label": "Voucher Detail No", + "search_index": 1 } ], "in_create": 1, "index_web_pages_for_search": 1, "links": [], - "modified": "2023-11-03 16:39:58.904113", + "modified": "2023-11-08 10:53:10.664896", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Ledger Entry", From 043dc1b14bbcf85a2c87f062a538fc34c731af50 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 8 Nov 2023 23:36:17 +0530 Subject: [PATCH 131/151] fix: asset depreciation ledger (backport #37991) (#37992) * fix: asset depreciation ledger (#37991) * fix: include opening acc depr while calculating asset depr ledger report * chore: include opening acc depr properly in acc depr amt * chore: add cost_center in asset depr ledger report * fix: handle finance books properly in asset depr ledger report * chore: rename 'include default book entries' to 'include default FB entries' (cherry picked from commit 9a171db97f67f88fad1589016afb4e808571d04c) # Conflicts: # erpnext/accounts/report/balance_sheet/balance_sheet.js # erpnext/accounts/report/cash_flow/cash_flow.js # erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js # erpnext/accounts/report/trial_balance/trial_balance.js # erpnext/translations/pl.csv # erpnext/translations/tr.csv * chore: fix conflict in balance_sheet.js * chore: fix conflict in cash_flow.js * chore: fix conflict in consolidated_financial_statement.js * chore: fix conflict in trial_balance.js * chore: fix conflict in tr.csv * chore: fix conflict in pl.csv --------- Co-authored-by: Anand Baburajan --- .../asset_depreciation_ledger.js | 22 ++++++-- .../asset_depreciation_ledger.json | 6 +-- .../asset_depreciation_ledger.py | 51 ++++++++++++++----- .../report/balance_sheet/balance_sheet.js | 2 +- .../accounts/report/cash_flow/cash_flow.js | 2 +- .../consolidated_financial_statement.js | 2 +- .../accounts/report/financial_statements.py | 4 +- .../report/general_ledger/general_ledger.js | 2 +- .../report/general_ledger/general_ledger.py | 4 +- .../report/trial_balance/trial_balance.js | 2 +- .../report/trial_balance/trial_balance.py | 4 +- .../fixed_asset_register.js | 2 +- .../fixed_asset_register.py | 2 +- erpnext/translations/af.csv | 2 +- erpnext/translations/am.csv | 2 +- erpnext/translations/ar.csv | 2 +- erpnext/translations/bg.csv | 2 +- erpnext/translations/bn.csv | 2 +- erpnext/translations/bs.csv | 2 +- erpnext/translations/ca.csv | 2 +- erpnext/translations/cs.csv | 2 +- erpnext/translations/da.csv | 2 +- erpnext/translations/de.csv | 2 +- erpnext/translations/el.csv | 2 +- erpnext/translations/es.csv | 2 +- erpnext/translations/et.csv | 2 +- erpnext/translations/fa.csv | 2 +- erpnext/translations/fi.csv | 2 +- erpnext/translations/fr.csv | 2 +- erpnext/translations/gu.csv | 2 +- erpnext/translations/he.csv | 2 +- erpnext/translations/hi.csv | 2 +- erpnext/translations/hr.csv | 2 +- erpnext/translations/hu.csv | 2 +- erpnext/translations/id.csv | 2 +- erpnext/translations/is.csv | 2 +- erpnext/translations/it.csv | 2 +- erpnext/translations/ja.csv | 2 +- erpnext/translations/km.csv | 2 +- erpnext/translations/kn.csv | 2 +- erpnext/translations/ko.csv | 2 +- erpnext/translations/ku.csv | 2 +- erpnext/translations/lo.csv | 2 +- erpnext/translations/lt.csv | 2 +- erpnext/translations/lv.csv | 2 +- erpnext/translations/mk.csv | 2 +- erpnext/translations/ml.csv | 2 +- erpnext/translations/mr.csv | 2 +- erpnext/translations/ms.csv | 2 +- erpnext/translations/my.csv | 2 +- erpnext/translations/nl.csv | 2 +- erpnext/translations/no.csv | 2 +- erpnext/translations/pl.csv | 2 +- erpnext/translations/ps.csv | 2 +- erpnext/translations/pt-BR.csv | 2 +- erpnext/translations/pt.csv | 2 +- erpnext/translations/ro.csv | 2 +- erpnext/translations/ru.csv | 2 +- erpnext/translations/rw.csv | 2 +- erpnext/translations/si.csv | 2 +- erpnext/translations/sk.csv | 2 +- erpnext/translations/sl.csv | 2 +- erpnext/translations/sq.csv | 2 +- erpnext/translations/sr.csv | 2 +- erpnext/translations/sv.csv | 2 +- erpnext/translations/sw.csv | 2 +- erpnext/translations/ta.csv | 2 +- erpnext/translations/te.csv | 2 +- erpnext/translations/th.csv | 2 +- erpnext/translations/tr.csv | 2 +- erpnext/translations/uk.csv | 2 +- erpnext/translations/ur.csv | 2 +- erpnext/translations/uz.csv | 2 +- erpnext/translations/vi.csv | 2 +- erpnext/translations/zh.csv | 2 +- erpnext/translations/zh_tw.csv | 2 +- 76 files changed, 131 insertions(+), 100 deletions(-) diff --git a/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.js b/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.js index 126cd037955..12b94347e00 100644 --- a/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.js +++ b/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.js @@ -31,6 +31,18 @@ frappe.query_reports["Asset Depreciation Ledger"] = { "fieldtype": "Link", "options": "Asset" }, + { + "fieldname":"asset_category", + "label": __("Asset Category"), + "fieldtype": "Link", + "options": "Asset Category" + }, + { + "fieldname":"cost_center", + "label": __("Cost Center"), + "fieldtype": "Link", + "options": "Cost Center" + }, { "fieldname":"finance_book", "label": __("Finance Book"), @@ -38,10 +50,10 @@ frappe.query_reports["Asset Depreciation Ledger"] = { "options": "Finance Book" }, { - "fieldname":"asset_category", - "label": __("Asset Category"), - "fieldtype": "Link", - "options": "Asset Category" - } + "fieldname": "include_default_book_assets", + "label": __("Include Default FB Assets"), + "fieldtype": "Check", + "default": 1 + }, ] } diff --git a/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.json b/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.json index 0ef9d858dd5..9002e23ed33 100644 --- a/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.json +++ b/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.json @@ -1,15 +1,15 @@ { - "add_total_row": 1, + "add_total_row": 0, "columns": [], "creation": "2016-04-08 14:49:58.133098", "disabled": 0, "docstatus": 0, "doctype": "Report", "filters": [], - "idx": 2, + "idx": 6, "is_standard": "Yes", "letterhead": null, - "modified": "2023-07-26 21:05:33.554778", + "modified": "2023-11-08 20:17:05.774211", "modified_by": "Administrator", "module": "Accounts", "name": "Asset Depreciation Ledger", diff --git a/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.py b/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.py index f21c94b4940..d285f28d8e3 100644 --- a/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.py +++ b/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.py @@ -4,7 +4,7 @@ import frappe from frappe import _ -from frappe.utils import flt +from frappe.utils import cstr, flt def execute(filters=None): @@ -32,7 +32,6 @@ def get_data(filters): filters_data.append(["against_voucher", "=", filters.get("asset")]) if filters.get("asset_category"): - assets = frappe.db.sql_list( """select name from tabAsset where asset_category = %s and docstatus=1""", @@ -41,12 +40,27 @@ def get_data(filters): filters_data.append(["against_voucher", "in", assets]) - if filters.get("finance_book"): - filters_data.append(["finance_book", "in", ["", filters.get("finance_book")]]) + company_fb = frappe.get_cached_value("Company", filters.get("company"), "default_finance_book") + + if filters.get("include_default_book_assets") and company_fb: + if filters.get("finance_book") and cstr(filters.get("finance_book")) != cstr(company_fb): + frappe.throw(_("To use a different finance book, please uncheck 'Include Default FB Assets'")) + else: + finance_book = company_fb + elif filters.get("finance_book"): + finance_book = filters.get("finance_book") + else: + finance_book = None + + if finance_book: + or_filters_data = [["finance_book", "in", ["", finance_book]], ["finance_book", "is", "not set"]] + else: + or_filters_data = [["finance_book", "in", [""]], ["finance_book", "is", "not set"]] gl_entries = frappe.get_all( "GL Entry", filters=filters_data, + or_filters=or_filters_data, fields=["against_voucher", "debit_in_account_currency as debit", "voucher_no", "posting_date"], order_by="against_voucher, posting_date", ) @@ -61,7 +75,9 @@ def get_data(filters): asset_data = assets_details.get(d.against_voucher) if asset_data: if not asset_data.get("accumulated_depreciation_amount"): - asset_data.accumulated_depreciation_amount = d.debit + asset_data.accumulated_depreciation_amount = d.debit + asset_data.get( + "opening_accumulated_depreciation" + ) else: asset_data.accumulated_depreciation_amount += d.debit @@ -70,7 +86,7 @@ def get_data(filters): { "depreciation_amount": d.debit, "depreciation_date": d.posting_date, - "amount_after_depreciation": ( + "value_after_depreciation": ( flt(row.gross_purchase_amount) - flt(row.accumulated_depreciation_amount) ), "depreciation_entry": d.voucher_no, @@ -88,10 +104,12 @@ def get_assets_details(assets): fields = [ "name as asset", "gross_purchase_amount", + "opening_accumulated_depreciation", "asset_category", "status", "depreciation_method", "purchase_date", + "cost_center", ] for d in frappe.get_all("Asset", fields=fields, filters={"name": ("in", assets)}): @@ -121,6 +139,12 @@ def get_columns(): "fieldtype": "Currency", "width": 120, }, + { + "label": _("Opening Accumulated Depreciation"), + "fieldname": "opening_accumulated_depreciation", + "fieldtype": "Currency", + "width": 140, + }, { "label": _("Depreciation Amount"), "fieldname": "depreciation_amount", @@ -134,8 +158,8 @@ def get_columns(): "width": 210, }, { - "label": _("Amount After Depreciation"), - "fieldname": "amount_after_depreciation", + "label": _("Value After Depreciation"), + "fieldname": "value_after_depreciation", "fieldtype": "Currency", "width": 180, }, @@ -153,12 +177,13 @@ def get_columns(): "options": "Asset Category", "width": 120, }, - {"label": _("Current Status"), "fieldname": "status", "fieldtype": "Data", "width": 120}, { - "label": _("Depreciation Method"), - "fieldname": "depreciation_method", - "fieldtype": "Data", - "width": 130, + "label": _("Cost Center"), + "fieldtype": "Link", + "fieldname": "cost_center", + "options": "Cost Center", + "width": 100, }, + {"label": _("Current Status"), "fieldname": "status", "fieldtype": "Data", "width": 120}, {"label": _("Purchase Date"), "fieldname": "purchase_date", "fieldtype": "Date", "width": 120}, ] diff --git a/erpnext/accounts/report/balance_sheet/balance_sheet.js b/erpnext/accounts/report/balance_sheet/balance_sheet.js index 4a4ad4d71cb..f1f8e5f6e7c 100644 --- a/erpnext/accounts/report/balance_sheet/balance_sheet.js +++ b/erpnext/accounts/report/balance_sheet/balance_sheet.js @@ -15,7 +15,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { frappe.query_reports["Balance Sheet"]["filters"].push({ "fieldname": "include_default_book_entries", - "label": __("Include Default Book Entries"), + "label": __("Include Default FB Entries"), "fieldtype": "Check", "default": 1 }); diff --git a/erpnext/accounts/report/cash_flow/cash_flow.js b/erpnext/accounts/report/cash_flow/cash_flow.js index a2c34c6ee26..b9c62b52861 100644 --- a/erpnext/accounts/report/cash_flow/cash_flow.js +++ b/erpnext/accounts/report/cash_flow/cash_flow.js @@ -16,7 +16,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { frappe.query_reports["Cash Flow"]["filters"].push( { "fieldname": "include_default_book_entries", - "label": __("Include Default Book Entries"), + "label": __("Include Default FB Entries"), "fieldtype": "Check", "default": 1 } 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 d58fd95a840..b7d25c41982 100644 --- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js +++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js @@ -105,7 +105,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { }, { "fieldname": "include_default_book_entries", - "label": __("Include Default Book Entries"), + "label": __("Include Default FB Entries"), "fieldtype": "Check", "default": 1 }, diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py index 693725d8f50..096bb107069 100644 --- a/erpnext/accounts/report/financial_statements.py +++ b/erpnext/accounts/report/financial_statements.py @@ -561,9 +561,7 @@ def apply_additional_conditions(doctype, query, from_date, ignore_closing_entrie company_fb = frappe.get_cached_value("Company", filters.company, "default_finance_book") if filters.finance_book and company_fb and cstr(filters.finance_book) != cstr(company_fb): - frappe.throw( - _("To use a different finance book, please uncheck 'Include Default Book Entries'") - ) + frappe.throw(_("To use a different finance book, please uncheck 'Include Default FB Entries'")) query = query.where( (gl_entry.finance_book.isin([cstr(filters.finance_book), cstr(company_fb), ""])) diff --git a/erpnext/accounts/report/general_ledger/general_ledger.js b/erpnext/accounts/report/general_ledger/general_ledger.js index 3bc2ced32fb..f0ac3d0ffdb 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.js +++ b/erpnext/accounts/report/general_ledger/general_ledger.js @@ -175,7 +175,7 @@ frappe.query_reports["General Ledger"] = { }, { "fieldname": "include_default_book_entries", - "label": __("Include Default Book Entries"), + "label": __("Include Default FB Entries"), "fieldtype": "Check", "default": 1 }, diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py index 8b938d3d83c..754f17c118e 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.py +++ b/erpnext/accounts/report/general_ledger/general_ledger.py @@ -252,9 +252,7 @@ def get_conditions(filters): if filters.get("company_fb") and cstr(filters.get("finance_book")) != cstr( filters.get("company_fb") ): - frappe.throw( - _("To use a different finance book, please uncheck 'Include Default Book Entries'") - ) + frappe.throw(_("To use a different finance book, please uncheck 'Include Default FB Entries'")) else: conditions.append("(finance_book in (%(finance_book)s, '') OR finance_book IS NULL)") else: diff --git a/erpnext/accounts/report/trial_balance/trial_balance.js b/erpnext/accounts/report/trial_balance/trial_balance.js index 6e233c802f0..ffb2931e902 100644 --- a/erpnext/accounts/report/trial_balance/trial_balance.js +++ b/erpnext/accounts/report/trial_balance/trial_balance.js @@ -96,7 +96,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { }, { "fieldname": "include_default_book_entries", - "label": __("Include Default Book Entries"), + "label": __("Include Default FB Entries"), "fieldtype": "Check", "default": 1 }, diff --git a/erpnext/accounts/report/trial_balance/trial_balance.py b/erpnext/accounts/report/trial_balance/trial_balance.py index 2a8aa0c202f..8b7f0bbc006 100644 --- a/erpnext/accounts/report/trial_balance/trial_balance.py +++ b/erpnext/accounts/report/trial_balance/trial_balance.py @@ -275,9 +275,7 @@ def get_opening_balance( company_fb = frappe.get_cached_value("Company", filters.company, "default_finance_book") if filters.finance_book and company_fb and cstr(filters.finance_book) != cstr(company_fb): - frappe.throw( - _("To use a different finance book, please uncheck 'Include Default Book Entries'") - ) + frappe.throw(_("To use a different finance book, please uncheck 'Include Default FB Entries'")) opening_balance = opening_balance.where( (closing_balance.finance_book.isin([cstr(filters.finance_book), cstr(company_fb), ""])) 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 48b17f58fb2..0497addf44a 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js @@ -52,7 +52,7 @@ frappe.query_reports["Fixed Asset Register"] = { }, { "fieldname": "include_default_book_assets", - "label": __("Include Default Book Assets"), + "label": __("Include Default FB Assets"), "fieldtype": "Check", "default": 1 }, diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py index 383be973477..45811a93444 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py @@ -223,7 +223,7 @@ def get_assets_linked_to_fb(filters): company_fb = frappe.get_cached_value("Company", filters.company, "default_finance_book") if filters.finance_book and company_fb and cstr(filters.finance_book) != cstr(company_fb): - frappe.throw(_("To use a different finance book, please uncheck 'Include Default Book Assets'")) + frappe.throw(_("To use a different finance book, please uncheck 'Include Default FB Assets'")) query = query.where( (afb.finance_book.isin([cstr(filters.finance_book), cstr(company_fb), ""])) diff --git a/erpnext/translations/af.csv b/erpnext/translations/af.csv index 10e1cba349e..7c3c6afca47 100644 --- a/erpnext/translations/af.csv +++ b/erpnext/translations/af.csv @@ -1260,7 +1260,7 @@ In Value,In Waarde, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",In die geval van 'n multi-vlak program sal kliënte outomaties toegewys word aan die betrokke vlak volgens hul besteding, Inactive,onaktiewe, Incentives,aansporings, -Include Default Book Entries,Sluit standaardboekinskrywings in, +Include Default FB Entries,Sluit standaardboekinskrywings in, Include Exploded Items,Sluit ontplofte items in, Include POS Transactions,Sluit POS-transaksies in, Include UOM,Sluit UOM in, diff --git a/erpnext/translations/am.csv b/erpnext/translations/am.csv index f1d979205ec..f6e0fb56f10 100644 --- a/erpnext/translations/am.csv +++ b/erpnext/translations/am.csv @@ -1260,7 +1260,7 @@ In Value,እሴት ውስጥ, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","በባለብዙ ደረጃ መርሃግብር ሁኔታ, ደንበኞች በተጠቀሱት ወጪ መሰረት ለተሰጣቸው ደረጃ ደረጃ በራስ መተላለፍ ይኖራቸዋል", Inactive,ገባሪ አይደለም, Incentives,ማበረታቻዎች, -Include Default Book Entries,ነባሪ የመጽሐፍ ግቤቶችን አካትት።, +Include Default FB Entries,ነባሪ የመጽሐፍ ግቤቶችን አካትት።, Include Exploded Items,የተበተኑ ንጥሎችን አካት, Include POS Transactions,የ POS ሽግግሮችን አክል, Include UOM,UOM አካት, diff --git a/erpnext/translations/ar.csv b/erpnext/translations/ar.csv index d4df088c778..4028ca7817c 100644 --- a/erpnext/translations/ar.csv +++ b/erpnext/translations/ar.csv @@ -1260,7 +1260,7 @@ In Value,القيمة القادمة, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",في حالة البرنامج متعدد المستويات ، سيتم تعيين العملاء تلقائيًا إلى الطبقة المعنية وفقًا للإنفاق, Inactive,غير نشط, Incentives,الحوافز, -Include Default Book Entries,تضمين إدخالات دفتر افتراضي, +Include Default FB Entries,تضمين إدخالات دفتر افتراضي, Include Exploded Items,تشمل البنود المستبعدة, Include POS Transactions,تشمل معاملات نقطه البيع, Include UOM,تضمين UOM, diff --git a/erpnext/translations/bg.csv b/erpnext/translations/bg.csv index 24a397210e1..7e26ba222ce 100644 --- a/erpnext/translations/bg.csv +++ b/erpnext/translations/bg.csv @@ -1260,7 +1260,7 @@ In Value,В стойност, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","В случай на многостепенна програма, клиентите ще бъдат автоматично зададени на съответния подреждан по тяхна сметка", Inactive,неактивен, Incentives,Стимули, -Include Default Book Entries,Включете записи по подразбиране на книги, +Include Default FB Entries,Включете записи по подразбиране на книги, Include Exploded Items,Включете експлодираните елементи, Include POS Transactions,Включете POS транзакции, Include UOM,Включете UOM, diff --git a/erpnext/translations/bn.csv b/erpnext/translations/bn.csv index 75532418ea4..3cf3317a307 100644 --- a/erpnext/translations/bn.csv +++ b/erpnext/translations/bn.csv @@ -1260,7 +1260,7 @@ In Value,মান, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","মাল্টি-টিয়ার প্রোগ্রামের ক্ষেত্রে, গ্রাহকরা তাদের ব্যয় অনুযায়ী সংশ্লিষ্ট টায়ারে স্বয়ংক্রিয়ভাবে নিয়োগ পাবেন", Inactive,নিষ্ক্রিয়, Incentives,ইনসেনটিভ, -Include Default Book Entries,ডিফল্ট বুক এন্ট্রি অন্তর্ভুক্ত করুন, +Include Default FB Entries,ডিফল্ট বুক এন্ট্রি অন্তর্ভুক্ত করুন, Include Exploded Items,বিস্ফোরিত আইটেম অন্তর্ভুক্ত করুন, Include POS Transactions,পিওএস লেনদেন অন্তর্ভুক্ত করুন, Include UOM,UOM অন্তর্ভুক্ত করুন, diff --git a/erpnext/translations/bs.csv b/erpnext/translations/bs.csv index ab82d0fc107..cd7700fa591 100644 --- a/erpnext/translations/bs.csv +++ b/erpnext/translations/bs.csv @@ -1260,7 +1260,7 @@ In Value,u vrijednost, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","U slučaju višeslojnog programa, Korisnici će automatski biti dodeljeni za dotičnu grupu po njihovom trošenju", Inactive,Neaktivan, Incentives,Poticaji, -Include Default Book Entries,Uključite zadane unose knjiga, +Include Default FB Entries,Uključite zadane unose knjiga, Include Exploded Items,Uključite eksplodirane predmete, Include POS Transactions,Uključite POS transakcije, Include UOM,Uključite UOM, diff --git a/erpnext/translations/ca.csv b/erpnext/translations/ca.csv index afac395efa9..162fd60cae7 100644 --- a/erpnext/translations/ca.csv +++ b/erpnext/translations/ca.csv @@ -1260,7 +1260,7 @@ In Value,En valor, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","En el cas del programa de diversos nivells, els clients seran assignats automàticament al nivell corresponent segons el seu gastat", Inactive,Inactiu, Incentives,Incentius, -Include Default Book Entries,Inclou les entrades de llibres predeterminats, +Include Default FB Entries,Inclou les entrades de llibres predeterminats, Include Exploded Items,Inclou articles explotats, Include POS Transactions,Inclou transaccions de POS, Include UOM,Inclou UOM, diff --git a/erpnext/translations/cs.csv b/erpnext/translations/cs.csv index 64dec565400..11fa70b4bab 100644 --- a/erpnext/translations/cs.csv +++ b/erpnext/translations/cs.csv @@ -1260,7 +1260,7 @@ In Value,v Hodnota, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",V případě víceúrovňového programu budou zákazníci automaticky přiděleni danému vrstvě podle svých vynaložených nákladů, Inactive,Neaktivní, Incentives,Pobídky, -Include Default Book Entries,Zahrnout výchozí položky knihy, +Include Default FB Entries,Zahrnout výchozí položky knihy, Include Exploded Items,Zahrnout výbušné položky, Include POS Transactions,Zahrnout POS transakce, Include UOM,Zahrnout UOM, diff --git a/erpnext/translations/da.csv b/erpnext/translations/da.csv index 8a735b1e271..007d46d2dcf 100644 --- a/erpnext/translations/da.csv +++ b/erpnext/translations/da.csv @@ -1260,7 +1260,7 @@ In Value,I Value, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","I tilfælde af multi-tier program, vil kunder automatisk blive tildelt den pågældende tier som per deres brugt", Inactive,inaktive, Incentives,Incitamenter, -Include Default Book Entries,Inkluder standardbogsindlæg, +Include Default FB Entries,Inkluder standardbogsindlæg, Include Exploded Items,Inkluder eksploderede elementer, Include POS Transactions,Inkluder POS-transaktioner, Include UOM,Inkluder UOM, diff --git a/erpnext/translations/de.csv b/erpnext/translations/de.csv index 63d78adc6a4..038db208a2c 100644 --- a/erpnext/translations/de.csv +++ b/erpnext/translations/de.csv @@ -1268,7 +1268,7 @@ In Value,Wert bei, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",Im Falle eines mehrstufigen Programms werden Kunden automatisch der betroffenen Ebene entsprechend ihrer Ausgaben zugewiesen, Inactive,Inaktiv, Incentives,Anreize, -Include Default Book Entries,Standardbucheinträge einschließen, +Include Default FB Entries,Standardbucheinträge einschließen, Include Exploded Items,Unterartikel einbeziehen, Include POS Transactions,POS-Transaktionen einschließen, Include UOM,Fügen Sie UOM hinzu, diff --git a/erpnext/translations/el.csv b/erpnext/translations/el.csv index cacce1efada..74cba44b1d1 100644 --- a/erpnext/translations/el.csv +++ b/erpnext/translations/el.csv @@ -1260,7 +1260,7 @@ In Value,στην Αξία, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","Στην περίπτωση προγράμματος πολλαπλών βαθμίδων, οι Πελάτες θα αντιστοιχούν αυτόματα στη σχετική βαθμίδα σύμφωνα με το ποσό που δαπανώνται", Inactive,Αδρανής, Incentives,Κίνητρα, -Include Default Book Entries,Συμπεριλάβετε τις προεπιλεγμένες καταχωρίσεις βιβλίων, +Include Default FB Entries,Συμπεριλάβετε τις προεπιλεγμένες καταχωρίσεις βιβλίων, Include Exploded Items,Συμπεριλάβετε εκραγμένα στοιχεία, Include POS Transactions,Συμπεριλάβετε τις συναλλαγές POS, Include UOM,Συμπεριλάβετε UOM, diff --git a/erpnext/translations/es.csv b/erpnext/translations/es.csv index 8999d90ae87..be2bededf5c 100644 --- a/erpnext/translations/es.csv +++ b/erpnext/translations/es.csv @@ -1260,7 +1260,7 @@ In Value,En valor, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","En el caso del programa de varios niveles, los Clientes se asignarán automáticamente al nivel correspondiente según su gasto", Inactive,Inactivo, Incentives,Incentivos, -Include Default Book Entries,Incluir entradas de libro predeterminadas, +Include Default FB Entries,Incluir entradas de libro predeterminadas, Include Exploded Items,Incluir Elementos Estallados, Include POS Transactions,Incluir transacciones POS, Include UOM,Incluir UOM, diff --git a/erpnext/translations/et.csv b/erpnext/translations/et.csv index 0d509deda1a..180d97cbb8e 100644 --- a/erpnext/translations/et.csv +++ b/erpnext/translations/et.csv @@ -1260,7 +1260,7 @@ In Value,väärtuse, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",Mitmekordsete programmide korral määratakse Kliendid automaatselt asjaomasele tasemele vastavalt nende kasutatud kuludele, Inactive,Mitteaktiivne, Incentives,Soodustused, -Include Default Book Entries,Lisage vaikeraamatu kanded, +Include Default FB Entries,Lisage vaikeraamatu kanded, Include Exploded Items,Kaasa lõhutud esemed, Include POS Transactions,Kaasa POS-tehingud, Include UOM,Lisa UOM, diff --git a/erpnext/translations/fa.csv b/erpnext/translations/fa.csv index 3e054606fc7..21354935f23 100644 --- a/erpnext/translations/fa.csv +++ b/erpnext/translations/fa.csv @@ -1260,7 +1260,7 @@ In Value,با ارزش, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",در مورد برنامه چند لایه، مشتریان به صورت خودکار به سطر مربوطه اختصاص داده می شوند، همانطور که در هزینه های خود هستند, Inactive,غیر فعال, Incentives,انگیزه, -Include Default Book Entries,شامل ورودی های پیش فرض کتاب, +Include Default FB Entries,شامل ورودی های پیش فرض کتاب, Include Exploded Items,شامل موارد انفجار, Include POS Transactions,شامل معاملات POS, Include UOM,شامل UOM, diff --git a/erpnext/translations/fi.csv b/erpnext/translations/fi.csv index 892261d0178..5801a4904e2 100644 --- a/erpnext/translations/fi.csv +++ b/erpnext/translations/fi.csv @@ -1260,7 +1260,7 @@ In Value,in Arvo, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",Monitasoisen ohjelman tapauksessa asiakkaat määräytyvät automaattisesti kyseiselle tasolle niiden kulutuksen mukaan, Inactive,Epäaktiivinen, Incentives,kannustimet/bonukset, -Include Default Book Entries,Sisällytä oletustiedot, +Include Default FB Entries,Sisällytä oletustiedot, Include Exploded Items,Sisällytä räjähtämättömiä kohteita, Include POS Transactions,Sisällytä POS-tapahtumia, Include UOM,Sisällytä UOM, diff --git a/erpnext/translations/fr.csv b/erpnext/translations/fr.csv index 0865e2f7a7d..ee14868b4d3 100644 --- a/erpnext/translations/fr.csv +++ b/erpnext/translations/fr.csv @@ -1260,7 +1260,7 @@ In Value,En valeur, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","Dans le cas d'un programme à plusieurs échelons, les clients seront automatiquement affectés au niveau approprié en fonction de leurs dépenses", Inactive,Inactif, Incentives,Incitations, -Include Default Book Entries,Inclure les entrées de livre par défaut, +Include Default FB Entries,Inclure les entrées de livre par défaut, Include Exploded Items,Inclure les articles éclatés, Include POS Transactions,Inclure les transactions du point de vente, Include UOM,Inclure UdM, diff --git a/erpnext/translations/gu.csv b/erpnext/translations/gu.csv index 787e29d6219..81437b5fc4c 100644 --- a/erpnext/translations/gu.csv +++ b/erpnext/translations/gu.csv @@ -1260,7 +1260,7 @@ In Value,ભાવ, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","મલ્ટિ-ટાયર પ્રોગ્રામના કિસ્સામાં, ગ્રાહક તેમના ખર્ચ મુજબ સંબંધિત ટાયરમાં ઓટો હશે", Inactive,નિષ્ક્રિય, Incentives,ઇનસેન્ટીવ્સ, -Include Default Book Entries,ડિફaultલ્ટ બુક એન્ટ્રીઓ શામેલ કરો, +Include Default FB Entries,ડિફaultલ્ટ બુક એન્ટ્રીઓ શામેલ કરો, Include Exploded Items,વિસ્ફોટ થયેલ આઇટમ્સ શામેલ કરો, Include POS Transactions,POS વ્યવહારો શામેલ કરો, Include UOM,યુએમએમ શામેલ કરો, diff --git a/erpnext/translations/he.csv b/erpnext/translations/he.csv index c8a9c7546e1..4805f205933 100644 --- a/erpnext/translations/he.csv +++ b/erpnext/translations/he.csv @@ -1260,7 +1260,7 @@ In Value,ערך, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","במקרה של תוכנית רב-שכבתית, הלקוחות יוקצו אוטומטית לשכבה הנוגעת בדבר שהוצאו", Inactive,לֹא פָּעִיל, Incentives,תמריצים, -Include Default Book Entries,כלול רשומות ברירת מחדל לספרים, +Include Default FB Entries,כלול רשומות ברירת מחדל לספרים, Include Exploded Items,כלול פריטים מפוצצים, Include POS Transactions,כלול עסקאות קופה, Include UOM,כלול UOM, diff --git a/erpnext/translations/hi.csv b/erpnext/translations/hi.csv index 21e659aabec..a1cfd8dbb72 100644 --- a/erpnext/translations/hi.csv +++ b/erpnext/translations/hi.csv @@ -1260,7 +1260,7 @@ In Value,मूल्य में, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","मल्टी-स्तरीय कार्यक्रम के मामले में, ग्राहक अपने खर्च के अनुसार संबंधित स्तर को स्वचालित रूप से सौंपा जाएगा", Inactive,निष्क्रिय, Incentives,प्रोत्साहन, -Include Default Book Entries,डिफ़ॉल्ट बुक प्रविष्टियाँ शामिल करें, +Include Default FB Entries,डिफ़ॉल्ट बुक प्रविष्टियाँ शामिल करें, Include Exploded Items,विस्फोट किए गए आइटम शामिल करें, Include POS Transactions,पीओएस लेनदेन शामिल करें, Include UOM,यूओएम शामिल करें, diff --git a/erpnext/translations/hr.csv b/erpnext/translations/hr.csv index 9d0b9526f79..55379d4105e 100644 --- a/erpnext/translations/hr.csv +++ b/erpnext/translations/hr.csv @@ -1260,7 +1260,7 @@ In Value,u vrijednost, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","U slučaju višerazinskog programa, Kupci će biti automatski dodijeljeni odgovarajućem stupcu po njihovu potrošenom", Inactive,neaktivan, Incentives,poticaji, -Include Default Book Entries,Uključite zadane unose u knjige, +Include Default FB Entries,Uključite zadane unose u knjige, Include Exploded Items,Uključi eksplodirane predmete, Include POS Transactions,Uključi POS transakcije, Include UOM,Uključi UOM, diff --git a/erpnext/translations/hu.csv b/erpnext/translations/hu.csv index c7966b5113e..bdd2e1278c9 100644 --- a/erpnext/translations/hu.csv +++ b/erpnext/translations/hu.csv @@ -1260,7 +1260,7 @@ In Value,Az Értékben, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","Többszintű program esetében az ügyfeleket automatikusan az adott kategóriába sorolják, az általuk elköltöttek szerint", Inactive,Inaktív, Incentives,Ösztönzők, -Include Default Book Entries,Tartalmazza az alapértelmezett könyvbejegyzéseket, +Include Default FB Entries,Tartalmazza az alapértelmezett könyvbejegyzéseket, Include Exploded Items,Tartalmazza a robbantott elemeket, Include POS Transactions,Tartalmazza a POS kassza tranzakciókat, Include UOM,Ide tartozik az ANYJ, diff --git a/erpnext/translations/id.csv b/erpnext/translations/id.csv index de44c6fd782..2d83bc3703b 100644 --- a/erpnext/translations/id.csv +++ b/erpnext/translations/id.csv @@ -1260,7 +1260,7 @@ In Value,Nilai, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","Dalam kasus program multi-tier, Pelanggan akan ditugaskan secara otomatis ke tingkat yang bersangkutan sesuai yang mereka habiskan", Inactive,Tidak aktif, Incentives,Insentif, -Include Default Book Entries,Sertakan Entri Buku Default, +Include Default FB Entries,Sertakan Entri Buku Default, Include Exploded Items,Sertakan barang yang meledak, Include POS Transactions,Sertakan Transaksi POS, Include UOM,Termasuk UOM, diff --git a/erpnext/translations/is.csv b/erpnext/translations/is.csv index b9174e26e51..2d48ca51a66 100644 --- a/erpnext/translations/is.csv +++ b/erpnext/translations/is.csv @@ -1260,7 +1260,7 @@ In Value,Virði, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","Þegar um er að ræða fjölþættaráætlun, verða viðskiptavinir sjálfkrafa tengdir viðkomandi flokka eftir því sem þeir eru í", Inactive,Óvirkt, Incentives,Incentives, -Include Default Book Entries,Hafa sjálfgefnar bókarfærslur með, +Include Default FB Entries,Hafa sjálfgefnar bókarfærslur með, Include Exploded Items,Inniheldur sprauta hluti, Include POS Transactions,Innifalið POS-viðskipti, Include UOM,Innifalið UOM, diff --git a/erpnext/translations/it.csv b/erpnext/translations/it.csv index d432eaf1e96..2ea39ad67e9 100644 --- a/erpnext/translations/it.csv +++ b/erpnext/translations/it.csv @@ -1260,7 +1260,7 @@ In Value,In valore, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","Nel caso di un programma multilivello, i clienti verranno assegnati automaticamente al livello interessato come da loro speso", Inactive,Inattivo, Incentives,Incentivi, -Include Default Book Entries,Includi voci di libro predefinite, +Include Default FB Entries,Includi voci di libro predefinite, Include Exploded Items,Includi elementi esplosi, Include POS Transactions,Includi transazioni POS, Include UOM,Includi UOM, diff --git a/erpnext/translations/ja.csv b/erpnext/translations/ja.csv index 2bf91fbe251..31e2360ed06 100644 --- a/erpnext/translations/ja.csv +++ b/erpnext/translations/ja.csv @@ -1260,7 +1260,7 @@ In Value,値内, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",マルチティアプログラムの場合、顧客は、消費されるごとに自動的に関係する層に割り当てられます, Inactive,非アクティブ, Incentives,インセンティブ, -Include Default Book Entries,デフォルトのブックエントリを含める, +Include Default FB Entries,デフォルトのブックエントリを含める, Include Exploded Items,分解された項目を含める, Include POS Transactions,POSトランザクションを含める, Include UOM,UOMを含める, diff --git a/erpnext/translations/km.csv b/erpnext/translations/km.csv index 8b25f998356..efd30df2125 100644 --- a/erpnext/translations/km.csv +++ b/erpnext/translations/km.csv @@ -1260,7 +1260,7 @@ In Value,នៅក្នុងតម្លៃ, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",ក្នុងករណីមានកម្មវិធីពហុលំដាប់អតិថិជននឹងត្រូវបានចាត់តាំងដោយខ្លួនឯងទៅថ្នាក់ដែលពាក់ព័ន្ធដោយចំណាយរបស់ពួកគេ, Inactive,អសកម្ម, Incentives,ការលើកទឹកចិត្ត, -Include Default Book Entries,រួមបញ្ចូលធាតុសៀវភៅលំនាំដើម។, +Include Default FB Entries,រួមបញ្ចូលធាតុសៀវភៅលំនាំដើម។, Include Exploded Items,រួមបញ្ចូលធាតុផ្ទុះ, Include POS Transactions,បញ្ចូលប្រតិបត្តិការ POS, Include UOM,រួមបញ្ចូល UOM, diff --git a/erpnext/translations/kn.csv b/erpnext/translations/kn.csv index 944a59ef336..7372e1b9bac 100644 --- a/erpnext/translations/kn.csv +++ b/erpnext/translations/kn.csv @@ -1260,7 +1260,7 @@ In Value,ಮೌಲ್ಯ, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","ಮಲ್ಟಿ-ಟೈರ್ ಪ್ರೋಗ್ರಾಂನ ಸಂದರ್ಭದಲ್ಲಿ, ಗ್ರಾಹಕರು ತಮ್ಮ ಖರ್ಚುಗೆ ಅನುಗುಣವಾಗಿ ಆಯಾ ಶ್ರೇಣಿಗೆ ಸ್ವಯಂ ನಿಯೋಜಿಸಲಾಗುವುದು", Inactive,ನಿಷ್ಕ್ರಿಯವಾಗಿದೆ, Incentives,ಪ್ರೋತ್ಸಾಹ, -Include Default Book Entries,ಡೀಫಾಲ್ಟ್ ಪುಸ್ತಕ ನಮೂದುಗಳನ್ನು ಸೇರಿಸಿ, +Include Default FB Entries,ಡೀಫಾಲ್ಟ್ ಪುಸ್ತಕ ನಮೂದುಗಳನ್ನು ಸೇರಿಸಿ, Include Exploded Items,ಸ್ಫೋಟಗೊಂಡ ವಸ್ತುಗಳನ್ನು ಸೇರಿಸಿ, Include POS Transactions,ಪಿಒಎಸ್ ಟ್ರಾನ್ಸಾಕ್ಷನ್ಸ್ ಸೇರಿಸಿ, Include UOM,UOM ಸೇರಿಸಿ, diff --git a/erpnext/translations/ko.csv b/erpnext/translations/ko.csv index 66b8692afe8..8c0285bc28a 100644 --- a/erpnext/translations/ko.csv +++ b/erpnext/translations/ko.csv @@ -1260,7 +1260,7 @@ In Value,값에서, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",다중 계층 프로그램의 경우 고객은 지출 한대로 해당 계층에 자동으로 할당됩니다., Inactive,비활성, Incentives,장려책, -Include Default Book Entries,기본 도서 항목 포함, +Include Default FB Entries,기본 도서 항목 포함, Include Exploded Items,분해 된 항목 포함, Include POS Transactions,POS 트랜잭션 포함, Include UOM,UOM 포함, diff --git a/erpnext/translations/ku.csv b/erpnext/translations/ku.csv index 325a0a7d256..35f593a509e 100644 --- a/erpnext/translations/ku.csv +++ b/erpnext/translations/ku.csv @@ -1260,7 +1260,7 @@ In Value,di Nirx, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","Di rewşê de bernameya pir-tier, Ewrûpa dê ji hêla xercê xwe ve girêdayî xerîb be", Inactive,Bêkar, Incentives,aborîve, -Include Default Book Entries,Navnîşanên Pirtûka Pêvek Bawer bikin, +Include Default FB Entries,Navnîşanên Pirtûka Pêvek Bawer bikin, Include Exploded Items,Included Dead Items, Include POS Transactions,Têkiliyên POSê de, Include UOM,UOM, diff --git a/erpnext/translations/lo.csv b/erpnext/translations/lo.csv index b8b7a5d623b..73a33440bbc 100644 --- a/erpnext/translations/lo.csv +++ b/erpnext/translations/lo.csv @@ -1260,7 +1260,7 @@ In Value,ໃນມູນຄ່າ, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","ໃນກໍລະນີຂອງໂຄງການຫຼາຍຂັ້ນ, ລູກຄ້າຈະໄດ້ຮັບການມອບຫມາຍໂດຍອັດຕະໂນມັດໃຫ້ກັບຂັ້ນຕອນທີ່ກ່ຽວຂ້ອງຕາມການໃຊ້ຈ່າຍຂອງເຂົາເຈົ້າ", Inactive,Inactive, Incentives,ສິ່ງຈູງໃຈ, -Include Default Book Entries,ລວມທັງການອອກສຽງປື້ມແບບເລີ່ມຕົ້ນ, +Include Default FB Entries,ລວມທັງການອອກສຽງປື້ມແບບເລີ່ມຕົ້ນ, Include Exploded Items,ລວມເອົາສິ່ງທີ່ເກີດຂື້ນ, Include POS Transactions,ລວມທຸລະກໍາ POS, Include UOM,ລວມ UOM, diff --git a/erpnext/translations/lt.csv b/erpnext/translations/lt.csv index 5f249a742df..aeeef9af5b1 100644 --- a/erpnext/translations/lt.csv +++ b/erpnext/translations/lt.csv @@ -1260,7 +1260,7 @@ In Value,vertės, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","Kalbant apie daugiapakopę programą, klientai bus automatiškai priskirti atitinkamam lygmeniui, atsižvelgiant į jų išleidimą", Inactive,Neaktyvus, Incentives,Paskatos, -Include Default Book Entries,Įtraukite numatytuosius knygų įrašus, +Include Default FB Entries,Įtraukite numatytuosius knygų įrašus, Include Exploded Items,Įtraukti sprogus elementus, Include POS Transactions,Įtraukti POS operacijas, Include UOM,Įtraukti UOM, diff --git a/erpnext/translations/lv.csv b/erpnext/translations/lv.csv index f7a3d356f80..b3db9019afd 100644 --- a/erpnext/translations/lv.csv +++ b/erpnext/translations/lv.csv @@ -1260,7 +1260,7 @@ In Value,Vērtībā, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","Daudzpakāpju programmas gadījumā Klienti tiks automātiski piešķirti attiecīgajam līmenim, salīdzinot ar viņu iztērēto", Inactive,Neaktīvs, Incentives,Stimuli, -Include Default Book Entries,Iekļaujiet noklusējuma grāmatas ierakstus, +Include Default FB Entries,Iekļaujiet noklusējuma grāmatas ierakstus, Include Exploded Items,Iekļaut izpūstas preces, Include POS Transactions,Iekļaut POS darījumus, Include UOM,Iekļaut UOM, diff --git a/erpnext/translations/mk.csv b/erpnext/translations/mk.csv index 06c2aff0b44..212d5d7a834 100644 --- a/erpnext/translations/mk.csv +++ b/erpnext/translations/mk.csv @@ -1260,7 +1260,7 @@ In Value,Во вредност, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","Во случај на повеќеслојна програма, корисниците ќе бидат автоматски доделени на засегнатите нивоа, како на нивните потрошени", Inactive,Неактивен, Incentives,Стимулации, -Include Default Book Entries,Вклучете стандардни записи за книги, +Include Default FB Entries,Вклучете стандардни записи за книги, Include Exploded Items,Вклучи експлодирани елементи, Include POS Transactions,Вклучете POS-трансакции, Include UOM,Вклучете UOM, diff --git a/erpnext/translations/ml.csv b/erpnext/translations/ml.csv index f28ea330402..036ec78b847 100644 --- a/erpnext/translations/ml.csv +++ b/erpnext/translations/ml.csv @@ -1260,7 +1260,7 @@ In Value,മൂല്യത്തിൽ, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","മൾട്ടി-ടയർ പരിപാടിയുടെ കാര്യത്തിൽ, കസ്റ്റമർമാർ ചെലവഴിച്ച തുക പ്രകാരം ബന്ധപ്പെട്ട ടീമിൽ ഓട്ടോ നിർണ്ണയിക്കും", Inactive,നിഷ്ക്രിയം, Incentives,ഇൻസെന്റീവ്സ്, -Include Default Book Entries,സ്ഥിരസ്ഥിതി പുസ്തക എൻ‌ട്രികൾ‌ ഉൾ‌പ്പെടുത്തുക, +Include Default FB Entries,സ്ഥിരസ്ഥിതി പുസ്തക എൻ‌ട്രികൾ‌ ഉൾ‌പ്പെടുത്തുക, Include Exploded Items,എക്സ്പ്ലോഡഡ് ഇനങ്ങൾ ഉൾപ്പെടുത്തുക, Include POS Transactions,POS ഇടപാടുകൾ ഉൾപ്പെടുത്തുക, Include UOM,UOM ഉൾപ്പെടുത്തുക, diff --git a/erpnext/translations/mr.csv b/erpnext/translations/mr.csv index 64b07488296..89915b74693 100644 --- a/erpnext/translations/mr.csv +++ b/erpnext/translations/mr.csv @@ -1260,7 +1260,7 @@ In Value,मूल्य, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",बहु-स्तरीय कार्यक्रमाच्या बाबतीत ग्राहक त्यांच्या खर्चानुसार संबंधित टायरला स्वयंचलितरित्या नियुक्त केले जातील, Inactive,निष्क्रिय, Incentives,प्रोत्साहन, -Include Default Book Entries,डीफॉल्ट पुस्तक नोंदी समाविष्ट करा, +Include Default FB Entries,डीफॉल्ट पुस्तक नोंदी समाविष्ट करा, Include Exploded Items,विस्फोट केलेल्या वस्तू समाविष्ट करा, Include POS Transactions,पीओएस व्यवहार समाविष्ट करा, Include UOM,यूओएम समाविष्ट करा, diff --git a/erpnext/translations/ms.csv b/erpnext/translations/ms.csv index 5d2c2a890e7..0f9cc036efd 100644 --- a/erpnext/translations/ms.csv +++ b/erpnext/translations/ms.csv @@ -1260,7 +1260,7 @@ In Value,Dalam Nilai, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","Dalam hal program multi-tier, Pelanggan akan ditugaskan secara automatik ke peringkat yang bersangkutan seperti yang dibelanjakannya", Inactive,Tidak aktif, Incentives,Insentif, -Include Default Book Entries,Sertakan Penyertaan Buku Lalai, +Include Default FB Entries,Sertakan Penyertaan Buku Lalai, Include Exploded Items,Termasuk Item Meletup, Include POS Transactions,Termasuk Transaksi POS, Include UOM,Termasuk UOM, diff --git a/erpnext/translations/my.csv b/erpnext/translations/my.csv index 0471f66bc55..3f6038db8d5 100644 --- a/erpnext/translations/my.csv +++ b/erpnext/translations/my.csv @@ -1260,7 +1260,7 @@ In Value,Value တစ်ခုအတွက်, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","Multi-tier အစီအစဉ်၏အမှု၌, Customer များအလိုအလျောက်သူတို့ရဲ့သုံးစွဲနှုန်းအတိုင်းစိုးရိမ်ပူပန်ဆင့်မှတာဝန်ပေးအပ်ပါလိမ့်မည်", Inactive,မလှုပ်ရှားတတ်သော, Incentives,မက်လုံးတွေပေးပြီး, -Include Default Book Entries,ပုံမှန်စာအုပ် Entries Include, +Include Default FB Entries,ပုံမှန်စာအုပ် Entries Include, Include Exploded Items,ပေါက်ကွဲပစ္စည်းများ Include, Include POS Transactions,POS အရောင်းအဝယ် Include, Include UOM,UOM Include, diff --git a/erpnext/translations/nl.csv b/erpnext/translations/nl.csv index 92671115bfc..9b2b54b5ded 100644 --- a/erpnext/translations/nl.csv +++ b/erpnext/translations/nl.csv @@ -1260,7 +1260,7 @@ In Value,in Value, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",In het geval van een meerlagig programma worden klanten automatisch toegewezen aan de betreffende laag op basis van hun bestede tijd, Inactive,Inactief, Incentives,Incentives, -Include Default Book Entries,Standaard boekvermeldingen opnemen, +Include Default FB Entries,Standaard boekvermeldingen opnemen, Include Exploded Items,Exploded Items opnemen, Include POS Transactions,POS-transacties opnemen, Include UOM,Inclusief UOM, diff --git a/erpnext/translations/no.csv b/erpnext/translations/no.csv index 2642a9c7087..bd7735aa6a5 100644 --- a/erpnext/translations/no.csv +++ b/erpnext/translations/no.csv @@ -1260,7 +1260,7 @@ In Value,I verdi, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","Når det gjelder flerlagsprogram, vil kundene automatisk bli tilordnet den aktuelle delen som brukt", Inactive,inaktiv, Incentives,Motivasjon, -Include Default Book Entries,Inkluder standardbokoppføringer, +Include Default FB Entries,Inkluder standardbokoppføringer, Include Exploded Items,Inkluder eksploderte elementer, Include POS Transactions,Inkluder POS-transaksjoner, Include UOM,Inkluder UOM, diff --git a/erpnext/translations/pl.csv b/erpnext/translations/pl.csv index ca820258f43..0507e2e2436 100644 --- a/erpnext/translations/pl.csv +++ b/erpnext/translations/pl.csv @@ -1260,7 +1260,7 @@ In Value,w polu Wartość, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","W przypadku programu wielowarstwowego Klienci zostaną automatycznie przypisani do danego poziomu, zgodnie z wydatkami", Inactive,Nieaktywny, Incentives,, -Include Default Book Entries,Dołącz domyślne wpisy książki, +Include Default FB Entries,Dołącz domyślne wpisy książki, Include Exploded Items,Dołącz rozstrzelone przedmioty, Include POS Transactions,Uwzględnij transakcje POS, Include UOM,Dołącz UOM, diff --git a/erpnext/translations/ps.csv b/erpnext/translations/ps.csv index ed15740e378..ffb5511fac4 100644 --- a/erpnext/translations/ps.csv +++ b/erpnext/translations/ps.csv @@ -1260,7 +1260,7 @@ In Value,په ارزښت, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",د څو اړخیز پروګرام په صورت کې، پیرودونکي به د خپل مصرف په اساس اړوند ټیټ ته ګمارل کیږي, Inactive,غیر فعال, Incentives,هڅوونکي, -Include Default Book Entries,د ډیفالټ کتاب ننوتل شامل کړئ, +Include Default FB Entries,د ډیفالټ کتاب ننوتل شامل کړئ, Include Exploded Items,چاودیدونکي توکي شامل کړئ, Include POS Transactions,د POS تعاملات شامل کړئ, Include UOM,UOM شامل کړئ, diff --git a/erpnext/translations/pt-BR.csv b/erpnext/translations/pt-BR.csv index 503a16f7ebe..28ad958955e 100644 --- a/erpnext/translations/pt-BR.csv +++ b/erpnext/translations/pt-BR.csv @@ -1260,7 +1260,7 @@ In Value,Valor Entrada, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","No caso do programa multicamadas, os Clientes serão atribuídos automaticamente ao nível em questão de acordo com o gasto", Inactive,Inativo, Incentives,Incentivos, -Include Default Book Entries,Incluir Entradas de Livro Padrão, +Include Default FB Entries,Incluir Entradas de Livro Padrão, Include Exploded Items,Incluir Itens Explodidos, Include POS Transactions,Incluir Transações PDV, Include UOM,Incluir UDM, diff --git a/erpnext/translations/pt.csv b/erpnext/translations/pt.csv index 3e83df5f693..a0d11eecd5e 100644 --- a/erpnext/translations/pt.csv +++ b/erpnext/translations/pt.csv @@ -1260,7 +1260,7 @@ In Value,No Valor, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","No caso do programa multicamadas, os Clientes serão atribuídos automaticamente ao nível em questão de acordo com o gasto", Inactive,Inativo, Incentives,Incentivos, -Include Default Book Entries,Incluir entradas de livro padrão, +Include Default FB Entries,Incluir entradas de livro padrão, Include Exploded Items,Incluir itens explodidos, Include POS Transactions,Incluir transações POS, Include UOM,Incluir UOM, diff --git a/erpnext/translations/ro.csv b/erpnext/translations/ro.csv index 91c92070758..01ac73d60de 100644 --- a/erpnext/translations/ro.csv +++ b/erpnext/translations/ro.csv @@ -1260,7 +1260,7 @@ In Value,În valoare, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","În cazul unui program cu mai multe niveluri, Clienții vor fi automat alocați nivelului respectiv în funcție de cheltuielile efectuate", Inactive,Inactiv, Incentives,stimulente, -Include Default Book Entries,Includeți intrări implicite în cărți, +Include Default FB Entries,Includeți intrări implicite în cărți, Include Exploded Items,Includeți articole explodate, Include POS Transactions,Includeți tranzacțiile POS, Include UOM,Includeți UOM, diff --git a/erpnext/translations/ru.csv b/erpnext/translations/ru.csv index 00641159e06..75c721dcc01 100644 --- a/erpnext/translations/ru.csv +++ b/erpnext/translations/ru.csv @@ -1258,7 +1258,7 @@ In Value,В цене, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",В случае многоуровневой программы Клиенты будут автоматически назначены соответствующему уровню в соответствии с затраченными, Inactive,Неактивный, Incentives,Стимулирование, -Include Default Book Entries,Включить записи в книгу по умолчанию, +Include Default FB Entries,Включить записи в книгу по умолчанию, Include Exploded Items,Включить раздробленные элементы, Include POS Transactions,Включить POS-транзакции, Include UOM,Включить UOM, diff --git a/erpnext/translations/rw.csv b/erpnext/translations/rw.csv index 2d8c07ee58d..da3915bb271 100644 --- a/erpnext/translations/rw.csv +++ b/erpnext/translations/rw.csv @@ -1260,7 +1260,7 @@ In Value,Agaciro, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","Kubijyanye na gahunda yo mu byiciro byinshi, Abakiriya bazahabwa imodoka bashinzwe urwego bireba nkuko bakoresheje", Inactive,Kudakora, Incentives,Inkunga, -Include Default Book Entries,Shyiramo Ibisanzwe Byanditswe, +Include Default FB Entries,Shyiramo Ibisanzwe Byanditswe, Include Exploded Items,Shyiramo Ibintu Biturika, Include POS Transactions,Shyiramo ibikorwa bya POS, Include UOM,Shyiramo UOM, diff --git a/erpnext/translations/si.csv b/erpnext/translations/si.csv index 67fa6ae51dd..63d1d539b74 100644 --- a/erpnext/translations/si.csv +++ b/erpnext/translations/si.csv @@ -1260,7 +1260,7 @@ In Value,අගය දී, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","බහු ස්ථරයේ වැඩසටහනක දී, පාරිභෝගිකයින් විසින් වැය කරනු ලබන පරිදි පාරිභෝගිකයින්ට අදාල ස්ථානයට ස්වයංක්රීයව පවරනු ලැබේ", Inactive,අක්රියයි, Incentives,සහන, -Include Default Book Entries,පෙරනිමි පොත් ඇතුළත් කිරීම් ඇතුළත් කරන්න, +Include Default FB Entries,පෙරනිමි පොත් ඇතුළත් කිරීම් ඇතුළත් කරන්න, Include Exploded Items,පුපුරණ ද්රව්ය අඩංගු කරන්න, Include POS Transactions,POS ගනුදෙනු, Include UOM,UOM ඇතුළත් කරන්න, diff --git a/erpnext/translations/sk.csv b/erpnext/translations/sk.csv index 7bdfdffbaf9..d4ff8ee1845 100644 --- a/erpnext/translations/sk.csv +++ b/erpnext/translations/sk.csv @@ -1260,7 +1260,7 @@ In Value,v Hodnota, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",V prípade viacvrstvového programu budú zákazníci automaticky priradení príslušnému vrstvu podľa ich vynaložených prostriedkov, Inactive,neaktívne, Incentives,Pobídky, -Include Default Book Entries,Zahrnúť predvolené položky knihy, +Include Default FB Entries,Zahrnúť predvolené položky knihy, Include Exploded Items,Zahrňte explodované položky, Include POS Transactions,Zahrňte POS transakcie, Include UOM,Zahrňte UOM, diff --git a/erpnext/translations/sl.csv b/erpnext/translations/sl.csv index 370c3c61ae7..1c562e312ce 100644 --- a/erpnext/translations/sl.csv +++ b/erpnext/translations/sl.csv @@ -1260,7 +1260,7 @@ In Value,V vrednosti, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",V primeru večstopenjskega programa bodo stranke samodejno dodeljene zadevni stopnji glede na porabljene, Inactive,Neaktivno, Incentives,Spodbude, -Include Default Book Entries,Vključi privzete vnose v knjige, +Include Default FB Entries,Vključi privzete vnose v knjige, Include Exploded Items,Vključi eksplodirane elemente, Include POS Transactions,Vključite POS transakcije, Include UOM,Vključi UOM, diff --git a/erpnext/translations/sq.csv b/erpnext/translations/sq.csv index f069a059d4e..fdf34d1fd2b 100644 --- a/erpnext/translations/sq.csv +++ b/erpnext/translations/sq.csv @@ -1260,7 +1260,7 @@ In Value,Në Vlera, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","Në rastin e programit multi-shtresor, Konsumatorët do të caktohen automatikisht në nivelin përkatës sipas shpenzimeve të tyre", Inactive,joaktiv, Incentives,Nxitjet, -Include Default Book Entries,Përfshini hyrje të librave të paracaktuar, +Include Default FB Entries,Përfshini hyrje të librave të paracaktuar, Include Exploded Items,Përfshirja e artikujve të eksploduar, Include POS Transactions,Përfshi transaksione POS, Include UOM,Përfshi UOM, diff --git a/erpnext/translations/sr.csv b/erpnext/translations/sr.csv index 9558e07797b..34889f04f34 100644 --- a/erpnext/translations/sr.csv +++ b/erpnext/translations/sr.csv @@ -1260,7 +1260,7 @@ In Value,У вредности, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","У случају мулти-тиер програма, Корисници ће аутоматски бити додијељени за одређени ниво према њиховом потрошеном", Inactive,Неактиван, Incentives,Подстицаји, -Include Default Book Entries,Укључивање заданих уноса у књиге, +Include Default FB Entries,Укључивање заданих уноса у књиге, Include Exploded Items,Укључите експлодиране ставке, Include POS Transactions,Укључите ПОС трансакције, Include UOM,Укључите УОМ, diff --git a/erpnext/translations/sv.csv b/erpnext/translations/sv.csv index 7d9b70024cd..28188a5efc4 100644 --- a/erpnext/translations/sv.csv +++ b/erpnext/translations/sv.csv @@ -1260,7 +1260,7 @@ In Value,Värde, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",När det gäller program med flera nivåer kommer kunderna automatiskt att tilldelas den aktuella tiern enligt deras tillbringade, Inactive,Inaktiv, Incentives,Sporen, -Include Default Book Entries,Inkludera standardbokposter, +Include Default FB Entries,Inkludera standardbokposter, Include Exploded Items,Inkludera explosiva artiklar, Include POS Transactions,Inkludera POS-transaktioner, Include UOM,Inkludera UOM, diff --git a/erpnext/translations/sw.csv b/erpnext/translations/sw.csv index 6a0ef8cc7c1..261396c1f77 100644 --- a/erpnext/translations/sw.csv +++ b/erpnext/translations/sw.csv @@ -1260,7 +1260,7 @@ In Value,Kwa Thamani, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","Katika kesi ya mpango wa mipango mbalimbali, Wateja watapatiwa auto kwa tier husika kama kwa matumizi yao", Inactive,Haikufanya kazi, Incentives,Vidokezo, -Include Default Book Entries,Jumuisha Ingizo Mbadala za Kitabu, +Include Default FB Entries,Jumuisha Ingizo Mbadala za Kitabu, Include Exploded Items,Jumuisha Vipengee Vipengee, Include POS Transactions,Jumuisha Shughuli za POS, Include UOM,Jumuisha UOM, diff --git a/erpnext/translations/ta.csv b/erpnext/translations/ta.csv index 35cdc92b374..51008fdb801 100644 --- a/erpnext/translations/ta.csv +++ b/erpnext/translations/ta.csv @@ -1260,7 +1260,7 @@ In Value,மதிப்பு, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","பல அடுக்கு திட்டத்தின் விஷயத்தில், வாடிக்கையாளர்கள் தங்கள் செலவினங்களின்படி சம்பந்தப்பட்ட அடுக்குக்கு கார் ஒதுக்கப்படுவார்கள்", Inactive,செயல்படா, Incentives,செயல் தூண்டுதல், -Include Default Book Entries,இயல்புநிலை புத்தக உள்ளீடுகளைச் சேர்க்கவும், +Include Default FB Entries,இயல்புநிலை புத்தக உள்ளீடுகளைச் சேர்க்கவும், Include Exploded Items,வெடித்துள்ள பொருட்கள் அடங்கும், Include POS Transactions,POS பரிமாற்றங்களைச் சேர்க்கவும், Include UOM,UOM ஐ சேர்க்கவும், diff --git a/erpnext/translations/te.csv b/erpnext/translations/te.csv index 9b8b2d7d1ae..c86db9cca14 100644 --- a/erpnext/translations/te.csv +++ b/erpnext/translations/te.csv @@ -1260,7 +1260,7 @@ In Value,విలువ, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","మల్టీ-టైర్ ప్రోగ్రామ్ విషయంలో, కస్టమర్లు వారి గడువు ప్రకారం, సంబంధిత స్థాయికి కేటాయించబడతారు", Inactive,క్రియారహిత, Incentives,ఇన్సెంటివ్స్, -Include Default Book Entries,డిఫాల్ట్ బుక్ ఎంట్రీలను చేర్చండి, +Include Default FB Entries,డిఫాల్ట్ బుక్ ఎంట్రీలను చేర్చండి, Include Exploded Items,ఎక్స్ప్లోడ్ ఐటెమ్లను చేర్చండి, Include POS Transactions,POS లావాదేవీలను చేర్చండి, Include UOM,UOM ని చేర్చండి, diff --git a/erpnext/translations/th.csv b/erpnext/translations/th.csv index a97be2b8bf0..2285ea47641 100644 --- a/erpnext/translations/th.csv +++ b/erpnext/translations/th.csv @@ -1260,7 +1260,7 @@ In Value,ในราคา, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",ในกรณีของโปรแกรมแบบหลายชั้นลูกค้าจะได้รับการกำหนดให้โดยอัตโนมัติตามระดับที่เกี่ยวข้องตามการใช้จ่าย, Inactive,เฉื่อยชา, Incentives,แรงจูงใจ, -Include Default Book Entries,รวมรายการหนังสือเริ่มต้น, +Include Default FB Entries,รวมรายการหนังสือเริ่มต้น, Include Exploded Items,รวมรายการที่ระเบิดแล้ว, Include POS Transactions,รวมธุรกรรม POS, Include UOM,รวม UOM, diff --git a/erpnext/translations/tr.csv b/erpnext/translations/tr.csv index 14d842425e3..3fae8bdfc33 100644 --- a/erpnext/translations/tr.csv +++ b/erpnext/translations/tr.csv @@ -1260,7 +1260,7 @@ In Value,Değer, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","Çok katmanlı program söz konusu olduğunda, Müşteriler harcanan esasa göre ilgili kademeye otomatik olarak atanacaktır.", Inactive,etkisiz, Incentives,Teşvikler, -Include Default Book Entries,Varsayılan Kitap Girişlerini Dahil Et, +Include Default FB Entries,Varsayılan Kitap Girişlerini Dahil Et, Include Exploded Items,Patlatılmış Öğeleri Dahil Et, Include POS Transactions,POS İşlemlerini Dahil Et, Include UOM,UOM'yi dahil et, diff --git a/erpnext/translations/uk.csv b/erpnext/translations/uk.csv index d25b242c7d2..35f166a35f5 100644 --- a/erpnext/translations/uk.csv +++ b/erpnext/translations/uk.csv @@ -1260,7 +1260,7 @@ In Value,У Сумі, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","У випадку багаторівневої програми, Клієнти будуть автоматично призначені для відповідного рівня відповідно до витрачених ними витрат", Inactive,Неактивний, Incentives,Стимули, -Include Default Book Entries,Включити записи за замовчуванням, +Include Default FB Entries,Включити записи за замовчуванням, Include Exploded Items,Включити вибухнуті елементи, Include POS Transactions,Включити POS-транзакції, Include UOM,Включити UOM, diff --git a/erpnext/translations/ur.csv b/erpnext/translations/ur.csv index eb26f7cee27..2f5f2e1bf09 100644 --- a/erpnext/translations/ur.csv +++ b/erpnext/translations/ur.csv @@ -1260,7 +1260,7 @@ In Value,قدر میں, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",کثیر درجے کے پروگرام کے معاملے میں، صارفین اپنے اخراجات کے مطابق متعلقہ درجے کو خود کار طریقے سے تفویض کریں گے, Inactive,غیر فعال, Incentives,ترغیبات, -Include Default Book Entries,ڈیفالٹ کتاب اندراجات شامل کریں۔, +Include Default FB Entries,ڈیفالٹ کتاب اندراجات شامل کریں۔, Include Exploded Items,دھماکہ خیز اشیاء شامل کریں, Include POS Transactions,پی او ایس کے لین دین میں شامل کریں, Include UOM,UOM شامل کریں, diff --git a/erpnext/translations/uz.csv b/erpnext/translations/uz.csv index 95141bdb993..dfc34f8ab2e 100644 --- a/erpnext/translations/uz.csv +++ b/erpnext/translations/uz.csv @@ -1260,7 +1260,7 @@ In Value,Qiymatida, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",Ko'p qatlamli dasturda mijozlar o'zlari sarflagan xarajatlariga muvofiq tegishli darajaga avtomatik tarzda topshiriladi, Inactive,Faol emas, Incentives,Rag'batlantirish, -Include Default Book Entries,Odatiy kitob yozuvlarini qo'shing, +Include Default FB Entries,Odatiy kitob yozuvlarini qo'shing, Include Exploded Items,Portlatilgan narsalarni qo'shish, Include POS Transactions,Qalin operatsiyalarni qo'shish, Include UOM,UOM ni qo'shing, diff --git a/erpnext/translations/vi.csv b/erpnext/translations/vi.csv index de35891698d..6714e67fe5b 100644 --- a/erpnext/translations/vi.csv +++ b/erpnext/translations/vi.csv @@ -1260,7 +1260,7 @@ In Value,Trong giá trị, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","Trong trường hợp chương trình nhiều tầng, Khách hàng sẽ được tự động chỉ định cho cấp có liên quan theo mức chi tiêu của họ", Inactive,Không hoạt động, Incentives,Ưu đãi, -Include Default Book Entries,Bao gồm các mục sách mặc định, +Include Default FB Entries,Bao gồm các mục sách mặc định, Include Exploded Items,Bao gồm các mục đã Phát hiện, Include POS Transactions,Bao gồm giao dịch POS, Include UOM,Bao gồm UOM, diff --git a/erpnext/translations/zh.csv b/erpnext/translations/zh.csv index e2e6899ed5e..3e95226d3e1 100644 --- a/erpnext/translations/zh.csv +++ b/erpnext/translations/zh.csv @@ -1260,7 +1260,7 @@ In Value,金额, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",在多层程序的情况下,客户将根据其花费自动分配到相关层, Inactive,非活动的, Incentives,激励政策, -Include Default Book Entries,包括默认工作簿条目, +Include Default FB Entries,包括默认工作簿条目, Include Exploded Items,包含爆炸物料, Include POS Transactions,包括POS交易, Include UOM,包括基本计量单位, diff --git a/erpnext/translations/zh_tw.csv b/erpnext/translations/zh_tw.csv index 9fd737bed29..b6d0adf57b5 100644 --- a/erpnext/translations/zh_tw.csv +++ b/erpnext/translations/zh_tw.csv @@ -1166,7 +1166,7 @@ In Value,在數值, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",在多層程序的情況下,客戶將根據其花費自動分配到相關層, Inactive,待用, Incentives,獎勵, -Include Default Book Entries,包括默認工作簿條目, +Include Default FB Entries,包括默認工作簿條目, Include Exploded Items,包含爆炸物品, Include UOM,包括UOM, Included in Gross Profit,包含在毛利潤中, From 3f7f5bd8f0cb0ffec19ccf2680293cc22bef2247 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 2 Nov 2023 17:19:06 +0530 Subject: [PATCH 132/151] refactor: ignore disabled account while selecting Income Accounts (cherry picked from commit 6e3e094c957d8c2b71ed522e9a8034364345e5a7) --- .../doctype/sales_invoice/sales_invoice.js | 20 +++++++++---------- erpnext/controllers/queries.py | 2 ++ 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index a411889fbdd..75b87421dd7 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -181,7 +181,6 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends e erpnext.accounts.unreconcile_payments.add_unreconcile_btn(me.frm); } - make_maintenance_schedule() { frappe.model.open_mapped_doc({ method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.make_maintenance_schedule", @@ -557,15 +556,6 @@ cur_frm.fields_dict.write_off_cost_center.get_query = function(doc) { } } -// Income Account in Details Table -// -------------------------------- -cur_frm.set_query("income_account", "items", function(doc) { - return{ - query: "erpnext.controllers.queries.get_income_account", - filters: {'company': doc.company} - } -}); - // Cost Center in Details Table // ----------------------------- cur_frm.fields_dict["items"].grid.get_field("cost_center").get_query = function(doc) { @@ -660,6 +650,16 @@ frappe.ui.form.on('Sales Invoice', { }; }); + frm.set_query("income_account", "items", function() { + return{ + query: "erpnext.controllers.queries.get_income_account", + filters: { + 'company': frm.doc.company, + "disabled": 0 + } + } + }); + frm.custom_make_buttons = { 'Delivery Note': 'Delivery', 'Sales Invoice': 'Return / Credit Note', diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index 36225e3dd5e..b73ebf53ae8 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -560,6 +560,8 @@ def get_income_account(doctype, txt, searchfield, start, page_len, filters): if filters.get("company"): condition += "and tabAccount.company = %(company)s" + condition += f"and tabAccount.disabled = {filters.get('disabled', 0)}" + return frappe.db.sql( """select tabAccount.name from `tabAccount` where (tabAccount.report_type = "Profit and Loss" From db29180eec052c865f82c31a08d84303a002279b Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Thu, 9 Nov 2023 12:30:22 +0530 Subject: [PATCH 133/151] fix: pick serial nos from selected batch only (#37988) * fix: pick current serial nos from selected batch only * test: add test case for current qty and current serial nos --- .../stock_reconciliation.py | 1 + .../test_stock_reconciliation.py | 46 +++++++++++++++++++ erpnext/stock/utils.py | 21 ++++++--- 3 files changed, 61 insertions(+), 7 deletions(-) diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index d37b2a67e11..118c3a1d029 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -890,6 +890,7 @@ def get_stock_balance_for( with_valuation_rate=with_valuation_rate, with_serial_no=has_serial_no, inventory_dimensions_dict=inventory_dimensions_dict, + batch_no=batch_no, ) if has_serial_no: diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py index dd1220a0ddb..05c60175f51 100644 --- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py @@ -1009,6 +1009,52 @@ class TestStockReconciliation(FrappeTestCase, StockTestMixin): self.assertEqual(sr2.docstatus, 1) + def test_current_qty_and_current_serial_no_count(self): + # Step - 1: Create a Serial Batch Item + item = self.make_item( + properties={ + "is_stock_item": 1, + "has_serial_no": 1, + "serial_no_series": "TEST-SERIAL-.###", + "has_batch_no": 1, + "create_new_batch": 1, + "batch_number_series": "TEST-BATCH-.###", + } + ).name + + # Step - 2: Inward stock in multiple Batches + from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry + + se1 = make_stock_entry( + item_code=item, + target="_Test Warehouse - _TC", + qty=10, + ) + se2 = make_stock_entry( + item_code=item, + target="_Test Warehouse - _TC", + qty=5, + ) + + # Step - 3: Create Stock Reconciliation + sr = create_stock_reconciliation( + item_code=item, + warehouse="_Test Warehouse - _TC", + qty=0, + batch_no=se1.items[0].batch_no, + do_not_submit=True, + ) + + # Test - 1: Current Serial No Count should be equal to Current Qty + self.assertEqual(sr.items[0].current_qty, se1.items[0].qty) + self.assertEqual(len(sr.items[0].current_serial_no.split("\n")), sr.items[0].current_qty) + + sr.items[0].batch_no = se2.items[0].batch_no + sr.save() + + self.assertEqual(sr.items[0].current_qty, se2.items[0].qty) + self.assertEqual(len(sr.items[0].current_serial_no.split("\n")), sr.items[0].current_qty) + def create_batch_item_with_batch(item_name, batch_id): batch_item_doc = create_item(item_name, is_stock_item=1) diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py index 9f654fc6632..e019c572daf 100644 --- a/erpnext/stock/utils.py +++ b/erpnext/stock/utils.py @@ -95,6 +95,7 @@ def get_stock_balance( with_valuation_rate=False, with_serial_no=False, inventory_dimensions_dict=None, + batch_no=None, ): """Returns stock balance quantity at given warehouse on given posting date or current date. @@ -124,6 +125,9 @@ def get_stock_balance( if with_valuation_rate: if with_serial_no: + if batch_no: + args["batch_no"] = batch_no + serial_nos = get_serial_nos_data_after_transactions(args) return ( @@ -140,27 +144,30 @@ def get_stock_balance( def get_serial_nos_data_after_transactions(args): - serial_nos = set() args = frappe._dict(args) - sle = frappe.qb.DocType("Stock Ledger Entry") - stock_ledger_entries = ( + sle = frappe.qb.DocType("Stock Ledger Entry") + query = ( frappe.qb.from_(sle) - .select("serial_no", "actual_qty") + .select(sle.serial_no, sle.actual_qty) .where( - (sle.item_code == args.item_code) + (sle.is_cancelled == 0) + & (sle.item_code == args.item_code) & (sle.warehouse == args.warehouse) & ( CombineDatetime(sle.posting_date, sle.posting_time) < CombineDatetime(args.posting_date, args.posting_time) ) - & (sle.is_cancelled == 0) ) .orderby(sle.posting_date, sle.posting_time, sle.creation) - .run(as_dict=1) ) + if args.batch_no: + query = query.where(sle.batch_no == args.batch_no) + + stock_ledger_entries = query.run(as_dict=True) + for stock_ledger_entry in stock_ledger_entries: changed_serial_no = get_serial_nos_data(stock_ledger_entry.serial_no) if stock_ledger_entry.actual_qty > 0: From 935286ff94a6779c16e0a9ee5fcbe1a9a5c7f985 Mon Sep 17 00:00:00 2001 From: vishal Date: Fri, 27 Oct 2023 11:12:55 +0530 Subject: [PATCH 134/151] feat: multi-select customer group in AR Report (cherry picked from commit 8903c1bc6f24c4e0187f05739887ac6f4af6a7b6) --- .../accounts_receivable.js | 9 ++++++--- .../accounts_receivable.py | 19 ++++++++++++++++++- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js index 786aad601ba..ba21d8a3eb0 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js @@ -114,10 +114,13 @@ frappe.query_reports["Accounts Receivable"] = { "reqd": 1 }, { - "fieldname": "customer_group", + "fieldname":"customer_group", "label": __("Customer Group"), - "fieldtype": "Link", - "options": "Customer Group" + "fieldtype": "MultiSelectList", + "options": "Customer Group", + get_data: function(txt) { + return frappe.db.get_link_options('Customer Group', txt); + } }, { "fieldname": "payment_terms_template", diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index 12e40037f1e..a9b2fb2a476 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -840,7 +840,9 @@ class ReceivablePayableReport(object): self.customer = qb.DocType("Customer") if self.filters.get("customer_group"): - self.get_hierarchical_filters("Customer Group", "customer_group") + groups = get_customer_group_with_children(self.filters.customer_group) + customers = qb.from_(self.customer).select(self.customer.name).where(self.customer['customer_group'].isin(groups)) + self.qb_selection_filter.append(self.ple.party.isin(customers)) if self.filters.get("territory"): self.get_hierarchical_filters("Territory", "territory") @@ -1132,3 +1134,18 @@ class ReceivablePayableReport(object): .run() ) self.err_journals = [x[0] for x in results] if results else [] + +def get_customer_group_with_children(customer_groups): + if not isinstance(customer_groups, list): + customer_groups = [d.strip() for d in customer_groups.strip().split(",") if d] + + all_customer_groups = [] + for d in customer_groups: + if frappe.db.exists("Customer Group", d): + lft, rgt = frappe.db.get_value("Customer Group", d, ["lft", "rgt"]) + children = frappe.get_all("Customer Group", filters={"lft": [">=", lft], "rgt": ["<=", rgt]}) + all_customer_groups += [c.name for c in children] + else: + frappe.throw(_("Customer Group: {0} does not exist").format(d)) + + return list(set(all_customer_groups)) From 0eab723243d0a79ae511896313621add0741a97b Mon Sep 17 00:00:00 2001 From: vishal Date: Fri, 27 Oct 2023 11:22:55 +0530 Subject: [PATCH 135/151] fix: minor issue (cherry picked from commit b60c57a97dee1ab1a2b1654d4cc2d146e0141ff2) --- .../report/accounts_receivable/accounts_receivable.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index a9b2fb2a476..da956aa3844 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -841,7 +841,11 @@ class ReceivablePayableReport(object): if self.filters.get("customer_group"): groups = get_customer_group_with_children(self.filters.customer_group) - customers = qb.from_(self.customer).select(self.customer.name).where(self.customer['customer_group'].isin(groups)) + customers = ( + qb.from_(self.customer) + .select(self.customer.name) + .where(self.customer["customer_group"].isin(groups)) + ) self.qb_selection_filter.append(self.ple.party.isin(customers)) if self.filters.get("territory"): @@ -1135,6 +1139,7 @@ class ReceivablePayableReport(object): ) self.err_journals = [x[0] for x in results] if results else [] + def get_customer_group_with_children(customer_groups): if not isinstance(customer_groups, list): customer_groups = [d.strip() for d in customer_groups.strip().split(",") if d] From cf0d936195401d4098592a4db5c41ac54e7a55ce Mon Sep 17 00:00:00 2001 From: vishal Date: Mon, 6 Nov 2023 12:21:47 +0530 Subject: [PATCH 136/151] feat(accounts_receivable): test_case added for multi-select customer group (cherry picked from commit de445b32f5a13d115ce3f49c190f23b1efdf6632) --- .../test_accounts_receivable.py | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py index cbeb6d3106d..3582fe0c4a9 100644 --- a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py @@ -475,6 +475,30 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase): report = execute(filters)[1] self.assertEqual(len(report), 0) + def test_multi_customer_group_filter(self): + si = self.create_sales_invoice() + cus_group = frappe.db.get_value("Customer", self.customer, "customer_group") + # Create a list of customer groups, e.g., ["Group1", "Group2"] + cus_groups_list = [cus_group, "Group2"] + + filters = { + "company": self.company, + "report_date": today(), + "range1": 30, + "range2": 60, + "range3": 90, + "range4": 120, + "customer_group": cus_groups_list, # Use the list of customer groups + } + report = execute(filters)[1] + + # Assert that the report contains data for the specified customer groups + self.assertTrue(len(report) > 0) + + for row in report: + # Assert that the customer group of each row is in the list of customer groups + self.assertIn(row.customer_group, cus_groups_list) + def test_party_account_filter(self): si1 = self.create_sales_invoice() self.customer2 = ( From 6b3456812869c5f288914fdf9bbfd7ab9dcfb5f1 Mon Sep 17 00:00:00 2001 From: vishal Date: Mon, 6 Nov 2023 13:02:04 +0530 Subject: [PATCH 137/151] fix: minor change added to test_case (cherry picked from commit 30402033bc06296a32ffaf98fd28f18974be2248) --- .../report/accounts_receivable/test_accounts_receivable.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py index 3582fe0c4a9..f83285a1a72 100644 --- a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py @@ -479,7 +479,7 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase): si = self.create_sales_invoice() cus_group = frappe.db.get_value("Customer", self.customer, "customer_group") # Create a list of customer groups, e.g., ["Group1", "Group2"] - cus_groups_list = [cus_group, "Group2"] + cus_groups_list = [cus_group, "_Test Customer Group 1"] filters = { "company": self.company, From 984703c3c9e0201cd17d444da6f549884943d960 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 9 Nov 2023 13:40:14 +0530 Subject: [PATCH 138/151] fix: make adjustment entry using stock reconciliation (backport #37995) (#38008) * fix: make adjustment entry using stock reconciliation (#37995) fix: do adjustment entry using stock reconciliation (cherry picked from commit a8216b9727401bfb6900f15447ab935756d7f353) # Conflicts: # erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json # erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py * chore: fix conflicts * fix: conflicts * chore: fix conflicts --------- Co-authored-by: rohitwaghchaure --- .../stock_ledger_entry.json | 11 +++++-- .../stock_reconciliation.py | 19 ++++++++++++ erpnext/stock/stock_ledger.py | 30 +++++++++++++++++-- 3 files changed, 56 insertions(+), 4 deletions(-) diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json index 46ce9debf3b..b07c0876a25 100644 --- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json +++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json @@ -11,6 +11,7 @@ "warehouse", "posting_date", "posting_time", + "is_adjustment_entry", "column_break_6", "voucher_type", "voucher_no", @@ -309,6 +310,12 @@ "label": "Recalculate Incoming/Outgoing Rate", "no_copy": 1, "read_only": 1 + }, + { + "default": "0", + "fieldname": "is_adjustment_entry", + "fieldtype": "Check", + "label": "Is Adjustment Entry" } ], "hide_toolbar": 1, @@ -317,7 +324,7 @@ "in_create": 1, "index_web_pages_for_search": 1, "links": [], - "modified": "2021-12-21 06:25:30.040801", + "modified": "2023-10-23 18:07:42.063615", "modified_by": "Administrator", "module": "Stock", "name": "Stock Ledger Entry", @@ -341,4 +348,4 @@ "sort_field": "modified", "sort_order": "DESC", "states": [] -} \ No newline at end of file +} diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index 118c3a1d029..dd39f103cd7 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -269,6 +269,10 @@ class StockReconciliation(StockController): if item.has_batch_no: has_batch_no = True + if not row.qty and not row.valuation_rate and not row.current_qty: + self.make_adjustment_entry(row, sl_entries) + continue + if item.has_serial_no or item.has_batch_no: has_serial_no = True self.get_sle_for_serialized_items(row, sl_entries, item) @@ -402,6 +406,21 @@ class StockReconciliation(StockController): # update valuation rate self.update_valuation_rate_for_serial_nos(row, serial_nos) + def make_adjustment_entry(self, row, sl_entries): + from erpnext.stock.stock_ledger import get_stock_value_difference + + difference_amount = get_stock_value_difference( + row.item_code, row.warehouse, self.posting_date, self.posting_time + ) + + if not difference_amount: + return + + args = self.get_sle_for_items(row) + args.update({"stock_value_difference": -1 * difference_amount, "is_adjustment_entry": 1}) + + sl_entries.append(args) + def update_valuation_rate_for_serial_no(self): for d in self.items: if not d.serial_no: diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index bdae87c7ee7..8a262497926 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -635,9 +635,11 @@ class update_entries_after(object): sle.valuation_rate = self.wh_data.valuation_rate sle.stock_value = self.wh_data.stock_value sle.stock_queue = json.dumps(self.wh_data.stock_queue) - sle.stock_value_difference = stock_value_difference - sle.doctype = "Stock Ledger Entry" + if not sle.is_adjustment_entry or not self.args.get("sle_id"): + sle.stock_value_difference = stock_value_difference + + sle.doctype = "Stock Ledger Entry" frappe.get_doc(sle).db_update() if not self.args.get("sle_id"): @@ -1711,3 +1713,27 @@ def is_internal_transfer(sle): if data.is_internal_supplier and data.represents_company == data.company: return True + + +def get_stock_value_difference(item_code, warehouse, posting_date, posting_time, voucher_no=None): + table = frappe.qb.DocType("Stock Ledger Entry") + + query = ( + frappe.qb.from_(table) + .select(Sum(table.stock_value_difference).as_("value")) + .where( + (table.is_cancelled == 0) + & (table.item_code == item_code) + & (table.warehouse == warehouse) + & ( + (table.posting_date < posting_date) + | ((table.posting_date == posting_date) & (table.posting_time <= posting_time)) + ) + ) + ) + + if voucher_no: + query = query.where(table.voucher_no != voucher_no) + + difference_amount = query.run() + return flt(difference_amount[0][0]) if difference_amount else 0 From 620f4f2c109f3b073e578247331ab50280a24100 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 9 Nov 2023 21:52:43 +0530 Subject: [PATCH 139/151] fix: sales order not assigned to territory orders (#37905) (#38024) fix: sales order not assigned to territory orders (#37905) filtered sales order are not assigned to 'territory_orders' which results in 0 order amount and 0 billing amount in the output (cherry picked from commit 45b4bfc94783bb0180c0a57a2f3a5bb7086008a3) Co-authored-by: jabir-elat <44110258+jabir-elat@users.noreply.github.com> --- .../selling/report/territory_wise_sales/territory_wise_sales.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/selling/report/territory_wise_sales/territory_wise_sales.py b/erpnext/selling/report/territory_wise_sales/territory_wise_sales.py index 5dfc1db0976..ecb63d890a7 100644 --- a/erpnext/selling/report/territory_wise_sales/territory_wise_sales.py +++ b/erpnext/selling/report/territory_wise_sales/territory_wise_sales.py @@ -80,7 +80,7 @@ def get_data(filters=None): territory_orders = [] if t_quotation_names and sales_orders: - list(filter(lambda x: x.quotation in t_quotation_names, sales_orders)) + territory_orders = list(filter(lambda x: x.quotation in t_quotation_names, sales_orders)) t_order_names = [] if territory_orders: t_order_names = [t.name for t in territory_orders] From 9c0d80cef4fdeb673bc05acdd3e4f3855ab761b2 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 10 Nov 2023 09:49:55 +0530 Subject: [PATCH 140/151] fix: new logic for handling revaluation journals (cherry picked from commit 1d8fcd66e6ca6d17a274a8b18699b40a7ac93e1a) --- .../accounts_receivable/accounts_receivable.js | 6 ++++++ .../accounts_receivable/accounts_receivable.py | 17 +++++++++++++---- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js index ba21d8a3eb0..d6e3098e171 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js @@ -176,12 +176,18 @@ frappe.query_reports["Accounts Receivable"] = { "label": __("Show Remarks"), "fieldtype": "Check", }, + { + "fieldname": "for_revaluation_journals", + "label": __("Revaluation Journals"), + "fieldtype": "Check", + }, { "fieldname": "ignore_accounts", "label": __("Group by Voucher"), "fieldtype": "Check", } + ], "formatter": function(value, row, column, data, default_formatter) { diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index da956aa3844..c077d829939 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -281,11 +281,20 @@ class ReceivablePayableReport(object): row.invoice_grand_total = row.invoiced - if (abs(row.outstanding) > 1.0 / 10**self.currency_precision) and ( - (abs(row.outstanding_in_account_currency) > 1.0 / 10**self.currency_precision) - or (row.voucher_no in self.err_journals) - ): + must_consider = False + if self.filters.get("for_revaluation_journals"): + if (abs(row.outstanding) > 1.0 / 10**self.currency_precision) or ( + (abs(row.outstanding_in_account_currency) > 1.0 / 10**self.currency_precision) + ): + must_consider = True + else: + if (abs(row.outstanding) > 1.0 / 10**self.currency_precision) and ( + (abs(row.outstanding_in_account_currency) > 1.0 / 10**self.currency_precision) + or (row.voucher_no in self.err_journals) + ): + must_consider = True + if must_consider: # non-zero oustanding, we must consider this row if self.is_invoice(row) and self.filters.based_on_payment_terms: From 424ebd154a2c9a5dbe4d096871c7e9b2e87af529 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sat, 11 Nov 2023 10:51:36 +0530 Subject: [PATCH 141/151] fix: make item field read-only in batch (backport #38010) (#38033) * fix: make item field read-only in batch (cherry picked from commit 779260fb497d6ac8000a89b7b762145c58e84471) # Conflicts: # erpnext/stock/doctype/batch/batch.json * chore: `conflicts` --------- Co-authored-by: s-aga-r --- erpnext/stock/doctype/batch/batch.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/batch/batch.json b/erpnext/stock/doctype/batch/batch.json index 967c5729bf4..4bc14a976d6 100644 --- a/erpnext/stock/doctype/batch/batch.json +++ b/erpnext/stock/doctype/batch/batch.json @@ -61,6 +61,7 @@ "oldfieldname": "item", "oldfieldtype": "Link", "options": "Item", + "read_only_depends_on": "eval:!doc.__islocal", "reqd": 1 }, { @@ -207,7 +208,7 @@ "image_field": "image", "links": [], "max_attachments": 5, - "modified": "2022-02-21 08:08:23.999236", + "modified": "2023-11-09 12:17:28.339975", "modified_by": "Administrator", "module": "Stock", "name": "Batch", From 57ce73d5400f9ef5f9e51b061a2a7e73a9e384b2 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sun, 12 Nov 2023 17:40:56 +0530 Subject: [PATCH 142/151] fix: COA Importer app related issues (#37238) fix: COA Importer app related issues (#37238) * fix: COA Importer app related issues * fix: Clear all account links fields befor import * fix: Attribute error (cherry picked from commit 8634abc0210fdb259c8b0e7ffc93976ea0099057) Co-authored-by: Deepesh Garg --- .../chart_of_accounts_importer.js | 15 ++++-- .../chart_of_accounts_importer.py | 52 ++++++------------- .../coa_sample_template.csv | 17 ++++++ 3 files changed, 44 insertions(+), 40 deletions(-) create mode 100644 erpnext/accounts/doctype/chart_of_accounts_importer/coa_sample_template.csv diff --git a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.js b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.js index d61f8a6c01c..56fa6ce2f30 100644 --- a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.js +++ b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.js @@ -53,10 +53,18 @@ frappe.ui.form.on('Chart of Accounts Importer', { of Accounts. Please enter the account names and add more rows as per your requirement.`); } } - } + }, + { + label : "Company", + fieldname: "company", + fieldtype: "Link", + reqd: 1, + hidden: 1, + default: frm.doc.company, + }, ], primary_action: function() { - var data = d.get_values(); + let data = d.get_values(); if (!data.template_type) { frappe.throw(__('Please select Template Type to download template')); @@ -66,7 +74,8 @@ frappe.ui.form.on('Chart of Accounts Importer', { '/api/method/erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.download_template', { file_type: data.file_type, - template_type: data.template_type + template_type: data.template_type, + company: data.company } ); diff --git a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py index e78f5f46dbc..8c2e0f87f82 100644 --- a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py +++ b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py @@ -8,6 +8,7 @@ from functools import reduce import frappe from frappe import _ +from frappe.desk.form.linked_with import get_linked_fields from frappe.model.document import Document from frappe.utils import cint, cstr from frappe.utils.csvutils import UnicodeWriter @@ -294,10 +295,8 @@ def build_response_as_excel(writer): @frappe.whitelist() -def download_template(file_type, template_type): - data = frappe._dict(frappe.local.form_dict) - - writer = get_template(template_type) +def download_template(file_type, template_type, company): + writer = get_template(template_type, company) if file_type == "CSV": # download csv file @@ -308,8 +307,7 @@ def download_template(file_type, template_type): build_response_as_excel(writer) -def get_template(template_type): - +def get_template(template_type, company): fields = [ "Account Name", "Parent Account", @@ -335,34 +333,17 @@ def get_template(template_type): ["", "", "", "", 0, account_type.get("account_type"), account_type.get("root_type")] ) else: - writer = get_sample_template(writer) + writer = get_sample_template(writer, company) return writer -def get_sample_template(writer): - template = [ - ["Application Of Funds(Assets)", "", "", "", 1, "", "Asset"], - ["Sources Of Funds(Liabilities)", "", "", "", 1, "", "Liability"], - ["Equity", "", "", "", 1, "", "Equity"], - ["Expenses", "", "", "", 1, "", "Expense"], - ["Income", "", "", "", 1, "", "Income"], - ["Bank Accounts", "Application Of Funds(Assets)", "", "", 1, "Bank", "Asset"], - ["Cash In Hand", "Application Of Funds(Assets)", "", "", 1, "Cash", "Asset"], - ["Stock Assets", "Application Of Funds(Assets)", "", "", 1, "Stock", "Asset"], - ["Cost Of Goods Sold", "Expenses", "", "", 0, "Cost of Goods Sold", "Expense"], - ["Asset Depreciation", "Expenses", "", "", 0, "Depreciation", "Expense"], - ["Fixed Assets", "Application Of Funds(Assets)", "", "", 0, "Fixed Asset", "Asset"], - ["Accounts Payable", "Sources Of Funds(Liabilities)", "", "", 0, "Payable", "Liability"], - ["Accounts Receivable", "Application Of Funds(Assets)", "", "", 1, "Receivable", "Asset"], - ["Stock Expenses", "Expenses", "", "", 0, "Stock Adjustment", "Expense"], - ["Sample Bank", "Bank Accounts", "", "", 0, "Bank", "Asset"], - ["Cash", "Cash In Hand", "", "", 0, "Cash", "Asset"], - ["Stores", "Stock Assets", "", "", 0, "Stock", "Asset"], - ] - - for row in template: - writer.writerow(row) +def get_sample_template(writer, company): + currency = frappe.db.get_value("Company", company, "default_currency") + with open(os.path.join(os.path.dirname(__file__), "coa_sample_template.csv"), "r") as f: + for row in f: + row = row.strip().split(",") + [currency] + writer.writerow(row) return writer @@ -453,14 +434,11 @@ def get_mandatory_account_types(): def unset_existing_data(company): - linked = frappe.db.sql( - '''select fieldname from tabDocField - where fieldtype="Link" and options="Account" and parent="Company"''', - as_dict=True, - ) - # remove accounts data from company - update_values = {d.fieldname: "" for d in linked} + + fieldnames = get_linked_fields("Account").get("Company", {}).get("fieldname", []) + linked = [{"fieldname": name} for name in fieldnames] + update_values = {d.get("fieldname"): "" for d in linked} frappe.db.set_value("Company", company, update_values, update_values) # remove accounts data from various doctypes diff --git a/erpnext/accounts/doctype/chart_of_accounts_importer/coa_sample_template.csv b/erpnext/accounts/doctype/chart_of_accounts_importer/coa_sample_template.csv new file mode 100644 index 00000000000..85a2f2112f1 --- /dev/null +++ b/erpnext/accounts/doctype/chart_of_accounts_importer/coa_sample_template.csv @@ -0,0 +1,17 @@ +Application Of Funds(Assets),,,,1,,Asset +Sources Of Funds(Liabilities),,,,1,,Liability +Equity,,,,1,,Equity +Expenses,,,,1,Expense Account,Expense +Income,,,,1,Income Account,Income +Bank Accounts,Application Of Funds(Assets),,,1,Bank,Asset +Cash In Hand,Application Of Funds(Assets),,,1,Cash,Asset +Stock Assets,Application Of Funds(Assets),,,1,Stock,Asset +Cost Of Goods Sold,Expenses,,,0,Cost of Goods Sold,Expense +Asset Depreciation,Expenses,,,0,Depreciation,Expense +Fixed Assets,Application Of Funds(Assets),,,0,Fixed Asset,Asset +Accounts Payable,Sources Of Funds(Liabilities),,,0,Payable,Liability +Accounts Receivable,Application Of Funds(Assets),,,1,Receivable,Asset +Stock Expenses,Expenses,,,0,Stock Adjustment,Expense +Sample Bank,Bank Accounts,,,0,Bank,Asset +Cash,Cash In Hand,,,0,Cash,Asset +Stores,Stock Assets,,,0,Stock,Asset \ No newline at end of file From 6979c3e490beecdd890eddd33b4e2acb9ba66af7 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sun, 12 Nov 2023 18:07:31 +0530 Subject: [PATCH 143/151] fix: close `Credit Limit Crossed` dialog (backport #38052) (#38058) fix: close `Credit Limit Crossed` dialog (#38052) (cherry picked from commit 94faa44697144157940a874090fd1bd02c1eb981) Co-authored-by: Arjun --- erpnext/selling/doctype/customer/customer.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index 18fd8ff8ef7..20d6268d739 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -541,6 +541,7 @@ def check_credit_limit(customer, company, ignore_outstanding_sales_order=False, primary_action={ "label": "Send Email", "server_action": "erpnext.selling.doctype.customer.customer.send_emails", + "hide_on_success": True, "args": { "customer": customer, "customer_outstanding": customer_outstanding, From a104a4bca60c5fc3999c8581228f61e10fe877be Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 13 Nov 2023 11:56:43 +0530 Subject: [PATCH 144/151] refactor: add revaluation journal checkbox in AR/AP summary reports (cherry picked from commit 95edd82638c5cb5aabefd871966d558f51095f94) --- .../accounts_payable_summary/accounts_payable_summary.js | 5 +++++ .../accounts_receivable_summary.js | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js b/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js index 9e575e669d2..0f206b1cf42 100644 --- a/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js +++ b/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js @@ -110,6 +110,11 @@ frappe.query_reports["Accounts Payable Summary"] = { "fieldname":"based_on_payment_terms", "label": __("Based On Payment Terms"), "fieldtype": "Check", + }, + { + "fieldname": "for_revaluation_journals", + "label": __("Revaluation Journals"), + "fieldtype": "Check", } ], diff --git a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js index 5ad10c7890a..2f6d2582b33 100644 --- a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js +++ b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js @@ -139,6 +139,11 @@ frappe.query_reports["Accounts Receivable Summary"] = { "label": __("Show GL Balance"), "fieldtype": "Check", }, + { + "fieldname": "for_revaluation_journals", + "label": __("Revaluation Journals"), + "fieldtype": "Check", + } ], onload: function(report) { From c5528844fd30e229e6b4064bd93075b1b8fa0a90 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 13 Nov 2023 14:03:45 +0530 Subject: [PATCH 145/151] fix: indentation issue in the Production Plan Summary report (backport #38019) (#38068) fix: indentation issue in the Production Plan Summary report (#38019) fix: Production Plan Summary report (cherry picked from commit 4a111f73620136cda022e030c58d7d58b0c22a8e) Co-authored-by: rohitwaghchaure --- .../production_plan/production_plan.js | 4 -- .../production_plan/production_plan.py | 2 - .../production_plan/test_production_plan.py | 43 ------------------- .../production_plan_summary.js | 4 +- .../production_plan_summary.py | 26 ++++++++--- 5 files changed, 22 insertions(+), 57 deletions(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.js b/erpnext/manufacturing/doctype/production_plan/production_plan.js index 72438ddceea..dd102b0fae0 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.js +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.js @@ -89,10 +89,6 @@ frappe.ui.form.on('Production Plan', { frm.trigger("show_progress"); if (frm.doc.status !== "Completed") { - frm.add_custom_button(__("Work Order Tree"), ()=> { - frappe.set_route('Tree', 'Work Order', {production_plan: frm.doc.name}); - }, __('View')); - frm.add_custom_button(__("Production Plan Summary"), ()=> { frappe.set_route('query-report', 'Production Plan Summary', {production_plan: frm.doc.name}); }, __('View')); diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index 2d202386263..8d12ba92103 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -827,8 +827,6 @@ class ProductionPlan(Document): # Combine subassembly items sub_assembly_items_store = self.combine_subassembly_items(sub_assembly_items_store) - sub_assembly_items_store.sort(key=lambda d: d.bom_level, reverse=True) # sort by bom level - for idx, row in enumerate(sub_assembly_items_store): row.idx = idx + 1 self.append("sub_assembly_items", row) diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py index 749c33f01bb..4ad7d06c707 100644 --- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py @@ -653,49 +653,6 @@ class TestProductionPlan(FrappeTestCase): frappe.db.rollback() - def test_subassmebly_sorting(self): - "Test subassembly sorting in case of multiple items with nested BOMs." - from erpnext.manufacturing.doctype.bom.test_bom import create_nested_bom - - prefix = "_TestLevel_" - boms = { - "Assembly": { - "SubAssembly1": { - "ChildPart1": {}, - "ChildPart2": {}, - }, - "ChildPart6": {}, - "SubAssembly4": {"SubSubAssy2": {"ChildPart7": {}}}, - }, - "MegaDeepAssy": { - "SecretSubassy": { - "SecretPart": {"VerySecret": {"SuperSecret": {"Classified": {}}}}, - }, - # ^ assert that this is - # first item in subassy table - }, - } - create_nested_bom(boms, prefix=prefix) - - items = [prefix + item_code for item_code in boms.keys()] - plan = create_production_plan(item_code=items[0], do_not_save=True) - plan.append( - "po_items", - { - "use_multi_level_bom": 1, - "item_code": items[1], - "bom_no": frappe.db.get_value("Item", items[1], "default_bom"), - "planned_qty": 1, - "planned_start_date": now_datetime(), - }, - ) - plan.get_sub_assembly_items() - - bom_level_order = [d.bom_level for d in plan.sub_assembly_items] - self.assertEqual(bom_level_order, sorted(bom_level_order, reverse=True)) - # lowest most level of subassembly should be first - self.assertIn("SuperSecret", plan.sub_assembly_items[0].production_item) - def test_multiple_work_order_for_production_plan_item(self): "Test producing Prod Plan (making WO) in parts." diff --git a/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.js b/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.js index 59396fef16e..a4d7fae4560 100644 --- a/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.js +++ b/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.js @@ -22,9 +22,9 @@ frappe.query_reports["Production Plan Summary"] = { "formatter": function(value, row, column, data, default_formatter) { value = default_formatter(value, row, column, data); - if (column.fieldname == "document_name") { + if (column.fieldname == "item_code") { var color = data.pending_qty > 0 ? 'red': 'green'; - value = `${data['document_name']}`; + value = `${data['item_code']}`; } return value; diff --git a/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.py b/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.py index 2c8f82f2cc6..076690ff090 100644 --- a/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.py +++ b/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.py @@ -44,6 +44,7 @@ def get_production_plan_item_details(filters, data, order_details): { "indent": 0, "item_code": row.item_code, + "sales_order": row.get("sales_order"), "item_name": frappe.get_cached_value("Item", row.item_code, "item_name"), "qty": row.planned_qty, "document_type": "Work Order", @@ -80,7 +81,7 @@ def get_production_plan_sub_assembly_item_details( data.append( { - "indent": 1, + "indent": 1 + item.indent, "item_code": item.production_item, "item_name": item.item_name, "qty": item.qty, @@ -98,7 +99,7 @@ def get_work_order_details(filters, order_details): for row in frappe.get_all( "Work Order", filters={"production_plan": filters.get("production_plan")}, - fields=["name", "produced_qty", "production_plan", "production_item"], + fields=["name", "produced_qty", "production_plan", "production_item", "sales_order"], ): order_details.setdefault((row.name, row.production_item), row) @@ -118,10 +119,17 @@ def get_column(filters): "label": _("Finished Good"), "fieldtype": "Link", "fieldname": "item_code", - "width": 300, + "width": 240, "options": "Item", }, - {"label": _("Item Name"), "fieldtype": "data", "fieldname": "item_name", "width": 100}, + {"label": _("Item Name"), "fieldtype": "data", "fieldname": "item_name", "width": 150}, + { + "label": _("Sales Order"), + "options": "Sales Order", + "fieldtype": "Link", + "fieldname": "sales_order", + "width": 100, + }, { "label": _("Document Type"), "fieldtype": "Link", @@ -133,10 +141,16 @@ def get_column(filters): "label": _("Document Name"), "fieldtype": "Dynamic Link", "fieldname": "document_name", - "width": 150, + "options": "document_type", + "width": 180, }, {"label": _("BOM Level"), "fieldtype": "Int", "fieldname": "bom_level", "width": 100}, {"label": _("Order Qty"), "fieldtype": "Float", "fieldname": "qty", "width": 120}, - {"label": _("Received Qty"), "fieldtype": "Float", "fieldname": "produced_qty", "width": 160}, + { + "label": _("Produced / Received Qty"), + "fieldtype": "Float", + "fieldname": "produced_qty", + "width": 200, + }, {"label": _("Pending Qty"), "fieldtype": "Float", "fieldname": "pending_qty", "width": 110}, ] From 8446e87cc2262d20497edd95a15804285ec3e893 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 13 Nov 2023 19:20:52 +0530 Subject: [PATCH 146/151] chore: delete comments and unlink attachments on company transactions deletion (backport #38077) (#38078) * chore: delete comments and unlink attachments on company transactions deletion (cherry picked from commit 2f9e96e3245e210ab1a26ffe99499367b0e0afa5) * fix: unrelated transation date typo (cherry picked from commit b097bb20d99f1d25fbd8bdcf0277859741a0ee53) --------- Co-authored-by: anandbaburajan --- erpnext/controllers/buying_controller.py | 2 +- .../transaction_deletion_record.py | 49 +++++++++++++++++-- 2 files changed, 45 insertions(+), 6 deletions(-) diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index 2a5f0b48228..913e2274b1a 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -338,7 +338,7 @@ class BuyingController(SubcontractingController): { "item_code": d.item_code, "warehouse": d.get("from_warehouse"), - "posting_date": self.get("posting_date") or self.get("transation_date"), + "posting_date": self.get("posting_date") or self.get("transaction_date"), "posting_time": posting_time, "qty": -1 * flt(d.get("stock_qty")), "serial_no": d.get("serial_no"), diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py index 481a3a5ebea..d266285b29a 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py +++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py @@ -108,7 +108,16 @@ class TransactionDeletionRecord(Document): if no_of_docs > 0: self.delete_version_log(docfield["parent"], docfield["fieldname"]) - self.delete_communications(docfield["parent"], docfield["fieldname"]) + + reference_docs = frappe.get_all( + docfield["parent"], filters={docfield["fieldname"]: self.company} + ) + reference_doc_names = [r.name for r in reference_docs] + + self.delete_communications(docfield["parent"], reference_doc_names) + self.delete_comments(docfield["parent"], reference_doc_names) + self.unlink_attachments(docfield["parent"], reference_doc_names) + self.populate_doctypes_table(tables, docfield["parent"], no_of_docs) self.delete_child_tables(docfield["parent"], docfield["fieldname"]) @@ -197,19 +206,49 @@ class TransactionDeletionRecord(Document): (versions.ref_doctype == doctype) & (versions.docname.isin(batch)) ).run() - def delete_communications(self, doctype, company_fieldname): - reference_docs = frappe.get_all(doctype, filters={company_fieldname: self.company}) - reference_doc_names = [r.name for r in reference_docs] - + def delete_communications(self, doctype, reference_doc_names): communications = frappe.get_all( "Communication", filters={"reference_doctype": doctype, "reference_name": ["in", reference_doc_names]}, ) communication_names = [c.name for c in communications] + if not communication_names: + return + for batch in create_batch(communication_names, self.batch_size): frappe.delete_doc("Communication", batch, ignore_permissions=True) + def delete_comments(self, doctype, reference_doc_names): + comments = frappe.get_all( + "Comment", + filters={"reference_doctype": doctype, "reference_name": ["in", reference_doc_names]}, + ) + comment_names = [c.name for c in comments] + + if not comment_names: + return + + for batch in create_batch(comment_names, self.batch_size): + frappe.delete_doc("Comment", batch, ignore_permissions=True) + + def unlink_attachments(self, doctype, reference_doc_names): + files = frappe.get_all( + "File", + filters={"attached_to_doctype": doctype, "attached_to_name": ["in", reference_doc_names]}, + ) + file_names = [c.name for c in files] + + if not file_names: + return + + file = qb.DocType("File") + + for batch in create_batch(file_names, self.batch_size): + qb.update(file).set(file.attached_to_doctype, None).set(file.attached_to_name, None).where( + file.name.isin(batch) + ).run() + @frappe.whitelist() def get_doctypes_to_be_ignored(): From d1a8202624ef19ede288a86d838f0271bb86301f Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 14 Nov 2023 20:11:23 +0530 Subject: [PATCH 147/151] chore: refetch item images on transaction save (backport #38095) (#38098) * chore: refetch item images on transaction save (#38095) chore: re fetch item images on transaction save (cherry picked from commit e93a19ffb5d45a3bf0136ca3fc24a828b008aeee) # Conflicts: # erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.json # erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json # erpnext/selling/doctype/quotation_item/quotation_item.json * chore: fix conflict in pos_invoice_item.json * chore: fix conflict in sales_invoice_item.json * chore: fix conflict in quotation_item.json --------- Co-authored-by: Anand Baburajan --- .../doctype/pos_invoice_item/pos_invoice_item.json | 5 +++-- .../purchase_invoice_item/purchase_invoice_item.json | 3 ++- .../doctype/sales_invoice_item/sales_invoice_item.json | 5 +++-- .../purchase_order_item/purchase_order_item.json | 3 ++- .../request_for_quotation_item.json | 5 ++++- .../supplier_quotation_item.json | 5 ++++- .../crm/doctype/opportunity_item/opportunity_item.json | 4 +++- .../doctype/bom_explosion_item/bom_explosion_item.json | 3 ++- erpnext/manufacturing/doctype/bom_item/bom_item.json | 3 ++- .../selling/doctype/quotation_item/quotation_item.json | 5 +++-- .../doctype/sales_order_item/sales_order_item.json | 10 ++-------- .../doctype/delivery_note_item/delivery_note_item.json | 3 ++- .../material_request_item/material_request_item.json | 3 ++- .../purchase_receipt_item/purchase_receipt_item.json | 3 ++- .../subcontracting_order_item.json | 3 ++- .../subcontracting_receipt_item.json | 3 ++- 16 files changed, 40 insertions(+), 26 deletions(-) diff --git a/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.json b/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.json index 4bb18655b47..f741cd94a1e 100644 --- a/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.json +++ b/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.json @@ -185,6 +185,7 @@ "label": "Image" }, { + "fetch_from": "item_code.image", "fieldname": "image", "fieldtype": "Attach", "hidden": 1, @@ -821,7 +822,7 @@ ], "istable": 1, "links": [], - "modified": "2022-11-02 12:52:39.125295", + "modified": "2023-11-14 18:33:22.585715", "modified_by": "Administrator", "module": "Accounts", "name": "POS Invoice Item", @@ -831,4 +832,4 @@ "sort_field": "modified", "sort_order": "DESC", "states": [] -} \ No newline at end of file +} 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 c7357360ec0..7f71bd61e35 100644 --- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json +++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json @@ -156,6 +156,7 @@ "width": "300px" }, { + "fetch_from": "item_code.image", "fieldname": "image", "fieldtype": "Attach", "hidden": 1, @@ -890,7 +891,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2023-10-03 21:01:01.824892", + "modified": "2023-11-14 18:33:48.547297", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice Item", diff --git a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json index f25280f007c..87cf1ae80d6 100644 --- a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json +++ b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json @@ -167,6 +167,7 @@ "print_hide": 1 }, { + "fetch_from": "item_code.image", "fieldname": "image", "fieldtype": "Attach", "hidden": 1, @@ -891,7 +892,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2023-07-25 11:58:10.723833", + "modified": "2023-11-14 18:34:10.479329", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice Item", @@ -901,4 +902,4 @@ "sort_field": "modified", "sort_order": "DESC", "states": [] -} \ No newline at end of file +} diff --git a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json index bd7532e9554..0b390e542a9 100644 --- a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json +++ b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json @@ -190,6 +190,7 @@ "fieldtype": "Column Break" }, { + "fetch_from": "item_code.image", "fieldname": "image", "fieldtype": "Attach", "hidden": 1, @@ -917,7 +918,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2023-11-06 11:00:53.596417", + "modified": "2023-11-14 18:34:27.267382", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order Item", diff --git a/erpnext/buying/doctype/request_for_quotation_item/request_for_quotation_item.json b/erpnext/buying/doctype/request_for_quotation_item/request_for_quotation_item.json index e07f4626b8f..1b171c8e9eb 100644 --- a/erpnext/buying/doctype/request_for_quotation_item/request_for_quotation_item.json +++ b/erpnext/buying/doctype/request_for_quotation_item/request_for_quotation_item.json @@ -88,6 +88,7 @@ "width": "300px" }, { + "fetch_from": "item_code.image", "fieldname": "image", "fieldtype": "Attach", "hidden": 1, @@ -261,13 +262,15 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2020-09-24 17:26:46.276934", + "modified": "2023-11-14 18:34:48.327224", "modified_by": "Administrator", "module": "Buying", "name": "Request for Quotation Item", + "naming_rule": "Random", "owner": "Administrator", "permissions": [], "sort_field": "modified", "sort_order": "DESC", + "states": [], "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/buying/doctype/supplier_quotation_item/supplier_quotation_item.json b/erpnext/buying/doctype/supplier_quotation_item/supplier_quotation_item.json index 638cde01be5..a4e3c4f32e2 100644 --- a/erpnext/buying/doctype/supplier_quotation_item/supplier_quotation_item.json +++ b/erpnext/buying/doctype/supplier_quotation_item/supplier_quotation_item.json @@ -134,6 +134,7 @@ "fieldtype": "Column Break" }, { + "fetch_from": "item_code.image", "fieldname": "image", "fieldtype": "Attach", "hidden": 1, @@ -560,13 +561,15 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2020-10-19 12:36:26.913211", + "modified": "2023-11-14 18:35:03.435817", "modified_by": "Administrator", "module": "Buying", "name": "Supplier Quotation Item", + "naming_rule": "Random", "owner": "Administrator", "permissions": [], "sort_field": "modified", "sort_order": "DESC", + "states": [], "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/crm/doctype/opportunity_item/opportunity_item.json b/erpnext/crm/doctype/opportunity_item/opportunity_item.json index 1b4973c1b2b..732f80d01c9 100644 --- a/erpnext/crm/doctype/opportunity_item/opportunity_item.json +++ b/erpnext/crm/doctype/opportunity_item/opportunity_item.json @@ -103,6 +103,7 @@ "fieldtype": "Column Break" }, { + "fetch_from": "item_code.image", "fieldname": "image", "fieldtype": "Attach", "hidden": 1, @@ -165,7 +166,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2021-07-30 16:39:09.775720", + "modified": "2023-11-14 18:35:30.887278", "modified_by": "Administrator", "module": "CRM", "name": "Opportunity Item", @@ -173,5 +174,6 @@ "permissions": [], "sort_field": "modified", "sort_order": "DESC", + "states": [], "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/bom_explosion_item/bom_explosion_item.json b/erpnext/manufacturing/doctype/bom_explosion_item/bom_explosion_item.json index 9b1db63494b..c75ac32cd12 100644 --- a/erpnext/manufacturing/doctype/bom_explosion_item/bom_explosion_item.json +++ b/erpnext/manufacturing/doctype/bom_explosion_item/bom_explosion_item.json @@ -85,6 +85,7 @@ "fieldtype": "Column Break" }, { + "fetch_from": "item_code.image", "fieldname": "image", "fieldtype": "Attach", "hidden": 1, @@ -169,7 +170,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2022-05-27 13:42:23.305455", + "modified": "2023-11-14 18:35:40.856895", "modified_by": "Administrator", "module": "Manufacturing", "name": "BOM Explosion Item", diff --git a/erpnext/manufacturing/doctype/bom_item/bom_item.json b/erpnext/manufacturing/doctype/bom_item/bom_item.json index c5266119dc2..cb58af1f29a 100644 --- a/erpnext/manufacturing/doctype/bom_item/bom_item.json +++ b/erpnext/manufacturing/doctype/bom_item/bom_item.json @@ -111,6 +111,7 @@ "fieldtype": "Column Break" }, { + "fetch_from": "item_code.image", "fieldname": "image", "fieldtype": "Attach", "hidden": 1, @@ -289,7 +290,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2022-07-28 10:20:51.559010", + "modified": "2023-11-14 18:35:51.378513", "modified_by": "Administrator", "module": "Manufacturing", "name": "BOM Item", diff --git a/erpnext/selling/doctype/quotation_item/quotation_item.json b/erpnext/selling/doctype/quotation_item/quotation_item.json index f5e641dceb4..555dc68f4eb 100644 --- a/erpnext/selling/doctype/quotation_item/quotation_item.json +++ b/erpnext/selling/doctype/quotation_item/quotation_item.json @@ -137,6 +137,7 @@ "width": "300px" }, { + "fetch_from": "item_code.image", "fieldname": "image", "fieldtype": "Attach", "hidden": 1, @@ -668,7 +669,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2023-09-27 14:02:12.332407", + "modified": "2023-11-14 18:24:24.619832", "modified_by": "Administrator", "module": "Selling", "name": "Quotation Item", @@ -678,4 +679,4 @@ "sort_order": "DESC", "states": [], "track_changes": 1 -} \ No newline at end of file +} diff --git a/erpnext/selling/doctype/sales_order_item/sales_order_item.json b/erpnext/selling/doctype/sales_order_item/sales_order_item.json index e9714113095..bf28b2054d5 100644 --- a/erpnext/selling/doctype/sales_order_item/sales_order_item.json +++ b/erpnext/selling/doctype/sales_order_item/sales_order_item.json @@ -66,7 +66,6 @@ "total_weight", "column_break_21", "weight_uom", - "accounting_dimensions_section", "warehouse_and_reference", "warehouse", "target_warehouse", @@ -177,6 +176,7 @@ "print_hide": 1 }, { + "fetch_from": "item_code.image", "fieldname": "image", "fieldtype": "Attach", "hidden": 1, @@ -869,18 +869,12 @@ "label": "Production Plan Qty", "no_copy": 1, "read_only": 1 - }, - { - "collapsible": 1, - "fieldname": "accounting_dimensions_section", - "fieldtype": "Section Break", - "label": "Accounting Dimensions" } ], "idx": 1, "istable": 1, "links": [], - "modified": "2023-10-17 18:18:26.475259", + "modified": "2023-11-14 18:37:12.787893", "modified_by": "Administrator", "module": "Selling", "name": "Sales Order Item", diff --git a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json index 237088f64a7..b85bfe5036d 100644 --- a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json +++ b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json @@ -165,6 +165,7 @@ "width": "300px" }, { + "fetch_from": "item_code.image", "fieldname": "image", "fieldtype": "Attach", "hidden": 1, @@ -869,7 +870,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2023-10-16 16:18:18.013379", + "modified": "2023-11-14 18:37:38.638144", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note Item", diff --git a/erpnext/stock/doctype/material_request_item/material_request_item.json b/erpnext/stock/doctype/material_request_item/material_request_item.json index f0e117ef48d..ed4a7e7cf64 100644 --- a/erpnext/stock/doctype/material_request_item/material_request_item.json +++ b/erpnext/stock/doctype/material_request_item/material_request_item.json @@ -111,6 +111,7 @@ "width": "250px" }, { + "fetch_from": "item_code.image", "fieldname": "image", "fieldtype": "Attach Image", "label": "Image", @@ -479,7 +480,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2023-10-27 15:53:41.444236", + "modified": "2023-11-14 18:37:59.599115", "modified_by": "Administrator", "module": "Stock", "name": "Material Request 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 47883bbf8a7..749dbf2111f 100644 --- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json +++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json @@ -186,6 +186,7 @@ "width": "300px" }, { + "fetch_from": "item_code.image", "fieldname": "image", "fieldtype": "Attach", "hidden": 1, @@ -1053,7 +1054,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2023-10-30 17:32:24.560337", + "modified": "2023-11-14 18:38:15.251994", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt Item", diff --git a/erpnext/subcontracting/doctype/subcontracting_order_item/subcontracting_order_item.json b/erpnext/subcontracting/doctype/subcontracting_order_item/subcontracting_order_item.json index d77e77440e0..46c229bfd37 100644 --- a/erpnext/subcontracting/doctype/subcontracting_order_item/subcontracting_order_item.json +++ b/erpnext/subcontracting/doctype/subcontracting_order_item/subcontracting_order_item.json @@ -112,6 +112,7 @@ "fieldtype": "Column Break" }, { + "fetch_from": "item_code.image", "fieldname": "image", "fieldtype": "Attach", "hidden": 1, @@ -337,7 +338,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2023-01-20 23:25:45.363281", + "modified": "2023-11-14 18:38:37.640677", "modified_by": "Administrator", "module": "Subcontracting", "name": "Subcontracting Order Item", 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 f7e88d0fb7f..28bd84e5895 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json +++ b/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json @@ -104,6 +104,7 @@ "width": "300px" }, { + "fetch_from": "item_code.image", "fieldname": "image", "fieldtype": "Attach", "hidden": 1, @@ -469,7 +470,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2023-09-03 17:04:21.214316", + "modified": "2023-11-14 18:38:26.459669", "modified_by": "Administrator", "module": "Subcontracting", "name": "Subcontracting Receipt Item", From 1f16c47a2c4562bf5c2e7d7b4bafe7604efbb36a Mon Sep 17 00:00:00 2001 From: Arjun Date: Wed, 15 Nov 2023 12:34:38 +0530 Subject: [PATCH 148/151] fix: duplicate field in `Closing Stock Balance` (#38105) (cherry picked from commit 908b21f7fd8d0c62ffd51eb2ca5238225d0f52ad) --- .../closing_stock_balance/closing_stock_balance.json | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/erpnext/stock/doctype/closing_stock_balance/closing_stock_balance.json b/erpnext/stock/doctype/closing_stock_balance/closing_stock_balance.json index 225da6d15ec..0c4757ffadb 100644 --- a/erpnext/stock/doctype/closing_stock_balance/closing_stock_balance.json +++ b/erpnext/stock/doctype/closing_stock_balance/closing_stock_balance.json @@ -103,15 +103,6 @@ "print_hide": 1, "read_only": 1 }, - { - "fieldname": "amended_from", - "fieldtype": "Link", - "label": "Amended From", - "no_copy": 1, - "options": "Closing Stock Balance", - "print_hide": 1, - "read_only": 1 - }, { "fieldname": "include_uom", "fieldtype": "Link", @@ -145,4 +136,4 @@ "sort_field": "modified", "sort_order": "DESC", "states": [] -} \ No newline at end of file +} From 58cb4303ee41a411cd91e09a49f90697d17f0de2 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 15 Nov 2023 18:01:28 +0530 Subject: [PATCH 149/151] chore: change read only condition of asset quantity field (backport #38111) (#38112) chore: change read only condition of asset quantity field (#38111) chore: change read only condition of asset quantity (cherry picked from commit 6e0362dee85811267edff53f23a24b48ebd91c11) Co-authored-by: Anand Baburajan --- erpnext/assets/doctype/asset/asset.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/erpnext/assets/doctype/asset/asset.json b/erpnext/assets/doctype/asset/asset.json index 963e4b8297a..b092d193cc4 100644 --- a/erpnext/assets/doctype/asset/asset.json +++ b/erpnext/assets/doctype/asset/asset.json @@ -495,11 +495,10 @@ "read_only": 1 }, { - "depends_on": "eval.doc.asset_quantity", "fieldname": "asset_quantity", "fieldtype": "Int", "label": "Asset Quantity", - "read_only": 1 + "read_only_depends_on": "eval:!doc.is_existing_asset && !doc.is_composite_asset" }, { "fieldname": "depr_entry_posting_status", @@ -565,7 +564,7 @@ "link_fieldname": "target_asset" } ], - "modified": "2023-10-27 17:03:46.629617", + "modified": "2023-11-15 17:40:17.315203", "modified_by": "Administrator", "module": "Assets", "name": "Asset", From bc01007c160ee4f0bb987fab77630c441f38a08b Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 16 Nov 2023 20:27:53 +0530 Subject: [PATCH 150/151] fix: remove ESS role when not mapped to employee (backport #37867) (#38132) fix: remove ESS role when not mapped to employee (#37867) * fix: remove ESS role when not mapped to employee * fix: emp role removal on unlinking * fix: test case for user employee role mapping * fix: mapped employee and user on creation (cherry picked from commit 56b8d1b9277a1290b18c6745736bef8dfa4e6f90) Co-authored-by: Dany Robert --- erpnext/setup/doctype/employee/employee.js | 4 ++- erpnext/setup/doctype/employee/employee.py | 29 +++++++++++++++---- .../setup/doctype/employee/test_employee.py | 9 ++++++ 3 files changed, 36 insertions(+), 6 deletions(-) diff --git a/erpnext/setup/doctype/employee/employee.js b/erpnext/setup/doctype/employee/employee.js index 39a215f3831..efc3fd1d33d 100755 --- a/erpnext/setup/doctype/employee/employee.js +++ b/erpnext/setup/doctype/employee/employee.js @@ -81,8 +81,10 @@ frappe.ui.form.on("Employee", { employee: frm.doc.name, email: frm.doc.prefered_email }, + freeze: true, + freeze_message: __("Creating User..."), callback: function (r) { - frm.set_value("user_id", r.message); + frm.reload_doc(); } }); } diff --git a/erpnext/setup/doctype/employee/employee.py b/erpnext/setup/doctype/employee/employee.py index 78f2e4935e7..a632c90f51e 100755 --- a/erpnext/setup/doctype/employee/employee.py +++ b/erpnext/setup/doctype/employee/employee.py @@ -49,6 +49,9 @@ class Employee(NestedSet): else: existing_user_id = frappe.db.get_value("Employee", self.name, "user_id") if existing_user_id: + user = frappe.get_doc("User", existing_user_id) + validate_employee_role(user, ignore_emp_check=True) + user.save(ignore_permissions=True) remove_user_permission("Employee", self.name, existing_user_id) def after_rename(self, old, new, merge): @@ -254,12 +257,26 @@ class Employee(NestedSet): frappe.cache().hdel("employees_with_number", prev_number) -def validate_employee_role(doc, method): +def validate_employee_role(doc, method=None, ignore_emp_check=False): # called via User hook - if "Employee" in [d.role for d in doc.get("roles")]: - if not frappe.db.get_value("Employee", {"user_id": doc.name}): - frappe.msgprint(_("Please set User ID field in an Employee record to set Employee Role")) - doc.get("roles").remove(doc.get("roles", {"role": "Employee"})[0]) + if not ignore_emp_check: + if frappe.db.get_value("Employee", {"user_id": doc.name}): + return + + user_roles = [d.role for d in doc.get("roles")] + if "Employee" in user_roles: + frappe.msgprint( + _("User {0}: Removed Employee role as there is no mapped employee.").format(doc.name) + ) + doc.get("roles").remove(doc.get("roles", {"role": "Employee"})[0]) + + if "Employee Self Service" in user_roles: + frappe.msgprint( + _("User {0}: Removed Employee Self Service role as there is no mapped employee.").format( + doc.name + ) + ) + doc.get("roles").remove(doc.get("roles", {"role": "Employee Self Service"})[0]) def update_user_permissions(doc, method): @@ -371,6 +388,8 @@ def create_user(employee, user=None, email=None): } ) user.insert() + emp.user_id = user.name + emp.save() return user.name diff --git a/erpnext/setup/doctype/employee/test_employee.py b/erpnext/setup/doctype/employee/test_employee.py index 5a693c5effb..9b706836269 100644 --- a/erpnext/setup/doctype/employee/test_employee.py +++ b/erpnext/setup/doctype/employee/test_employee.py @@ -25,6 +25,15 @@ class TestEmployee(unittest.TestCase): employee1_doc.status = "Left" self.assertRaises(InactiveEmployeeStatusError, employee1_doc.save) + def test_user_has_employee(self): + employee = make_employee("test_emp_user_creation@company.com") + employee_doc = frappe.get_doc("Employee", employee) + user = employee_doc.user_id + self.assertTrue("Employee" in frappe.get_roles(user)) + employee_doc.user_id = "" + employee_doc.save() + self.assertTrue("Employee" not in frappe.get_roles(user)) + def tearDown(self): frappe.db.rollback() From d0698b32bbde5ac4d4e0e33d3c8ca2f10f0d7cc3 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 16 Nov 2023 10:04:50 +0530 Subject: [PATCH 151/151] fix: add revaluation journal filter in Payable report (cherry picked from commit 134201794ace2863c02458b3e1797da816802d06) --- erpnext/accounts/report/accounts_payable/accounts_payable.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/erpnext/accounts/report/accounts_payable/accounts_payable.js b/erpnext/accounts/report/accounts_payable/accounts_payable.js index eff705dafac..0979cffbf3c 100644 --- a/erpnext/accounts/report/accounts_payable/accounts_payable.js +++ b/erpnext/accounts/report/accounts_payable/accounts_payable.js @@ -144,6 +144,11 @@ frappe.query_reports["Accounts Payable"] = { "label": __("Show Future Payments"), "fieldtype": "Check", }, + { + "fieldname": "for_revaluation_journals", + "label": __("Revaluation Journals"), + "fieldtype": "Check", + }, { "fieldname": "ignore_accounts", "label": __("Group by Voucher"),