From b80a5f27a904be30572742ec446feea335a20a28 Mon Sep 17 00:00:00 2001 From: Danny <66078599+mmdanny89@users.noreply.github.com> Date: Wed, 27 Mar 2024 12:41:58 -0400 Subject: [PATCH 001/734] fix: set default warehouse for pos invoice (cherry picked from commit b156937254a93dcd9fe5ff8ac598261526f8292e) --- erpnext/selling/page/point_of_sale/pos_controller.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/selling/page/point_of_sale/pos_controller.js b/erpnext/selling/page/point_of_sale/pos_controller.js index 452019ebf4b..0d71932a1a9 100644 --- a/erpnext/selling/page/point_of_sale/pos_controller.js +++ b/erpnext/selling/page/point_of_sale/pos_controller.js @@ -547,6 +547,8 @@ erpnext.PointOfSale.Controller = class { async on_cart_update(args) { frappe.dom.freeze(); + if (this.frm.doc.set_warehouse != this.settings.warehouse) + this.frm.doc.set_warehouse = this.settings.warehouse; let item_row = undefined; try { let { field, value, item } = args; From 58125778545e8e1166e110a563eb896d9b2bde52 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 3 Jul 2024 05:06:09 +0000 Subject: [PATCH 002/734] chore(release): Bumped to Version 15.29.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # [15.29.0](https://github.com/frappe/erpnext/compare/v15.28.2...v15.29.0) (2024-07-03) ### Bug Fixes * add auto-update for overdue status ([#42105](https://github.com/frappe/erpnext/issues/42105)) ([317cc03](https://github.com/frappe/erpnext/commit/317cc0358c0e3cb07da0d72657dddeb9ef7ee546)) * add string for translation (backport [#41903](https://github.com/frappe/erpnext/issues/41903)) ([#41963](https://github.com/frappe/erpnext/issues/41963)) ([48dc24b](https://github.com/frappe/erpnext/commit/48dc24b9bf65e788bffc700f4002f66cef9bcfed)) * always post to tax account heads if LCV is booked ([706a6c1](https://github.com/frappe/erpnext/commit/706a6c1ad7c7850974d7dc7b6a0ff162ef4c1a28)) * batch picking in pick list based on Stock Settings (backport [#42021](https://github.com/frappe/erpnext/issues/42021)) ([#42134](https://github.com/frappe/erpnext/issues/42134)) ([a45f8ca](https://github.com/frappe/erpnext/commit/a45f8ca5fd7403e61f4c836dc28ea7742c24441e)) * batch reset while making SABB (backport [#42076](https://github.com/frappe/erpnext/issues/42076)) ([#42123](https://github.com/frappe/erpnext/issues/42123)) ([c3f5a49](https://github.com/frappe/erpnext/commit/c3f5a494f32a4fff787dbd04c5d36f006bac0865)) * decimal issue in pick list (backport [#41972](https://github.com/frappe/erpnext/issues/41972)) ([#41982](https://github.com/frappe/erpnext/issues/41982)) ([9945a90](https://github.com/frappe/erpnext/commit/9945a90b3fdfdbe95c7205c7228372d3b47e0ce6)) * **Delivery Note:** only show permitted actions ([cef6d0d](https://github.com/frappe/erpnext/commit/cef6d0d74d11067e29e58d080fa09a328a5f016a)) * do not show zero balance stock items in stock balance report (backport [#41958](https://github.com/frappe/erpnext/issues/41958)) ([#41961](https://github.com/frappe/erpnext/issues/41961)) ([c10b123](https://github.com/frappe/erpnext/commit/c10b123a817ba0e1285a607c8514b57747d0ebf5)) * expense account from item group not fetched (backport [#41957](https://github.com/frappe/erpnext/issues/41957)) ([#41962](https://github.com/frappe/erpnext/issues/41962)) ([760b2e2](https://github.com/frappe/erpnext/commit/760b2e24f27c1dfdc70eb685562e83afc9f35ae3)) * fixed asset value in Fixed Asset Register (backport [#41930](https://github.com/frappe/erpnext/issues/41930)) ([#42027](https://github.com/frappe/erpnext/issues/42027)) ([f2feeaf](https://github.com/frappe/erpnext/commit/f2feeaf264febdbb3b9489b9d363c1e2798dd5f9)) * handle none type object error ([b0aef9e](https://github.com/frappe/erpnext/commit/b0aef9e42b34dfe6b8b0002647b8ba966a735a9a)) * incorrect against_account upon reposting ([a41577a](https://github.com/frappe/erpnext/commit/a41577a1cd901f7c66256f9bb877af1ddc610913)) * incorrect Difference Amount (backport [#42008](https://github.com/frappe/erpnext/issues/42008)) ([#42013](https://github.com/frappe/erpnext/issues/42013)) ([838cc5b](https://github.com/frappe/erpnext/commit/838cc5b72aff39d53093c71a52fe2645c69fe1a8)) * incorrect discount on other item ([77f4199](https://github.com/frappe/erpnext/commit/77f4199e2ad4b1b7b2b4122a67affcc2d845a80b)) * incorrect dr/cr on Adv Payment against Journals ([4e74257](https://github.com/frappe/erpnext/commit/4e74257ba9bf520d0a92622adf979e77cd4d8222)) * incorrect time period in asset depreciation schedule (backport [#41805](https://github.com/frappe/erpnext/issues/41805)) ([#42043](https://github.com/frappe/erpnext/issues/42043)) ([cf4d4ba](https://github.com/frappe/erpnext/commit/cf4d4ba3e97094bbe5d61b2f119ab157d4ffac08)) * lead status filter (backport [#41816](https://github.com/frappe/erpnext/issues/41816)) ([#42046](https://github.com/frappe/erpnext/issues/42046)) ([3536a75](https://github.com/frappe/erpnext/commit/3536a754ff8dd778e72679dd906e7703f1ef7c0f)) * manufacturing date issue in the batch (backport [#42034](https://github.com/frappe/erpnext/issues/42034)) ([#42037](https://github.com/frappe/erpnext/issues/42037)) ([a981633](https://github.com/frappe/erpnext/commit/a981633d949fb43054bb22b78c77c5bde3296742)) * move condition for shipment ([2180239](https://github.com/frappe/erpnext/commit/21802396ce0659de9097074916b3144ce576171e)) * not able to make purchase return (backport [#42053](https://github.com/frappe/erpnext/issues/42053)) ([#42055](https://github.com/frappe/erpnext/issues/42055)) ([8a91bf3](https://github.com/frappe/erpnext/commit/8a91bf315491e6b3b54c1b0f681397bb5acd4a66)) * pricing rule with and without 'apply multiple' and priority ([f3aa885](https://github.com/frappe/erpnext/commit/f3aa8854880173dd89a911ca4198c9fc605e35f1)) * provisional entry for non stock items ([d61dab8](https://github.com/frappe/erpnext/commit/d61dab856942ff94092c90963dedef71fdf6dfed)) * Re-open allows SO's to be over credit limit ([7fcb0f5](https://github.com/frappe/erpnext/commit/7fcb0f578ae7456c71313f1240d3a6e855d8f86e)) * refactor Asset Repair and Stock Entry linkage to resolve amendme… (backport [#41919](https://github.com/frappe/erpnext/issues/41919)) ([#42058](https://github.com/frappe/erpnext/issues/42058)) ([97c49b9](https://github.com/frappe/erpnext/commit/97c49b93b673f5a9472d505cc981a12e5e4aa00b)) * reload asset when creating asset depreciation ([7b5d504](https://github.com/frappe/erpnext/commit/7b5d5043c5220189240c04f90abb7ffbe75d7cd3)) * reposting file attachment permission issue (backport [#42068](https://github.com/frappe/erpnext/issues/42068)) ([#42075](https://github.com/frappe/erpnext/issues/42075)) ([1f3374f](https://github.com/frappe/erpnext/commit/1f3374fcdf3e29a9fa6ad7d317db1b9c087bdbfb)) * resolve gl entries duplication in asset purchase workflow (backport [#41845](https://github.com/frappe/erpnext/issues/41845)) ([#42120](https://github.com/frappe/erpnext/issues/42120)) ([58e18e2](https://github.com/frappe/erpnext/commit/58e18e2b1fa7d8e9e6fb43282be777af711ac865)) * **Sales Order:** only show permitted actions ([a0011c5](https://github.com/frappe/erpnext/commit/a0011c5b529662d28a1e0fc623c96644fbb68c96)) * show zero stock items filter in the stock balance report (backport [#42147](https://github.com/frappe/erpnext/issues/42147)) ([#42152](https://github.com/frappe/erpnext/issues/42152)) ([11ebbf2](https://github.com/frappe/erpnext/commit/11ebbf2a9cfad99c1e2a6406f537f75a4526185b)) * stock qty validation in SCR (backport [#42124](https://github.com/frappe/erpnext/issues/42124)) ([#42133](https://github.com/frappe/erpnext/issues/42133)) ([d9e62fe](https://github.com/frappe/erpnext/commit/d9e62fef2121efb39f9a2ef220e92bae98bf848d)) * Stock Reservation Entry was not getting created (backport [#42033](https://github.com/frappe/erpnext/issues/42033)) ([#42035](https://github.com/frappe/erpnext/issues/42035)) ([e278fc6](https://github.com/frappe/erpnext/commit/e278fc683f9cad5d709c1719d2bae502193387a5)) * **test:** incorrect field for customer default billing currency ([3b15708](https://github.com/frappe/erpnext/commit/3b15708f18e695a93f740f03ddb8b1153abc6208)) * this.frm.events.update_cost is not a function (backport [#41960](https://github.com/frappe/erpnext/issues/41960)) ([#41965](https://github.com/frappe/erpnext/issues/41965)) ([3b4d397](https://github.com/frappe/erpnext/commit/3b4d39766f78492bd2ba92dc6c6c5b91263d3e6d)) * timeout error while submitting JV (backport [#42040](https://github.com/frappe/erpnext/issues/42040)) ([#42099](https://github.com/frappe/erpnext/issues/42099)) ([a0e06a4](https://github.com/frappe/erpnext/commit/a0e06a4ba5c5efc2cd69a8b561c5832dcd210761)) * timeout while cancelling LCV (backport [#42030](https://github.com/frappe/erpnext/issues/42030)) (backport [#42031](https://github.com/frappe/erpnext/issues/42031)) ([#42032](https://github.com/frappe/erpnext/issues/42032)) ([068de08](https://github.com/frappe/erpnext/commit/068de08bbbcbe885523b484ed964e73ef38c4fdc)) * unhide serial no field (backport [#42045](https://github.com/frappe/erpnext/issues/42045)) ([#42047](https://github.com/frappe/erpnext/issues/42047)) ([482832f](https://github.com/frappe/erpnext/commit/482832f3c2be1b7cd63e5ffea595a88abdd3869b)) * valuation rate for the legacy batches (backport [#42011](https://github.com/frappe/erpnext/issues/42011)) ([#42020](https://github.com/frappe/erpnext/issues/42020)) ([f6be19c](https://github.com/frappe/erpnext/commit/f6be19cb7c46e67dde6c8dc43afe08d57d87acc5)) * Wrong Delete Batch on Purchase Receipt (backport [#42007](https://github.com/frappe/erpnext/issues/42007)) ([#42012](https://github.com/frappe/erpnext/issues/42012)) ([68b318a](https://github.com/frappe/erpnext/commit/68b318a94b679fef505090fa1ebb4adecb3862a8)) ### Features * accounting dimension filters in gp report ([fe9dffb](https://github.com/frappe/erpnext/commit/fe9dffb271021e554331f048931fe793b05bbaf1)) * default account head for operating cost (backport [#41985](https://github.com/frappe/erpnext/issues/41985)) ([#41987](https://github.com/frappe/erpnext/issues/41987)) ([44c1671](https://github.com/frappe/erpnext/commit/44c16713ba9fdda293d612b10720d63f6a83b0c8)) * **gp:** group by cost center ([068ae87](https://github.com/frappe/erpnext/commit/068ae87b8d56cb1161a9fd42136b72da33f6313f)) * Turkish Chart Of Accounts (backport [#41756](https://github.com/frappe/erpnext/issues/41756)) ([#42028](https://github.com/frappe/erpnext/issues/42028)) ([63b26e6](https://github.com/frappe/erpnext/commit/63b26e679b05d8f2c334257ba4f8921ad72a48db)) ### Performance Improvements * code optimization to handle large asset creation (backport [#42018](https://github.com/frappe/erpnext/issues/42018)) ([#42025](https://github.com/frappe/erpnext/issues/42025)) ([c27f272](https://github.com/frappe/erpnext/commit/c27f272f061732e9fc58d35a95f5c34c52c0a4ab)) * dont run queries unnecessarily, improved filters ([#41993](https://github.com/frappe/erpnext/issues/41993)) ([b59c91a](https://github.com/frappe/erpnext/commit/b59c91a341d3f4265ae966a12e3a002c0a4dbc0b)) * Performance optmization for Purchase Invoice submission (backport [#40263](https://github.com/frappe/erpnext/issues/40263)) ([#41946](https://github.com/frappe/erpnext/issues/41946)) ([d396c18](https://github.com/frappe/erpnext/commit/d396c18689e530d3f11a791ef1064c2d9775466e)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 4a9236066cc..4ebd5ee55d8 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "15.28.2" +__version__ = "15.29.0" def get_default_company(user=None): From 473aaf4e5baeccb1e2ed96a0ace637bb405c8692 Mon Sep 17 00:00:00 2001 From: "Nihantra C. Patel" <141945075+Nihantra-Patel@users.noreply.github.com> Date: Wed, 3 Jul 2024 13:12:00 +0530 Subject: [PATCH 003/734] fix: path of automatically updates the status of asset maintenance log (cherry picked from commit 909aa8f359c77cb92c3607cb46fea6a5292730dc) (cherry picked from commit 5317418a53815f421296d3861a7d10aa25d31136) --- erpnext/hooks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/hooks.py b/erpnext/hooks.py index a080af9a827..028acc9cb11 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -442,7 +442,7 @@ scheduler_events = { "erpnext.accounts.doctype.process_statement_of_accounts.process_statement_of_accounts.send_auto_email", "erpnext.accounts.utils.auto_create_exchange_rate_revaluation_daily", "erpnext.accounts.utils.run_ledger_health_checks", - "erpnext.assets.doctype.asset.asset_maintenance_log.update_asset_maintenance_log_status", + "erpnext.assets.doctype.asset_maintenance_log.asset_maintenance_log.update_asset_maintenance_log_status", ], "weekly": [ "erpnext.accounts.utils.auto_create_exchange_rate_revaluation_weekly", From 7568af67e90b2a7ee56e298c29ec2f53a31b590e Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 3 Jul 2024 14:56:03 +0000 Subject: [PATCH 004/734] chore(release): Bumped to Version 15.29.1 ## [15.29.1](https://github.com/frappe/erpnext/compare/v15.29.0...v15.29.1) (2024-07-03) ### Bug Fixes * path of automatically updates the status of asset maintenance log ([473aaf4](https://github.com/frappe/erpnext/commit/473aaf4e5baeccb1e2ed96a0ace637bb405c8692)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 4ebd5ee55d8..e82ecf1039a 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "15.29.0" +__version__ = "15.29.1" def get_default_company(user=None): From 20d481de5e5b220db88191482e008ae9ad2a0778 Mon Sep 17 00:00:00 2001 From: "Nihantra C. Patel" <141945075+Nihantra-Patel@users.noreply.github.com> Date: Fri, 21 Jun 2024 15:49:40 +0530 Subject: [PATCH 005/734] fix: group by in item-wise purchase register (cherry picked from commit 3fab00135b1391c5f505fee599dac7234b0e1992) --- .../item_wise_purchase_register/item_wise_purchase_register.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py index 1cd9b872a9b..8bc05273092 100644 --- a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py +++ b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py @@ -309,7 +309,7 @@ def apply_conditions(query, pi, pii, filters): query = query.orderby(pi.posting_date, order=Order.desc) query = query.orderby(pii.item_group, order=Order.desc) else: - query = apply_group_by_conditions(filters, "Purchase Invoice") + query = apply_group_by_conditions(query, pi, pii, filters) return query From 8baef2454196cb57bd428f8b46f344b88a651b36 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 5 Jul 2024 07:43:13 +0530 Subject: [PATCH 006/734] fix: blank item-wise sales/purchase register reports on first load --- .../item_wise_purchase_register/item_wise_purchase_register.js | 2 +- .../report/item_wise_sales_register/item_wise_sales_register.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.js b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.js index e05591e49df..836ec7b456c 100644 --- a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.js +++ b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.js @@ -52,7 +52,7 @@ frappe.query_reports["Item-wise Purchase Register"] = { label: __("Group By"), fieldname: "group_by", fieldtype: "Select", - options: ["Supplier", "Item Group", "Item", "Invoice"], + options: ["", "Supplier", "Item Group", "Item", "Invoice"], }, ], formatter: function (value, row, column, data, default_formatter) { diff --git a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.js b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.js index 16a97733393..6fb015f7b74 100644 --- a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.js +++ b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.js @@ -70,7 +70,7 @@ frappe.query_reports["Item-wise Sales Register"] = { label: __("Group By"), fieldname: "group_by", fieldtype: "Select", - options: ["Customer Group", "Customer", "Item Group", "Item", "Territory", "Invoice"], + options: ["", "Customer Group", "Customer", "Item Group", "Item", "Territory", "Invoice"], }, ], formatter: function (value, row, column, data, default_formatter) { From c9e3dee5b2650ebe98774bf330194ca83f63db5d Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Fri, 5 Jul 2024 02:47:13 +0000 Subject: [PATCH 007/734] chore(release): Bumped to Version 15.29.2 ## [15.29.2](https://github.com/frappe/erpnext/compare/v15.29.1...v15.29.2) (2024-07-05) ### Bug Fixes * blank item-wise sales/purchase register reports on first load ([8baef24](https://github.com/frappe/erpnext/commit/8baef2454196cb57bd428f8b46f344b88a651b36)) * group by in item-wise purchase register ([20d481d](https://github.com/frappe/erpnext/commit/20d481de5e5b220db88191482e008ae9ad2a0778)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index e82ecf1039a..9170c683912 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "15.29.1" +__version__ = "15.29.2" def get_default_company(user=None): From f97533397030e81972dce301c7ed63e3e23f1559 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 10 Jul 2024 10:44:06 +0000 Subject: [PATCH 008/734] chore(release): Bumped to Version 15.29.3 ## [15.29.3](https://github.com/frappe/erpnext/compare/v15.29.2...v15.29.3) (2024-07-10) ### Bug Fixes * actual qty in sales order (backport [#42248](https://github.com/frappe/erpnext/issues/42248)) ([#42256](https://github.com/frappe/erpnext/issues/42256)) ([4866958](https://github.com/frappe/erpnext/commit/4866958a96bf99f69e842b6986ee339b885873ec)) * add missing german translations ([2f89461](https://github.com/frappe/erpnext/commit/2f89461ace6af52e17134cd2cb5ab535e3851985)) * added filter to show only submitted assets ([19ed6d1](https://github.com/frappe/erpnext/commit/19ed6d10815144c74e5221a3584c099022da5f11)) * auto serial and batch bundle not creating for Asset Capitalization (backport [#42231](https://github.com/frappe/erpnext/issues/42231)) ([#42242](https://github.com/frappe/erpnext/issues/42242)) ([7916d64](https://github.com/frappe/erpnext/commit/7916d6436f2152e90ec5d2f8de3416937c6de800)) * Billed Qty and Qty to Bill Calculation in Purchase Order Analysis (backport [#42100](https://github.com/frappe/erpnext/issues/42100)) ([#42249](https://github.com/frappe/erpnext/issues/42249)) ([43c7513](https://github.com/frappe/erpnext/commit/43c7513cfe20713e23627a77e0b6b23471073c0a)) * BOM Creator Recursion Error on duplicate save (backport [#41622](https://github.com/frappe/erpnext/issues/41622)) ([#42179](https://github.com/frappe/erpnext/issues/42179)) ([68a39df](https://github.com/frappe/erpnext/commit/68a39dfa331d7f2922f7961a243d303015e90008)) * changes as per review ([57896a8](https://github.com/frappe/erpnext/commit/57896a8f9936f4716d9282df04e890ab5201e44e)) * completed DC will not appear in a delivery trip ([0bab609](https://github.com/frappe/erpnext/commit/0bab609a6fd502740bb3e7f9f8dfa5bc50ea9e6a)) * completed DC will not appear in a delivery trip ([#41655](https://github.com/frappe/erpnext/issues/41655)) ([a3444a0](https://github.com/frappe/erpnext/commit/a3444a07b7f597f57b89ed2119be2a9da5273e2a)) * corrected mismatch in the Purchase Receipt Status [#15620](https://github.com/frappe/erpnext/issues/15620) (backport [#42138](https://github.com/frappe/erpnext/issues/42138)) ([#42252](https://github.com/frappe/erpnext/issues/42252)) ([e1b50ef](https://github.com/frappe/erpnext/commit/e1b50efeea222fd78eb68332638bbd17544a260e)) * correcting balance sheet calculation for zero liabilities and equity ([d48a2c9](https://github.com/frappe/erpnext/commit/d48a2c9f8e3c7bd47e253cb2c88ffc2c75290ed8)) * correcting balance sheet calculation for zero liabilities and equity ([#41497](https://github.com/frappe/erpnext/issues/41497)) ([2104d90](https://github.com/frappe/erpnext/commit/2104d903aa65a5a09b5ecfbb6b937fa181fc33af)) * custom delimiters ([43ad2fe](https://github.com/frappe/erpnext/commit/43ad2fed631a689cce9b88369f959f6e7049e8d7)) * download_import_log if rows are greater than 5000 ([4eb251b](https://github.com/frappe/erpnext/commit/4eb251b59ac5e26b06b0640a77bd6fe52f3e491b)) * empty item-wise sales/purchase register reports on initial load ([5ac3b34](https://github.com/frappe/erpnext/commit/5ac3b34a6fa85da41ec168037f520a503056df66)) * field position ([a04938d](https://github.com/frappe/erpnext/commit/a04938d5ae9f9312757568ee4e01e18c9f4df901)) * group by in item-wise purchase register ([a967d59](https://github.com/frappe/erpnext/commit/a967d5984434029ed47fd1807a23cf82592c43e0)) * **Holiday List:** sort holidays on save to avoid disorienting the user (backport [#42236](https://github.com/frappe/erpnext/issues/42236)) ([#42250](https://github.com/frappe/erpnext/issues/42250)) ([b555615](https://github.com/frappe/erpnext/commit/b5556156c18f218338deafb49e8581be73b6361e)) * import log preview ([62aac8b](https://github.com/frappe/erpnext/commit/62aac8bb85b3fc28d5db9074ccfc2a03a3d76fb9)) * import status ([71311ff](https://github.com/frappe/erpnext/commit/71311ffd624560994f7f558c3af73a66d4db38c6)) * **Inventory Dimension:** reduce perms for Stock User (backport [#42226](https://github.com/frappe/erpnext/issues/42226)) ([#42243](https://github.com/frappe/erpnext/issues/42243)) ([3cc59e4](https://github.com/frappe/erpnext/commit/3cc59e4a7a379b0dd22b95882ca26a07acdbbee2)) * manual pick allow to pick more than available stock (backport [#42155](https://github.com/frappe/erpnext/issues/42155)) ([#42159](https://github.com/frappe/erpnext/issues/42159)) ([a7b6530](https://github.com/frappe/erpnext/commit/a7b6530fde4ad7ae298f685d9917d9a701fcfd72)) * Multiple fixes for General Ledger Report ([ca57fd4](https://github.com/frappe/erpnext/commit/ca57fd42551dd3a4c6e8a2ffcd58ed75d89c532c)) * multiple free items on same Item Group ([9352863](https://github.com/frappe/erpnext/commit/93528631c3652edecfee3e712d94c4ace9d4b26a)) * path of automatically updates the status of asset maintenance log ([5317418](https://github.com/frappe/erpnext/commit/5317418a53815f421296d3861a7d10aa25d31136)) * Project Status should be Open again if `percent_complete` is not 100 ([90f5c78](https://github.com/frappe/erpnext/commit/90f5c78607656ed8b3a644f30de4efce2c127110)) * provision to enable do not use batch-wise valuation (backport [#42186](https://github.com/frappe/erpnext/issues/42186)) ([#42198](https://github.com/frappe/erpnext/issues/42198)) ([ec881ac](https://github.com/frappe/erpnext/commit/ec881ace76bd6c8b63dc6793cff29bde60b74795)) * **Putaway Rule:** reduce perms for Stock User (backport [#42227](https://github.com/frappe/erpnext/issues/42227)) ([#42244](https://github.com/frappe/erpnext/issues/42244)) ([b78a97d](https://github.com/frappe/erpnext/commit/b78a97df85c66ba0d3a8f4598d239be7df39c41d)) * remove deprecated field "statement_import_log" ([2f0b97d](https://github.com/frappe/erpnext/commit/2f0b97d91b6027126978642090f0e30686181ee6)) * removed max discount validation for sales return ([ab987e9](https://github.com/frappe/erpnext/commit/ab987e9a86ba143a6920609fb3818dd162a05bba)) * **Stock Entry Type:** reduce perms for Stock User (backport [#42225](https://github.com/frappe/erpnext/issues/42225)) ([#42245](https://github.com/frappe/erpnext/issues/42245)) ([954d9ab](https://github.com/frappe/erpnext/commit/954d9ab154ef824f3cdf847fbc7e8c02c55c6fb0)) * tax on stock_rbnb on repost of Purchase Receipt ([427439c](https://github.com/frappe/erpnext/commit/427439c3f19bfd1401628cf39a30ddd291dbfec7)) * **tds:** use doctype reference when mapping keys across multiple doctypes ([#42258](https://github.com/frappe/erpnext/issues/42258)) ([8264e3b](https://github.com/frappe/erpnext/commit/8264e3bc77dacc2ac62f46fac1bec83cf0ec93cb)) * updated logic for calculating tax_withholding_net_total in payment entry ([3fb5c7a](https://github.com/frappe/erpnext/commit/3fb5c7a3a629e60925709c136e735df73042e52d)) * use standard method to get `_doc_before_save` ([cfda5f6](https://github.com/frappe/erpnext/commit/cfda5f6d0b1b6f9757b6010c98c675c768e0cbb5)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 9170c683912..2fbbc6ac1d2 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "15.29.2" +__version__ = "15.29.3" def get_default_company(user=None): From ce8b423ad6aefd2a0355a8efd3505c2d9e161cee Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 12 Jul 2024 20:47:27 +0530 Subject: [PATCH 009/734] chore: release v15 (#42308) * fix(Warehouse): add buttons only if the user can use them (cherry picked from commit 10ae5aaf520dbe5a2c3d38d227dd9a3103f356c3) * fix: missing discount on POS Credit Notes (cherry picked from commit 1049550951011c09fd705c4b01b925e02b6a84ee) * chore: rename test suite for payable report (cherry picked from commit 9474f727760da7592fec05331643b145e9f132f9) * refactor: test suite for item-wise sales register (cherry picked from commit 3aaa22e672ae5363ca6347d0ce280aa7fba062f0) * refactor(test): use each instance UOM for assertion (cherry picked from commit cf4fbfb60150e5af44641a2dd0811fb64d003774) * fix: slowness in reposting dependent vouchers. (backport #42282) (#42292) fix: slowness in reposting dependent vouchers. (#42282) (cherry picked from commit b17696a8ae7f0ea1128887d29336de4ef67a220f) Co-authored-by: rohitwaghchaure * refactor(test): clear old records * fix: keep status as In Progress for RIV for Timeout Error (backport #42274) (#42296) fix: keep status as In Progress for RIV for Timeout Error (#42274) (cherry picked from commit 10280d6140837ffe2fcebb70a57311780c160e25) Co-authored-by: rohitwaghchaure * fix: cost center filter by company (backport #42297) (#42299) fix: cost center filter by company (#42297) (cherry picked from commit 9838f7e6bad8c9e205ca8e8c67f77a8f156ba355) Co-authored-by: rohitwaghchaure * feat: configurable depreciation calculation via accounts settings (#42276) * feat: configurable depreciation calculation via accounts settings * refactor: code optimization * style: changes in description and label (cherry picked from commit b04da63aad0bcf13c15c49d578d291f4d6f8f25b) * fix: not able to submit LCV entry (backport #42303) (#42304) fix: not able to submit LCV entry (#42303) (cherry picked from commit 9cf92eaeab359cb0f5ca46b84d36ebed60a68f1b) Co-authored-by: rohitwaghchaure * fix: While submitting PCV ensure previous FY is closed (backport #42284) (#42300) fix: While submitting PCV ensure previous FY is closed (#42284) (cherry picked from commit d0bbc8ca705f42a0a3c8be2508919f49b67fbdad) Co-authored-by: Nabin Hait --------- Co-authored-by: barredterra <14891507+barredterra@users.noreply.github.com> Co-authored-by: ruthra kumar Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> Co-authored-by: rohitwaghchaure Co-authored-by: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> --- .../accounts_settings/accounts_settings.json | 17 ++++- .../accounts_settings/accounts_settings.py | 1 + .../journal_entry/test_journal_entry.py | 37 +++++++++++ .../period_closing_voucher.py | 20 ++++-- .../doctype/sales_invoice/sales_invoice.js | 3 + erpnext/accounts/general_ledger.py | 12 ++++ .../accounts_payable/test_accounts_payable.py | 2 +- .../test_item_wise_sales_register.py | 65 +++++++++++++++++++ .../asset_depreciation_schedule.py | 51 +++++++++++++-- .../test_asset_depreciation_schedule.py | 62 ++++++++++++++++++ .../repost_item_valuation.py | 7 +- erpnext/stock/doctype/warehouse/warehouse.js | 48 ++++++++------ erpnext/stock/stock_ledger.py | 34 +++++++++- .../subcontracting_order.js | 16 +++++ .../subcontracting_receipt.js | 16 +++++ 15 files changed, 354 insertions(+), 37 deletions(-) create mode 100644 erpnext/accounts/report/item_wise_sales_register/test_item_wise_sales_register.py diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json index 7bf3826e781..31991648158 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json @@ -55,6 +55,8 @@ "post_change_gl_entries", "assets_tab", "asset_settings_section", + "calculate_depr_using_total_days", + "column_break_gjcc", "book_asset_depreciation_entry_automatically", "closing_settings_tab", "period_closing_settings_section", @@ -462,6 +464,17 @@ "fieldname": "enable_immutable_ledger", "fieldtype": "Check", "label": "Enable Immutable Ledger" + }, + { + "fieldname": "column_break_gjcc", + "fieldtype": "Column Break" + }, + { + "default": "0", + "description": "Enable this option to calculate daily depreciation by considering the total number of days in the entire depreciation period, (including leap years) while using daily pro-rata based depreciation", + "fieldname": "calculate_depr_using_total_days", + "fieldtype": "Check", + "label": "Calculate daily depreciation using total days in depreciation period" } ], "icon": "icon-cog", @@ -469,7 +482,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2024-05-11 23:19:44.673975", + "modified": "2024-07-12 00:24:20.957726", "modified_by": "Administrator", "module": "Accounts", "name": "Accounts Settings", @@ -498,4 +511,4 @@ "sort_order": "ASC", "states": [], "track_changes": 1 -} +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py index 34f0f24047b..93ff1e207c9 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py @@ -33,6 +33,7 @@ class AccountsSettings(Document): book_deferred_entries_based_on: DF.Literal["Days", "Months"] book_deferred_entries_via_journal_entry: DF.Check book_tax_discount_loss: DF.Check + calculate_depr_using_total_days: DF.Check check_supplier_invoice_uniqueness: DF.Check credit_controller: DF.Link | None delete_linked_ledger_entries: DF.Check diff --git a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py index 5bfb65a3138..cf0aae96260 100644 --- a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py @@ -481,6 +481,43 @@ class TestJournalEntry(unittest.TestCase): for field in self.fields: self.assertEqual(self.expected_gle[i][field], gl_entries[i][field]) + def test_negative_debit_and_credit_with_same_account_head(self): + from erpnext.accounts.general_ledger import process_gl_map + + # Create JV with defaut cost center - _Test Cost Center + frappe.db.set_single_value("Accounts Settings", "merge_similar_account_heads", 0) + + jv = make_journal_entry("_Test Bank - _TC", "_Test Bank - _TC", 100 * -1, save=True) + jv.append( + "accounts", + { + "account": "_Test Cash - _TC", + "debit": 100 * -1, + "credit": 100 * -1, + "debit_in_account_currency": 100 * -1, + "credit_in_account_currency": 100 * -1, + "exchange_rate": 1, + }, + ) + jv.flags.ignore_validate = True + jv.save() + + self.assertEqual(len(jv.accounts), 3) + + gl_map = jv.build_gl_map() + + for row in gl_map: + if row.account == "_Test Cash - _TC": + self.assertEqual(row.debit_in_account_currency, 100 * -1) + self.assertEqual(row.credit_in_account_currency, 100 * -1) + + gl_map = process_gl_map(gl_map, False) + + for row in gl_map: + if row.account == "_Test Cash - _TC": + self.assertEqual(row.debit_in_account_currency, 100) + self.assertEqual(row.credit_in_account_currency, 100) + def make_journal_entry( account1, diff --git a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py index e75057c7a7f..9bc110d243e 100644 --- a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py +++ b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py @@ -136,18 +136,28 @@ class PeriodClosingVoucher(AccountsController): def check_if_previous_year_closed(self): last_year_closing = add_days(self.year_start_date, -1) - previous_fiscal_year = get_fiscal_year(last_year_closing, company=self.company, boolean=True) + if not previous_fiscal_year: + return - if previous_fiscal_year and not frappe.db.exists( + previous_fiscal_year_start_date = previous_fiscal_year[0][1] + if not frappe.db.exists( "GL Entry", - {"posting_date": ("<=", last_year_closing), "company": self.company, "is_cancelled": 0}, + { + "posting_date": ("between", [previous_fiscal_year_start_date, last_year_closing]), + "company": self.company, + "is_cancelled": 0, + }, ): return - if previous_fiscal_year and not frappe.db.exists( + if not frappe.db.exists( "Period Closing Voucher", - {"posting_date": ("<=", last_year_closing), "docstatus": 1, "company": self.company}, + { + "posting_date": ("between", [previous_fiscal_year_start_date, last_year_closing]), + "docstatus": 1, + "company": self.company, + }, ): frappe.throw(_("Previous Year is not closed, please close it first")) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index c7505ce007d..1df21b7ebc4 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -505,6 +505,9 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends ( return this.frm.call({ doc: me.frm.doc, method: "set_missing_values", + args: { + for_validate: true, + }, callback: function (r) { if (!r.exc) { if (r.message && r.message.print_format) { diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py index 2fd7b5d3c81..a4d128a5845 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -310,6 +310,18 @@ def check_if_in_list(gle, gl_map): def toggle_debit_credit_if_negative(gl_map): for entry in gl_map: # toggle debit, credit if negative entry + if flt(entry.debit) < 0 and flt(entry.credit) < 0 and flt(entry.debit) == flt(entry.credit): + entry.credit *= -1 + entry.debit *= -1 + + if ( + flt(entry.debit_in_account_currency) < 0 + and flt(entry.credit_in_account_currency) < 0 + and flt(entry.debit_in_account_currency) == flt(entry.credit_in_account_currency) + ): + entry.credit_in_account_currency *= -1 + entry.debit_in_account_currency *= -1 + if flt(entry.debit) < 0: entry.credit = flt(entry.credit) - flt(entry.debit) entry.debit = 0.0 diff --git a/erpnext/accounts/report/accounts_payable/test_accounts_payable.py b/erpnext/accounts/report/accounts_payable/test_accounts_payable.py index f5c9d16073e..43856bf569f 100644 --- a/erpnext/accounts/report/accounts_payable/test_accounts_payable.py +++ b/erpnext/accounts/report/accounts_payable/test_accounts_payable.py @@ -7,7 +7,7 @@ from erpnext.accounts.report.accounts_payable.accounts_payable import execute from erpnext.accounts.test.accounts_mixin import AccountsTestMixin -class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase): +class TestAccountsPayable(AccountsTestMixin, FrappeTestCase): def setUp(self): self.create_company() self.create_customer() diff --git a/erpnext/accounts/report/item_wise_sales_register/test_item_wise_sales_register.py b/erpnext/accounts/report/item_wise_sales_register/test_item_wise_sales_register.py new file mode 100644 index 00000000000..4dfdf3058e4 --- /dev/null +++ b/erpnext/accounts/report/item_wise_sales_register/test_item_wise_sales_register.py @@ -0,0 +1,65 @@ +import frappe +from frappe.tests.utils import FrappeTestCase +from frappe.utils import getdate, today + +from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice +from erpnext.accounts.report.item_wise_sales_register.item_wise_sales_register import execute +from erpnext.accounts.test.accounts_mixin import AccountsTestMixin + + +class TestItemWiseSalesRegister(AccountsTestMixin, FrappeTestCase): + def setUp(self): + self.create_company() + self.create_customer() + self.create_item() + self.clear_old_entries() + + def tearDown(self): + frappe.db.rollback() + + def create_sales_invoice(self, do_not_submit=False): + si = create_sales_invoice( + item=self.item, + company=self.company, + customer=self.customer, + debit_to=self.debit_to, + posting_date=today(), + parent_cost_center=self.cost_center, + cost_center=self.cost_center, + rate=100, + price_list_rate=100, + do_not_save=1, + ) + si = si.save() + if not do_not_submit: + si = si.submit() + return si + + def test_basic_report_output(self): + si = self.create_sales_invoice() + + filters = frappe._dict({"from_date": today(), "to_date": today(), "company": self.company}) + report = execute(filters) + + self.assertEqual(len(report[1]), 1) + + expected_result = { + "item_code": si.items[0].item_code, + "invoice": si.name, + "posting_date": getdate(), + "customer": si.customer, + "debit_to": si.debit_to, + "company": self.company, + "income_account": si.items[0].income_account, + "stock_qty": 1.0, + "stock_uom": si.items[0].stock_uom, + "rate": 100.0, + "amount": 100.0, + "total_tax": 0, + "total_other_charges": 0, + "total": 100.0, + "currency": "INR", + } + + report_output = {k: v for k, v in report[1][0].items() if k in expected_result} + self.assertDictEqual(report_output, expected_result) diff --git a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py index bd67a173343..c533a634a5b 100644 --- a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py +++ b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py @@ -701,20 +701,57 @@ def get_straight_line_or_manual_depr_amount( def get_daily_prorata_based_straight_line_depr( asset, row, schedule_idx, number_of_pending_depreciations, amount ): - total_years = flt(number_of_pending_depreciations * row.frequency_of_depreciation) / 12 - every_year_depr = amount / total_years + daily_depr_amount = get_daily_depr_amount(asset, row, schedule_idx, amount) - year_start_date = add_years( - row.depreciation_start_date, (row.frequency_of_depreciation * schedule_idx) // 12 - ) - year_end_date = add_days(add_years(year_start_date, 1), -1) - daily_depr_amount = every_year_depr / (date_diff(year_end_date, year_start_date) + 1) from_date, total_depreciable_days = _get_total_days( row.depreciation_start_date, schedule_idx, row.frequency_of_depreciation ) return daily_depr_amount * total_depreciable_days +def get_daily_depr_amount(asset, row, schedule_idx, amount): + if cint(frappe.db.get_single_value("Accounts Settings", "calculate_depr_using_total_days")): + total_days = ( + date_diff( + get_last_day( + add_months( + row.depreciation_start_date, + flt( + row.total_number_of_depreciations + - asset.opening_number_of_booked_depreciations + - 1 + ) + * row.frequency_of_depreciation, + ) + ), + add_days( + get_last_day(add_months(row.depreciation_start_date, -1 * row.frequency_of_depreciation)), + 1, + ), + ) + + 1 + ) + + return amount / total_days + else: + total_years = ( + flt( + (row.total_number_of_depreciations - row.total_number_of_booked_depreciations) + * row.frequency_of_depreciation + ) + / 12 + ) + + every_year_depr = amount / total_years + + year_start_date = add_years( + row.depreciation_start_date, (row.frequency_of_depreciation * schedule_idx) // 12 + ) + year_end_date = add_days(add_years(year_start_date, 1), -1) + + return every_year_depr / (date_diff(year_end_date, year_start_date) + 1) + + def get_shift_depr_amount(asset_depr_schedule, asset, row, schedule_idx): if asset_depr_schedule.get("__islocal") and not asset.flags.shift_allocation: return ( diff --git a/erpnext/assets/doctype/asset_depreciation_schedule/test_asset_depreciation_schedule.py b/erpnext/assets/doctype/asset_depreciation_schedule/test_asset_depreciation_schedule.py index c359715571e..107d38057a2 100644 --- a/erpnext/assets/doctype/asset_depreciation_schedule/test_asset_depreciation_schedule.py +++ b/erpnext/assets/doctype/asset_depreciation_schedule/test_asset_depreciation_schedule.py @@ -75,6 +75,68 @@ class TestAssetDepreciationSchedule(FrappeTestCase): ] self.assertEqual(schedules, expected_schedules) + # Enable Checkbox to Calculate depreciation using total days in depreciation period + def test_daily_prorata_based_depr_after_enabling_configuration(self): + frappe.db.set_single_value("Accounts Settings", "calculate_depr_using_total_days", 1) + + asset = create_asset( + calculate_depreciation=1, + depreciation_method="Straight Line", + daily_prorata_based=1, + gross_purchase_amount=1096, + available_for_use_date="2020-01-15", + depreciation_start_date="2020-01-31", + frequency_of_depreciation=1, + total_number_of_depreciations=36, + ) + + expected_schedule = [ + ["2020-01-31", 17.0, 17.0], + ["2020-02-29", 29.0, 46.0], + ["2020-03-31", 31.0, 77.0], + ["2020-04-30", 30.0, 107.0], + ["2020-05-31", 31.0, 138.0], + ["2020-06-30", 30.0, 168.0], + ["2020-07-31", 31.0, 199.0], + ["2020-08-31", 31.0, 230.0], + ["2020-09-30", 30.0, 260.0], + ["2020-10-31", 31.0, 291.0], + ["2020-11-30", 30.0, 321.0], + ["2020-12-31", 31.0, 352.0], + ["2021-01-31", 31.0, 383.0], + ["2021-02-28", 28.0, 411.0], + ["2021-03-31", 31.0, 442.0], + ["2021-04-30", 30.0, 472.0], + ["2021-05-31", 31.0, 503.0], + ["2021-06-30", 30.0, 533.0], + ["2021-07-31", 31.0, 564.0], + ["2021-08-31", 31.0, 595.0], + ["2021-09-30", 30.0, 625.0], + ["2021-10-31", 31.0, 656.0], + ["2021-11-30", 30.0, 686.0], + ["2021-12-31", 31.0, 717.0], + ["2022-01-31", 31.0, 748.0], + ["2022-02-28", 28.0, 776.0], + ["2022-03-31", 31.0, 807.0], + ["2022-04-30", 30.0, 837.0], + ["2022-05-31", 31.0, 868.0], + ["2022-06-30", 30.0, 898.0], + ["2022-07-31", 31.0, 929.0], + ["2022-08-31", 31.0, 960.0], + ["2022-09-30", 30.0, 990.0], + ["2022-10-31", 31.0, 1021.0], + ["2022-11-30", 30.0, 1051.0], + ["2022-12-31", 31.0, 1082.0], + ["2023-01-15", 14.0, 1096.0], + ] + + schedules = [ + [cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount] + for d in get_depr_schedule(asset.name, "Draft") + ] + self.assertEqual(schedules, expected_schedule) + frappe.db.set_single_value("Accounts Settings", "calculate_depr_using_total_days", 0) + # Test for Written Down Value Method # Frequency of deprciation = 3 def test_for_daily_prorata_based_depreciation_wdv_method_frequency_3_months(self): diff --git a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py index 717b1c31026..c20eadeb78d 100644 --- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py +++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py @@ -289,6 +289,11 @@ def repost(doc): if isinstance(message, dict): message = message.get("message") + status = "Failed" + # If failed because of timeout, set status to In Progress + if traceback and "timeout" in traceback.lower(): + status = "In Progress" + if traceback: message += "

" + "Traceback:
" + traceback @@ -297,7 +302,7 @@ def repost(doc): doc.name, { "error_log": message, - "status": "Failed", + "status": status, }, ) diff --git a/erpnext/stock/doctype/warehouse/warehouse.js b/erpnext/stock/doctype/warehouse/warehouse.js index 96cac9c06b2..6606a4f11a0 100644 --- a/erpnext/stock/doctype/warehouse/warehouse.js +++ b/erpnext/stock/doctype/warehouse/warehouse.js @@ -40,32 +40,40 @@ frappe.ui.form.on("Warehouse", { if (!frm.is_new()) { frappe.contacts.render_address_and_contact(frm); - let enable_toggle = frm.doc.disabled ? "Enable" : "Disable"; - frm.add_custom_button(__(enable_toggle), () => { - frm.set_value("disabled", 1 - frm.doc.disabled); - frm.save(); - }); - - frm.add_custom_button(__("Stock Balance"), function () { - frappe.set_route("query-report", "Stock Balance", { - warehouse: frm.doc.name, - company: frm.doc.company, + if (frm.has_perm("write")) { + let enable_toggle = frm.doc.disabled ? "Enable" : "Disable"; + frm.add_custom_button(__(enable_toggle), () => { + frm.set_value("disabled", 1 - frm.doc.disabled); + frm.save(); }); - }); - frm.add_custom_button( - frm.doc.is_group - ? __("Convert to Ledger", null, "Warehouse") - : __("Convert to Group", null, "Warehouse"), - function () { - convert_to_group_or_ledger(frm); - } - ); + frm.add_custom_button( + frm.doc.is_group + ? __("Convert to Ledger", null, "Warehouse") + : __("Convert to Group", null, "Warehouse"), + function () { + convert_to_group_or_ledger(frm); + } + ); + } + + if ("Stock Balance" in frappe.boot.user.all_reports) { + frm.add_custom_button(__("Stock Balance"), function () { + frappe.set_route("query-report", "Stock Balance", { + warehouse: frm.doc.name, + company: frm.doc.company, + }); + }); + } } else { frappe.contacts.clear_address_and_contact(frm); } - if (!frm.doc.is_group && frm.doc.__onload && frm.doc.__onload.account) { + if ( + !frm.doc.is_group && + frm.doc.__onload?.account && + "General Ledger" in frappe.boot.user.all_reports + ) { frm.add_custom_button(__("General Ledger", null, "Warehouse"), function () { frappe.route_options = { account: frm.doc.__onload.account, diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index ff4798515c3..e14b0f87501 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -267,6 +267,8 @@ def repost_future_sle( "posting_time": args[i].get("posting_time"), "creation": args[i].get("creation"), "distinct_item_warehouses": distinct_item_warehouses, + "items_to_be_repost": args, + "current_index": i, }, allow_negative_stock=allow_negative_stock, via_landed_cost_voucher=via_landed_cost_voucher, @@ -685,11 +687,20 @@ class update_entries_after: self.distinct_item_warehouses[key] = val self.new_items_found = True else: + # Check if the dependent voucher is reposted + # If not, then do not add it to the list + if not self.is_dependent_voucher_reposted(dependant_sle): + return + existing_sle_posting_date = self.distinct_item_warehouses[key].get("sle", {}).get("posting_date") dependent_voucher_detail_nos = self.get_dependent_voucher_detail_nos(key) - if getdate(dependant_sle.posting_date) < getdate(existing_sle_posting_date): + if dependent_voucher_detail_nos and dependant_sle.voucher_detail_no in set( + dependent_voucher_detail_nos + ): + return + val.sle_changed = True dependent_voucher_detail_nos.append(dependant_sle.voucher_detail_no) val.dependent_voucher_detail_nos = dependent_voucher_detail_nos @@ -703,6 +714,27 @@ class update_entries_after: val.dependent_voucher_detail_nos = dependent_voucher_detail_nos self.distinct_item_warehouses[key] = val + def is_dependent_voucher_reposted(self, dependant_sle) -> bool: + # Return False if the dependent voucher is not reposted + + if self.args.items_to_be_repost and self.args.current_index: + index = self.args.current_index + while index < len(self.args.items_to_be_repost): + if ( + self.args.items_to_be_repost[index].get("item_code") == dependant_sle.item_code + and self.args.items_to_be_repost[index].get("warehouse") == dependant_sle.warehouse + ): + if getdate(self.args.items_to_be_repost[index].get("posting_date")) > getdate( + dependant_sle.posting_date + ): + self.args.items_to_be_repost[index]["posting_date"] = dependant_sle.posting_date + + return False + + index += 1 + + return True + def get_dependent_voucher_detail_nos(self, key): if "dependent_voucher_detail_nos" not in self.distinct_item_warehouses[key]: self.distinct_item_warehouses[key].dependent_voucher_detail_nos = [] diff --git a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js index 4ed73805314..c4eea3fda45 100644 --- a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js +++ b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js @@ -31,6 +31,22 @@ frappe.ui.form.on("Subcontracting Order", { }; }); + frm.set_query("cost_center", (doc) => { + return { + filters: { + company: doc.company, + }, + }; + }); + + frm.set_query("cost_center", "items", (doc) => { + return { + filters: { + company: doc.company, + }, + }; + }); + frm.set_query("set_warehouse", () => { return { filters: { diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js index 8dfd9bd486d..b4a127702e0 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js @@ -174,6 +174,22 @@ frappe.ui.form.on("Subcontracting Receipt", { }; }); + frm.set_query("cost_center", (doc) => { + return { + filters: { + company: doc.company, + }, + }; + }); + + frm.set_query("cost_center", "items", (doc) => { + return { + filters: { + company: doc.company, + }, + }; + }); + frm.set_query("supplier_warehouse", () => { return { filters: { From 4b8b3ee46a88c5e478995cd4c88d4fd55e227f73 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 17 Jul 2024 05:19:44 +0000 Subject: [PATCH 010/734] chore(release): Bumped to Version 15.30.0 # [15.30.0](https://github.com/frappe/erpnext/compare/v15.29.4...v15.30.0) (2024-07-17) ### Bug Fixes * address and contact filters for SCO and SCR (backport [#42310](https://github.com/frappe/erpnext/issues/42310)) ([#42312](https://github.com/frappe/erpnext/issues/42312)) ([cb64c73](https://github.com/frappe/erpnext/commit/cb64c73c9e54dc6001a80fafb1f26faaa6352da9)) * bin deadlock issue (backport [#42342](https://github.com/frappe/erpnext/issues/42342)) ([#42357](https://github.com/frappe/erpnext/issues/42357)) ([29ee2d4](https://github.com/frappe/erpnext/commit/29ee2d46f0e493a3f22775a0cb7646555496c668)) * cost center filter by company (backport [#42297](https://github.com/frappe/erpnext/issues/42297)) ([#42299](https://github.com/frappe/erpnext/issues/42299)) ([4c9ce1b](https://github.com/frappe/erpnext/commit/4c9ce1b188672fe43f62735023b43694d71fa9f6)) * extra qty pick in pick list (backport [#42345](https://github.com/frappe/erpnext/issues/42345)) ([#42349](https://github.com/frappe/erpnext/issues/42349)) ([1754adf](https://github.com/frappe/erpnext/commit/1754adfcd64985866f0cf8d3aa5b2a6414ebc374)) * **gross profit:** incorrect valuation rate on different warehouses ([f161e59](https://github.com/frappe/erpnext/commit/f161e59cd7de888e6dc1975c736f480b0e4964ae)) * incoming rate zero for supplied items in returned SCR (backport [#42314](https://github.com/frappe/erpnext/issues/42314)) ([#42315](https://github.com/frappe/erpnext/issues/42315)) ([076bf17](https://github.com/frappe/erpnext/commit/076bf174396e045f8e87af165e40dbea718111cc)) * items not fetching in End Transit entry (backport [#42358](https://github.com/frappe/erpnext/issues/42358)) ([#42361](https://github.com/frappe/erpnext/issues/42361)) ([b5a2e5a](https://github.com/frappe/erpnext/commit/b5a2e5a375b39edf5f22a4d2276faeb3a4f85ced)) * keep status as In Progress for RIV for Timeout Error (backport [#42274](https://github.com/frappe/erpnext/issues/42274)) ([#42296](https://github.com/frappe/erpnext/issues/42296)) ([1de66e5](https://github.com/frappe/erpnext/commit/1de66e56eeb97d2f1c589298400e79ee9aaf1b02)) * missing discount on POS Credit Notes ([4055ef9](https://github.com/frappe/erpnext/commit/4055ef92b53e1528b2c286ff4028f3c501d1ea30)) * not able to cancel the inter transfer DN (backport [#42333](https://github.com/frappe/erpnext/issues/42333)) ([#42340](https://github.com/frappe/erpnext/issues/42340)) ([cf2651d](https://github.com/frappe/erpnext/commit/cf2651dd8597a4aacae9b6f89fea7d2ea138851d)) * not able to submit LCV entry (backport [#42303](https://github.com/frappe/erpnext/issues/42303)) ([#42304](https://github.com/frappe/erpnext/issues/42304)) ([6d098b7](https://github.com/frappe/erpnext/commit/6d098b7302f3409a7fa20e89611a6346f57d1463)) * remove doctype link from serial no ledger report (backport [#42327](https://github.com/frappe/erpnext/issues/42327)) ([#42348](https://github.com/frappe/erpnext/issues/42348)) ([b741b2a](https://github.com/frappe/erpnext/commit/b741b2a2858e0d106635c6be40ee2a6781af4d57)) * removed patch from patches.txt ([c45d11c](https://github.com/frappe/erpnext/commit/c45d11cd60d21064564d8266d70db6c08a25a91d)) * same posting date and time causing incorrect valuation rate (backport [#42351](https://github.com/frappe/erpnext/issues/42351)) ([#42356](https://github.com/frappe/erpnext/issues/42356)) ([62fc428](https://github.com/frappe/erpnext/commit/62fc42803f69a6e8a99b88f993276d0207a30792)) * service item capitalization ([#42188](https://github.com/frappe/erpnext/issues/42188)) ([2ffe7d5](https://github.com/frappe/erpnext/commit/2ffe7d5838464a222047cd26a045bc522da86edd)) * show total rows credit row in balance sheet ([0d2ef0d](https://github.com/frappe/erpnext/commit/0d2ef0df7dbc48d759fccd7516b5382eb4f45324)) * slowness in reposting dependent vouchers. (backport [#42282](https://github.com/frappe/erpnext/issues/42282)) ([#42292](https://github.com/frappe/erpnext/issues/42292)) ([ef16313](https://github.com/frappe/erpnext/commit/ef16313e0a4e09105dd1f0a3c004076675927b14)) * **Warehouse:** add buttons only if the user can use them ([a2b21c7](https://github.com/frappe/erpnext/commit/a2b21c757043cddd48fb67394d6c09a41bbfc65c)) * While submitting PCV ensure previous FY is closed (backport [#42284](https://github.com/frappe/erpnext/issues/42284)) ([#42300](https://github.com/frappe/erpnext/issues/42300)) ([e250dcc](https://github.com/frappe/erpnext/commit/e250dcc7c87e706055bfb92b72c7fa459d3208bc)) ### Features * configurable depreciation calculation via accounts settings ([#42276](https://github.com/frappe/erpnext/issues/42276)) ([ddd1ca7](https://github.com/frappe/erpnext/commit/ddd1ca7f7c2e2bdf5af2e535889d5ba714240079)) * create variant with/without image (backport [#41317](https://github.com/frappe/erpnext/issues/41317)) ([#42343](https://github.com/frappe/erpnext/issues/42343)) ([5f1d6ed](https://github.com/frappe/erpnext/commit/5f1d6ede31fd37454fd9de5f6500481ad3b4267a)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 2fbbc6ac1d2..63bf7fc9dc2 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "15.29.3" +__version__ = "15.30.0" def get_default_company(user=None): From 976abf7b3c92cd6387275442f87c784d4adf16e1 Mon Sep 17 00:00:00 2001 From: Syed Mujeer Hashmi Date: Wed, 24 Jul 2024 11:59:45 +0530 Subject: [PATCH 011/734] fix: Update get_amount to return currency precision grand total In case of multi-currency purchase invoice, we are getting the error "Total Payment Request amount cannot be greater than Purchase Invoice amount" because of rounding difference. --- erpnext/accounts/doctype/payment_request/payment_request.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index 03435a26011..74fd5f5301a 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -15,7 +15,7 @@ from erpnext.accounts.doctype.payment_entry.payment_entry import ( ) from erpnext.accounts.doctype.subscription_plan.subscription_plan import get_plan_rate from erpnext.accounts.party import get_party_account, get_party_bank_account -from erpnext.accounts.utils import get_account_currency +from erpnext.accounts.utils import get_account_currency, get_currency_precision from erpnext.utilities import payment_app_import_guard @@ -540,7 +540,7 @@ def get_amount(ref_doc, payment_account=None): grand_total = ref_doc.outstanding_amount if grand_total > 0: - return grand_total + return flt(grand_total, get_currency_precision()) else: frappe.throw(_("Payment Entry is already created")) From e28c1e9c4bc987ba38ffef4414738d6a14642ded Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 24 Jul 2024 07:28:24 +0000 Subject: [PATCH 012/734] chore(release): Bumped to Version 15.31.0 # [15.31.0](https://github.com/frappe/erpnext/compare/v15.30.0...v15.31.0) (2024-07-24) ### Bug Fixes * Consider adding warranty period to serial nos (backport [#42051](https://github.com/frappe/erpnext/issues/42051)) ([#42182](https://github.com/frappe/erpnext/issues/42182)) ([8da28dc](https://github.com/frappe/erpnext/commit/8da28dcfb29ad5f922bd9d24215fe95c2909feb9)) * correct validation for depreciation posting date ([ffacf42](https://github.com/frappe/erpnext/commit/ffacf4222b0401f19a3986ff9d29d513084a6e32)) * Don't allow negative amount on Payment Request [#41905](https://github.com/frappe/erpnext/issues/41905) ([aee2cc2](https://github.com/frappe/erpnext/commit/aee2cc2e033b429a98bda6a54562a652a4d17f9f)) * missing cr/dr notes on payment reconciliation ([0a41ccd](https://github.com/frappe/erpnext/commit/0a41ccda9935d5096c235d43111a9283d7287760)) * not able to save QC (backport [#42371](https://github.com/frappe/erpnext/issues/42371)) ([#42373](https://github.com/frappe/erpnext/issues/42373)) ([18500b8](https://github.com/frappe/erpnext/commit/18500b8e3a887649413294be9d7960b8981e01e9)) * provide initial value for `.reduce()` call ([72bc539](https://github.com/frappe/erpnext/commit/72bc539ffdca0594d2175ba967c1285b691c74ac)) * Purchase Order Analysis Report Data (backport [#42387](https://github.com/frappe/erpnext/issues/42387)) ([#42394](https://github.com/frappe/erpnext/issues/42394)) ([709be13](https://github.com/frappe/erpnext/commit/709be13e8288d12204fc30109273293fb4d44582)) * qty in the 'Serial No Ledger' report (backport [#42429](https://github.com/frappe/erpnext/issues/42429)) ([#42433](https://github.com/frappe/erpnext/issues/42433)) ([926fd41](https://github.com/frappe/erpnext/commit/926fd41a2b4b5b08883383f9dbe5e3df701b0e04)) * remove proprietorship and update it with individual ([527781a](https://github.com/frappe/erpnext/commit/527781a588a986223716cf838f6a6a4c1a1882c3)) * rounding issue causing incorrect quantity in SE (backport [#42380](https://github.com/frappe/erpnext/issues/42380)) ([#42395](https://github.com/frappe/erpnext/issues/42395)) ([54791e9](https://github.com/frappe/erpnext/commit/54791e938bd56eb81f7d8d923381a006998919fe)) * serial and batch bundle for POS Invoice (backport [#41491](https://github.com/frappe/erpnext/issues/41491)) ([#42396](https://github.com/frappe/erpnext/issues/42396)) ([555be2b](https://github.com/frappe/erpnext/commit/555be2be11623d8fd8018ab3a2807e07a1050cf6)) * set filter to show only submitted asset ([29fc975](https://github.com/frappe/erpnext/commit/29fc975fb8830f3ce0dbed5e5797bd100718de9b)) * Show the rows in AR/AP report where outstanding equals to 0.01 ([886256c](https://github.com/frappe/erpnext/commit/886256c86b94aaf2e91e4ca6c6652bd503bb2ca2)) ### Features * add make_regional_gl_entries override for Sales Invoice ([#42399](https://github.com/frappe/erpnext/issues/42399)) ([22b17de](https://github.com/frappe/erpnext/commit/22b17de2b4adfc2383e2ed3ba1377c2febd2b54c)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 63bf7fc9dc2..4de860c504e 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "15.30.0" +__version__ = "15.31.0" def get_default_company(user=None): From 4bae4194abbf962d2f4b7fdfeaea0232fdaa6aa5 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 24 Jul 2024 19:29:40 +0530 Subject: [PATCH 013/734] fix: incorrect current qty for the batch in stock reco (backport #42434) (backport #42459) (#42463) fix: incorrect current qty for the batch in stock reco (backport #42434) (#42459) fix: incorrect current qty for the batch in stock reco (#42434) (cherry picked from commit 9cd33741014e2f84614df73090617a9f53294d01) Co-authored-by: rohitwaghchaure (cherry picked from commit 298a5699f138d795d69889b63ece45400328499b) Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- .../serial_and_batch_bundle.py | 15 +++ .../stock_reconciliation.py | 25 +++-- .../test_stock_reconciliation.py | 93 +++++++++++++++++++ 3 files changed, 124 insertions(+), 9 deletions(-) diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py index 9b65e11f430..615d98a448d 100644 --- a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py +++ b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py @@ -2188,6 +2188,8 @@ def get_stock_ledgers_for_serial_nos(kwargs): def get_stock_ledgers_batches(kwargs): + from erpnext.stock.utils import get_combine_datetime + stock_ledger_entry = frappe.qb.DocType("Stock Ledger Entry") batch_table = frappe.qb.DocType("Batch") @@ -2214,6 +2216,19 @@ def get_stock_ledgers_batches(kwargs): else: query = query.where(stock_ledger_entry[field] == kwargs.get(field)) + if kwargs.get("posting_date"): + if kwargs.get("posting_time") is None: + kwargs.posting_time = nowtime() + + timestamp_condition = stock_ledger_entry.posting_datetime <= get_combine_datetime( + kwargs.posting_date, kwargs.posting_time + ) + + query = query.where(timestamp_condition) + + if kwargs.get("ignore_voucher_nos"): + query = query.where(stock_ledger_entry.voucher_no.notin(kwargs.get("ignore_voucher_nos"))) + if kwargs.based_on == "LIFO": query = query.orderby(batch_table.creation, order=frappe.qb.desc) elif kwargs.based_on == "Expiry": diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index 674624e184b..93e3e69729b 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -16,7 +16,7 @@ from erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle impor get_available_serial_nos, ) from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos -from erpnext.stock.utils import get_stock_balance +from erpnext.stock.utils import get_incoming_rate, get_stock_balance class OpeningEntryAccountError(frappe.ValidationError): @@ -952,14 +952,21 @@ class StockReconciliation(StockController): precesion = row.precision("current_qty") if flt(current_qty, precesion) != flt(row.current_qty, precesion): if not row.serial_no: - val_rate = get_valuation_rate( - row.item_code, - row.warehouse, - self.doctype, - self.name, - company=self.company, - batch_no=row.batch_no, - serial_and_batch_bundle=row.current_serial_and_batch_bundle, + val_rate = get_incoming_rate( + frappe._dict( + { + "item_code": row.item_code, + "warehouse": row.warehouse, + "qty": current_qty * -1, + "serial_and_batch_bundle": row.current_serial_and_batch_bundle, + "batch_no": row.batch_no, + "voucher_type": self.doctype, + "voucher_no": self.name, + "company": self.company, + "posting_date": self.posting_date, + "posting_time": self.posting_time, + } + ) ) row.current_valuation_rate = val_rate diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py index 48d67c2cf46..9bb6ba9ec90 100644 --- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py @@ -4,6 +4,7 @@ # ERPNext - web based ERP (http://erpnext.com) # For license information, please see license.txt +import json import frappe from frappe.tests.utils import FrappeTestCase, change_settings @@ -1182,6 +1183,98 @@ class TestStockReconciliation(FrappeTestCase, StockTestMixin): self.assertAlmostEqual(row.incoming_rate, 1000.00) self.assertEqual(row.serial_no, serial_nos[row.idx - 1]) + def test_stock_reco_with_legacy_batch(self): + from erpnext.stock.doctype.batch.batch import get_batch_qty + + batch_item_code = self.make_item( + "Test Batch Item Legacy Batch 1", + { + "is_stock_item": 1, + "has_batch_no": 1, + "create_new_batch": 1, + "batch_number_series": "BH1-NRALL-S-.###", + }, + ).name + + warehouse = "_Test Warehouse - _TC" + + frappe.flags.ignore_serial_batch_bundle_validation = True + frappe.flags.use_serial_and_batch_fields = True + + batch_id = "BH1-NRALL-S-0001" + if not frappe.db.exists("Batch", batch_id): + batch_doc = frappe.get_doc( + { + "doctype": "Batch", + "batch_id": batch_id, + "item": batch_item_code, + "use_batchwise_valuation": 0, + } + ).insert(ignore_permissions=True) + + self.assertTrue(batch_doc.use_batchwise_valuation) + + stock_queue = [] + qty_after_transaction = 0 + balance_value = 0 + i = 0 + for qty, valuation in {10: 100, 20: 200}.items(): + i += 1 + stock_queue.append([qty, valuation]) + qty_after_transaction += qty + balance_value += qty_after_transaction * valuation + + doc = frappe.get_doc( + { + "doctype": "Stock Ledger Entry", + "posting_date": add_days(nowdate(), -2 * i), + "posting_time": nowtime(), + "batch_no": batch_id, + "incoming_rate": valuation, + "qty_after_transaction": qty_after_transaction, + "stock_value_difference": valuation * qty, + "balance_value": balance_value, + "valuation_rate": balance_value / qty_after_transaction, + "actual_qty": qty, + "item_code": batch_item_code, + "warehouse": "_Test Warehouse - _TC", + "stock_queue": json.dumps(stock_queue), + } + ) + + doc.flags.ignore_permissions = True + doc.flags.ignore_mandatory = True + doc.flags.ignore_links = True + doc.flags.ignore_validate = True + doc.submit() + doc.reload() + + frappe.flags.ignore_serial_batch_bundle_validation = False + frappe.flags.use_serial_and_batch_fields = False + + batch_doc = frappe.get_doc("Batch", batch_id) + + qty = get_batch_qty(batch_id, warehouse, batch_item_code) + self.assertEqual(qty, 30) + + sr = create_stock_reconciliation( + item_code=batch_item_code, + posting_date=add_days(nowdate(), -3), + posting_time=nowtime(), + warehouse=warehouse, + qty=100, + rate=1000, + reconcile_all_serial_batch=0, + batch_no=batch_id, + use_serial_batch_fields=1, + ) + + self.assertEqual(sr.items[0].current_qty, 20) + self.assertEqual(sr.items[0].qty, 100) + + qty = get_batch_qty(batch_id, warehouse, batch_item_code) + self.assertEqual(qty, 110) + def create_batch_item_with_batch(item_name, batch_id): batch_item_doc = create_item(item_name, is_stock_item=1) From 2de69e2b1267c62fd8568801e948a47a81bf8bdf Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 24 Jul 2024 14:00:57 +0000 Subject: [PATCH 014/734] chore(release): Bumped to Version 15.31.1 ## [15.31.1](https://github.com/frappe/erpnext/compare/v15.31.0...v15.31.1) (2024-07-24) ### Bug Fixes * incorrect current qty for the batch in stock reco (backport [#42434](https://github.com/frappe/erpnext/issues/42434)) (backport [#42459](https://github.com/frappe/erpnext/issues/42459)) ([#42463](https://github.com/frappe/erpnext/issues/42463)) ([4bae419](https://github.com/frappe/erpnext/commit/4bae4194abbf962d2f4b7fdfeaea0232fdaa6aa5)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 4de860c504e..62a018c0e6b 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "15.31.0" +__version__ = "15.31.1" def get_default_company(user=None): From 334c4d06766b67a4e35b4ec4fe99a9d8dc07691a Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sat, 27 Jul 2024 11:07:20 +0530 Subject: [PATCH 015/734] fix: dynamic condition in the pricing rule not working (backport #42467) (backport #42495) (#42496) fix: dynamic condition in the pricing rule not working (backport #42467) (#42495) fix: dynamic condition in the pricing rule not working (#42467) (cherry picked from commit 0e817f42ef96632df164fac6e5b2949a0d18e9f5) Co-authored-by: rohitwaghchaure (cherry picked from commit ac2ef218968e3855e015cc20cb2225f6d1f32c0c) Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- erpnext/public/js/controllers/transaction.js | 2 +- erpnext/stock/get_item_details.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index dfb0ded8139..3e9a286b4cf 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -1796,7 +1796,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe me.in_apply_price_list = true; return this.frm.call({ method: "erpnext.stock.get_item_details.apply_price_list", - args: { args: args }, + args: { args: args, doc: me.frm.doc }, callback: function(r) { if (!r.exc) { frappe.run_serially([ diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index a7aa7dce452..fe4d3b8602c 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -1189,7 +1189,7 @@ def get_batch_qty(batch_no, warehouse, item_code): @frappe.whitelist() -def apply_price_list(args, as_doc=False): +def apply_price_list(args, as_doc=False, doc=None): """Apply pricelist on a document-like dict object and return as {'parent': dict, 'children': list} @@ -1228,7 +1228,7 @@ def apply_price_list(args, as_doc=False): for item in item_list: args_copy = frappe._dict(args.copy()) args_copy.update(item) - item_details = apply_price_list_on_item(args_copy) + item_details = apply_price_list_on_item(args_copy, doc=doc) children.append(item_details) if as_doc: @@ -1246,10 +1246,10 @@ def apply_price_list(args, as_doc=False): return {"parent": parent, "children": children} -def apply_price_list_on_item(args): +def apply_price_list_on_item(args, doc=None): item_doc = frappe.db.get_value("Item", args.item_code, ["name", "variant_of"], as_dict=1) item_details = get_price_list_rate(args, item_doc) - item_details.update(get_pricing_rule_for_item(args)) + item_details.update(get_pricing_rule_for_item(args, doc=doc)) return item_details From 4b66fcad64aa3f64aa3d4727d2d89e23d4f5b617 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Sat, 27 Jul 2024 05:38:30 +0000 Subject: [PATCH 016/734] chore(release): Bumped to Version 15.31.2 ## [15.31.2](https://github.com/frappe/erpnext/compare/v15.31.1...v15.31.2) (2024-07-27) ### Bug Fixes * dynamic condition in the pricing rule not working (backport [#42467](https://github.com/frappe/erpnext/issues/42467)) (backport [#42495](https://github.com/frappe/erpnext/issues/42495)) ([#42496](https://github.com/frappe/erpnext/issues/42496)) ([334c4d0](https://github.com/frappe/erpnext/commit/334c4d06766b67a4e35b4ec4fe99a9d8dc07691a)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 62a018c0e6b..496ee5fe286 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "15.31.1" +__version__ = "15.31.2" def get_default_company(user=None): From da3eddeb26fa0bf2510c73b34ddeaba91bfa7938 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 31 Jul 2024 06:03:49 +0000 Subject: [PATCH 017/734] chore(release): Bumped to Version 15.31.3 ## [15.31.3](https://github.com/frappe/erpnext/compare/v15.31.2...v15.31.3) (2024-07-31) ### Bug Fixes * Adjust initial month's depreciation to end of depreciation period ([9d2ef4d](https://github.com/frappe/erpnext/commit/9d2ef4d3e8430f358e80a8d171816ef807d4d84d)) * builtins.KeyError: ('ABC', 'Store - CP') (backport [#42505](https://github.com/frappe/erpnext/issues/42505)) ([#42509](https://github.com/frappe/erpnext/issues/42509)) ([f25b38c](https://github.com/frappe/erpnext/commit/f25b38caf58ff5cb469c3c71a080a339c5eaef06)) * consider payment entries for checking if tds is deducted ([183ac41](https://github.com/frappe/erpnext/commit/183ac4155046dc5d18f9a28c560b7586c5f12b4b)) * dynamic condition in the pricing rule not working (backport [#42467](https://github.com/frappe/erpnext/issues/42467)) ([#42495](https://github.com/frappe/erpnext/issues/42495)) ([ac2ef21](https://github.com/frappe/erpnext/commit/ac2ef218968e3855e015cc20cb2225f6d1f32c0c)) * field_type is small text for v15 ([9e99eda](https://github.com/frappe/erpnext/commit/9e99eda3c3d2fa1ca409639fe559a5763b03b6d1)) * fields alteration related to subcontracting ([80d4dc2](https://github.com/frappe/erpnext/commit/80d4dc2016cd692b5e515b08e559324600b0ce43)) * Fields Modification for Subcontracting DocTypes ([#42383](https://github.com/frappe/erpnext/issues/42383)) ([422824b](https://github.com/frappe/erpnext/commit/422824b9e78963bb20fde2f3967950e20b78525e)) * ignore duplicates while creating default templates ([aea8271](https://github.com/frappe/erpnext/commit/aea8271f7e14ff6179e6f211d00175e086d90758)) * incorrect cost_center on AR/AP report ([0c2e948](https://github.com/frappe/erpnext/commit/0c2e9480cb8e4f42a2d90fd6b51e0479be20bb14)) * incorrect current qty for the batch in stock reco (backport [#42434](https://github.com/frappe/erpnext/issues/42434)) ([#42459](https://github.com/frappe/erpnext/issues/42459)) ([298a569](https://github.com/frappe/erpnext/commit/298a5699f138d795d69889b63ece45400328499b)) * keyerror posting_time (backport [#42452](https://github.com/frappe/erpnext/issues/42452)) ([#42460](https://github.com/frappe/erpnext/issues/42460)) ([2d2140a](https://github.com/frappe/erpnext/commit/2d2140aad0fb306d9f6a65402aea1fc2d8e13deb)) * not able to save BOM Creator ([#42540](https://github.com/frappe/erpnext/issues/42540)) ([61280e6](https://github.com/frappe/erpnext/commit/61280e607269bc4991ec863fe166895e55468afd)) * parenttype in item wise purchase and sales register ([322fbe9](https://github.com/frappe/erpnext/commit/322fbe92eed9d0fa6bd5d597d382443bcf819c7a)) * performance issue for the report Purchase Order Analysis report (backport [#42503](https://github.com/frappe/erpnext/issues/42503)) ([#42507](https://github.com/frappe/erpnext/issues/42507)) ([edf1fcb](https://github.com/frappe/erpnext/commit/edf1fcb7429eab23ca54c8c0c5c1d600e1d0dcd5)) * price_list_currency not found error (backport [#42534](https://github.com/frappe/erpnext/issues/42534)) ([#42539](https://github.com/frappe/erpnext/issues/42539)) ([5fa185d](https://github.com/frappe/erpnext/commit/5fa185d480000ead606e8c0dcfe93c303b6b2f82)) * purchase return from rejected warehouse (backport [#42531](https://github.com/frappe/erpnext/issues/42531)) ([#42535](https://github.com/frappe/erpnext/issues/42535)) ([b7d70ac](https://github.com/frappe/erpnext/commit/b7d70ac9286aad917bc73a903dc2557c59c8c64f)) * set pos data if not return doc ([25fe08e](https://github.com/frappe/erpnext/commit/25fe08eb740ebeb8e5a6c39189c1d92651f08a6b)) * **tests:** added tests for usecase ([1390c86](https://github.com/frappe/erpnext/commit/1390c86fc488e17d64fe6dc5b8bd22c03c08f9e7)) * warehouse filter in Product Bundle Balance (backport [#42532](https://github.com/frappe/erpnext/issues/42532)) ([#42537](https://github.com/frappe/erpnext/issues/42537)) ([826577c](https://github.com/frappe/erpnext/commit/826577c88f12dcb4b25a5a051c97b9158f4a6632)) * Warranty Expiry Date not set in the serial number (backport [#42513](https://github.com/frappe/erpnext/issues/42513)) ([#42515](https://github.com/frappe/erpnext/issues/42515)) ([fc0db19](https://github.com/frappe/erpnext/commit/fc0db1941a5915f7d2d3c9b79e27d7fce8e30b35)) ### Performance Improvements * huge number of serial no creation (backport [#42522](https://github.com/frappe/erpnext/issues/42522)) ([#42544](https://github.com/frappe/erpnext/issues/42544)) ([6840f6c](https://github.com/frappe/erpnext/commit/6840f6cb26054c89b8277bbbd4a06a96b39dcd68)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 496ee5fe286..29a6e351461 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "15.31.2" +__version__ = "15.31.3" def get_default_company(user=None): From bf0e2b3b52d2bb0f5f0d4a07923c4ac23a7842df Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 1 Aug 2024 13:22:33 +0530 Subject: [PATCH 018/734] fix: inter transfer delivery note issue with batch (backport #42552) (#42556) fix: inter transfer delivery note issue with batch (#42552) (cherry picked from commit f620ef20aee3e74528d1c3916a77ff4adfeb0f08) Co-authored-by: rohitwaghchaure (cherry picked from commit 97cc3082e190e730c467c3db85dddb1f62a963cb) --- erpnext/controllers/selling_controller.py | 4 +- .../purchase_receipt/test_purchase_receipt.py | 116 ++++++++++++++++++ 2 files changed, 119 insertions(+), 1 deletion(-) diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index 7a85d2230f1..fd856194559 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -539,7 +539,9 @@ class SellingController(StockController): def get_sle_for_source_warehouse(self, item_row): serial_and_batch_bundle = ( - item_row.serial_and_batch_bundle if not self.is_internal_transfer() else None + item_row.serial_and_batch_bundle + if not self.is_internal_transfer() or self.docstatus == 1 + else None ) if serial_and_batch_bundle and self.is_internal_transfer() and self.is_return: if self.docstatus == 1: diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index 416a2db43c4..c239360d945 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -3476,6 +3476,122 @@ class TestPurchaseReceipt(FrappeTestCase): frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 1) + def test_internal_transfer_for_batch_items_with_cancel_use_serial_batch_fields(self): + from erpnext.controllers.sales_and_purchase_return import make_return_doc + 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 + + frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 1) + frappe.db.set_single_value("Stock Settings", "auto_create_serial_and_batch_bundle_for_outward", 0) + + prepare_data_for_internal_transfer() + + customer = "_Test Internal Customer 2" + company = "_Test Company with perpetual inventory" + + batch_item_doc = make_item( + "_Test Batch Item For Stock Transfer Cancel Case 11", + {"has_batch_no": 1, "create_new_batch": 1, "batch_number_series": "USBF11-BT-CANBIFST-.####"}, + ) + + serial_item_doc = make_item( + "_Test Serial No Item For Stock Transfer Cancel Case 11", + {"has_serial_no": 1, "serial_no_series": "USBF11-BT-CANBIFST-.####"}, + ) + + inward_entry = make_purchase_receipt( + item_code=batch_item_doc.name, + qty=10, + rate=150, + warehouse="Stores - TCP1", + company="_Test Company with perpetual inventory", + use_serial_batch_fields=1, + do_not_submit=1, + ) + + inward_entry.append( + "items", + { + "item_code": serial_item_doc.name, + "qty": 15, + "rate": 250, + "item_name": serial_item_doc.item_name, + "conversion_factor": 1.0, + "uom": serial_item_doc.stock_uom, + "stock_uom": serial_item_doc.stock_uom, + "warehouse": "Stores - TCP1", + "use_serial_batch_fields": 1, + }, + ) + + inward_entry.submit() + inward_entry.reload() + + for row in inward_entry.items: + self.assertTrue(row.serial_and_batch_bundle) + + inter_transfer_dn = create_delivery_note( + item_code=inward_entry.items[0].item_code, + company=company, + customer=customer, + cost_center="Main - TCP1", + expense_account="Cost of Goods Sold - TCP1", + qty=10, + rate=500, + warehouse="Stores - TCP1", + target_warehouse="Work In Progress - TCP1", + batch_no=get_batch_from_bundle(inward_entry.items[0].serial_and_batch_bundle), + use_serial_batch_fields=1, + do_not_submit=1, + ) + + inter_transfer_dn.append( + "items", + { + "item_code": serial_item_doc.name, + "qty": 15, + "rate": 350, + "item_name": serial_item_doc.item_name, + "conversion_factor": 1.0, + "uom": serial_item_doc.stock_uom, + "stock_uom": serial_item_doc.stock_uom, + "warehouse": "Stores - TCP1", + "target_warehouse": "Work In Progress - TCP1", + "serial_no": "\n".join( + get_serial_nos_from_bundle(inward_entry.items[1].serial_and_batch_bundle) + ), + "use_serial_batch_fields": 1, + }, + ) + + inter_transfer_dn.submit() + inter_transfer_dn.reload() + for row in inter_transfer_dn.items: + if row.item_code == batch_item_doc.name: + self.assertEqual(row.rate, 150.0) + else: + self.assertEqual(row.rate, 250.0) + + self.assertTrue(row.serial_and_batch_bundle) + + inter_transfer_pr = make_inter_company_purchase_receipt(inter_transfer_dn.name) + for row in inter_transfer_pr.items: + row.from_warehouse = "Work In Progress - TCP1" + row.warehouse = "Stores - TCP1" + inter_transfer_pr.submit() + + for row in inter_transfer_pr.items: + if row.item_code == batch_item_doc.name: + self.assertEqual(row.rate, 150.0) + else: + self.assertEqual(row.rate, 250.0) + + self.assertTrue(row.serial_and_batch_bundle) + + inter_transfer_pr.cancel() + inter_transfer_dn.cancel() + frappe.db.set_single_value("Stock Settings", "auto_create_serial_and_batch_bundle_for_outward", 1) + def prepare_data_for_internal_transfer(): from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier From 703b9accb8b6ffa3666f6d9c7d34a9d248fd4b0a Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Thu, 1 Aug 2024 09:12:25 +0000 Subject: [PATCH 019/734] chore(release): Bumped to Version 15.31.4 ## [15.31.4](https://github.com/frappe/erpnext/compare/v15.31.3...v15.31.4) (2024-08-01) ### Bug Fixes * inter transfer delivery note issue with batch (backport [#42552](https://github.com/frappe/erpnext/issues/42552)) ([#42556](https://github.com/frappe/erpnext/issues/42556)) ([bf0e2b3](https://github.com/frappe/erpnext/commit/bf0e2b3b52d2bb0f5f0d4a07923c4ac23a7842df)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 29a6e351461..c6ee5697976 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "15.31.3" +__version__ = "15.31.4" def get_default_company(user=None): From 02a31caa050556408fdae1e8ed5121e2783f4218 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sat, 3 Aug 2024 09:27:16 +0530 Subject: [PATCH 020/734] fix: use get_last_day to get the correct date (backport #42564) (#42602) fix: use get_last_day to get the correct date (#42564) (cherry picked from commit 5d58eb67a6e025811ef0b2f1b96e3a5ccbded336) Co-authored-by: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> --- .../asset_depreciation_schedule.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py index 02fcb8efb3d..e7b5a25cc73 100644 --- a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py +++ b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py @@ -729,10 +729,15 @@ def get_daily_depr_amount(asset, row, schedule_idx, amount): ) ), add_days( - add_months( - row.depreciation_start_date, - (row.frequency_of_depreciation * (asset.opening_number_of_booked_depreciations + 1)) - * -1, + get_last_day( + add_months( + row.depreciation_start_date, + ( + row.frequency_of_depreciation + * (asset.opening_number_of_booked_depreciations + 1) + ) + * -1, + ), ), 1, ), From d8080910c6a243b33e55409e42cb07ea56ca85c5 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Sat, 3 Aug 2024 03:58:31 +0000 Subject: [PATCH 021/734] chore(release): Bumped to Version 15.31.5 ## [15.31.5](https://github.com/frappe/erpnext/compare/v15.31.4...v15.31.5) (2024-08-03) ### Bug Fixes * use get_last_day to get the correct date (backport [#42564](https://github.com/frappe/erpnext/issues/42564)) ([#42602](https://github.com/frappe/erpnext/issues/42602)) ([02a31ca](https://github.com/frappe/erpnext/commit/02a31caa050556408fdae1e8ed5121e2783f4218)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index c6ee5697976..efe34d54580 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "15.31.4" +__version__ = "15.31.5" def get_default_company(user=None): From 2c21df2ad96f4eb2fee044ecd9b4828a3dad0ee3 Mon Sep 17 00:00:00 2001 From: Abhishek Chougule Date: Sat, 3 Aug 2024 10:54:51 +0530 Subject: [PATCH 022/734] fix: correct garbage value on Razorpay Payments Page --- .../payment_request/payment_request.py | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index 48bb6f2ae86..707cfc07533 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -258,19 +258,19 @@ class PaymentRequest(Document): if hasattr(controller, "validate_minimum_transaction_amount"): controller.validate_minimum_transaction_amount(self.currency, self.grand_total) - return controller.get_payment_url( - **{ - "amount": flt(self.grand_total, self.precision("grand_total")), - "title": data.company.encode("utf-8"), - "description": self.subject.encode("utf-8"), - "reference_doctype": "Payment Request", - "reference_docname": self.name, - "payer_email": self.email_to or frappe.session.user, - "payer_name": frappe.safe_encode(data.customer_name), - "order_id": self.name, - "currency": self.currency, - } - ) + return controller.get_payment_url( + **{ + "amount": flt(self.grand_total, self.precision("grand_total")), + "title": data.company, + "description": self.subject, + "reference_doctype": "Payment Request", + "reference_docname": self.name, + "payer_email": self.email_to or frappe.session.user, + "payer_name": data.customer_name, + "order_id": self.name, + "currency": self.currency, + } + ) def set_as_paid(self): if self.payment_channel == "Phone": From 2e3b2db03b902fbed0b3fec165f78d9b4a2110de Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 7 Aug 2024 09:30:48 +0000 Subject: [PATCH 023/734] chore(release): Bumped to Version 15.32.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # [15.32.0](https://github.com/frappe/erpnext/compare/v15.31.5...v15.32.0) (2024-08-07) ### Bug Fixes * 'undefined' in PL and BS report summary on Consolidated report ([3da7071](https://github.com/frappe/erpnext/commit/3da7071327810a0a6c10c1decc841cd2a2df5e4c)) * all warehouse filter for the stock report (backport [#42584](https://github.com/frappe/erpnext/issues/42584)) ([#42604](https://github.com/frappe/erpnext/issues/42604)) ([830b95b](https://github.com/frappe/erpnext/commit/830b95bdcbfb4e7cb513ef8f3dd21072538b3a07)) * company filter for filtring tax withheld vouchers ([3ad1f2d](https://github.com/frappe/erpnext/commit/3ad1f2d0d0ae299dddcb53010f276c54dc43fdad)) * disable primary action button only when there are no active capitalization ([2d644ac](https://github.com/frappe/erpnext/commit/2d644ac066746f4ce2e6335dcf49f2765aafc5d2)) * Discount and taxes in return document should follow the reference document (backport [#41911](https://github.com/frappe/erpnext/issues/41911)) ([#42574](https://github.com/frappe/erpnext/issues/42574)) ([9321408](https://github.com/frappe/erpnext/commit/93214081549295af170e838e6ebdf5bc8274c9c2)) * do not update item price and last purchase rate for inter transf… (backport [#42616](https://github.com/frappe/erpnext/issues/42616)) ([#42633](https://github.com/frappe/erpnext/issues/42633)) ([59b9b7d](https://github.com/frappe/erpnext/commit/59b9b7dc9185fee1814d5ef7d89d0875fead4bf7)) * german translations ([f27e9f3](https://github.com/frappe/erpnext/commit/f27e9f30890badb5b793e5523acc5fc2836ec2c8)) * inter transfer delivery note issue with batch (backport [#42552](https://github.com/frappe/erpnext/issues/42552)) ([#42556](https://github.com/frappe/erpnext/issues/42556)) ([97cc308](https://github.com/frappe/erpnext/commit/97cc3082e190e730c467c3db85dddb1f62a963cb)) * min height for rows in sales funnel ([2f81c99](https://github.com/frappe/erpnext/commit/2f81c991438a678ab163c0dc796124f447dc02cc)) * resolved conflict ([#42557](https://github.com/frappe/erpnext/issues/42557)) ([c3293d1](https://github.com/frappe/erpnext/commit/c3293d110c538c9f0888dd6c7dffe8ab36bf3632)) * reverse debit credit for party gl entry in payment entry based on negative amount ([#42367](https://github.com/frappe/erpnext/issues/42367)) ([14f9aef](https://github.com/frappe/erpnext/commit/14f9aef55cd2b48478f48b5f8b3a65534724c352)) * set currency on change of company considering customer default currency (backport [#42405](https://github.com/frappe/erpnext/issues/42405)) ([#42547](https://github.com/frappe/erpnext/issues/42547)) ([7c8d13c](https://github.com/frappe/erpnext/commit/7c8d13c51adc9be25264cfbba96321ef655970ee)) * set query filters for sales / purchase tax template on PE ([dc9cf74](https://github.com/frappe/erpnext/commit/dc9cf74be87c26ad8b0bf22c6bc145523953b28a)) * use get_last_day to get the correct date (backport [#42564](https://github.com/frappe/erpnext/issues/42564)) ([#42598](https://github.com/frappe/erpnext/issues/42598)) ([2de86eb](https://github.com/frappe/erpnext/commit/2de86eb0f45cbc287f238d7091ee2af99b8df5a7)) ### Features * expiry date column in Available Batch Report (backport [#42628](https://github.com/frappe/erpnext/issues/42628)) ([#42642](https://github.com/frappe/erpnext/issues/42642)) ([d8768c5](https://github.com/frappe/erpnext/commit/d8768c537733cf105761e9f95b523f44ec4cf5f7)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index efe34d54580..08765895684 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "15.31.5" +__version__ = "15.32.0" def get_default_company(user=None): From 2aa90fdc14bbd59d5f7471865038430f049c9ed2 Mon Sep 17 00:00:00 2001 From: Lakshit Jain <108322669+ljain112@users.noreply.github.com> Date: Wed, 31 Jul 2024 13:19:54 +0530 Subject: [PATCH 024/734] fix: promotional scheme doctype fields in consitency with pricing rule (#42432) * fix: add "round_free_qty" check box in promotional scheme * fix: add `add_for_price_list` field * fix: set_query in setup for promotional scheme --------- (cherry picked from commit 8624aeca54eacf4b32e86b8d1d82245fcef65355) # Conflicts: # erpnext/accounts/doctype/promotional_scheme_price_discount/promotional_scheme_price_discount.json # erpnext/accounts/doctype/promotional_scheme_product_discount/promotional_scheme_product_discount.json (cherry picked from commit 87d8603d1d70c4a1d169322d99de3218a16ad5da) --- .../promotional_scheme/promotional_scheme.js | 12 ++++++++++++ .../promotional_scheme/promotional_scheme.py | 2 ++ .../promotional_scheme_price_discount.json | 12 ++++++++++++ .../promotional_scheme_price_discount.py | 1 + .../promotional_scheme_product_discount.json | 13 ++++++++++++- .../promotional_scheme_product_discount.py | 1 + 6 files changed, 40 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.js b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.js index 7a26c07d01f..43261e4080a 100644 --- a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.js +++ b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.js @@ -2,6 +2,18 @@ // For license information, please see license.txt frappe.ui.form.on("Promotional Scheme", { + setup: function (frm) { + frm.set_query("for_price_list", "price_discount_slabs", (doc) => { + return { + filters: { + selling: doc.selling, + buying: doc.buying, + currency: doc.currency, + }, + }; + }); + }, + refresh: function (frm) { frm.trigger("set_options_for_applicable_for"); frm.trigger("toggle_reqd_apply_on"); diff --git a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py index 71fe156dd7a..86bd2135515 100644 --- a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py +++ b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py @@ -51,6 +51,7 @@ price_discount_fields = [ "discount_percentage", "validate_applied_rule", "apply_multiple_pricing_rules", + "for_price_list", ] product_discount_fields = [ @@ -63,6 +64,7 @@ product_discount_fields = [ "recurse_for", "apply_recursion_over", "apply_multiple_pricing_rules", + "round_free_qty", ] diff --git a/erpnext/accounts/doctype/promotional_scheme_price_discount/promotional_scheme_price_discount.json b/erpnext/accounts/doctype/promotional_scheme_price_discount/promotional_scheme_price_discount.json index aa3696d216d..fc7cd6e0921 100644 --- a/erpnext/accounts/doctype/promotional_scheme_price_discount/promotional_scheme_price_discount.json +++ b/erpnext/accounts/doctype/promotional_scheme_price_discount/promotional_scheme_price_discount.json @@ -21,6 +21,7 @@ "rate", "discount_amount", "discount_percentage", + "for_price_list", "section_break_11", "warehouse", "threshold_percentage", @@ -120,6 +121,13 @@ "fieldtype": "Float", "label": "Discount Percentage" }, + { + "depends_on": "eval:doc.rate_or_discount!=\"Rate\"", + "fieldname": "for_price_list", + "fieldtype": "Link", + "label": "For Price List", + "options": "Price List" + }, { "fieldname": "section_break_11", "fieldtype": "Section Break" @@ -169,7 +177,11 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], +<<<<<<< HEAD "modified": "2021-11-16 00:25:33.843996", +======= + "modified": "2024-07-23 12:33:46.574950", +>>>>>>> 8624aeca54 (fix: promotional scheme doctype fields in consitency with pricing rule (#42432)) "modified_by": "Administrator", "module": "Accounts", "name": "Promotional Scheme Price Discount", diff --git a/erpnext/accounts/doctype/promotional_scheme_price_discount/promotional_scheme_price_discount.py b/erpnext/accounts/doctype/promotional_scheme_price_discount/promotional_scheme_price_discount.py index 545c17dda89..3e07309dca5 100644 --- a/erpnext/accounts/doctype/promotional_scheme_price_discount/promotional_scheme_price_discount.py +++ b/erpnext/accounts/doctype/promotional_scheme_price_discount/promotional_scheme_price_discount.py @@ -19,6 +19,7 @@ class PromotionalSchemePriceDiscount(Document): disable: DF.Check discount_amount: DF.Currency discount_percentage: DF.Float + for_price_list: DF.Link | None max_amount: DF.Currency max_qty: DF.Float min_amount: DF.Currency diff --git a/erpnext/accounts/doctype/promotional_scheme_product_discount/promotional_scheme_product_discount.json b/erpnext/accounts/doctype/promotional_scheme_product_discount/promotional_scheme_product_discount.json index 4e61d04ad21..75bcad8c2c4 100644 --- a/erpnext/accounts/doctype/promotional_scheme_product_discount/promotional_scheme_product_discount.json +++ b/erpnext/accounts/doctype/promotional_scheme_product_discount/promotional_scheme_product_discount.json @@ -22,6 +22,7 @@ "column_break_9", "free_item_uom", "free_item_rate", + "round_free_qty", "section_break_12", "warehouse", "threshold_percentage", @@ -181,12 +182,22 @@ "fieldtype": "Float", "label": "Apply Recursion Over (As Per Transaction UOM)", "mandatory_depends_on": "is_recursive" + }, + { + "default": "0", + "fieldname": "round_free_qty", + "fieldtype": "Check", + "label": "Round Free Qty" } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], +<<<<<<< HEAD "modified": "2024-03-12 12:53:58.199108", +======= + "modified": "2024-07-22 17:25:07.880984", +>>>>>>> 8624aeca54 (fix: promotional scheme doctype fields in consitency with pricing rule (#42432)) "modified_by": "Administrator", "module": "Accounts", "name": "Promotional Scheme Product Discount", @@ -195,4 +206,4 @@ "sort_field": "modified", "sort_order": "DESC", "states": [] -} \ No newline at end of file +} diff --git a/erpnext/accounts/doctype/promotional_scheme_product_discount/promotional_scheme_product_discount.py b/erpnext/accounts/doctype/promotional_scheme_product_discount/promotional_scheme_product_discount.py index 1463a7b93a4..b071d658cb4 100644 --- a/erpnext/accounts/doctype/promotional_scheme_product_discount/promotional_scheme_product_discount.py +++ b/erpnext/accounts/doctype/promotional_scheme_product_discount/promotional_scheme_product_discount.py @@ -53,6 +53,7 @@ class PromotionalSchemeProductDiscount(Document): "20", ] recurse_for: DF.Float + round_free_qty: DF.Check rule_description: DF.SmallText same_item: DF.Check threshold_percentage: DF.Percent From a983d65404f73ced665834dbfb06945217879d87 Mon Sep 17 00:00:00 2001 From: Lakshit Jain <108322669+ljain112@users.noreply.github.com> Date: Wed, 31 Jul 2024 13:38:38 +0530 Subject: [PATCH 025/734] chore: resolve conflicts (#42553) (cherry picked from commit 78eb4436149c5eeff71ee78f0d56cfa433f4f937) --- .../promotional_scheme_price_discount.json | 6 +----- .../promotional_scheme_product_discount.json | 4 ---- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/erpnext/accounts/doctype/promotional_scheme_price_discount/promotional_scheme_price_discount.json b/erpnext/accounts/doctype/promotional_scheme_price_discount/promotional_scheme_price_discount.json index fc7cd6e0921..32de0cec6fe 100644 --- a/erpnext/accounts/doctype/promotional_scheme_price_discount/promotional_scheme_price_discount.json +++ b/erpnext/accounts/doctype/promotional_scheme_price_discount/promotional_scheme_price_discount.json @@ -177,11 +177,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], -<<<<<<< HEAD - "modified": "2021-11-16 00:25:33.843996", -======= "modified": "2024-07-23 12:33:46.574950", ->>>>>>> 8624aeca54 (fix: promotional scheme doctype fields in consitency with pricing rule (#42432)) "modified_by": "Administrator", "module": "Accounts", "name": "Promotional Scheme Price Discount", @@ -189,4 +185,4 @@ "permissions": [], "sort_field": "modified", "sort_order": "DESC" -} \ No newline at end of file +} diff --git a/erpnext/accounts/doctype/promotional_scheme_product_discount/promotional_scheme_product_discount.json b/erpnext/accounts/doctype/promotional_scheme_product_discount/promotional_scheme_product_discount.json index 75bcad8c2c4..50d3ccb0dec 100644 --- a/erpnext/accounts/doctype/promotional_scheme_product_discount/promotional_scheme_product_discount.json +++ b/erpnext/accounts/doctype/promotional_scheme_product_discount/promotional_scheme_product_discount.json @@ -193,11 +193,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], -<<<<<<< HEAD - "modified": "2024-03-12 12:53:58.199108", -======= "modified": "2024-07-22 17:25:07.880984", ->>>>>>> 8624aeca54 (fix: promotional scheme doctype fields in consitency with pricing rule (#42432)) "modified_by": "Administrator", "module": "Accounts", "name": "Promotional Scheme Product Discount", From 6a4f06cf4a547b569b0bea79ff76f01b3b93783c Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Thu, 8 Aug 2024 07:06:38 +0000 Subject: [PATCH 026/734] chore(release): Bumped to Version 15.32.1 ## [15.32.1](https://github.com/frappe/erpnext/compare/v15.32.0...v15.32.1) (2024-08-08) ### Bug Fixes * promotional scheme doctype fields in consitency with pricing rule ([#42432](https://github.com/frappe/erpnext/issues/42432)) ([2aa90fd](https://github.com/frappe/erpnext/commit/2aa90fdc14bbd59d5f7471865038430f049c9ed2)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 08765895684..987a8717ce7 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "15.32.0" +__version__ = "15.32.1" def get_default_company(user=None): From 73af5be1c3be9a1fab19de1b96d63f907dcde8a1 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 14 Aug 2024 08:02:34 +0000 Subject: [PATCH 027/734] chore(release): Bumped to Version 15.33.0 # [15.33.0](https://github.com/frappe/erpnext/compare/v15.32.1...v15.33.0) (2024-08-14) ### Bug Fixes * allow sale of asset for internal transfer ([97cadfe](https://github.com/frappe/erpnext/commit/97cadfe5d360128934aaaac9346537a47515cf75)) * cancel Journal Entry on cancellation of asset value adjustment ([a429f2f](https://github.com/frappe/erpnext/commit/a429f2f626fdae163b7661935dd28f42ed68ce24)) * currency changing while making PR from the PO (backport [#42718](https://github.com/frappe/erpnext/issues/42718)) ([#42721](https://github.com/frappe/erpnext/issues/42721)) ([50b1fa5](https://github.com/frappe/erpnext/commit/50b1fa5deb2a502782cac6884c4ed9f82b1cd335)) * delivery note creation issue (backport [#42696](https://github.com/frappe/erpnext/issues/42696)) ([#42697](https://github.com/frappe/erpnext/issues/42697)) ([6f16ae3](https://github.com/frappe/erpnext/commit/6f16ae3e00d276b95cf805318d70591aecadfb5e)) * dimensions in common party journal entry ([fd4143e](https://github.com/frappe/erpnext/commit/fd4143e6862b3caa38d3f579bb2402be83f313fc)) * duplicate labels in stock entry (backport [#42756](https://github.com/frappe/erpnext/issues/42756)) ([#42758](https://github.com/frappe/erpnext/issues/42758)) ([8624a0a](https://github.com/frappe/erpnext/commit/8624a0abce26576616783b7ce4d43f912bc80d68)) * error message in coa importer ([20c1bcd](https://github.com/frappe/erpnext/commit/20c1bcd654ea6e6e795546379355b17758bb86b5)) * **Exchange Rate Revaluation:** translatable strings ([da6eea7](https://github.com/frappe/erpnext/commit/da6eea77437cffce628a08ef2ce5e6da2afdf193)) * fetch months last date to avoid miscalculation ([765c110](https://github.com/frappe/erpnext/commit/765c1104c4c4a76a2e65914b6e9fbf58e32ecb09)) * force fetch updates for subcription ([582fffc](https://github.com/frappe/erpnext/commit/582fffca931eca7754e24eee61d52c1d68ef8a4c)) * german translation of exit ([a2df276](https://github.com/frappe/erpnext/commit/a2df2768802c54001286913ac17ebaf207e40351)) * german translations for incoterms ([9789648](https://github.com/frappe/erpnext/commit/9789648175e61ea64d9a326c2f4b274067eecbc0)) * ledger entries for pos return with update outstanding for self ([8cd1952](https://github.com/frappe/erpnext/commit/8cd1952da3d07dc741aff6f3608480f9101f1e15)) * Maintain same rate on qty change on Quotation to Sales Order ([7ed7c22](https://github.com/frappe/erpnext/commit/7ed7c22469ba8876476214240540237491097b45)) * patch to fix incorrect against_voucher references in ledger ([389227b](https://github.com/frappe/erpnext/commit/389227bce8e62d25e0dc97a51885df1674e56b54)) * pre-commit for better code formatting ([94f4c92](https://github.com/frappe/erpnext/commit/94f4c92a039ae4039df55eb32bec0739387c6a65)) * price list when invoice created from timesheet ([2926915](https://github.com/frappe/erpnext/commit/2926915a06b13e2a29b07cad589ad0f2677b54e2)) * price list when invoice created from timesheet ([536dc47](https://github.com/frappe/erpnext/commit/536dc47eb062f7d512b0909a920f09592e528e49)) * promotional scheme doctype fields in consitency with pricing rule ([#42432](https://github.com/frappe/erpnext/issues/42432)) ([87d8603](https://github.com/frappe/erpnext/commit/87d8603d1d70c4a1d169322d99de3218a16ad5da)) * resolved conflict ([defd554](https://github.com/frappe/erpnext/commit/defd5541b060dd901171fcb2ffc0029c75931b26)) * Sort lists before calling itertools.groupby ([d8939e0](https://github.com/frappe/erpnext/commit/d8939e0bb0136ed6947d0f542ae0c91873f57237)) * text color in sales funnel report based on theme ([a8de8ae](https://github.com/frappe/erpnext/commit/a8de8aecf5a2e85bab16ae9a52b7c2bfe7038a4a)) * typeerror on payment entry ([64e75a8](https://github.com/frappe/erpnext/commit/64e75a8e089c7b347f255c7c8083d4297c0ea069)) * update 'Paid Amount' on forex payment request ([c71f06b](https://github.com/frappe/erpnext/commit/c71f06be9e8ad793a3d57cef7598415220e67925)) * Update Rate as per Valuation Rate for Internal Transfers only if Setting is Enabled ([#42050](https://github.com/frappe/erpnext/issues/42050)) ([6e833cc](https://github.com/frappe/erpnext/commit/6e833cce6af9d9542c23e74bda7c0b0e029a683e)) * warning message for negative stock (backport [#42683](https://github.com/frappe/erpnext/issues/42683)) ([#42710](https://github.com/frappe/erpnext/issues/42710)) ([a990577](https://github.com/frappe/erpnext/commit/a99057754d436022555101230b6a53e417fe9e61)) ### Features * changes in opportunity.py to show contacts and addresses from referenced and opportunities ([3cac4a5](https://github.com/frappe/erpnext/commit/3cac4a598fdead4f35f4130a899950625f51f167)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 987a8717ce7..b7406969305 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "15.32.1" +__version__ = "15.33.0" def get_default_company(user=None): From 9f970189feb9193fb80ac9564bb6d23bd3d05f66 Mon Sep 17 00:00:00 2001 From: Sanket322 Date: Wed, 24 Jul 2024 13:42:04 +0530 Subject: [PATCH 028/734] fix: set proper currency format (cherry picked from commit 2533808f1e457cc87e3bc41cd9c0c97ad1f0c771) --- erpnext/accounts/report/payment_ledger/payment_ledger.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/report/payment_ledger/payment_ledger.py b/erpnext/accounts/report/payment_ledger/payment_ledger.py index 9852c6e7ab9..41e34cff8b2 100644 --- a/erpnext/accounts/report/payment_ledger/payment_ledger.py +++ b/erpnext/accounts/report/payment_ledger/payment_ledger.py @@ -130,6 +130,7 @@ class PaymentLedger: ) def get_columns(self): + company_currency = frappe.get_cached_value("Company", self.filters.get("company"), "default_currency") options = None self.columns.append( dict( @@ -194,7 +195,7 @@ class PaymentLedger: label=_("Amount"), fieldname="amount", fieldtype="Currency", - options="Company:company:default_currency", + options=company_currency, width="100", ) ) From a3e3585e5054d254ebc198e34498738160e8569d Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 16 Aug 2024 11:47:53 +0530 Subject: [PATCH 029/734] fix: not able to create the batch (backport #42784) (backport #42785) (#42786) fix: not able to create the batch (backport #42784) (#42785) fix: not able to create the batch (#42784) (cherry picked from commit 32c4fab14f83dc5e2cec0c21395713e54d2537c9) Co-authored-by: rohitwaghchaure (cherry picked from commit 0f9849e6727b4fef3e3bf75048de553714b08298) Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- erpnext/stock/doctype/batch/batch.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/batch/batch.js b/erpnext/stock/doctype/batch/batch.js index 4ed428421ca..77e4e560acf 100644 --- a/erpnext/stock/doctype/batch/batch.js +++ b/erpnext/stock/doctype/batch/batch.js @@ -3,7 +3,7 @@ frappe.ui.form.on("Batch", { setup: (frm) => { - frm.fields_dict["item"].get_query = function (doc, cdt, cdn) { + frm.set_query("item", () => { return { query: "erpnext.controllers.queries.item_query", filters: { @@ -11,7 +11,7 @@ frappe.ui.form.on("Batch", { has_batch_no: 1, }, }; - }; + }); }, refresh: (frm) => { if (!frm.is_new()) { From fbf1160357d4dd3c7970f8663c8cfa8a5bbc1f35 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Fri, 16 Aug 2024 06:19:07 +0000 Subject: [PATCH 030/734] chore(release): Bumped to Version 15.33.1 ## [15.33.1](https://github.com/frappe/erpnext/compare/v15.33.0...v15.33.1) (2024-08-16) ### Bug Fixes * not able to create the batch (backport [#42784](https://github.com/frappe/erpnext/issues/42784)) (backport [#42785](https://github.com/frappe/erpnext/issues/42785)) ([#42786](https://github.com/frappe/erpnext/issues/42786)) ([a3e3585](https://github.com/frappe/erpnext/commit/a3e3585e5054d254ebc198e34498738160e8569d)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index b7406969305..e28c96e466e 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "15.33.0" +__version__ = "15.33.1" def get_default_company(user=None): From 4d9f522f22d1abaea2a61714151767002f322a45 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 16 Aug 2024 13:07:53 +0530 Subject: [PATCH 031/734] fix(patch): replace repost with direct sql to update 'against_voucher (cherry picked from commit 13bb48434fd6897dcef2a7d79333d5373bec0cdb) --- .../v14_0/update_pos_return_ledger_entries.py | 112 ++++++++++++++---- 1 file changed, 89 insertions(+), 23 deletions(-) diff --git a/erpnext/patches/v14_0/update_pos_return_ledger_entries.py b/erpnext/patches/v14_0/update_pos_return_ledger_entries.py index 60e867d1bcb..6a6d15e0b34 100644 --- a/erpnext/patches/v14_0/update_pos_return_ledger_entries.py +++ b/erpnext/patches/v14_0/update_pos_return_ledger_entries.py @@ -1,8 +1,64 @@ import frappe from frappe import qb +from erpnext.accounts.utils import update_voucher_outstanding -def execute(): + +def get_valid_against_voucher_ref(pos_returns): + sinv = qb.DocType("Sales Invoice") + res = ( + qb.from_(sinv) + .select(sinv.name, sinv.return_against) + .where(sinv.name.isin(pos_returns) & sinv.return_against.notnull()) + .orderby(sinv.name) + .run(as_dict=True) + ) + return res + + +def build_dict_of_valid_against_reference(pos_returns): + _against_ref_dict = frappe._dict() + res = get_valid_against_voucher_ref(pos_returns) + for x in res: + _against_ref_dict[x.name] = x.return_against + return _against_ref_dict + + +def fix_incorrect_against_voucher_ref(affected_pos_returns): + if affected_pos_returns: + valid_against_voucher_dict = build_dict_of_valid_against_reference(affected_pos_returns) + + gle = qb.DocType("GL Entry") + gles_with_invalid_against = ( + qb.from_(gle) + .select(gle.name, gle.voucher_no) + .where( + gle.voucher_no.isin(affected_pos_returns) + & gle.against_voucher.notnull() + & gle.against_voucher.eq(gle.voucher_no) + & gle.is_cancelled.eq(0) + ) + .run(as_dict=True) + ) + # Update GL + if gles_with_invalid_against: + for gl in gles_with_invalid_against: + frappe.db.set_value( + "GL Entry", + gl.name, + "against_voucher", + valid_against_voucher_dict[gl.voucher_no], + ) + + # Update Payment Ledger + ple = qb.DocType("Payment Ledger Entry") + for x in affected_pos_returns: + qb.update(ple).set(ple.against_voucher_no, valid_against_voucher_dict[x]).where( + ple.voucher_no.eq(x) & ple.delinked.eq(0) + ).run() + + +def get_pos_returns_with_invalid_against_ref(): sinv = qb.DocType("Sales Invoice") pos_returns_without_self = ( qb.from_(sinv) @@ -32,30 +88,40 @@ def execute(): .run() ) - _vouchers = list(set([x[0] for x in gl_against_references])) - invoice_return_against = ( + if gl_against_references: + _vouchers = list(set([x[0] for x in gl_against_references])) + invoice_return_against = ( + qb.from_(sinv) + .select(sinv.name, sinv.return_against) + .where(sinv.name.isin(_vouchers) & sinv.return_against.notnull()) + .orderby(sinv.name) + .run() + ) + + valid_references = set(invoice_return_against) + actual_references = set(gl_against_references) + + invalid_references = actual_references.difference(valid_references) + if invalid_references: + return [x[0] for x in invalid_references] + return None + + +def update_outstanding_for_affected(affected_pos_returns): + if affected_pos_returns: + sinv = qb.DocType("Sales Invoice") + pos_with_accounts = ( qb.from_(sinv) - .select(sinv.name, sinv.return_against) - .where(sinv.name.isin(_vouchers) & sinv.return_against.notnull()) - .orderby(sinv.name) - .run() + .select(sinv.return_against, sinv.debit_to, sinv.customer) + .where(sinv.name.isin(affected_pos_returns)) + .run(as_dict=True) ) - valid_references = set(invoice_return_against) - actual_references = set(gl_against_references) + for x in pos_with_accounts: + update_voucher_outstanding("Sales Invoice", x.return_against, x.debit_to, "Customer", x.customer) - invalid_references = actual_references.difference(valid_references) - if invalid_references: - # Repost Accounting Ledger - pos_for_reposting = ( - qb.from_(sinv) - .select(sinv.company, sinv.name) - .where(sinv.name.isin([x[0] for x in invalid_references])) - .run(as_dict=True) - ) - for x in pos_for_reposting: - ral = frappe.new_doc("Repost Accounting Ledger") - ral.company = x.company - ral.append("vouchers", {"voucher_type": "Sales Invoice", "voucher_no": x.name}) - ral.save().submit() +def execute(): + affected_pos_returns = get_pos_returns_with_invalid_against_ref() + fix_incorrect_against_voucher_ref(affected_pos_returns) + update_outstanding_for_affected(affected_pos_returns) From eb8213c4e7b47f0dd30b33fb16906486b34ddf7f Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 16 Aug 2024 14:32:53 +0530 Subject: [PATCH 032/734] chore: update patches.txt (cherry picked from commit 1721175a207fde5e06d3f8646a766e4eb3b325b5) --- erpnext/patches.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 797e8cebc80..208c9e58d4f 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -357,7 +357,7 @@ erpnext.patches.v14_0.create_accounting_dimensions_in_reconciliation_tool erpnext.patches.v15_0.allow_on_submit_dimensions_for_repostable_doctypes erpnext.patches.v14_0.update_flag_for_return_invoices #2024-03-22 erpnext.patches.v15_0.create_accounting_dimensions_in_payment_request -erpnext.patches.v14_0.update_pos_return_ledger_entries +erpnext.patches.v14_0.update_pos_return_ledger_entries #2024-08-16 # below migration patch should always run last erpnext.patches.v14_0.migrate_gl_to_payment_ledger erpnext.stock.doctype.delivery_note.patches.drop_unused_return_against_index # 2023-12-20 From 81b1cbd367298d4f7d6f7566e8d2620bcef2277b Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Fri, 16 Aug 2024 11:09:43 +0000 Subject: [PATCH 033/734] chore(release): Bumped to Version 15.33.2 ## [15.33.2](https://github.com/frappe/erpnext/compare/v15.33.1...v15.33.2) (2024-08-16) ### Bug Fixes * **patch:** replace repost with direct sql to update 'against_voucher ([4d9f522](https://github.com/frappe/erpnext/commit/4d9f522f22d1abaea2a61714151767002f322a45)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index e28c96e466e..c31f06dfa79 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "15.33.1" +__version__ = "15.33.2" def get_default_company(user=None): From 4914481105bba3a665b63784e4b48c03342e3270 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Fri, 16 Aug 2024 21:38:56 +0200 Subject: [PATCH 034/734] fix: translatability of boldened text (cherry picked from commit af0ae930ca0eb0bad2509797e54db76fe62c0339) # Conflicts: # erpnext/controllers/accounts_controller.py --- erpnext/accounts/doctype/account/account.py | 2 +- .../pos_closing_entry/pos_closing_entry.py | 10 +++------- .../accounts/doctype/pos_invoice/pos_invoice.py | 2 +- .../pos_invoice_merge_log.py | 11 +++++------ .../accounts/doctype/pricing_rule/pricing_rule.py | 5 +++-- .../doctype/purchase_invoice/purchase_invoice.py | 12 ++++++------ .../doctype/sales_invoice/sales_invoice.py | 6 +++--- .../tax_withholding_details.py | 2 +- erpnext/assets/doctype/asset/asset.py | 2 +- erpnext/controllers/accounts_controller.py | 15 +++++++++++---- erpnext/controllers/item_variant.py | 2 +- erpnext/controllers/selling_controller.py | 2 +- .../doctype/plaid_settings/plaid_settings.py | 2 +- erpnext/setup/doctype/company/company.py | 6 +++--- erpnext/stock/doctype/batch/batch.py | 4 ++-- erpnext/stock/doctype/stock_entry/stock_entry.py | 6 +++--- .../stock_reservation_entry.py | 5 +++-- .../doctype/stock_settings/stock_settings.py | 8 ++++---- .../service_level_agreement.py | 2 +- 19 files changed, 54 insertions(+), 50 deletions(-) diff --git a/erpnext/accounts/doctype/account/account.py b/erpnext/accounts/doctype/account/account.py index 69c1e16bb1e..1a5ee36b95a 100644 --- a/erpnext/accounts/doctype/account/account.py +++ b/erpnext/accounts/doctype/account/account.py @@ -202,7 +202,7 @@ class Account(NestedSet): msg = _( "There are ledger entries against this account. Changing {0} to non-{1} in live system will cause incorrect output in 'Accounts {2}' report" ).format( - frappe.bold("Account Type"), doc_before_save.account_type, doc_before_save.account_type + frappe.bold(_("Account Type")), doc_before_save.account_type, doc_before_save.account_type ) frappe.msgprint(msg) self.add_comment("Comment", msg) diff --git a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py index 9faf8693a8a..fda868cfe51 100644 --- a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py +++ b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py @@ -87,19 +87,15 @@ class POSClosingEntry(StatusUpdater): as_dict=1, )[0] if pos_invoice.consolidated_invoice: - invalid_row.setdefault("msg", []).append( - _("POS Invoice is {}").format(frappe.bold("already consolidated")) - ) + invalid_row.setdefault("msg", []).append(_("POS Invoice is already consolidated")) invalid_rows.append(invalid_row) continue if pos_invoice.pos_profile != self.pos_profile: invalid_row.setdefault("msg", []).append( - _("POS Profile doesn't matches {}").format(frappe.bold(self.pos_profile)) + _("POS Profile doesn't match {}").format(frappe.bold(self.pos_profile)) ) if pos_invoice.docstatus != 1: - invalid_row.setdefault("msg", []).append( - _("POS Invoice is not {}").format(frappe.bold("submitted")) - ) + invalid_row.setdefault("msg", []).append(_("POS Invoice is not submitted")) if pos_invoice.owner != self.user: invalid_row.setdefault("msg", []).append( _("POS Invoice isn't created by user {}").format(frappe.bold(self.owner)) diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py index ef4db1dac98..d517c9d6da2 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py @@ -188,7 +188,7 @@ class POSInvoice(SalesInvoice): def validate(self): if not cint(self.is_pos): frappe.throw( - _("POS Invoice should have {} field checked.").format(frappe.bold("Include Payment")) + _("POS Invoice should have the field {0} checked.").format(frappe.bold(_("Include Payment"))) ) # run on validate method of selling controller diff --git a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py index 2cf204dd347..aecdac0b3a1 100644 --- a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py +++ b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py @@ -97,16 +97,15 @@ class POSInvoiceMergeLog(Document): return_against_status = frappe.db.get_value("POS Invoice", return_against, "status") if return_against_status != "Consolidated": # if return entry is not getting merged in the current pos closing and if it is not consolidated - bold_unconsolidated = frappe.bold("not Consolidated") - msg = _("Row #{}: Original Invoice {} of return invoice {} is {}.").format( - d.idx, bold_return_against, bold_pos_invoice, bold_unconsolidated - ) + msg = _( + "Row #{}: The original Invoice {} of return invoice {} is not consolidated." + ).format(d.idx, bold_return_against, bold_pos_invoice) msg += " " msg += _( - "Original invoice should be consolidated before or along with the return invoice." + "The original invoice should be consolidated before or along with the return invoice." ) msg += "

" - msg += _("You can add original invoice {} manually to proceed.").format( + msg += _("You can add the original invoice {} manually to proceed.").format( bold_return_against ) frappe.throw(msg) diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py index de29ec0f004..1a1ff78a217 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py @@ -186,7 +186,8 @@ class PricingRule(Document): if not self.priority: throw( _("As the field {0} is enabled, the field {1} is mandatory.").format( - frappe.bold("Apply Discount on Discounted Rate"), frappe.bold("Priority") + frappe.bold(_("Apply Discount on Discounted Rate")), + frappe.bold(_("Priority")), ) ) @@ -194,7 +195,7 @@ class PricingRule(Document): throw( _( "As the field {0} is enabled, the value of the field {1} should be more than 1." - ).format(frappe.bold("Apply Discount on Discounted Rate"), frappe.bold("Priority")) + ).format(frappe.bold(_("Apply Discount on Discounted Rate")), frappe.bold(_("Priority"))) ) def validate_applicable_for_selling_or_buying(self): diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index c33b2c0ff17..78fc02999ef 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -377,16 +377,16 @@ class PurchaseInvoice(BuyingController): if account.report_type != "Balance Sheet": frappe.throw( _( - "Please ensure {} account is a Balance Sheet account. You can change the parent account to a Balance Sheet account or select a different account." - ).format(frappe.bold("Credit To")), + "Please ensure that the {0} account is a Balance Sheet account. You can change the parent account to a Balance Sheet account or select a different account." + ).format(frappe.bold(_("Credit To"))), title=_("Invalid Account"), ) if self.supplier and account.account_type != "Payable": frappe.throw( _( - "Please ensure {} account {} is a Payable account. Change the account type to Payable or select a different account." - ).format(frappe.bold("Credit To"), frappe.bold(self.credit_to)), + "Please ensure that the {0} account {1} is a Payable account. You can change the account type to Payable or select a different account." + ).format(frappe.bold(_("Credit To")), frappe.bold(self.credit_to)), title=_("Invalid Account"), ) @@ -634,7 +634,7 @@ class PurchaseInvoice(BuyingController): "To submit the invoice without purchase order please set {0} as {1} in {2}" ).format( frappe.bold(_("Purchase Order Required")), - frappe.bold("No"), + frappe.bold(_("No")), get_link_to_form("Buying Settings", "Buying Settings", "Buying Settings"), ) throw(msg, title=_("Mandatory Purchase Order")) @@ -655,7 +655,7 @@ class PurchaseInvoice(BuyingController): "To submit the invoice without purchase receipt please set {0} as {1} in {2}" ).format( frappe.bold(_("Purchase Receipt Required")), - frappe.bold("No"), + frappe.bold(_("No")), get_link_to_form("Buying Settings", "Buying Settings", "Buying Settings"), ) throw(msg, title=_("Mandatory Purchase Receipt")) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 5d6c8a353fb..1966a78ef49 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -514,7 +514,7 @@ class SalesInvoice(SellingController): ) if pos_closing_entry and pos_closing_entry[0]: msg = _("To cancel a {} you need to cancel the POS Closing Entry {}.").format( - frappe.bold("Consolidated Sales Invoice"), + frappe.bold(_("Consolidated Sales Invoice")), get_link_to_form("POS Closing Entry", pos_closing_entry[0]), ) frappe.throw(msg, title=_("Not Allowed")) @@ -858,7 +858,7 @@ class SalesInvoice(SellingController): if account.report_type != "Balance Sheet": msg = ( - _("Please ensure {} account is a Balance Sheet account.").format(frappe.bold("Debit To")) + _("Please ensure {} account is a Balance Sheet account.").format(frappe.bold(_("Debit To"))) + " " ) msg += _( @@ -869,7 +869,7 @@ class SalesInvoice(SellingController): if self.customer and account.account_type != "Receivable": msg = ( _("Please ensure {} account {} is a Receivable account.").format( - frappe.bold("Debit To"), frappe.bold(self.debit_to) + frappe.bold(_("Debit To")), frappe.bold(self.debit_to) ) + " " ) diff --git a/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py b/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py index 276e95bd5d1..0c18dac7768 100644 --- a/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py +++ b/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py @@ -336,7 +336,7 @@ 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")), + _("No {0} Accounts found for this company.").format(frappe.bold(_("Tax Withholding"))), title=_("Accounts Missing Error"), ) gle = frappe.qb.DocType("GL Entry") diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 1b9e911be5b..76eeca03ab5 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -669,7 +669,7 @@ class Asset(AccountsController): if not fixed_asset_account: frappe.throw( _("Set {0} in asset category {1} for company {2}").format( - frappe.bold("Fixed Asset Account"), + frappe.bold(_("Fixed Asset Account")), frappe.bold(self.asset_category), frappe.bold(self.company), ), diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 63d0404642a..ad9bafc93c0 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -233,8 +233,13 @@ class AccountsController(TransactionBase): ).format( frappe.bold(document_type), get_link_to_form(self.doctype, self.get("return_against")), +<<<<<<< HEAD frappe.bold("Update Outstanding for Self"), get_link_to_form("Payment Reconciliation", "Payment Reconciliation"), +======= + frappe.bold(_("Update Outstanding for Self")), + get_link_to_form("Payment Reconciliation"), +>>>>>>> af0ae930ca (fix: translatability of boldened text) ) ) @@ -1962,7 +1967,9 @@ class AccountsController(TransactionBase): def raise_missing_debit_credit_account_error(self, party_type, party): """Raise an error if debit to/credit to account does not exist.""" - db_or_cr = frappe.bold("Debit To") if self.doctype == "Sales Invoice" else frappe.bold("Credit To") + db_or_cr = ( + frappe.bold(_("Debit To")) if self.doctype == "Sales Invoice" else frappe.bold(_("Credit To")) + ) rec_or_pay = "Receivable" if self.doctype == "Sales Invoice" else "Payable" link_to_party = frappe.utils.get_link_to_form(party_type, party) @@ -3085,9 +3092,9 @@ def set_order_defaults(parent_doctype, parent_doctype_name, child_doctype, child child_item.warehouse = get_item_warehouse(item, p_doc, overwrite_warehouse=True) if not child_item.warehouse: frappe.throw( - _("Cannot find {} for item {}. Please set the same in Item Master or Stock Settings.").format( - frappe.bold("default warehouse"), frappe.bold(item.item_code) - ) + _( + "Cannot find a default warehouse for item {0}. Please set one in the Item Master or in Stock Settings." + ).format(frappe.bold(item.item_code)) ) set_child_tax_template_and_map(item, child_item, p_doc) diff --git a/erpnext/controllers/item_variant.py b/erpnext/controllers/item_variant.py index cc6870f892a..5dace4af884 100644 --- a/erpnext/controllers/item_variant.py +++ b/erpnext/controllers/item_variant.py @@ -150,7 +150,7 @@ def validate_item_attribute_value(attributes_list, attribute, attribute_value, i ) msg += "
" + _( "To still proceed with editing this Attribute Value, enable {0} in Item Variant Settings." - ).format(frappe.bold("Allow Rename Attribute Value")) + ).format(frappe.bold(_("Allow Rename Attribute Value"))) frappe.throw(msg, InvalidItemAttributeValueError, title=_("Edit Not Allowed")) diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index c1f565e7323..766e67be0f6 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -697,7 +697,7 @@ class SellingController(StockController): duplicate_items_msg = _("Item {0} entered multiple times.").format(frappe.bold(d.item_code)) duplicate_items_msg += "

" duplicate_items_msg += _("Please enable {} in {} to allow same item in multiple rows").format( - frappe.bold("Allow Item to Be Added Multiple Times in a Transaction"), + frappe.bold(_("Allow Item to Be Added Multiple Times in a Transaction")), get_link_to_form("Selling Settings", "Selling Settings"), ) if frappe.db.get_value("Item", d.item_code, "is_stock_item") == 1: diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py index edfab4777a7..e187c075077 100644 --- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py +++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py @@ -96,7 +96,7 @@ def add_bank_accounts(response, bank, company): frappe.throw( _( "Please setup and enable a group account with the Account Type - {0} for the company {1}" - ).format(frappe.bold("Bank"), company) + ).format(frappe.bold(_("Bank")), company) ) for account in response["accounts"]: diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py index f79ea60f5c4..8028b8e6af4 100644 --- a/erpnext/setup/doctype/company/company.py +++ b/erpnext/setup/doctype/company/company.py @@ -203,7 +203,7 @@ class Company(NestedSet): ): frappe.throw( _("'{0}' should be in company currency {1}.").format( - frappe.bold("Default Advance Received Account"), frappe.bold(self.default_currency) + frappe.bold(_("Default Advance Received Account")), frappe.bold(self.default_currency) ) ) @@ -214,7 +214,7 @@ class Company(NestedSet): ): frappe.throw( _("'{0}' should be in company currency {1}.").format( - frappe.bold("Default Advance Paid Account"), frappe.bold(self.default_currency) + frappe.bold(_("Default Advance Paid Account")), frappe.bold(self.default_currency) ) ) @@ -447,7 +447,7 @@ class Company(NestedSet): ): frappe.throw( _("Set default {0} account for non stock items").format( - frappe.bold("Provisional Account") + frappe.bold(_("Provisional Account")) ) ) diff --git a/erpnext/stock/doctype/batch/batch.py b/erpnext/stock/doctype/batch/batch.py index c539c315747..5d71ca06cb4 100644 --- a/erpnext/stock/doctype/batch/batch.py +++ b/erpnext/stock/doctype/batch/batch.py @@ -188,9 +188,9 @@ class Batch(Document): if has_expiry_date and not self.expiry_date: frappe.throw( msg=_("Please set {0} for Batched Item {1}, which is used to set {2} on Submit.").format( - frappe.bold("Shelf Life in Days"), + frappe.bold(_("Shelf Life in Days")), get_link_to_form("Item", self.item), - frappe.bold("Batch Expiry Date"), + frappe.bold(_("Batch Expiry Date")), ), title=_("Expiry Date Mandatory"), ) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 14e0fdf6388..83ae8d09f66 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -898,8 +898,8 @@ class StockEntry(StockController): ).format( item.idx, frappe.bold(label), - frappe.bold("Manufacture"), - frappe.bold("Material Consumption for Manufacture"), + frappe.bold(_("Manufacture")), + frappe.bold(_("Material Consumption for Manufacture")), ) ) @@ -909,7 +909,7 @@ class StockEntry(StockController): ): frappe.throw( _("Only one {0} entry can be created against the Work Order {1}").format( - frappe.bold("Manufacture"), frappe.bold(self.work_order) + frappe.bold(_("Manufacture")), frappe.bold(self.work_order) ) ) diff --git a/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py b/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py index 9808d2dfe72..8316488e253 100644 --- a/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py +++ b/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py @@ -255,7 +255,7 @@ class StockReservationEntry(Document): if self.has_batch_no else _("Warehouse"), frappe.bold(self.warehouse), - frappe.bold("Stock Reservation Entry"), + frappe.bold(_("Stock Reservation Entry")), ) frappe.throw(msg) @@ -497,7 +497,8 @@ def validate_stock_reservation_settings(voucher: object) -> None: if not frappe.db.get_single_value("Stock Settings", "enable_stock_reservation"): msg = _("Please enable {0} in the {1}.").format( - frappe.bold("Stock Reservation"), frappe.bold("Stock Settings") + frappe.bold(_("Stock Reservation")), + frappe.bold(_("Stock Settings")), ) frappe.throw(msg) diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.py b/erpnext/stock/doctype/stock_settings/stock_settings.py index c029b7bd1fb..229ff944750 100644 --- a/erpnext/stock/doctype/stock_settings/stock_settings.py +++ b/erpnext/stock/doctype/stock_settings/stock_settings.py @@ -175,7 +175,7 @@ class StockSettings(Document): if self.allow_negative_stock and self.enable_stock_reservation: frappe.throw( _("As {0} is enabled, you can not enable {1}.").format( - frappe.bold("Stock Reservation"), frappe.bold("Allow Negative Stock") + frappe.bold(_("Stock Reservation")), frappe.bold(_("Allow Negative Stock")) ) ) @@ -187,7 +187,7 @@ class StockSettings(Document): if self.allow_negative_stock: frappe.throw( _("As {0} is enabled, you can not enable {1}.").format( - frappe.bold("Allow Negative Stock"), frappe.bold("Stock Reservation") + frappe.bold(_("Allow Negative Stock")), frappe.bold(_("Stock Reservation")) ) ) @@ -207,7 +207,7 @@ class StockSettings(Document): if bin_with_negative_stock: frappe.throw( _("As there are negative stock, you can not enable {0}.").format( - frappe.bold("Stock Reservation") + frappe.bold(_("Stock Reservation")) ) ) @@ -221,7 +221,7 @@ class StockSettings(Document): if has_reserved_stock: frappe.throw( _("As there are reserved stock, you cannot disable {0}.").format( - frappe.bold("Stock Reservation") + frappe.bold(_("Stock Reservation")) ) ) diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py index b140fdab51f..011a5bc371f 100644 --- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py +++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py @@ -144,7 +144,7 @@ class ServiceLevelAgreement(Document): ): frappe.throw( _("{0} is not enabled in {1}").format( - frappe.bold("Track Service Level Agreement"), + frappe.bold(_("Track Service Level Agreement")), get_link_to_form("Support Settings", "Support Settings"), ) ) From dbd466b6b2afdff4a1b3816359cab55207c44318 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Sat, 17 Aug 2024 02:24:58 +0200 Subject: [PATCH 035/734] chore: resolve conflicts --- erpnext/controllers/accounts_controller.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index ad9bafc93c0..58e120e14b0 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -233,13 +233,8 @@ class AccountsController(TransactionBase): ).format( frappe.bold(document_type), get_link_to_form(self.doctype, self.get("return_against")), -<<<<<<< HEAD - frappe.bold("Update Outstanding for Self"), - get_link_to_form("Payment Reconciliation", "Payment Reconciliation"), -======= frappe.bold(_("Update Outstanding for Self")), - get_link_to_form("Payment Reconciliation"), ->>>>>>> af0ae930ca (fix: translatability of boldened text) + get_link_to_form("Payment Reconciliation", "Payment Reconciliation"), ) ) From 0344442d42cbdbe8db2401f1c35d18f13674f786 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 19 Aug 2024 14:55:41 +0530 Subject: [PATCH 036/734] perf: data import for stock entries (backport #42711) (#42819) perf: data import for stock entries (#42711) (cherry picked from commit 151128046440ee32f8bf8056942c47f65b033365) Co-authored-by: rohitwaghchaure --- .../doctype/stock_entry/test_stock_entry.py | 70 ++++++++++++++++++- .../test_stock_ledger_entry.py | 3 + erpnext/stock/stock_ledger.py | 6 ++ 3 files changed, 78 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py index a680b7733d3..a9529cc2ede 100644 --- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py @@ -4,7 +4,7 @@ from frappe.permissions import add_user_permission, remove_user_permission from frappe.tests.utils import FrappeTestCase, change_settings -from frappe.utils import add_days, flt, nowtime, today +from frappe.utils import add_days, cstr, flt, get_time, getdate, nowtime, today from erpnext.accounts.doctype.account.test_account import get_inventory_account from erpnext.stock.doctype.item.test_item import ( @@ -1780,6 +1780,74 @@ class TestStockEntry(FrappeTestCase): frappe.db.set_value("Serial and Batch Bundle", sbb, "type_of_transaction", "Inward") self.assertRaises(frappe.ValidationError, se.submit) + def test_stock_entry_for_same_posting_date_and_time(self): + warehouse = "_Test Warehouse - _TC" + item_code = "Test Stock Entry For Same Posting Datetime 1" + make_item(item_code, {"is_stock_item": 1}) + posting_date = nowdate() + posting_time = nowtime() + + for index in range(25): + se = make_stock_entry( + item_code=item_code, + qty=1, + to_warehouse=warehouse, + posting_date=posting_date, + posting_time=posting_time, + do_not_submit=True, + purpose="Material Receipt", + basic_rate=100, + ) + + se.append( + "items", + { + "item_code": item_code, + "item_name": se.items[0].item_name, + "description": se.items[0].description, + "t_warehouse": se.items[0].t_warehouse, + "basic_rate": 100, + "qty": 1, + "stock_qty": 1, + "conversion_factor": 1, + "expense_account": se.items[0].expense_account, + "cost_center": se.items[0].cost_center, + "uom": se.items[0].uom, + "stock_uom": se.items[0].stock_uom, + }, + ) + + se.remarks = f"The current number is {cstr(index)}" + + se.submit() + + sles = frappe.get_all( + "Stock Ledger Entry", + fields=[ + "posting_date", + "posting_time", + "actual_qty", + "qty_after_transaction", + "incoming_rate", + "stock_value_difference", + "stock_value", + ], + filters={"item_code": item_code, "warehouse": warehouse}, + order_by="creation", + ) + + self.assertEqual(len(sles), 50) + i = 0 + for sle in sles: + i += 1 + self.assertEqual(getdate(sle.posting_date), getdate(posting_date)) + self.assertEqual(get_time(sle.posting_time), get_time(posting_time)) + self.assertEqual(sle.actual_qty, 1) + self.assertEqual(sle.qty_after_transaction, i) + self.assertEqual(sle.incoming_rate, 100) + self.assertEqual(sle.stock_value_difference, 100) + self.assertEqual(sle.stock_value, 100 * i) + def make_serialized_item(**args): args = frappe._dict(args) diff --git a/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py index 069192fdb16..587b25ea154 100644 --- a/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py +++ b/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py @@ -1043,6 +1043,8 @@ class TestStockLedgerEntry(FrappeTestCase, StockTestMixin): self.assertEqual(50, _get_stock_credit(final_consumption)) def test_tie_breaking(self): + from erpnext.stock.doctype.repost_item_valuation.repost_item_valuation import repost_entries + frappe.flags.dont_execute_stock_reposts = True self.addCleanup(frappe.flags.pop, "dont_execute_stock_reposts") @@ -1085,6 +1087,7 @@ class TestStockLedgerEntry(FrappeTestCase, StockTestMixin): self.assertEqual([10, 11], ordered_qty_after_transaction()) first.cancel() + repost_entries() self.assertEqual([1], ordered_qty_after_transaction()) backdated = make_stock_entry( diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 693481dfba8..0961d9bd35e 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -647,6 +647,7 @@ class update_entries_after: and ( posting_datetime = %(posting_datetime)s ) + and creation = %(creation)s order by creation ASC for update @@ -1526,6 +1527,11 @@ def get_previous_sle_of_current_voucher(args, operator="<", exclude_current_vouc voucher_no = args.get("voucher_no") voucher_condition = f"and voucher_no != '{voucher_no}'" + elif args.get("creation"): + creation = args.get("creation") + operator = "<=" + voucher_condition = f"and creation < '{creation}'" + sle = frappe.db.sql( f""" select *, posting_datetime as "timestamp" From b24de3e35b5a9eb34390c866fde35dc9b4ea1fe7 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 19 Aug 2024 17:34:24 +0530 Subject: [PATCH 037/734] fix: dropping index to improve performance (backport #42820) (#42821) fix: dropping index to improve performance (#42820) fix: droping index to improve peformance (cherry picked from commit 5404b21c7ddbb7701be6b622d972e724fac167c1) Co-authored-by: rohitwaghchaure --- erpnext/patches.txt | 1 + .../drop_index_posting_datetime_from_sle.py | 16 ++++++++++++++++ .../stock_ledger_entry/stock_ledger_entry.py | 1 - 3 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 erpnext/patches/v15_0/drop_index_posting_datetime_from_sle.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 208c9e58d4f..bece96d64e8 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -371,3 +371,4 @@ erpnext.patches.v15_0.update_warehouse_field_in_asset_repair_consumed_item_docty erpnext.patches.v15_0.update_asset_repair_field_in_stock_entry erpnext.patches.v15_0.update_total_number_of_booked_depreciations erpnext.patches.v15_0.do_not_use_batchwise_valuation +erpnext.patches.v15_0.drop_index_posting_datetime_from_sle diff --git a/erpnext/patches/v15_0/drop_index_posting_datetime_from_sle.py b/erpnext/patches/v15_0/drop_index_posting_datetime_from_sle.py new file mode 100644 index 00000000000..40b0b2d6759 --- /dev/null +++ b/erpnext/patches/v15_0/drop_index_posting_datetime_from_sle.py @@ -0,0 +1,16 @@ +import click +import frappe + + +def execute(): + table = "tabStock Ledger Entry" + index = "posting_datetime_creation_index" + + if not frappe.db.has_index(table, index): + return + + try: + frappe.db.sql_ddl(f"ALTER TABLE `{table}` DROP INDEX `{index}`") + click.echo(f"āœ“ dropped {index} index from {table}") + except Exception: + frappe.log_error("Failed to drop index") diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py index 319303dbbb0..cff1886d7fe 100644 --- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py +++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py @@ -351,4 +351,3 @@ def on_doctype_update(): frappe.db.add_index("Stock Ledger Entry", ["voucher_no", "voucher_type"]) frappe.db.add_index("Stock Ledger Entry", ["batch_no", "item_code", "warehouse"]) frappe.db.add_index("Stock Ledger Entry", ["warehouse", "item_code"], "item_warehouse") - frappe.db.add_index("Stock Ledger Entry", ["posting_datetime", "creation"]) From 9e9de4c99e9f628469f93442c68a2fe8e69002fd Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Mon, 19 Aug 2024 16:43:22 +0200 Subject: [PATCH 038/734] fix: backport german translations from develop --- erpnext/translations/de.csv | 100 ++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/erpnext/translations/de.csv b/erpnext/translations/de.csv index 5f6e6f761e0..b9d790d2428 100644 --- a/erpnext/translations/de.csv +++ b/erpnext/translations/de.csv @@ -11816,3 +11816,103 @@ will be,wird sein, {} is a child company.,{} ist ein untergeordnetes Unternehmen., {} {} is already linked with another {},{} {} ist bereits mit einem anderen {} verknüpft, {} {} is already linked with {} {},{} {} ist bereits mit {} {} verknüpft, +A Transaction Deletion Document: {0} is triggered for {0},Eine Transaktion Lƶschungsdokument: {0} wird für {0} ausgelƶst, +About Us Settings,"Einstellungen zu ""Über uns""", +Allow Internal Transfers at Arm's Length Price,Interne Übertragungen zum Fremdvergleichspreis zulassen, +Asset decapitalized after Asset Capitalization {0} was submitted,"Vermƶgenswert freigegeben, nachdem Anlagenaktivierung {0} gebucht wurde", +Auto Email Report,Auto Email-Bericht, +Auto close Opportunity Replied after the no. of days mentioned above,Automatische Schließungschaltung antwortete nach der oben genannten Anzahl von Tagen, +Avg Rate (Balance Stock),Durchschnittliche Rate (Lagerbestand), +Billing Interval in Subscription Plan must be Month to follow calendar months,"Abrechnungsintervall im Abonnementplan muss ""Monat"" sein, um Kalendermonate zu folgen", +Bulk Update,Massen-Update, +Can't disable batch wise valuation for active batches.,Sie kƶnnen die chargenweise Bewertung für aktive Chargen nicht deaktivieren., +Can't disable batch wise valuation for items with FIFO valuation method.,Sie kƶnnen die chargenweise Bewertung für Artikel mit FIFO-Bewertungsmethode nicht deaktivieren., +Cannot disable batch wise valuation for FIFO valuation method.,Sie kƶnnen die chargenweise Bewertung für die FIFO-Bewertungsmethode nicht deaktivieren., +Cannot enqueue multi docs for one company. {0} is already queued/running for company: {1},Mehrere Dokumente für ein Unternehmen kƶnnen nicht in die Warteschlange gestellt werden. {0} ist bereits in die Warteschlange gestellt/wird für das Unternehmen ausgeführt: {1}, +Contact Us Settings,Einstellungen zu ā€žKontaktieren Sie unsā€œ, +Create Journal Entries,BuchungssƤtze erstellen, +Create a variant with the template image.,Eine Variante mit dem Vorlagenbild erstellen., +Create in Draft Status,In Entwurfsstatus erstellen, +Custom delimiters,Benutzerdefinierte Trennzeichen, +Deleted Documents,Gelƶschte Dokumente, +Delimiter options,Trennzeichenoptionen, +Dependent Task {0} is not a Template Task,AbhƤngige Aufgabe {0} ist keine Vorlage einer Aufgabe, +Depreciation Entry Posting Status,Buchungsstatus des Abschreibungseintrags, +Depreciation Schedule View,Ansicht Abschreibungsplan, +Depreciation cannot be calculated for fully depreciated assets,Für vollstƤndig abgeschriebene VermƶgensgegenstƤnde kann keine Abschreibung berechnet werden, +Do Not Use Batch-wise Valuation,Keine chargenweise Bewertung verwenden, +Domain Settings,DomƤneneinstellungen, +Email Domain,E-Mail-Domain, +Enable Immutable Ledger,UnverƤnderliches Hauptbuch aktivieren, +Enable it if users want to consider rejected materials to dispatch.,"Aktivieren Sie diese Option, wenn Benutzer zurückgewiesenes Material für den Versand berücksichtigen mƶchten.", +Excess Materials Consumed,Überschüssige Materialien verbraucht, +Excess Transfer,Überschuss-Übertragung, +FIFO Queue vs Qty After Transaction Comparison,Vergleich zwischen FIFO-Warteschlange und Menge nach Transaktion, +"For the {0}, the quantity is required to make the return entry","Für die {0} ist die Menge erforderlich, um die Retoure zu erstellen", +"If enabled, the item rate won't adjust to the valuation rate during internal transfers, but accounting will still use the valuation rate.","Falls aktiviert, wird der Artikelkurs bei internen Transfers nicht an den Bewertungskurs angepasst, aber die Buchhaltung verwendet weiterhin den Wertansatz.", +"If enabled, the system will use the moving average valuation method to calculate the valuation rate for the batched items and will not consider the individual batch-wise incoming rate.","Falls aktiviert, verwendet das System die Bewertungsmethode des gleitenden Durchschnitts zur Berechnung des Wertansatzes für die chargenweisen Artikel und berücksichtigt nicht den individuellen chargenweisen Eingangskurs.", +Job Worker,Unterauftragnehmer, +Job Worker Address,Unterauftragnehmer Adresse, +Job Worker Address Details,Vorschau Adresse Unterauftragnehmer, +Job Worker Contact,Vertrag des Unterauftragnehmers, +Job Worker Delivery Note,Lieferschein des Unterauftragnehmers, +Job Worker Name,Name des Unterauftragnehmer, +Job Worker Warehouse,Lagerhaus des Unterauftragnehmers, +"Learn about Common Party","Erfahren Sie mehr über die Verknüpfung von Kunden und Lieferanten", +Notification,Benachrichtigung, +Notification Settings,Benachrichtigungseinstellungen, +Offsetting for Accounting Dimension,Verrechnung für Buchhaltungsdimension, +Only Include Allocated Payments,Nur zugeordnete Zahlungen einbeziehen, +Only one {0} entry can be created against the Work Order {1},Nur ein {0} Eintrag kann gegen den Arbeitsauftrag {1} erstellt werden, +Over Picking Allowance,Überkommissionierzugabe, +Over Transfer Allowance,Überschlusstransferzugabe, +Overbilling of {} ignored because you have {} role.,"Überhƶhte Abrechnung von {} wurde ignoriert, weil Sie die Rolle {} haben.", +Payment Reconciliation Job: {0} is running for this party. Can't reconcile now.,Job für Zahlungsabgleich: {0} lƤuft für diese Partei. Kann jetzt nicht abgleichen., +Payment Request created from Sales Order or Purchase Order will be in Draft status. When disabled document will be in unsaved state.,"Eine Zahlungsanforderung, die aus einem Auftrag oder einer Bestellung erstellt wurde, wird im Entwurfsstatus sein. Wenn deaktiviert, wird das Dokument in ungespeichertem Zustand sein.", +Payment Request took too long to respond. Please try requesting for payment again.,"Zahlungsaufforderung hat zu lange gedauert, um zu antworten. Bitte versuchen Sie die Zahlung erneut anzufragen.", +Payment Terms Status for Sales Order,Status für Zahlungsbedingungen für AuftrƤge, +Pipeline By,Pipeline von, +Please enable Use Old Serial / Batch Fields to make_bundle,"Bitte aktivieren Sie ""Alte Serien-/Batchfelder verwenden"" für make_bundle", +Print Style,Druckstil, +Reconcile All Serial Nos / Batches,Alle Seriennummern/Chargen abgleichen, +Reset Company Default Values,Standardwerte des Unternehmens zurücksetzen, +Reset Raw Materials Table,Tabelle Rohstoffe zurücksetzen, +Return Against Subcontracting Receipt,Retoure gegen Unterauftragsbeleg, +Return Components,Komponenten zurückgeben, +Returned Against,Zurückgegeben gegen, +Returned exchange rate is neither integer not float.,Der zurückgegebene Wechselkurs ist weder Integer noch Float., +Round Off Tax Amount,Steuerbetrag abrunden, +Rounding Loss Allowance,Rundungsverlusttoleranz, +Rounding Loss Allowance should be between 0 and 1,Rundungsverlusttoleranz muss zwischen 0 und 1 sein, +Row #{0}: Rejected Warehouse is mandatory for the rejected Item {1},Zeile #{0}: Ausschusslager ist für den abgelehnten Artikel {1} obligatorisch, +Row #{0}: You cannot use the inventory dimension '{1}' in Stock Reconciliation to modify the quantity or valuation rate. Stock reconciliation with inventory dimensions is intended solely for performing opening entries.,"Zeile #{0}: Sie kƶnnen die Bestandsdimension '{1}' in der Bestandsabgleich nicht verwenden, um die Menge oder den Wertansatz zu Ƥndern. Die Bestandsabgleich mit Bestandsdimensionen ist ausschließlich für die Durchführung von Erƶffnungsbuchungen vorgesehen.", +Row {0}: Packed Qty must be equal to {1} Qty.,Zeile {0}: Verpackte Menge muss gleich der {1} Menge sein., +Row {0}: Total Number of Depreciations cannot be less than or equal to Opening Number of Booked Depreciations,Zeile {0}: Die Gesamtzahl der Abschreibungen kann nicht kleiner oder gleich der Anzahl der gebuchten Abschreibungen zu Beginn sein, +SCO Supplied Item,Artikel beigestellt für Unterauftrag, +SLA Fulfilled On Status,SLA erfüllt am Status, +SLA will be applied if {1} is set as {2}{3},"SLA wird angewendet, wenn {1} als {2}{3} eingestellt ist", +SMS Settings,SMS-Einstellungen, +SO Total Qty,Kd.-Auftr.-Gesamtmenge, +"Sales Order {0} already exists against Customer's Purchase Order {1}. To allow multiple Sales Orders, Enable {2} in {3}","Auftrag {0} existiert bereits für die Kundenbestellung {1}. Um mehrere VerkaufsauftrƤge zuzulassen, aktivieren Sie {2} in {3}", +"Scorecard variables can be used, as well as: +{total_score} (the total score from that period), +{period_number} (the number of periods to present day) +","Variablen der Bewertung kƶnnen verwendet werden, sowie: +{total_score} (die Gesamtpunktzahl aus diesem Zeitraum), +{period_number} (die Anzahl der ZeitrƤume bis zum heutigen Tag) +", +Select Accounting Dimension.,Buchhaltungsdimension auswƤhlen, +Select Corrective Operation,Nacharbeit auswƤhlen, +Select Date of Birth. This will validate Employees age and prevent hiring of under-age staff.,WƤhlen Sie Geburtsdatum. Damit wird das Alter der Mitarbeiter überprüft und die Einstellung von minderjƤhrigen Mitarbeitern verhindert., +"Select Date of joining. It will have impact on the first salary calculation, Leave allocation on pro-rata bases.",WƤhlen Sie Eintrittsdatum. Es wirkt sich auf die erste Gehaltsberechnung und die Zuteilung von Abwesenheiten auf Pro-rata-Basis aus., +Select Dimension,Dimension auswƤhlen, +Select Items for Quality Inspection,Artikel für die QualitƤtsprüfung auswƤhlen, +Select Job Worker Address,Unterauftragnehmer Adresse auswƤhlen, +Service Expenses,Wartungsaufwand, +Service Level Agreement for {0} {1} already exists.,Service Level Agreement für {0} {1} existiert bereits., +System Settings,Systemverwaltung, +Website Script,Webseiten-Skript, +Website Theme,Webseiten-Thema, +Workflow Action,Workflow-Aktion, +Workflow State,Workflow-Status, +{0} is not running. Cannot trigger events for this Document,{0} lƤuft nicht. Ereignisse für dieses Dokument kƶnnen nicht ausgelƶst werden, From cd5994017c6960d6d357784e111e3e825a8cad18 Mon Sep 17 00:00:00 2001 From: creative-paramu Date: Sun, 30 Jun 2024 20:37:09 +0530 Subject: [PATCH 039/734] fix: bank reconcilation tool cost center company filter adding (cherry picked from commit 6e2ac09821c7a5de93a12dfb4289ec0be9582e8a) --- .../public/js/bank_reconciliation_tool/dialog_manager.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/erpnext/public/js/bank_reconciliation_tool/dialog_manager.js b/erpnext/public/js/bank_reconciliation_tool/dialog_manager.js index 6ff38f91082..1ce6fea0c9e 100644 --- a/erpnext/public/js/bank_reconciliation_tool/dialog_manager.js +++ b/erpnext/public/js/bank_reconciliation_tool/dialog_manager.js @@ -374,6 +374,13 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager { label: "Cost Center", options: "Cost Center", depends_on: "eval:doc.action=='Create Voucher' && doc.document_type=='Payment Entry'", + get_query: () => { + return { + filters: { + company: this.company, + }, + }; + }, }, { fieldtype: "Section Break", From b96361e837d1303d42b22cf7a29910c9bf2d2078 Mon Sep 17 00:00:00 2001 From: Parameshwari Palanisamy <101092028+creative-paramu@users.noreply.github.com> Date: Tue, 13 Aug 2024 10:15:37 +0530 Subject: [PATCH 040/734] refactor: update dialog_manager.js (cherry picked from commit 6d19aae423c9684740a2b08ddcbc20f13da6e849) --- erpnext/public/js/bank_reconciliation_tool/dialog_manager.js | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/public/js/bank_reconciliation_tool/dialog_manager.js b/erpnext/public/js/bank_reconciliation_tool/dialog_manager.js index 1ce6fea0c9e..bab25b6eca2 100644 --- a/erpnext/public/js/bank_reconciliation_tool/dialog_manager.js +++ b/erpnext/public/js/bank_reconciliation_tool/dialog_manager.js @@ -377,6 +377,7 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager { get_query: () => { return { filters: { + is_group: 0, company: this.company, }, }; From f547befeb9f366311f2d9c874498193745d825c3 Mon Sep 17 00:00:00 2001 From: Nihantra Patel Date: Thu, 18 Jul 2024 12:14:53 +0530 Subject: [PATCH 041/734] fix: Create Sales Order from Quotation for Prospect (cherry picked from commit 2f63fae31da75425f01f5c7c8d57d72514b3eb9f) # Conflicts: # erpnext/selling/doctype/quotation/quotation.py --- erpnext/crm/doctype/prospect/test_prospect.py | 20 ++++++ .../selling/doctype/customer/customer.json | 9 +++ erpnext/selling/doctype/customer/customer.py | 1 + .../selling/doctype/quotation/quotation.py | 61 +++++++++++++++++++ 4 files changed, 91 insertions(+) diff --git a/erpnext/crm/doctype/prospect/test_prospect.py b/erpnext/crm/doctype/prospect/test_prospect.py index c3930ee6c93..286b91e2086 100644 --- a/erpnext/crm/doctype/prospect/test_prospect.py +++ b/erpnext/crm/doctype/prospect/test_prospect.py @@ -27,6 +27,26 @@ class TestProspect(unittest.TestCase): address_doc.reload() self.assertEqual(address_doc.has_link("Prospect", prospect_doc.name), True) + def test_make_customer_from_prospect(self): + from erpnext.crm.doctype.prospect.prospect import make_customer as make_customer_from_prospect + + frappe.delete_doc_if_exists("Customer", "_Test Prospect") + + prospect = frappe.get_doc({ + "doctype": "Prospect", + "company_name": "_Test Prospect", + "customer_group": "_Test Customer Group", + }) + prospect.insert() + + customer = make_customer_from_prospect("_Test Prospect") + + self.assertEqual(customer.doctype, "Customer") + self.assertEqual(customer.company_name, "_Test Prospect") + self.assertEqual(customer.customer_group, "_Test Customer Group") + + customer.company = "_Test Company" + customer.insert() def make_prospect(**args): args = frappe._dict(args) diff --git a/erpnext/selling/doctype/customer/customer.json b/erpnext/selling/doctype/customer/customer.json index af32f4ecf84..59f082431b8 100644 --- a/erpnext/selling/doctype/customer/customer.json +++ b/erpnext/selling/doctype/customer/customer.json @@ -21,6 +21,7 @@ "gender", "lead_name", "opportunity_name", + "prospect_name", "account_manager", "image", "defaults_tab", @@ -570,6 +571,14 @@ { "fieldname": "column_break_nwor", "fieldtype": "Column Break" + }, + { + "fieldname": "prospect_name", + "fieldtype": "Link", + "label": "From Prospect", + "no_copy": 1, + "options": "Prospect", + "print_hide": 1 } ], "icon": "fa fa-user", diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index 71f0022df14..9bd9b5e760a 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -77,6 +77,7 @@ class Customer(TransactionBase): payment_terms: DF.Link | None portal_users: DF.Table[PortalUser] primary_address: DF.Text | None + prospect_name: DF.Link | None represents_company: DF.Link | None sales_team: DF.Table[SalesTeam] salutation: DF.Link | None diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index f9442204df1..f48f8a88712 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -516,6 +516,7 @@ def _make_customer(source_name, ignore_permissions=False, customer_group=None): if not customer_name: from erpnext.crm.doctype.lead.lead import _make_customer +<<<<<<< HEAD customer_doclist = _make_customer(lead_name, ignore_permissions=ignore_permissions) customer = frappe.get_doc(customer_doclist) customer.flags.ignore_permissions = ignore_permissions @@ -535,6 +536,25 @@ def _make_customer(source_name, ignore_permissions=False, customer_group=None): except frappe.MandatoryError as e: mandatory_fields = e.args[0].split(":")[1].split(",") mandatory_fields = [customer.meta.get_label(field.strip()) for field in mandatory_fields] +======= + # Check if a Customer already exists for the Lead or Prospect. + existing_customer = None + if quotation.quotation_to == "Lead": + existing_customer = frappe.db.get_value("Customer", {"lead_name": quotation.party_name}) + elif quotation.quotation_to == "Prospect": + existing_customer = frappe.db.get_value("Customer", {"prospect_name": quotation.party_name}) + + if existing_customer: + return frappe.get_doc("Customer", existing_customer) + + # If no Customer exists, create a new Customer or Prospect. + if quotation.quotation_to == "Lead": + return create_customer_from_lead(quotation.party_name, ignore_permissions=ignore_permissions) + elif quotation.quotation_to == "Prospect": + return create_customer_from_prospect(quotation.party_name, ignore_permissions=ignore_permissions) + + return None +>>>>>>> 2f63fae31d (fix: Create Sales Order from Quotation for Prospect) frappe.local.message_log = [] lead_link = frappe.utils.get_link_to_form("Lead", lead_name) @@ -545,8 +565,49 @@ def _make_customer(source_name, ignore_permissions=False, customer_group=None): message += "
  • " + "
  • ".join(mandatory_fields) + "
" message += _("Please create Customer from Lead {0}.").format(lead_link) +<<<<<<< HEAD frappe.throw(message, title=_("Mandatory Missing")) else: return customer_name else: return frappe.get_doc("Customer", quotation.get("party_name")) +======= +def create_customer_from_lead(lead_name, ignore_permissions=False): + from erpnext.crm.doctype.lead.lead import _make_customer + + customer = _make_customer(lead_name, ignore_permissions=ignore_permissions) + customer.flags.ignore_permissions = ignore_permissions + + try: + customer.insert() + return customer + except frappe.MandatoryError as e: + handle_mandatory_error(e, customer, lead_name) + + +def create_customer_from_prospect(prospect_name, ignore_permissions=False): + from erpnext.crm.doctype.prospect.prospect import make_customer as make_customer_from_prospect + + customer = make_customer_from_prospect(prospect_name) + customer.flags.ignore_permissions = ignore_permissions + + try: + customer.insert() + return customer + except frappe.MandatoryError as e: + handle_mandatory_error(e, customer, prospect_name) + + +def handle_mandatory_error(e, customer, lead_name): + from frappe.utils import get_link_to_form + + mandatory_fields = e.args[0].split(":")[1].split(",") + mandatory_fields = [customer.meta.get_label(field.strip()) for field in mandatory_fields] + + frappe.local.message_log = [] + message = _("Could not auto create Customer due to the following missing mandatory field(s):") + "
" + message += "
  • " + "
  • ".join(mandatory_fields) + "
" + message += _("Please create Customer from Lead {0}.").format(get_link_to_form("Lead", lead_name)) + + frappe.throw(message, title=_("Mandatory Missing")) +>>>>>>> 2f63fae31d (fix: Create Sales Order from Quotation for Prospect) From 549dc286d03a7ae267b5e2742704dd9f023ee363 Mon Sep 17 00:00:00 2001 From: Nihantra Patel Date: Thu, 18 Jul 2024 12:24:52 +0530 Subject: [PATCH 042/734] fix: update the testcase format (cherry picked from commit 29d50b770e3d8de745a75cecbf18c0d745c4de6f) --- erpnext/crm/doctype/prospect/test_prospect.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/erpnext/crm/doctype/prospect/test_prospect.py b/erpnext/crm/doctype/prospect/test_prospect.py index 286b91e2086..e89bade19a1 100644 --- a/erpnext/crm/doctype/prospect/test_prospect.py +++ b/erpnext/crm/doctype/prospect/test_prospect.py @@ -32,11 +32,13 @@ class TestProspect(unittest.TestCase): frappe.delete_doc_if_exists("Customer", "_Test Prospect") - prospect = frappe.get_doc({ - "doctype": "Prospect", - "company_name": "_Test Prospect", - "customer_group": "_Test Customer Group", - }) + prospect = frappe.get_doc( + { + "doctype": "Prospect", + "company_name": "_Test Prospect", + "customer_group": "_Test Customer Group", + } + ) prospect.insert() customer = make_customer_from_prospect("_Test Prospect") From 33542cb9097751492204d6ec3b0fe3573dbddf36 Mon Sep 17 00:00:00 2001 From: Nihantra Patel Date: Thu, 18 Jul 2024 12:26:23 +0530 Subject: [PATCH 043/734] fix: update the testcase format (cherry picked from commit ee44022249c39bc17d9cbf29c854c64c41f9a911) --- erpnext/crm/doctype/prospect/test_prospect.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/crm/doctype/prospect/test_prospect.py b/erpnext/crm/doctype/prospect/test_prospect.py index e89bade19a1..47e38515cac 100644 --- a/erpnext/crm/doctype/prospect/test_prospect.py +++ b/erpnext/crm/doctype/prospect/test_prospect.py @@ -50,6 +50,7 @@ class TestProspect(unittest.TestCase): customer.company = "_Test Company" customer.insert() + def make_prospect(**args): args = frappe._dict(args) From 5d7fb1d9457c276d6bd91c66546f001f56fdb728 Mon Sep 17 00:00:00 2001 From: "Nihantra C. Patel" <141945075+Nihantra-Patel@users.noreply.github.com> Date: Tue, 20 Aug 2024 11:31:49 +0530 Subject: [PATCH 044/734] fix: create SO from Quot for Prospect --conflicts --- .../selling/doctype/quotation/quotation.py | 63 ++++--------------- 1 file changed, 12 insertions(+), 51 deletions(-) diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index f48f8a88712..bf2c166069f 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -347,8 +347,8 @@ def make_sales_order(source_name: str, target_doc=None): return _make_sales_order(source_name, target_doc) -def _make_sales_order(source_name, target_doc=None, customer_group=None, ignore_permissions=False): - customer = _make_customer(source_name, ignore_permissions, customer_group) +def _make_sales_order(source_name, target_doc=None, ignore_permissions=False): + customer = _make_customer(source_name, ignore_permissions) ordered_items = frappe._dict( frappe.db.get_all( "Sales Order Item", @@ -502,41 +502,17 @@ def _make_sales_invoice(source_name, target_doc=None, ignore_permissions=False): return doclist -def _make_customer(source_name, ignore_permissions=False, customer_group=None): +def _make_customer(source_name, ignore_permissions=False): quotation = frappe.db.get_value( - "Quotation", source_name, ["order_type", "party_name", "customer_name"], as_dict=1 + "Quotation", + source_name, + ["order_type", "quotation_to", "party_name", "customer_name"], + as_dict=1, ) - if quotation and quotation.get("party_name"): - if not frappe.db.exists("Customer", quotation.get("party_name")): - lead_name = quotation.get("party_name") - customer_name = frappe.db.get_value( - "Customer", {"lead_name": lead_name}, ["name", "customer_name"], as_dict=True - ) - if not customer_name: - from erpnext.crm.doctype.lead.lead import _make_customer + if quotation.quotation_to == "Customer": + return frappe.get_doc("Customer", quotation.party_name) -<<<<<<< HEAD - customer_doclist = _make_customer(lead_name, ignore_permissions=ignore_permissions) - customer = frappe.get_doc(customer_doclist) - customer.flags.ignore_permissions = ignore_permissions - customer.customer_group = customer_group - - try: - customer.insert() - return customer - except frappe.NameError: - if frappe.defaults.get_global_default("cust_master_name") == "Customer Name": - customer.run_method("autoname") - customer.name += "-" + lead_name - customer.insert() - return customer - else: - raise - except frappe.MandatoryError as e: - mandatory_fields = e.args[0].split(":")[1].split(",") - mandatory_fields = [customer.meta.get_label(field.strip()) for field in mandatory_fields] -======= # Check if a Customer already exists for the Lead or Prospect. existing_customer = None if quotation.quotation_to == "Lead": @@ -554,24 +530,8 @@ def _make_customer(source_name, ignore_permissions=False, customer_group=None): return create_customer_from_prospect(quotation.party_name, ignore_permissions=ignore_permissions) return None ->>>>>>> 2f63fae31d (fix: Create Sales Order from Quotation for Prospect) - frappe.local.message_log = [] - lead_link = frappe.utils.get_link_to_form("Lead", lead_name) - message = ( - _("Could not auto create Customer due to the following missing mandatory field(s):") - + "
" - ) - message += "
  • " + "
  • ".join(mandatory_fields) + "
" - message += _("Please create Customer from Lead {0}.").format(lead_link) -<<<<<<< HEAD - frappe.throw(message, title=_("Mandatory Missing")) - else: - return customer_name - else: - return frappe.get_doc("Customer", quotation.get("party_name")) -======= def create_customer_from_lead(lead_name, ignore_permissions=False): from erpnext.crm.doctype.lead.lead import _make_customer @@ -605,9 +565,10 @@ def handle_mandatory_error(e, customer, lead_name): mandatory_fields = [customer.meta.get_label(field.strip()) for field in mandatory_fields] frappe.local.message_log = [] - message = _("Could not auto create Customer due to the following missing mandatory field(s):") + "
" + message = ( + _("Could not auto create Customer due to the following missing mandatory field(s):") + "
" + ) message += "
  • " + "
  • ".join(mandatory_fields) + "
" message += _("Please create Customer from Lead {0}.").format(get_link_to_form("Lead", lead_name)) frappe.throw(message, title=_("Mandatory Missing")) ->>>>>>> 2f63fae31d (fix: Create Sales Order from Quotation for Prospect) From ec0201cb85f0c46a92993c6082b51507091dffd9 Mon Sep 17 00:00:00 2001 From: "Nihantra C. Patel" <141945075+Nihantra-Patel@users.noreply.github.com> Date: Tue, 20 Aug 2024 11:34:27 +0530 Subject: [PATCH 045/734] fix: create SO from Quot for Prospect --conflicts --- erpnext/selling/doctype/quotation/quotation.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index bf2c166069f..2a4b04b9db5 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -565,9 +565,7 @@ def handle_mandatory_error(e, customer, lead_name): mandatory_fields = [customer.meta.get_label(field.strip()) for field in mandatory_fields] frappe.local.message_log = [] - message = ( - _("Could not auto create Customer due to the following missing mandatory field(s):") + "
" - ) + message = _("Could not auto create Customer due to the following missing mandatory field(s):") + "
" message += "
  • " + "
  • ".join(mandatory_fields) + "
" message += _("Please create Customer from Lead {0}.").format(get_link_to_form("Lead", lead_name)) From 1040198ce1a4077ec88d9f36adb7db792779e2ab Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Mon, 19 Aug 2024 17:24:31 +0530 Subject: [PATCH 046/734] perf: asset creation from purchase receipt (cherry picked from commit 6e84fc5143f8bbc36babbd608aff161f65550bc5) --- erpnext/assets/doctype/asset/asset.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 76eeca03ab5..db37e54ada8 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -904,7 +904,7 @@ def transfer_asset(args): @frappe.whitelist() def get_item_details(item_code, asset_category, gross_purchase_amount): - asset_category_doc = frappe.get_doc("Asset Category", asset_category) + asset_category_doc = frappe.get_cached_doc("Asset Category", asset_category) books = [] for d in asset_category_doc.finance_books: books.append( From ae6c1a30ac0f8d99de5d4a3868ca8c328fcd1e46 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 20 Aug 2024 15:03:23 +0530 Subject: [PATCH 047/734] refactor: brand js and allow quick entry (backport #42829) (#42830) * refactor: brand js and allow quick entry (#42829) * refactor: brand js and allow quick entry * refactor: brand js and allow quick entry --prettier * refactor: brand js and allow quick entry --prettier (cherry picked from commit a1183f01652567dc4d7fdfa250dfd428cfe1dcda) # Conflicts: # erpnext/setup/doctype/brand/brand.json * refactor: brand js and allow quick entry --refactor --------- Co-authored-by: Nihantra C. Patel <141945075+Nihantra-Patel@users.noreply.github.com> --- erpnext/setup/doctype/brand/brand.js | 56 +++++++------------------- erpnext/setup/doctype/brand/brand.json | 5 ++- 2 files changed, 17 insertions(+), 44 deletions(-) diff --git a/erpnext/setup/doctype/brand/brand.js b/erpnext/setup/doctype/brand/brand.js index 5d16d734e0f..99b4ace6cd2 100644 --- a/erpnext/setup/doctype/brand/brand.js +++ b/erpnext/setup/doctype/brand/brand.js @@ -3,22 +3,14 @@ frappe.ui.form.on("Brand", { setup: (frm) => { - frm.fields_dict["brand_defaults"].grid.get_field("default_warehouse").get_query = function ( - doc, - cdt, - cdn - ) { + frm.set_query("default_warehouse", "brand_defaults", function (doc, cdt, cdn) { const row = locals[cdt][cdn]; return { filters: { company: row.company }, }; - }; + }); - frm.fields_dict["brand_defaults"].grid.get_field("default_discount_account").get_query = function ( - doc, - cdt, - cdn - ) { + frm.set_query("default_discount_account", "brand_defaults", function (doc, cdt, cdn) { const row = locals[cdt][cdn]; return { filters: { @@ -27,13 +19,9 @@ frappe.ui.form.on("Brand", { is_group: 0, }, }; - }; + }); - frm.fields_dict["brand_defaults"].grid.get_field("buying_cost_center").get_query = function ( - doc, - cdt, - cdn - ) { + frm.set_query("buying_cost_center", "brand_defaults", function (doc, cdt, cdn) { const row = locals[cdt][cdn]; return { filters: { @@ -41,25 +29,17 @@ frappe.ui.form.on("Brand", { company: row.company, }, }; - }; + }); - frm.fields_dict["brand_defaults"].grid.get_field("expense_account").get_query = function ( - doc, - cdt, - cdn - ) { + frm.set_query("expense_account", "brand_defaults", function (doc, cdt, cdn) { const row = locals[cdt][cdn]; return { query: "erpnext.controllers.queries.get_expense_account", filters: { company: row.company }, }; - }; + }); - frm.fields_dict["brand_defaults"].grid.get_field("default_provisional_account").get_query = function ( - doc, - cdt, - cdn - ) { + frm.set_query("default_provisional_account", "brand_defaults", function (doc, cdt, cdn) { const row = locals[cdt][cdn]; return { filters: { @@ -68,13 +48,9 @@ frappe.ui.form.on("Brand", { is_group: 0, }, }; - }; + }); - frm.fields_dict["brand_defaults"].grid.get_field("selling_cost_center").get_query = function ( - doc, - cdt, - cdn - ) { + frm.set_query("selling_cost_center", "brand_defaults", function (doc, cdt, cdn) { const row = locals[cdt][cdn]; return { filters: { @@ -82,18 +58,14 @@ frappe.ui.form.on("Brand", { company: row.company, }, }; - }; + }); - frm.fields_dict["brand_defaults"].grid.get_field("income_account").get_query = function ( - doc, - cdt, - cdn - ) { + frm.set_query("income_account", "brand_defaults", function (doc, cdt, cdn) { const row = locals[cdt][cdn]; return { query: "erpnext.controllers.queries.get_income_account", filters: { company: row.company }, }; - }; + }); }, }); diff --git a/erpnext/setup/doctype/brand/brand.json b/erpnext/setup/doctype/brand/brand.json index 45b4db81f1f..8511d221551 100644 --- a/erpnext/setup/doctype/brand/brand.json +++ b/erpnext/setup/doctype/brand/brand.json @@ -56,10 +56,11 @@ "idx": 1, "image_field": "image", "links": [], - "modified": "2021-03-01 15:57:30.005783", + "modified": "2024-08-20 14:10:21.377962", "modified_by": "Administrator", "module": "Setup", "name": "Brand", + "naming_rule": "By fieldname", "owner": "Administrator", "permissions": [ { @@ -108,4 +109,4 @@ "show_name_in_global_search": 1, "sort_field": "modified", "sort_order": "ASC" -} \ No newline at end of file +} From 28c9f2adab218368001e22f9edf66af19d8a8c70 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 21 Aug 2024 05:22:44 +0000 Subject: [PATCH 048/734] chore(release): Bumped to Version 15.33.3 ## [15.33.3](https://github.com/frappe/erpnext/compare/v15.33.2...v15.33.3) (2024-08-21) ### Bug Fixes * Auto Create Serial and Batch Bundle For Outward (backport [#42778](https://github.com/frappe/erpnext/issues/42778)) ([#42792](https://github.com/frappe/erpnext/issues/42792)) ([7cc7179](https://github.com/frappe/erpnext/commit/7cc7179b050edd87cda6519fd76c0e83a31aa909)) * backport german translations from develop ([9e9de4c](https://github.com/frappe/erpnext/commit/9e9de4c99e9f628469f93442c68a2fe8e69002fd)) * bank reconcilation tool cost center company filter adding ([cd59940](https://github.com/frappe/erpnext/commit/cd5994017c6960d6d357784e111e3e825a8cad18)) * Create Sales Order from Quotation for Prospect ([f547bef](https://github.com/frappe/erpnext/commit/f547befeb9f366311f2d9c874498193745d825c3)) * create SO from Quot for Prospect --conflicts ([ec0201c](https://github.com/frappe/erpnext/commit/ec0201cb85f0c46a92993c6082b51507091dffd9)) * create SO from Quot for Prospect --conflicts ([5d7fb1d](https://github.com/frappe/erpnext/commit/5d7fb1d9457c276d6bd91c66546f001f56fdb728)) * disable rename from warehouse ([3a1ad6e](https://github.com/frappe/erpnext/commit/3a1ad6e844e00f6ef12e56404c2efe1575add24b)) * disable rename from warehouse ([40abd82](https://github.com/frappe/erpnext/commit/40abd82e2dd8308a686484b77deec714cea0d05a)) * dropping index to improve performance (backport [#42820](https://github.com/frappe/erpnext/issues/42820)) ([#42821](https://github.com/frappe/erpnext/issues/42821)) ([b24de3e](https://github.com/frappe/erpnext/commit/b24de3e35b5a9eb34390c866fde35dc9b4ea1fe7)) * german translations ([751c209](https://github.com/frappe/erpnext/commit/751c20984f3e05944acdaa2ba73a7506e89badac)) * german translations of "HR" ([6f7fdbe](https://github.com/frappe/erpnext/commit/6f7fdbefac26b8098141880d3f3ea7e74a1b782c)) * ignore pricing rule while making DN from Pick List (backport [#42763](https://github.com/frappe/erpnext/issues/42763)) ([#42768](https://github.com/frappe/erpnext/issues/42768)) ([aba54ba](https://github.com/frappe/erpnext/commit/aba54ba18feadade01f9ff9d2150259c15bfc13e)) * not able to create the batch (backport [#42784](https://github.com/frappe/erpnext/issues/42784)) ([#42785](https://github.com/frappe/erpnext/issues/42785)) ([0f9849e](https://github.com/frappe/erpnext/commit/0f9849e6727b4fef3e3bf75048de553714b08298)) * **patch:** replace repost with direct sql to update 'against_voucher ([e420fa9](https://github.com/frappe/erpnext/commit/e420fa97799ad27a7ddb97c604319774b39e0aca)) * removed extra filter condition ([b84ca04](https://github.com/frappe/erpnext/commit/b84ca04975f5daf691c04870409d3c24dc1cc9d2)) * set up filters for dimensions ([abb8866](https://github.com/frappe/erpnext/commit/abb88662c1ac89dccc42aa3ff3d9825c062a3fa3)) * translatability of boldened text ([4914481](https://github.com/frappe/erpnext/commit/4914481105bba3a665b63784e4b48c03342e3270)) * update the testcase format ([33542cb](https://github.com/frappe/erpnext/commit/33542cb9097751492204d6ec3b0fe3573dbddf36)) * update the testcase format ([549dc28](https://github.com/frappe/erpnext/commit/549dc286d03a7ae267b5e2742704dd9f023ee363)) ### Performance Improvements * asset creation from purchase receipt ([1040198](https://github.com/frappe/erpnext/commit/1040198ce1a4077ec88d9f36adb7db792779e2ab)) * data import for stock entries (backport [#42711](https://github.com/frappe/erpnext/issues/42711)) ([#42819](https://github.com/frappe/erpnext/issues/42819)) ([0344442](https://github.com/frappe/erpnext/commit/0344442d42cbdbe8db2401f1c35d18f13674f786)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index c31f06dfa79..4a3021bda07 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "15.33.2" +__version__ = "15.33.3" def get_default_company(user=None): From 2203ea9301eea01714809811ec8f774ebbc59b1e Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 21 Aug 2024 15:20:50 +0530 Subject: [PATCH 049/734] fix: last purchase rate not updated from purchase invoice (backport #42847) (#42853) fix: last purchase rate not updated from purchase invoice (#42847) (cherry picked from commit 5b9309cf3410a4be0e94682ca6151bcc0b4ccb53) Co-authored-by: rohitwaghchaure --- erpnext/controllers/buying_controller.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index a55eded2a4c..f797374d64e 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -689,9 +689,11 @@ class BuyingController(SubcontractingController): if self.doctype in ["Purchase Receipt", "Purchase Invoice"]: self.process_fixed_asset() - if self.doctype in ["Purchase Order", "Purchase Receipt"] and not frappe.db.get_single_value( - "Buying Settings", "disable_last_purchase_rate" - ): + if self.doctype in [ + "Purchase Order", + "Purchase Receipt", + "Purchase Invoice", + ] and not frappe.db.get_single_value("Buying Settings", "disable_last_purchase_rate"): update_last_purchase_rate(self, is_submit=1) def on_cancel(self): From 72c16097d64384a4218e7646e2c4efcdc0790d82 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 21 Aug 2024 16:12:33 +0530 Subject: [PATCH 050/734] fix: incorrect Received Qty Amount in Purchase Order Analysis (backport #42852) (#42854) fix: incorrect Received Qty Amount in Purchase Order Analysis (#42852) (cherry picked from commit fb846ffa125cbc67f775a35376d0b541b1f5d9d1) Co-authored-by: rohitwaghchaure --- .../purchase_order_analysis/purchase_order_analysis.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py index da1c70d3179..084c3b5fc2b 100644 --- a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py +++ b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py @@ -40,6 +40,7 @@ def get_data(filters): po = frappe.qb.DocType("Purchase Order") po_item = frappe.qb.DocType("Purchase Order Item") pi_item = frappe.qb.DocType("Purchase Invoice Item") + pr_item = frappe.qb.DocType("Purchase Receipt Item") query = ( frappe.qb.from_(po) @@ -47,6 +48,8 @@ def get_data(filters): .on(po_item.parent == po.name) .left_join(pi_item) .on((pi_item.po_detail == po_item.name) & (pi_item.docstatus == 1)) + .left_join(pr_item) + .on((pr_item.purchase_order_item == po_item.name) & (pr_item.docstatus == 1)) .select( po.transaction_date.as_("date"), po_item.schedule_date.as_("required_date"), @@ -60,7 +63,7 @@ def get_data(filters): (po_item.qty - po_item.received_qty).as_("pending_qty"), Sum(IfNull(pi_item.qty, 0)).as_("billed_qty"), po_item.base_amount.as_("amount"), - (po_item.received_qty * po_item.base_rate).as_("received_qty_amount"), + (pr_item.base_amount).as_("received_qty_amount"), (po_item.billed_amt * IfNull(po.conversion_rate, 1)).as_("billed_amount"), (po_item.base_amount - (po_item.billed_amt * IfNull(po.conversion_rate, 1))).as_( "pending_amount" From 9f4cb98de65bd050a3ed846c9c6c67b248202714 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 21 Aug 2024 16:37:00 +0530 Subject: [PATCH 051/734] fix: last purchase rate not updated from purchase invoice (backport #42847) (backport #42853) (#42855) fix: last purchase rate not updated from purchase invoice (backport #42847) (#42853) fix: last purchase rate not updated from purchase invoice (#42847) (cherry picked from commit 5b9309cf3410a4be0e94682ca6151bcc0b4ccb53) Co-authored-by: rohitwaghchaure (cherry picked from commit 2203ea9301eea01714809811ec8f774ebbc59b1e) Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- erpnext/controllers/buying_controller.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index a55eded2a4c..f797374d64e 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -689,9 +689,11 @@ class BuyingController(SubcontractingController): if self.doctype in ["Purchase Receipt", "Purchase Invoice"]: self.process_fixed_asset() - if self.doctype in ["Purchase Order", "Purchase Receipt"] and not frappe.db.get_single_value( - "Buying Settings", "disable_last_purchase_rate" - ): + if self.doctype in [ + "Purchase Order", + "Purchase Receipt", + "Purchase Invoice", + ] and not frappe.db.get_single_value("Buying Settings", "disable_last_purchase_rate"): update_last_purchase_rate(self, is_submit=1) def on_cancel(self): From 8d8d84bae46ace28d05b79bee58a472360211dc9 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 21 Aug 2024 16:37:08 +0530 Subject: [PATCH 052/734] fix: incorrect Received Qty Amount in Purchase Order Analysis (backport #42852) (backport #42854) (#42856) fix: incorrect Received Qty Amount in Purchase Order Analysis (backport #42852) (#42854) fix: incorrect Received Qty Amount in Purchase Order Analysis (#42852) (cherry picked from commit fb846ffa125cbc67f775a35376d0b541b1f5d9d1) Co-authored-by: rohitwaghchaure (cherry picked from commit 72c16097d64384a4218e7646e2c4efcdc0790d82) Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- .../purchase_order_analysis/purchase_order_analysis.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py index da1c70d3179..084c3b5fc2b 100644 --- a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py +++ b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py @@ -40,6 +40,7 @@ def get_data(filters): po = frappe.qb.DocType("Purchase Order") po_item = frappe.qb.DocType("Purchase Order Item") pi_item = frappe.qb.DocType("Purchase Invoice Item") + pr_item = frappe.qb.DocType("Purchase Receipt Item") query = ( frappe.qb.from_(po) @@ -47,6 +48,8 @@ def get_data(filters): .on(po_item.parent == po.name) .left_join(pi_item) .on((pi_item.po_detail == po_item.name) & (pi_item.docstatus == 1)) + .left_join(pr_item) + .on((pr_item.purchase_order_item == po_item.name) & (pr_item.docstatus == 1)) .select( po.transaction_date.as_("date"), po_item.schedule_date.as_("required_date"), @@ -60,7 +63,7 @@ def get_data(filters): (po_item.qty - po_item.received_qty).as_("pending_qty"), Sum(IfNull(pi_item.qty, 0)).as_("billed_qty"), po_item.base_amount.as_("amount"), - (po_item.received_qty * po_item.base_rate).as_("received_qty_amount"), + (pr_item.base_amount).as_("received_qty_amount"), (po_item.billed_amt * IfNull(po.conversion_rate, 1)).as_("billed_amount"), (po_item.base_amount - (po_item.billed_amt * IfNull(po.conversion_rate, 1))).as_( "pending_amount" From 9ac665b4bdd4cde13b8b2a87198019b530adefab Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 21 Aug 2024 11:08:23 +0000 Subject: [PATCH 053/734] chore(release): Bumped to Version 15.33.4 ## [15.33.4](https://github.com/frappe/erpnext/compare/v15.33.3...v15.33.4) (2024-08-21) ### Bug Fixes * incorrect Received Qty Amount in Purchase Order Analysis (backport [#42852](https://github.com/frappe/erpnext/issues/42852)) (backport [#42854](https://github.com/frappe/erpnext/issues/42854)) ([#42856](https://github.com/frappe/erpnext/issues/42856)) ([8d8d84b](https://github.com/frappe/erpnext/commit/8d8d84bae46ace28d05b79bee58a472360211dc9)) * last purchase rate not updated from purchase invoice (backport [#42847](https://github.com/frappe/erpnext/issues/42847)) (backport [#42853](https://github.com/frappe/erpnext/issues/42853)) ([#42855](https://github.com/frappe/erpnext/issues/42855)) ([9f4cb98](https://github.com/frappe/erpnext/commit/9f4cb98de65bd050a3ed846c9c6c67b248202714)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 4a3021bda07..ecf8221f778 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "15.33.3" +__version__ = "15.33.4" def get_default_company(user=None): From 831e2aaf1840a2350249eefecb10ea29e7567908 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 21 Aug 2024 16:46:05 +0530 Subject: [PATCH 054/734] fix: custom stock entry type issue (backport #42835) (#42846) * fix: custom stock entry type issue (#42835) (cherry picked from commit 9c82c2b5d3bf790ba87a672d1b87e9efff8bc010) # Conflicts: # erpnext/stock/doctype/stock_entry_type/stock_entry_type.py * chore: fix conflicts * chore: fix linters issue --------- Co-authored-by: rohitwaghchaure --- erpnext/patches.txt | 1 + .../v15_0/set_standard_stock_entry_type.py | 16 ++++++++++ .../operations/install_fixtures.py | 32 ++++++++++++++++--- .../stock/doctype/stock_entry/stock_entry.py | 2 +- .../stock_entry_type/stock_entry_type.json | 12 +++++-- .../stock_entry_type/stock_entry_type.py | 17 +++++++++- .../stock_entry_type/test_stock_entry_type.py | 29 ++++++++++++++++- 7 files changed, 100 insertions(+), 9 deletions(-) create mode 100644 erpnext/patches/v15_0/set_standard_stock_entry_type.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index bece96d64e8..31ba6b23c01 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -372,3 +372,4 @@ erpnext.patches.v15_0.update_asset_repair_field_in_stock_entry erpnext.patches.v15_0.update_total_number_of_booked_depreciations erpnext.patches.v15_0.do_not_use_batchwise_valuation erpnext.patches.v15_0.drop_index_posting_datetime_from_sle +erpnext.patches.v15_0.set_standard_stock_entry_type diff --git a/erpnext/patches/v15_0/set_standard_stock_entry_type.py b/erpnext/patches/v15_0/set_standard_stock_entry_type.py new file mode 100644 index 00000000000..7551b0d4afc --- /dev/null +++ b/erpnext/patches/v15_0/set_standard_stock_entry_type.py @@ -0,0 +1,16 @@ +import frappe + + +def execute(): + for stock_entry_type in [ + "Material Issue", + "Material Receipt", + "Material Transfer", + "Material Transfer for Manufacture", + "Material Consumption for Manufacture", + "Manufacture", + "Repack", + "Send to Subcontractor", + ]: + if frappe.db.exists("Stock Entry Type", stock_entry_type): + frappe.db.set_value("Stock Entry Type", stock_entry_type, "is_standard", 1) diff --git a/erpnext/setup/setup_wizard/operations/install_fixtures.py b/erpnext/setup/setup_wizard/operations/install_fixtures.py index 0bcb9fb6019..a99289416ba 100644 --- a/erpnext/setup/setup_wizard/operations/install_fixtures.py +++ b/erpnext/setup/setup_wizard/operations/install_fixtures.py @@ -66,29 +66,53 @@ def install(country=None): "parent_item_group": _("All Item Groups"), }, # Stock Entry Type - {"doctype": "Stock Entry Type", "name": "Material Issue", "purpose": "Material Issue"}, - {"doctype": "Stock Entry Type", "name": "Material Receipt", "purpose": "Material Receipt"}, + { + "doctype": "Stock Entry Type", + "name": "Material Issue", + "purpose": "Material Issue", + "is_standard": 1, + }, + { + "doctype": "Stock Entry Type", + "name": "Material Receipt", + "purpose": "Material Receipt", + "is_standard": 1, + }, { "doctype": "Stock Entry Type", "name": "Material Transfer", "purpose": "Material Transfer", + "is_standard": 1, + }, + { + "doctype": "Stock Entry Type", + "name": "Manufacture", + "purpose": "Manufacture", + "is_standard": 1, + }, + { + "doctype": "Stock Entry Type", + "name": "Repack", + "purpose": "Repack", + "is_standard": 1, }, - {"doctype": "Stock Entry Type", "name": "Manufacture", "purpose": "Manufacture"}, - {"doctype": "Stock Entry Type", "name": "Repack", "purpose": "Repack"}, { "doctype": "Stock Entry Type", "name": "Send to Subcontractor", "purpose": "Send to Subcontractor", + "is_standard": 1, }, { "doctype": "Stock Entry Type", "name": "Material Transfer for Manufacture", "purpose": "Material Transfer for Manufacture", + "is_standard": 1, }, { "doctype": "Stock Entry Type", "name": "Material Consumption for Manufacture", "purpose": "Material Consumption for Manufacture", + "is_standard": 1, }, # territory: with two default territories, one for home country and one named Rest of the World { diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 83ae8d09f66..6b4164dee5f 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -983,7 +983,7 @@ class StockEntry(StockController): def set_stock_entry_type(self): if self.purpose: self.stock_entry_type = frappe.get_cached_value( - "Stock Entry Type", {"purpose": self.purpose}, "name" + "Stock Entry Type", {"purpose": self.purpose, "is_standard": 1}, "name" ) def set_purpose_for_stock_entry(self): diff --git a/erpnext/stock/doctype/stock_entry_type/stock_entry_type.json b/erpnext/stock/doctype/stock_entry_type/stock_entry_type.json index db8e1fef69b..9b426a6c940 100644 --- a/erpnext/stock/doctype/stock_entry_type/stock_entry_type.json +++ b/erpnext/stock/doctype/stock_entry_type/stock_entry_type.json @@ -7,7 +7,8 @@ "engine": "InnoDB", "field_order": [ "purpose", - "add_to_transit" + "add_to_transit", + "is_standard" ], "fields": [ { @@ -26,10 +27,17 @@ "fieldname": "add_to_transit", "fieldtype": "Check", "label": "Add to Transit" + }, + { + "default": "0", + "fieldname": "is_standard", + "fieldtype": "Check", + "label": "Is Standard", + "read_only": 1 } ], "links": [], - "modified": "2024-07-08 08:41:19.385020", + "modified": "2024-08-20 15:35:45.696958", "modified_by": "Administrator", "module": "Stock", "name": "Stock Entry Type", diff --git a/erpnext/stock/doctype/stock_entry_type/stock_entry_type.py b/erpnext/stock/doctype/stock_entry_type/stock_entry_type.py index 034223122f6..efbdd680c69 100644 --- a/erpnext/stock/doctype/stock_entry_type/stock_entry_type.py +++ b/erpnext/stock/doctype/stock_entry_type/stock_entry_type.py @@ -2,7 +2,7 @@ # For license information, please see license.txt -# import frappe +import frappe from frappe.model.document import Document @@ -16,6 +16,7 @@ class StockEntryType(Document): from frappe.types import DF add_to_transit: DF.Check + is_standard: DF.Check purpose: DF.Literal[ "", "Material Issue", @@ -30,5 +31,19 @@ class StockEntryType(Document): # end: auto-generated types def validate(self): + self.validate_standard_type() if self.add_to_transit and self.purpose != "Material Transfer": self.add_to_transit = 0 + + def validate_standard_type(self): + if self.is_standard and self.name not in [ + "Material Issue", + "Material Receipt", + "Material Transfer", + "Material Transfer for Manufacture", + "Material Consumption for Manufacture", + "Manufacture", + "Repack", + "Send to Subcontractor", + ]: + frappe.throw(f"Stock Entry Type {self.name} cannot be set as standard") diff --git a/erpnext/stock/doctype/stock_entry_type/test_stock_entry_type.py b/erpnext/stock/doctype/stock_entry_type/test_stock_entry_type.py index 83ebe7e651b..61156545e34 100644 --- a/erpnext/stock/doctype/stock_entry_type/test_stock_entry_type.py +++ b/erpnext/stock/doctype/stock_entry_type/test_stock_entry_type.py @@ -3,6 +3,33 @@ import unittest +import frappe + class TestStockEntryType(unittest.TestCase): - pass + def test_stock_entry_type_non_standard(self): + stock_entry_type = "Test Manufacturing" + + doc = frappe.get_doc( + { + "doctype": "Stock Entry Type", + "__newname": stock_entry_type, + "purpose": "Manufacture", + "is_standard": 1, + } + ) + + self.assertRaises(frappe.ValidationError, doc.insert) + + def test_stock_entry_type_is_standard(self): + for stock_entry_type in [ + "Material Issue", + "Material Receipt", + "Material Transfer", + "Material Transfer for Manufacture", + "Material Consumption for Manufacture", + "Manufacture", + "Repack", + "Send to Subcontractor", + ]: + self.assertTrue(frappe.db.get_value("Stock Entry Type", stock_entry_type, "is_standard")) From fa8548266290e543e77b8255b3b98645c7f0ed05 Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Thu, 22 Aug 2024 02:22:51 +0530 Subject: [PATCH 055/734] fix: calculation correction for annual depreciation (cherry picked from commit f440243b750ed61d1cbb0799b3f97116f2bb98ce) --- .../asset_depreciation_schedule.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py index bad89e93259..679fbfe2e58 100644 --- a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py +++ b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py @@ -767,8 +767,12 @@ def get_daily_depr_amount(asset, row, schedule_idx, amount): every_year_depr = amount / total_years + depr_period_start_date = add_days( + get_last_day(add_months(row.depreciation_start_date, row.frequency_of_depreciation * -1)), 1 + ) + year_start_date = add_years( - row.depreciation_start_date, (row.frequency_of_depreciation * schedule_idx) // 12 + depr_period_start_date, ((row.frequency_of_depreciation * schedule_idx) // 12) ) year_end_date = add_days(add_years(year_start_date, 1), -1) From 7428df8778ac770085bda24bdca21bc1959e298a Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Mon, 12 Aug 2024 17:45:11 +0530 Subject: [PATCH 056/734] fix: include erpnext in apps page (cherry picked from commit 1d52ef7afe34b229f33160d95b9acd463afb832f) --- erpnext/hooks.py | 10 ++++++++++ erpnext/public/images/erpnext-logo-blue.png | Bin 0 -> 4235 bytes 2 files changed, 10 insertions(+) create mode 100644 erpnext/public/images/erpnext-logo-blue.png diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 527be6ab337..ee0b33e27db 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -10,6 +10,16 @@ source_link = "https://github.com/frappe/erpnext" app_logo_url = "/assets/erpnext/images/erpnext-logo.svg" +include_as_app = [ + { + "name": "erpnext", + "logo": "/assets/erpnext/images/erpnext-logo-blue.png", + "title": "ERPNext", + "route": "/app/home", + # "has_permission": "erpnext.api.permission.has_app_permission" + } +] + develop_version = "14.x.x-develop" app_include_js = "erpnext.bundle.js" diff --git a/erpnext/public/images/erpnext-logo-blue.png b/erpnext/public/images/erpnext-logo-blue.png new file mode 100644 index 0000000000000000000000000000000000000000..0134b3f0627c5bc95aee1682f7979f9590751b4e GIT binary patch literal 4235 zcmds5X;f3!7Cs50f>0}zp$e$27MUc15RF0{7=$nd1B5|>BA^i&gvcO}I8Z<)6&lb8 zC}T*%Ok^f;K-4f<3?LW~kXV8cQw&I$Ug%q^|K5+jAMIQ3$60sXefC}F+xy#hf9ISe zCkJaq`K|H*07aV<7R~@bxNBqMda0$P;q<)pAs2GOJq!SS)Y^a)nUPDSMo5^m^)XQ1 zr75B{wM%d>6?~L%L4H24jYT3m`KPpuk(_ZUo>0vu~xS9s%57AWw}c()pk4k zYSb6xVQ!>Y*?~x~S$Wl162aQozeB zGStK zj_lvbn=%L38wLE(0sZfZ%G=!%s-E{5(j2F925xSq>`uILdaEqfbXjLE`D-EKrb&$! z+5puQ4Bt{V9;>XwezoUnY_$XG8C{dRg7Ij--dhs}!CJaBpBs~$<&HPrLmRZ)PDKXl z%3_WE&ez*j=tX}bF!Obb0p+yic;i;I0jIfc47E>o0JghaJCx``j?X$oYf;>)1s7_q z1C$GvX$^aWF?rnWCf$n;N1*SSuD$W;bnU7_vLUToP<`rs7^($$6?om8eQf0YvC#{? z%!t#-;Ro*oFgrc-bfgLD397bq^^yhie&U;(6$N#rW*~*eQ}B%-PhcP$YqL5=hl``e|@OIbut`3dqBNOr1G zZLHb`F%L^xuo6GmTX%M`k68jboX&UjCm-lSu&7$$s}tVqK*#Y0i?FWKIog7?ukn}RHHEUDXo;e z8+TOJC+gF4PtjrjbEX+IUnLubuw>4~ z-^oNytkcwtgAJ(EooOrKy4~I-Es2&GR%*4`cYu*LUVI~>Iw;aS7IP(nqN6Nu0BnKH z&<&!*m)0^=H@yxpigW5!a8}nQ^9K*@dl_T{7>^KhMh;&0VixK&tPScy!rH$FeU^0h z^(*y=6ghkI(ns%}D3VNi&y%lm8jdO7)0a}Ibr}l!I1bI4%7!xOtfir*7=8jE5llvn zK7Y-?3!|lgamssT($%;D;+D!!o7j?}BwQ+y!1;~Cx@-^i=?x#7J3u*>VqQ92{Za;O z^Y5_VX(grT{yKK?*73rk*FSMR_lfr^gUUOC*V_)nIGYqfD<`e)yCyi&iZQ^Hm)I-l z%%JJ0fM~01*Wg@?eJSWMeo`B~T!^=oB_;oCNALf`v;N$R+QG(A_>3$FDXta$Y<6{n zHCKxTU;~O*pg4GGoH}6d*nVpMF_%hK&eUIqvI&pIYoddDHnh=V& z?cEwGVoD7>51r6|-`5b)1OX}KVO2Q+Xy%tqV7%c?*K9k+o(5oP(LIHd=Hb>Q2!Or0 ze3*`#uTkS7H|YYq5ASSR%X&Yy0Px(ct!cS#*_S3<4`4|s4(pdPIf%~>V)x^oCAbhf z74TCHk9S1J7DM!0`*1v5rpPtWT_Q`;na(V6t>*L)Rdw<2{@w5&f%n%^6e!#AIC;ccW5tdAq?Rb3eGu)p_tob8$TuWH zz=GO8mgBz`#D7%?YcvilHt|LC5aX~f?Q1ORud%qBnoiV&85_nK()4pDaDRJw{q@k_ z%KPXWveLiuH$T2}e)YS`kbHSx32*z4-b>cN>-7IOi+^V85mcQsj+@tF zjD4N|`CjoSy#Lg%)~fKp3d&-!gcOfh$s^sRjXfxr0oe}PL7prB!}tp=n3{kcuH|wu zx49Mu#6( z@hj-EF&H3;axR=Rq-jc}0(QI4{W!fJ7hbIdQiNZ26^$*O!h6dCSR*RI(X&4m00lHo z<3xA9#Hv|}EW9jP<~(8aX@EGwo)94e)cN%Xb0zSPte(ctBAXaxzz*SZRfhYDaNDJd z=M_u%wnd$5&3)+k7{cBd*MLZTR339uyHy^%k9KKJTo%0gHrj;7tRX?(TZ!L?-J)F` zSz@d_T2Q1P{@VOA{G6Q+a1pC|z@OuHI9){FP$;hHz|f}@k*0`JSH{nT?%iAFr7U=2zLypY?LPIN`#V%;G@;SBWKYD=A~(kS#JRwu_wV)+iPxm+v=wgPT=W{ zY{9lDy(bhq^U}GN*QeG?Hw%p7;R$28;&lA#R~nBDu$B`}{O{V)-4t_L{5V}Ytt~R- za!%Y)=H_d>o35uQ0=;pYFbx7y$)1@1tKX_Cad_z@QXXKvaVAGw)L#Dn6StLEu4S?C z6w1DYxQQD(+Hqv1a4^D&buJgF0I*uSsS#&b_g=naE@a?GD4iov8h`UY?c9}nn(Ufp)bRgJ}2 z#Lh`Xaynkb7{0i2Jw}s!9indSKC{fK&xn`gB}dYI8TwsbJa z5apCSk&Wp-L=~ELcRzh2&)$JD&B?8cK~IMDoYa1C7g~!Y zr>xID^W~U_DLzsK39mp!r=I^=oF<5BS&pA?R1SPruA}N>)m8l3t#fJ=;ZWHB_CD3S zFFjB$P(_HyQ1>}AJ(FZbSlWxQw_E-7a9`xsmX~}h!Y!r24F78v3EFR~DZPg}-Rhcz z^cni3>VUqVfI+bimgx7Vtqe2==d~gQ!`mtS76hf1np8+%7S`dzDul`$3P%<1^_1Qn O02@mOi}GVW*Zu`KlJAZH literal 0 HcmV?d00001 From 4297895bd92baad6555f478403109c54b4689d00 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Mon, 12 Aug 2024 19:13:55 +0530 Subject: [PATCH 057/734] chore: renamed include_as_app to include_in_apps_screen (cherry picked from commit 528013242369d1dd6a32063e5011a91aff0550cd) --- erpnext/hooks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/hooks.py b/erpnext/hooks.py index ee0b33e27db..448d0d2a57b 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -10,7 +10,7 @@ source_link = "https://github.com/frappe/erpnext" app_logo_url = "/assets/erpnext/images/erpnext-logo.svg" -include_as_app = [ +add_to_apps_screen = [ { "name": "erpnext", "logo": "/assets/erpnext/images/erpnext-logo-blue.png", From 72ca2ec9a59732441d52a3940af29aa693c2cfdf Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 21 Aug 2024 11:16:41 +0530 Subject: [PATCH 058/734] refactor: allow equity types on Payment Entry (cherry picked from commit 6cbf98294ae9231843764cda9aa60b7bd15444f8) --- erpnext/accounts/doctype/payment_entry/payment_entry.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index c28dcf525df..1f1bb0d50dd 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -35,6 +35,11 @@ frappe.ui.form.on("Payment Entry", { var account_types = ["Pay", "Internal Transfer"].includes(frm.doc.payment_type) ? ["Bank", "Cash"] : [frappe.boot.party_account_types[frm.doc.party_type]]; + + if (frm.doc.party_type == "Shareholder") { + account_types.push("Equity"); + } + return { filters: { account_type: ["in", account_types], @@ -90,6 +95,9 @@ frappe.ui.form.on("Payment Entry", { var account_types = ["Receive", "Internal Transfer"].includes(frm.doc.payment_type) ? ["Bank", "Cash"] : [frappe.boot.party_account_types[frm.doc.party_type]]; + if (frm.doc.party_type == "Shareholder") { + account_types.push("Equity"); + } return { filters: { account_type: ["in", account_types], From 49d995c3ac98734d1b60d9aac15f72f535dbe861 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 21 Aug 2024 14:19:30 +0530 Subject: [PATCH 059/734] refactor: filter shareholder on company (cherry picked from commit 63ad9f4f86f841ffbf7c2e8b31fc16adc694259f) --- erpnext/accounts/doctype/payment_entry/payment_entry.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index 1f1bb0d50dd..57107c7a453 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -420,6 +420,12 @@ frappe.ui.form.on("Payment Entry", { return { query: "erpnext.controllers.queries.employee_query", }; + } else if (frm.doc.party_type == "Shareholder") { + return { + filters: { + company: frm.doc.company, + }, + }; } }); From 758282739ed6d7ab4f60c4e1fa4aaf752400a681 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 22 Aug 2024 16:58:39 +0530 Subject: [PATCH 060/734] fix: call 'process' directly instead of creating 'process_subscripti reason: 'process' follows simple DB transaction model. (cherry picked from commit b4d22c29369ba92c0b59be56c8b275e6e05f778c) --- erpnext/accounts/doctype/subscription/subscription.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/subscription/subscription.py b/erpnext/accounts/doctype/subscription/subscription.py index 916757a8d6d..d57f1de4379 100644 --- a/erpnext/accounts/doctype/subscription/subscription.py +++ b/erpnext/accounts/doctype/subscription/subscription.py @@ -737,10 +737,7 @@ class Subscription(Document): elif self.generate_invoice_at == "Days before the current subscription period": processing_date = add_days(self.current_invoice_start, -self.number_of_days) - process_subscription = frappe.new_doc("Process Subscription") - process_subscription.posting_date = processing_date - process_subscription.subscription = self.name - process_subscription.save().submit() + self.process(posting_date=processing_date) def is_prorate() -> int: From be736cf6415a18d0811095d389a378bb22854d58 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Mon, 12 Aug 2024 17:45:11 +0530 Subject: [PATCH 061/734] fix: include erpnext in apps page (cherry picked from commit 1d52ef7afe34b229f33160d95b9acd463afb832f) --- erpnext/hooks.py | 10 ++++++++++ erpnext/public/images/erpnext-logo-blue.png | Bin 0 -> 4235 bytes 2 files changed, 10 insertions(+) create mode 100644 erpnext/public/images/erpnext-logo-blue.png diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 527be6ab337..ee0b33e27db 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -10,6 +10,16 @@ source_link = "https://github.com/frappe/erpnext" app_logo_url = "/assets/erpnext/images/erpnext-logo.svg" +include_as_app = [ + { + "name": "erpnext", + "logo": "/assets/erpnext/images/erpnext-logo-blue.png", + "title": "ERPNext", + "route": "/app/home", + # "has_permission": "erpnext.api.permission.has_app_permission" + } +] + develop_version = "14.x.x-develop" app_include_js = "erpnext.bundle.js" diff --git a/erpnext/public/images/erpnext-logo-blue.png b/erpnext/public/images/erpnext-logo-blue.png new file mode 100644 index 0000000000000000000000000000000000000000..0134b3f0627c5bc95aee1682f7979f9590751b4e GIT binary patch literal 4235 zcmds5X;f3!7Cs50f>0}zp$e$27MUc15RF0{7=$nd1B5|>BA^i&gvcO}I8Z<)6&lb8 zC}T*%Ok^f;K-4f<3?LW~kXV8cQw&I$Ug%q^|K5+jAMIQ3$60sXefC}F+xy#hf9ISe zCkJaq`K|H*07aV<7R~@bxNBqMda0$P;q<)pAs2GOJq!SS)Y^a)nUPDSMo5^m^)XQ1 zr75B{wM%d>6?~L%L4H24jYT3m`KPpuk(_ZUo>0vu~xS9s%57AWw}c()pk4k zYSb6xVQ!>Y*?~x~S$Wl162aQozeB zGStK zj_lvbn=%L38wLE(0sZfZ%G=!%s-E{5(j2F925xSq>`uILdaEqfbXjLE`D-EKrb&$! z+5puQ4Bt{V9;>XwezoUnY_$XG8C{dRg7Ij--dhs}!CJaBpBs~$<&HPrLmRZ)PDKXl z%3_WE&ez*j=tX}bF!Obb0p+yic;i;I0jIfc47E>o0JghaJCx``j?X$oYf;>)1s7_q z1C$GvX$^aWF?rnWCf$n;N1*SSuD$W;bnU7_vLUToP<`rs7^($$6?om8eQf0YvC#{? z%!t#-;Ro*oFgrc-bfgLD397bq^^yhie&U;(6$N#rW*~*eQ}B%-PhcP$YqL5=hl``e|@OIbut`3dqBNOr1G zZLHb`F%L^xuo6GmTX%M`k68jboX&UjCm-lSu&7$$s}tVqK*#Y0i?FWKIog7?ukn}RHHEUDXo;e z8+TOJC+gF4PtjrjbEX+IUnLubuw>4~ z-^oNytkcwtgAJ(EooOrKy4~I-Es2&GR%*4`cYu*LUVI~>Iw;aS7IP(nqN6Nu0BnKH z&<&!*m)0^=H@yxpigW5!a8}nQ^9K*@dl_T{7>^KhMh;&0VixK&tPScy!rH$FeU^0h z^(*y=6ghkI(ns%}D3VNi&y%lm8jdO7)0a}Ibr}l!I1bI4%7!xOtfir*7=8jE5llvn zK7Y-?3!|lgamssT($%;D;+D!!o7j?}BwQ+y!1;~Cx@-^i=?x#7J3u*>VqQ92{Za;O z^Y5_VX(grT{yKK?*73rk*FSMR_lfr^gUUOC*V_)nIGYqfD<`e)yCyi&iZQ^Hm)I-l z%%JJ0fM~01*Wg@?eJSWMeo`B~T!^=oB_;oCNALf`v;N$R+QG(A_>3$FDXta$Y<6{n zHCKxTU;~O*pg4GGoH}6d*nVpMF_%hK&eUIqvI&pIYoddDHnh=V& z?cEwGVoD7>51r6|-`5b)1OX}KVO2Q+Xy%tqV7%c?*K9k+o(5oP(LIHd=Hb>Q2!Or0 ze3*`#uTkS7H|YYq5ASSR%X&Yy0Px(ct!cS#*_S3<4`4|s4(pdPIf%~>V)x^oCAbhf z74TCHk9S1J7DM!0`*1v5rpPtWT_Q`;na(V6t>*L)Rdw<2{@w5&f%n%^6e!#AIC;ccW5tdAq?Rb3eGu)p_tob8$TuWH zz=GO8mgBz`#D7%?YcvilHt|LC5aX~f?Q1ORud%qBnoiV&85_nK()4pDaDRJw{q@k_ z%KPXWveLiuH$T2}e)YS`kbHSx32*z4-b>cN>-7IOi+^V85mcQsj+@tF zjD4N|`CjoSy#Lg%)~fKp3d&-!gcOfh$s^sRjXfxr0oe}PL7prB!}tp=n3{kcuH|wu zx49Mu#6( z@hj-EF&H3;axR=Rq-jc}0(QI4{W!fJ7hbIdQiNZ26^$*O!h6dCSR*RI(X&4m00lHo z<3xA9#Hv|}EW9jP<~(8aX@EGwo)94e)cN%Xb0zSPte(ctBAXaxzz*SZRfhYDaNDJd z=M_u%wnd$5&3)+k7{cBd*MLZTR339uyHy^%k9KKJTo%0gHrj;7tRX?(TZ!L?-J)F` zSz@d_T2Q1P{@VOA{G6Q+a1pC|z@OuHI9){FP$;hHz|f}@k*0`JSH{nT?%iAFr7U=2zLypY?LPIN`#V%;G@;SBWKYD=A~(kS#JRwu_wV)+iPxm+v=wgPT=W{ zY{9lDy(bhq^U}GN*QeG?Hw%p7;R$28;&lA#R~nBDu$B`}{O{V)-4t_L{5V}Ytt~R- za!%Y)=H_d>o35uQ0=;pYFbx7y$)1@1tKX_Cad_z@QXXKvaVAGw)L#Dn6StLEu4S?C z6w1DYxQQD(+Hqv1a4^D&buJgF0I*uSsS#&b_g=naE@a?GD4iov8h`UY?c9}nn(Ufp)bRgJ}2 z#Lh`Xaynkb7{0i2Jw}s!9indSKC{fK&xn`gB}dYI8TwsbJa z5apCSk&Wp-L=~ELcRzh2&)$JD&B?8cK~IMDoYa1C7g~!Y zr>xID^W~U_DLzsK39mp!r=I^=oF<5BS&pA?R1SPruA}N>)m8l3t#fJ=;ZWHB_CD3S zFFjB$P(_HyQ1>}AJ(FZbSlWxQw_E-7a9`xsmX~}h!Y!r24F78v3EFR~DZPg}-Rhcz z^cni3>VUqVfI+bimgx7Vtqe2==d~gQ!`mtS76hf1np8+%7S`dzDul`$3P%<1^_1Qn O02@mOi}GVW*Zu`KlJAZH literal 0 HcmV?d00001 From 995773088a5c261b37c7900e827a28d78a22f4fb Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Mon, 12 Aug 2024 19:13:55 +0530 Subject: [PATCH 062/734] chore: renamed include_as_app to include_in_apps_screen (cherry picked from commit 528013242369d1dd6a32063e5011a91aff0550cd) --- erpnext/hooks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/hooks.py b/erpnext/hooks.py index ee0b33e27db..448d0d2a57b 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -10,7 +10,7 @@ source_link = "https://github.com/frappe/erpnext" app_logo_url = "/assets/erpnext/images/erpnext-logo.svg" -include_as_app = [ +add_to_apps_screen = [ { "name": "erpnext", "logo": "/assets/erpnext/images/erpnext-logo-blue.png", From eb7e063d5cad01cddfe0a442e4b4e73727aab152 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Fri, 23 Aug 2024 00:37:45 +0000 Subject: [PATCH 063/734] chore(release): Bumped to Version 15.33.5 ## [15.33.5](https://github.com/frappe/erpnext/compare/v15.33.4...v15.33.5) (2024-08-23) ### Bug Fixes * include erpnext in apps page ([be736cf](https://github.com/frappe/erpnext/commit/be736cf6415a18d0811095d389a378bb22854d58)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index ecf8221f778..5ff4bdbe8ce 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "15.33.4" +__version__ = "15.33.5" def get_default_company(user=None): From 7401dc4015fe15862faf59d0492e63a57cf13d73 Mon Sep 17 00:00:00 2001 From: ljain112 Date: Tue, 20 Aug 2024 13:45:57 +0530 Subject: [PATCH 064/734] fix: do not copy date fields in opportunity doctype (cherry picked from commit 74afa57a9fd6ef1e49eba1e284d02528c5bf08bf) # Conflicts: # erpnext/crm/doctype/opportunity/opportunity.json --- erpnext/crm/doctype/opportunity/opportunity.json | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/erpnext/crm/doctype/opportunity/opportunity.json b/erpnext/crm/doctype/opportunity/opportunity.json index 07641d20c33..8ca7bcf5c7e 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.json +++ b/erpnext/crm/doctype/opportunity/opportunity.json @@ -185,7 +185,8 @@ { "fieldname": "expected_closing", "fieldtype": "Date", - "label": "Expected Closing Date" + "label": "Expected Closing Date", + "no_copy": 1 }, { "fieldname": "section_break_14", @@ -357,6 +358,7 @@ "fieldname": "transaction_date", "fieldtype": "Date", "label": "Opportunity Date", + "no_copy": 1, "oldfieldname": "transaction_date", "oldfieldtype": "Date", "reqd": 1, @@ -388,6 +390,7 @@ "fieldname": "first_response_time", "fieldtype": "Duration", "label": "First Response Time", + "no_copy": 1, "read_only": 1 }, { @@ -622,7 +625,11 @@ "icon": "fa fa-info-sign", "idx": 195, "links": [], +<<<<<<< HEAD "modified": "2022-10-13 12:42:21.545636", +======= + "modified": "2024-08-20 04:12:29.095761", +>>>>>>> 74afa57a9f (fix: do not copy date fields in opportunity doctype) "modified_by": "Administrator", "module": "CRM", "name": "Opportunity", From d9ca680a29eea5a4c8b90fd66309df346c248359 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 23 Aug 2024 15:22:23 +0530 Subject: [PATCH 065/734] fix: incorrect in and out qty in the Batch-Wise Balance History (backport #42866) (#42876) fix: incorrect in and out qty in the Batch-Wise Balance History (#42866) (cherry picked from commit ce7f6ee71c29d02599195e2015c1ef0815db5306) Co-authored-by: rohitwaghchaure --- .../batch_wise_balance_history/batch_wise_balance_history.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py b/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py index 822da13cc72..0bb9d40581a 100644 --- a/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py +++ b/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py @@ -168,7 +168,7 @@ def get_stock_ledger_entries_for_batch_bundle(filters): & (sle.has_batch_no == 1) & (sle.posting_date <= filters["to_date"]) ) - .groupby(batch_package.batch_no, batch_package.warehouse) + .groupby(sle.voucher_no, batch_package.batch_no, batch_package.warehouse) .orderby(sle.item_code, sle.warehouse) ) From 08bed618f611560635b17fc5d77b6934b31b7551 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 23 Aug 2024 15:22:39 +0530 Subject: [PATCH 066/734] fix: not able to make stock entry against MR (backport #42874) (#42875) fix: not able to make stock entry against MR (#42874) (cherry picked from commit 63ca1025bcba469bb300f299fcbf31e09667526e) Co-authored-by: rohitwaghchaure --- erpnext/stock/doctype/material_request/material_request.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py index 117ed261439..7bf3ca4d728 100644 --- a/erpnext/stock/doctype/material_request/material_request.py +++ b/erpnext/stock/doctype/material_request/material_request.py @@ -267,6 +267,7 @@ class MaterialRequest(BuyingController): mr_qty_allowance = frappe.db.get_single_value("Stock Settings", "mr_qty_allowance") for d in self.get("items"): + precision = d.precision("ordered_qty") if d.name in mr_items: if self.material_request_type in ("Material Issue", "Material Transfer", "Customer Provided"): d.ordered_qty = flt(mr_items_ordered_qty.get(d.name)) @@ -276,14 +277,14 @@ class MaterialRequest(BuyingController): (d.qty + (d.qty * (mr_qty_allowance / 100))), d.precision("ordered_qty") ) - if d.ordered_qty and d.ordered_qty > allowed_qty: + if d.ordered_qty and flt(d.ordered_qty, precision) > flt(allowed_qty, precision): frappe.throw( _( "The total Issue / Transfer quantity {0} in Material Request {1} cannot be greater than allowed requested quantity {2} for Item {3}" ).format(d.ordered_qty, d.parent, allowed_qty, d.item_code) ) - elif d.ordered_qty and d.ordered_qty > d.stock_qty: + elif d.ordered_qty and flt(d.ordered_qty, precision) > flt(d.stock_qty, precision): frappe.throw( _( "The total Issue / Transfer quantity {0} in Material Request {1} cannot be greater than requested quantity {2} for Item {3}" From 3a149b3c9b07ea276ad6d7c30764cbde16e81202 Mon Sep 17 00:00:00 2001 From: Smit Vora Date: Sat, 24 Aug 2024 12:38:49 +0530 Subject: [PATCH 067/734] chore: resolve conflicts --- erpnext/crm/doctype/opportunity/opportunity.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/erpnext/crm/doctype/opportunity/opportunity.json b/erpnext/crm/doctype/opportunity/opportunity.json index 8ca7bcf5c7e..75373398b09 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.json +++ b/erpnext/crm/doctype/opportunity/opportunity.json @@ -625,11 +625,7 @@ "icon": "fa fa-info-sign", "idx": 195, "links": [], -<<<<<<< HEAD - "modified": "2022-10-13 12:42:21.545636", -======= "modified": "2024-08-20 04:12:29.095761", ->>>>>>> 74afa57a9f (fix: do not copy date fields in opportunity doctype) "modified_by": "Administrator", "module": "CRM", "name": "Opportunity", From ff868a9290f272b3dc6a47c5b9f890d392cc8c82 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 26 Aug 2024 13:25:18 +0530 Subject: [PATCH 068/734] fix: LCV based on purchase invoice amount with multi-currency (backport #42890) (#42894) fix: LCV based on purchase invoice amount with multi-currency (#42890) (cherry picked from commit 6721ae76dea1bebd328192a9c14325d904c8986b) Co-authored-by: rohitwaghchaure --- .../purchase_invoice/test_purchase_invoice.py | 56 +++++++++++++++++++ .../purchase_receipt/purchase_receipt.py | 1 + 2 files changed, 57 insertions(+) diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index eb7c486f2e1..31143fb72b8 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -2236,6 +2236,62 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin): self.assertEqual(pi_expected_values[i][1], gle.debit) self.assertEqual(pi_expected_values[i][2], gle.credit) + def test_adjust_incoming_rate_from_pi_with_multi_currency(self): + from erpnext.stock.doctype.landed_cost_voucher.test_landed_cost_voucher import ( + make_landed_cost_voucher, + ) + + frappe.db.set_single_value("Buying Settings", "maintain_same_rate", 0) + + frappe.db.set_single_value("Buying Settings", "set_landed_cost_based_on_purchase_invoice_rate", 1) + + # Increase the cost of the item + + pr = make_purchase_receipt( + qty=10, rate=1, currency="USD", do_not_save=1, supplier="_Test Supplier USD" + ) + pr.conversion_rate = 6300 + pr.plc_conversion_rate = 1 + pr.save() + pr.submit() + + self.assertEqual(pr.conversion_rate, 6300) + self.assertEqual(pr.plc_conversion_rate, 1) + self.assertEqual(pr.base_grand_total, 6300 * 10) + + stock_value_difference = frappe.db.get_value( + "Stock Ledger Entry", + {"voucher_type": "Purchase Receipt", "voucher_no": pr.name}, + "stock_value_difference", + ) + self.assertEqual(stock_value_difference, 6300 * 10) + + make_landed_cost_voucher( + company=pr.company, + receipt_document_type="Purchase Receipt", + receipt_document=pr.name, + charges=3000, + distribute_charges_based_on="Qty", + ) + + pi = create_purchase_invoice_from_receipt(pr.name) + for row in pi.items: + row.rate = 1.1 + + pi.save() + pi.submit() + + stock_value_difference = frappe.db.get_value( + "Stock Ledger Entry", + {"voucher_type": "Purchase Receipt", "voucher_no": pr.name}, + "stock_value_difference", + ) + self.assertEqual(stock_value_difference, 7230 * 10) + + frappe.db.set_single_value("Buying Settings", "set_landed_cost_based_on_purchase_invoice_rate", 0) + + frappe.db.set_single_value("Buying Settings", "maintain_same_rate", 1) + def set_advance_flag(company, flag, default_account): frappe.db.set_value( diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 3c97d13b904..afcb00141a1 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -1075,6 +1075,7 @@ def update_billing_percentage(pr_doc, update_modified=True, adjust_incoming_rate if item.billed_amt and item.amount: adjusted_amt = flt(item.billed_amt) - flt(item.amount) + adjusted_amt = adjusted_amt * flt(pr_doc.conversion_rate) item.db_set("rate_difference_with_purchase_invoice", adjusted_amt, update_modified=False) percent_billed = round(100 * (total_billed_amount / (total_amount or 1)), 6) From a3e5ffe915b78887a639827fbede35fadcdcdd94 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 26 Aug 2024 14:05:19 +0530 Subject: [PATCH 069/734] refactor: better err msg on clearance tool (cherry picked from commit 092411b54ff21d5bed74eb896b8d6bf7bc0d0ad9) --- .../accounts/doctype/bank_clearance/bank_clearance.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/bank_clearance/bank_clearance.py b/erpnext/accounts/doctype/bank_clearance/bank_clearance.py index 63758a5e7fb..85713c6c9a6 100644 --- a/erpnext/accounts/doctype/bank_clearance/bank_clearance.py +++ b/erpnext/accounts/doctype/bank_clearance/bank_clearance.py @@ -6,7 +6,7 @@ import frappe from frappe import _, msgprint from frappe.model.document import Document from frappe.query_builder.custom import ConstantColumn -from frappe.utils import flt, fmt_money, getdate +from frappe.utils import flt, fmt_money, get_link_to_form, getdate from pypika import Order import erpnext @@ -96,8 +96,11 @@ class BankClearance(Document): if d.cheque_date and getdate(d.clearance_date) < getdate(d.cheque_date): frappe.throw( - _("Row #{0}: Clearance date {1} cannot be before Cheque Date {2}").format( - d.idx, d.clearance_date, d.cheque_date + _("Row #{0}: For {1} Clearance date {2} cannot be before Cheque Date {3}").format( + d.idx, + get_link_to_form(d.payment_document, d.payment_entry), + d.clearance_date, + d.cheque_date, ) ) From 25193c5e92f7423668c696bd5996150299f58288 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 30 Jul 2024 17:20:21 +0530 Subject: [PATCH 070/734] feat: report to identify incorrectly cleared cheques (cherry picked from commit 28890fa833b31a40fd4f11affef4a15274335ebb) --- .../__init__.py | 0 ...heques_and_deposits_incorrectly_cleared.js | 6 ++++ ...ques_and_deposits_incorrectly_cleared.json | 29 +++++++++++++++++++ ...heques_and_deposits_incorrectly_cleared.py | 9 ++++++ 4 files changed, 44 insertions(+) create mode 100644 erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/__init__.py create mode 100644 erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.js create mode 100644 erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.json create mode 100644 erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py diff --git a/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/__init__.py b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.js b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.js new file mode 100644 index 00000000000..f28714d70fa --- /dev/null +++ b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.js @@ -0,0 +1,6 @@ +// Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.query_reports["Cheques and Deposits Incorrectly cleared"] = { + filters: [], +}; diff --git a/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.json b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.json new file mode 100644 index 00000000000..50cf765ca3d --- /dev/null +++ b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.json @@ -0,0 +1,29 @@ +{ + "add_total_row": 0, + "columns": [], + "creation": "2024-07-30 17:20:07.570971", + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [], + "idx": 0, + "is_standard": "Yes", + "letterhead": null, + "modified": "2024-07-30 17:20:07.570971", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Cheques and Deposits Incorrectly cleared", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Payment Entry", + "report_name": "Cheques and Deposits Incorrectly cleared", + "report_type": "Script Report", + "roles": [ + { + "role": "Accounts User" + }, + { + "role": "Accounts Manager" + } + ] +} \ No newline at end of file diff --git a/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py new file mode 100644 index 00000000000..9a1d41262fc --- /dev/null +++ b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +# import frappe + + +def execute(filters=None): + columns, data = [], [] + return columns, data From 80a5df0e960fe1a01e1ad7b4d7e59f47bf9b0b1b Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 30 Jul 2024 17:24:46 +0530 Subject: [PATCH 071/734] refactor: barebones functions (cherry picked from commit ceaa1be72935e8bd46921f465659d04edb164482) --- ...heques_and_deposits_incorrectly_cleared.js | 45 ++++++++++++++++++- ...heques_and_deposits_incorrectly_cleared.py | 37 ++++++++++++++- 2 files changed, 80 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.js b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.js index f28714d70fa..f2554c4442c 100644 --- a/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.js +++ b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.js @@ -2,5 +2,48 @@ // For license information, please see license.txt frappe.query_reports["Cheques and Deposits Incorrectly cleared"] = { - filters: [], + filters: [ + { + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + reqd: 1, + default: frappe.defaults.get_user_default("Company"), + }, + { + fieldname: "account", + label: __("Bank Account"), + fieldtype: "Link", + options: "Account", + default: frappe.defaults.get_user_default("Company") + ? locals[":Company"][frappe.defaults.get_user_default("Company")]["default_bank_account"] + : "", + reqd: 1, + get_query: function () { + var company = frappe.query_report.get_filter_value("company"); + return { + query: "erpnext.controllers.queries.get_account_list", + filters: [ + ["Account", "account_type", "in", "Bank, Cash"], + ["Account", "is_group", "=", 0], + ["Account", "disabled", "=", 0], + ["Account", "company", "=", company], + ], + }; + }, + }, + { + fieldname: "report_date", + label: __("Date"), + fieldtype: "Date", + default: frappe.datetime.get_today(), + reqd: 1, + }, + { + fieldname: "include_pos_transactions", + label: __("Include POS Transactions"), + fieldtype: "Check", + }, + ], }; diff --git a/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py index 9a1d41262fc..774c843b90a 100644 --- a/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py +++ b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py @@ -1,9 +1,44 @@ # Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt -# import frappe +import frappe +from frappe import _ def execute(filters=None): columns, data = [], [] return columns, data + + +def get_columns(): + return [ + {"fieldname": "posting_date", "label": _("Posting Date"), "fieldtype": "Date", "width": 90}, + { + "fieldname": "payment_document", + "label": _("Payment Document Type"), + "fieldtype": "Data", + "width": 220, + }, + { + "fieldname": "payment_entry", + "label": _("Payment Document"), + "fieldtype": "Dynamic Link", + "options": "payment_document", + "width": 220, + }, + { + "fieldname": "debit", + "label": _("Debit"), + "fieldtype": "Currency", + "options": "account_currency", + "width": 120, + }, + { + "fieldname": "credit", + "label": _("Credit"), + "fieldtype": "Currency", + "options": "account_currency", + "width": 120, + }, + {"fieldname": "clearance_date", "label": _("Clearance Date"), "fieldtype": "Date", "width": 110}, + ] From 5a28a1728ed4531e6ab4bf818ec8a3005ec47d6f Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 30 Jul 2024 17:52:52 +0530 Subject: [PATCH 072/734] refactor: working state with minimum functions (cherry picked from commit 4cd023444acc8b951d24c1a04ec8bbb120c4aeb7) --- ...heques_and_deposits_incorrectly_cleared.py | 71 ++++++++++++++++++- 1 file changed, 69 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py index 774c843b90a..14295a5a711 100644 --- a/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py +++ b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py @@ -2,14 +2,80 @@ # For license information, please see license.txt import frappe -from frappe import _ +from frappe import _, qb +from frappe.query_builder import CustomFunction def execute(filters=None): - columns, data = [], [] + columns = get_columns() + data = build_data(filters) return columns, data +def build_data(filters): + vouchers = get_amounts_not_reflected_in_system_for_bank_reconciliation_statement(filters) + data = [] + for x in vouchers: + data.append( + frappe._dict( + payment_document="Payment Entry", + payment_entry=x.name, + debit=x.amount, + credit=0, + posting_date=x.posting_date, + clearance_date=x.clearance_date, + ) + ) + return data + + +def get_amounts_not_reflected_in_system_for_bank_reconciliation_statement(filters): + je = qb.DocType("Journal Entry") + jea = qb.DocType("Journal Entry Account") + + journals = ( + qb.from_(je) + .inner_join(jea) + .on(je.name == jea.parent) + .select( + je.name, + jea.debit_in_account_currency, + jea.credit_in_account_currency, + je.posting_date, + je.clearance_date, + ) + .where( + je.docstatus.eq(1) + & jea.account.eq(filters.account) + & je.posting_date.gt(filters.report_date) + & je.clearance_date.lte(filters.report_date) + & (je.is_opening.isnull() | je.is_opening.eq("No")) + ) + .run(as_dict=1) + ) + + ifelse = CustomFunction("IF", ["condition", "then", "else"]) + pe = qb.DocType("Payment Entry") + payments = ( + qb.from_(pe) + .select( + pe.name, + ifelse(pe.paid_from.eq(filters.account), pe.paid_amount, pe.received_amount).as_("amount"), + pe.posting_date, + pe.clearance_date, + ) + .where( + pe.docstatus.eq(1) + & (pe.paid_from.eq(filters.account) | pe.paid_to.eq(filters.account)) + & pe.posting_date.gt(filters.report_date) + & pe.clearance_date.lte(filters.report_date) + ) + .run(as_dict=1) + ) + + return journals + payments + + def get_columns(): return [ {"fieldname": "posting_date", "label": _("Posting Date"), "fieldtype": "Date", "width": 90}, @@ -40,5 +106,6 @@ def get_columns(): "options": "account_currency", "width": 120, }, + {"fieldname": "posting_date", "label": _("Posting Date"), "fieldtype": "Date", "width": 110}, {"fieldname": "clearance_date", "label": _("Clearance Date"), "fieldtype": "Date", "width": 110}, ] From 993114942e65110cca75425aa84e0090a1821adf Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 31 Jul 2024 17:57:25 +0530 Subject: [PATCH 073/734] refactor: build dict for payment entry (cherry picked from commit 784dec24c8c521461f232658aca8c58c82c39ff8) --- ...heques_and_deposits_incorrectly_cleared.py | 44 ++++++++++++++----- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py index 14295a5a711..995ce4efd0b 100644 --- a/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py +++ b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py @@ -4,6 +4,7 @@ import frappe from frappe import _, qb from frappe.query_builder import CustomFunction +from frappe.query_builder.custom import ConstantColumn def execute(filters=None): @@ -12,20 +13,39 @@ def execute(filters=None): return columns, data +def build_payment_entry_dict(row: dict) -> dict: + row_dict = frappe._dict() + row_dict.update( + { + "payment_document": row.get("doctype"), + "payment_entry": row.get("name"), + "posting_date": row.get("posting_date"), + "clearance_date": row.get("clearance_date"), + } + ) + if row.get("payment_type") == "Receive" and row.get("party_type") in ["Customer", "Supplier"]: + row_dict.update( + { + "debit": row.get("amount"), + "credit": 0, + } + ) + else: + row_dict.update( + { + "debit": 0, + "credit": row.get("amount"), + } + ) + return row_dict + + def build_data(filters): vouchers = get_amounts_not_reflected_in_system_for_bank_reconciliation_statement(filters) data = [] for x in vouchers: - data.append( - frappe._dict( - payment_document="Payment Entry", - payment_entry=x.name, - debit=x.amount, - credit=0, - posting_date=x.posting_date, - clearance_date=x.clearance_date, - ) - ) + if x.doctype == "Payment Entry": + data.append(build_payment_entry_dict(x)) return data @@ -56,11 +76,15 @@ def get_amounts_not_reflected_in_system_for_bank_reconciliation_statement(filter ifelse = CustomFunction("IF", ["condition", "then", "else"]) pe = qb.DocType("Payment Entry") + doctype_name = ConstantColumn("Payment Entry") payments = ( qb.from_(pe) .select( + doctype_name.as_("doctype"), pe.name, ifelse(pe.paid_from.eq(filters.account), pe.paid_amount, pe.received_amount).as_("amount"), + pe.payment_type, + pe.party_type, pe.posting_date, pe.clearance_date, ) From 42382b39454483a80edccafe4dbbe02a6649317e Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 26 Aug 2024 14:16:32 +0530 Subject: [PATCH 074/734] chore: remove redundant column (cherry picked from commit 74b36db24e54e8e0a881259f51e79270a7057be2) --- .../cheques_and_deposits_incorrectly_cleared.py | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py index 995ce4efd0b..6bb090fd1b7 100644 --- a/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py +++ b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py @@ -102,7 +102,6 @@ def get_amounts_not_reflected_in_system_for_bank_reconciliation_statement(filter def get_columns(): return [ - {"fieldname": "posting_date", "label": _("Posting Date"), "fieldtype": "Date", "width": 90}, { "fieldname": "payment_document", "label": _("Payment Document Type"), From ecb0506dba19b503af5e82e3ab87ffa1ca7b4c70 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 26 Aug 2024 14:29:47 +0530 Subject: [PATCH 075/734] refactor: build dictionary for Journal remove redundant filter (cherry picked from commit 2144e0337d8194e466c315b8585872f8b21b7a44) --- ...heques_and_deposits_incorrectly_cleared.js | 5 ----- ...heques_and_deposits_incorrectly_cleared.py | 19 +++++++++++++++++++ 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.js b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.js index f2554c4442c..e83fc6f5b57 100644 --- a/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.js +++ b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.js @@ -40,10 +40,5 @@ frappe.query_reports["Cheques and Deposits Incorrectly cleared"] = { default: frappe.datetime.get_today(), reqd: 1, }, - { - fieldname: "include_pos_transactions", - label: __("Include POS Transactions"), - fieldtype: "Check", - }, ], }; diff --git a/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py index 6bb090fd1b7..891dc2c4bb1 100644 --- a/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py +++ b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py @@ -40,24 +40,43 @@ def build_payment_entry_dict(row: dict) -> dict: return row_dict +def build_journal_entry_dict(row: dict) -> dict: + row_dict = frappe._dict() + row_dict.update( + { + "payment_document": row.get("doctype"), + "payment_entry": row.get("name"), + "posting_date": row.get("posting_date"), + "clearance_date": row.get("clearance_date"), + "debit": row.get("debit_in_account_currency"), + "credit": row.get("credit_in_account_currency"), + } + ) + return row_dict + + def build_data(filters): vouchers = get_amounts_not_reflected_in_system_for_bank_reconciliation_statement(filters) data = [] for x in vouchers: if x.doctype == "Payment Entry": data.append(build_payment_entry_dict(x)) + elif x.doctype == "Journal Entry": + data.append(build_journal_entry_dict(x)) return data def get_amounts_not_reflected_in_system_for_bank_reconciliation_statement(filters): je = qb.DocType("Journal Entry") jea = qb.DocType("Journal Entry Account") + doctype_name = ConstantColumn("Journal Entry") journals = ( qb.from_(je) .inner_join(jea) .on(je.name == jea.parent) .select( + doctype_name.as_("doctype"), je.name, jea.debit_in_account_currency, jea.credit_in_account_currency, From caa6ca1d0b300e30f08bd431186e31161f8ef0a2 Mon Sep 17 00:00:00 2001 From: ljain112 Date: Thu, 22 Aug 2024 11:50:19 +0530 Subject: [PATCH 076/734] fix: update dimesions in exchange_gain_loss jv based on base document (cherry picked from commit 96df19149de806cea9798420640f9088f55925bc) --- erpnext/controllers/accounts_controller.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 58e120e14b0..ce5d813b801 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1334,6 +1334,12 @@ class AccountsController(TransactionBase): # Cancelling existing exchange gain/loss journals is handled during the `on_cancel` event. # see accounts/utils.py:cancel_exchange_gain_loss_journal() if self.docstatus == 1: + if dimensions_dict is None: + dimensions_dict = frappe._dict() + active_dimensions = get_dimensions()[0] + for dim in active_dimensions: + dimensions_dict[dim.fieldname] = self.get(dim.fieldname) + if self.get("doctype") == "Journal Entry": # 'args' is populated with exchange gain/loss account and the amount to be booked. # These are generated by Sales/Purchase Invoice during reconciliation and advance allocation. From 8c350d43b26fe195824eedd73092bc9345668e3f Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 26 Aug 2024 17:35:20 +0530 Subject: [PATCH 077/734] fix: Column 'valuation_rate' cannot be null (backport #42909) (#42913) fix: Column 'valuation_rate' cannot be null (#42909) (cherry picked from commit 92bde71ab13f90aef0d51672f1dcbbe079da6b9a) Co-authored-by: rohitwaghchaure --- erpnext/stock/serial_batch_bundle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/serial_batch_bundle.py b/erpnext/stock/serial_batch_bundle.py index 71e664dd066..46724be5927 100644 --- a/erpnext/stock/serial_batch_bundle.py +++ b/erpnext/stock/serial_batch_bundle.py @@ -183,7 +183,7 @@ class SerialBatchBundle: } if self.sle.actual_qty < 0 and self.is_material_transfer(): - values_to_update["valuation_rate"] = sn_doc.avg_rate + values_to_update["valuation_rate"] = flt(sn_doc.avg_rate) if not frappe.db.get_single_value( "Stock Settings", "do_not_update_serial_batch_on_creation_of_auto_bundle" From 8d29dc6a81e95703b1215b2278a7186f565eabe9 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 26 Aug 2024 18:18:31 +0530 Subject: [PATCH 078/734] fix: unsupported operand type(s) for *: 'float' and 'NoneType' (backport #42916) (#42918) fix: unsupported operand type(s) for *: 'float' and 'NoneType' (#42916) (cherry picked from commit 10434742e965d4003bb42f1e2335b8785ba3667f) Co-authored-by: rohitwaghchaure --- erpnext/accounts/doctype/pricing_rule/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index 9c7911d7cae..b848fde08d8 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -486,7 +486,7 @@ def get_qty_and_rate_for_other_item(doc, pr_doc, pricing_rules, row_item): continue stock_qty = row.get("qty") * (row.get("conversion_factor") or 1.0) - amount = stock_qty * (row.get("price_list_rate") or row.get("rate")) + amount = stock_qty * (flt(row.get("price_list_rate")) or flt(row.get("rate"))) pricing_rules = filter_pricing_rules_for_qty_amount(stock_qty, amount, pricing_rules, row) if pricing_rules and pricing_rules[0]: From 26248924b651d466fc9cf7ffe071787d65159af0 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 27 Aug 2024 09:30:47 +0530 Subject: [PATCH 079/734] fix: same posting date and time, creation causing incorrect balance qty (backport #42904) (#42920) fix: same posting date and time, creation causing incorrect balance qty (#42904) fix: same posting date and time, creation causing incorrect balance quantity (cherry picked from commit 27364b7e6b0b03aa0303e5b180cd867bbb312232) Co-authored-by: rohitwaghchaure --- .../purchase_receipt/test_purchase_receipt.py | 67 ++++++++++++++++++- .../test_stock_ledger_entry.py | 4 +- erpnext/stock/stock_ledger.py | 12 ++-- 3 files changed, 74 insertions(+), 9 deletions(-) diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index c239360d945..4cb53e753e7 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -3,7 +3,7 @@ import frappe from frappe.tests.utils import FrappeTestCase, change_settings -from frappe.utils import add_days, cint, cstr, flt, getdate, nowtime, today +from frappe.utils import add_days, cint, cstr, flt, get_datetime, getdate, nowtime, today from pypika import functions as fn import erpnext @@ -3592,6 +3592,71 @@ class TestPurchaseReceipt(FrappeTestCase): inter_transfer_dn.cancel() frappe.db.set_single_value("Stock Settings", "auto_create_serial_and_batch_bundle_for_outward", 1) + def test_sles_with_same_posting_datetime_and_creation(self): + from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry + from erpnext.stock.report.stock_balance.stock_balance import execute + + item_code = "Test Item for SLE with same posting datetime and creation" + create_item(item_code) + + pr = make_purchase_receipt( + item_code=item_code, + qty=10, + rate=100, + posting_date="2023-11-06", + posting_time="00:00:00", + ) + + sr = make_stock_entry( + item_code=item_code, + source=pr.items[0].warehouse, + qty=10, + posting_date="2023-11-07", + posting_time="14:28:0.330404", + ) + + sle = frappe.db.get_value( + "Stock Ledger Entry", + {"voucher_type": sr.doctype, "voucher_no": sr.name, "item_code": sr.items[0].item_code}, + "name", + ) + + sle_doc = frappe.get_doc("Stock Ledger Entry", sle) + sle_doc.db_set("creation", "2023-11-07 14:28:01.208930") + + sle_doc.reload() + self.assertEqual(get_datetime(sle_doc.creation), get_datetime("2023-11-07 14:28:01.208930")) + + sr = make_stock_entry( + item_code=item_code, + target=pr.items[0].warehouse, + qty=50, + posting_date="2023-11-07", + posting_time="14:28:0.920825", + ) + + sle = frappe.db.get_value( + "Stock Ledger Entry", + {"voucher_type": sr.doctype, "voucher_no": sr.name, "item_code": sr.items[0].item_code}, + "name", + ) + + sle_doc = frappe.get_doc("Stock Ledger Entry", sle) + sle_doc.db_set("creation", "2023-11-07 14:28:01.044561") + + sle_doc.reload() + self.assertEqual(get_datetime(sle_doc.creation), get_datetime("2023-11-07 14:28:01.044561")) + + pr.repost_future_sle_and_gle(force=True) + + columns, data = execute( + filters=frappe._dict( + {"item_code": item_code, "warehouse": pr.items[0].warehouse, "company": pr.company} + ) + ) + + self.assertEqual(data[0].get("bal_qty"), 50.0) + 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/stock_ledger_entry/test_stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py index 587b25ea154..42e402e0005 100644 --- a/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py +++ b/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py @@ -1187,7 +1187,7 @@ class TestStockLedgerEntry(FrappeTestCase, StockTestMixin): qty=5, posting_date="2021-01-01", rate=10, - posting_time="02:00:00.1234", + posting_time="02:00:00", ) time.sleep(3) @@ -1199,7 +1199,7 @@ class TestStockLedgerEntry(FrappeTestCase, StockTestMixin): qty=100, rate=10, posting_date="2021-01-01", - posting_time="02:00:00", + posting_time="02:00:00.1234", ) sle = frappe.get_all( diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 0961d9bd35e..2aacd86e3d2 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -1543,7 +1543,7 @@ def get_previous_sle_of_current_voucher(args, operator="<", exclude_current_vouc and ( posting_datetime {operator} %(posting_datetime)s ) - order by posting_datetime desc, creation desc + order by posting_date desc, posting_time desc, creation desc limit 1 for update""", { @@ -1636,7 +1636,7 @@ def get_stock_ledger_entries( where item_code = %(item_code)s and is_cancelled = 0 {conditions} - order by posting_datetime {order}, creation {order} + order by posting_date {order}, posting_time {order}, creation {order} {limit} {for_update}""".format( conditions=conditions, limit=limit or "", @@ -1753,7 +1753,7 @@ def get_valuation_rate( AND valuation_rate >= 0 AND is_cancelled = 0 AND NOT (voucher_no = %s AND voucher_type = %s) - order by posting_datetime desc, name desc limit 1""", + order by posting_date desc, posting_time desc, name desc limit 1""", (item_code, warehouse, voucher_no, voucher_type), ): return flt(last_valuation_rate[0][0]) @@ -2004,7 +2004,7 @@ def get_future_sle_with_negative_qty(args): and posting_datetime >= %(posting_datetime)s and is_cancelled = 0 and qty_after_transaction < 0 - order by posting_datetime asc + order by posting_date asc, posting_time asc limit 1 """, args, @@ -2018,14 +2018,14 @@ def get_future_sle_with_negative_batch_qty(args): with batch_ledger as ( select posting_date, posting_time, posting_datetime, voucher_type, voucher_no, - sum(actual_qty) over (order by posting_datetime, creation) as cumulative_total + sum(actual_qty) over (order by posting_date, posting_time, creation) as cumulative_total from `tabStock Ledger Entry` where item_code = %(item_code)s and warehouse = %(warehouse)s and batch_no=%(batch_no)s and is_cancelled = 0 - order by posting_datetime, creation + order by posting_date, posting_time, creation ) select * from batch_ledger where From ec26c92263ba2c7acdd3941b2569f59af6a6bed9 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 27 Aug 2024 11:03:45 +0530 Subject: [PATCH 080/734] fix: timeout while submitting stock entry (backport #42929) (#42931) fix: timeout while submitting stock entry (#42929) (cherry picked from commit ca2fde891ec32b162f6b6c33d9ff10b60e4ca57c) Co-authored-by: rohitwaghchaure --- .../stock/doctype/stock_ledger_entry/stock_ledger_entry.json | 5 +++-- .../stock/doctype/stock_ledger_entry/stock_ledger_entry.py | 1 + 2 files changed, 4 insertions(+), 2 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 58b6e4a74b6..d9af64c3983 100644 --- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json +++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json @@ -352,7 +352,8 @@ { "fieldname": "posting_datetime", "fieldtype": "Datetime", - "label": "Posting Datetime" + "label": "Posting Datetime", + "search_index": 1 } ], "hide_toolbar": 1, @@ -361,7 +362,7 @@ "in_create": 1, "index_web_pages_for_search": 1, "links": [], - "modified": "2024-06-27 16:23:18.820049", + "modified": "2024-08-27 09:28:03.961443", "modified_by": "Administrator", "module": "Stock", "name": "Stock Ledger Entry", diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py index cff1886d7fe..319303dbbb0 100644 --- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py +++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py @@ -351,3 +351,4 @@ def on_doctype_update(): frappe.db.add_index("Stock Ledger Entry", ["voucher_no", "voucher_type"]) frappe.db.add_index("Stock Ledger Entry", ["batch_no", "item_code", "warehouse"]) frappe.db.add_index("Stock Ledger Entry", ["warehouse", "item_code"], "item_warehouse") + frappe.db.add_index("Stock Ledger Entry", ["posting_datetime", "creation"]) From 0650c22b53cfd801970f88aa3b0230869ace8a00 Mon Sep 17 00:00:00 2001 From: venkat102 Date: Mon, 26 Aug 2024 19:30:30 +0530 Subject: [PATCH 081/734] fix: make party naming sequential when naming_by set as auto name (cherry picked from commit c9015f7c041a5a0af64972991cc0dfc48fe8cb20) --- erpnext/buying/doctype/supplier/supplier.py | 2 +- erpnext/selling/doctype/customer/customer.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/buying/doctype/supplier/supplier.py b/erpnext/buying/doctype/supplier/supplier.py index bccab8b01e0..3b72953c563 100644 --- a/erpnext/buying/doctype/supplier/supplier.py +++ b/erpnext/buying/doctype/supplier/supplier.py @@ -97,7 +97,7 @@ class Supplier(TransactionBase): elif supp_master_name == "Naming Series": set_name_by_naming_series(self) else: - self.name = set_name_from_naming_options(frappe.get_meta(self.doctype).autoname, self) + set_name_from_naming_options(frappe.get_meta(self.doctype).autoname, self) def on_update(self): self.create_primary_contact() diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index 9bd9b5e760a..0e9c1f3e790 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -105,7 +105,7 @@ class Customer(TransactionBase): elif cust_master_name == "Naming Series": set_name_by_naming_series(self) else: - self.name = set_name_from_naming_options(frappe.get_meta(self.doctype).autoname, self) + set_name_from_naming_options(frappe.get_meta(self.doctype).autoname, self) def get_customer_name(self): if frappe.db.get_value("Customer", self.customer_name) and not frappe.flags.in_import: From 5bdd2989c67293ed0e5d786637f4373ef7bb7c97 Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Tue, 27 Aug 2024 15:02:01 +0530 Subject: [PATCH 082/734] feat: added finance book filter in depreciation and balances report (cherry picked from commit 45804c68f0d52281b233f91279abdc86644bac10) --- .../asset_depreciations_and_balances.js | 6 ++++++ .../asset_depreciations_and_balances.py | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.js b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.js index 49771d7ebe9..0f74df6f909 100644 --- a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.js +++ b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.js @@ -46,5 +46,11 @@ frappe.query_reports["Asset Depreciations and Balances"] = { options: "Asset", depends_on: "eval: doc.group_by == 'Asset'", }, + { + fieldname: "finance_book", + label: __("Finance Book"), + fieldtype: "Link", + options: "Finance Book", + }, ], }; diff --git a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py index f9a008ade7f..4a6318382cc 100644 --- a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py +++ b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py @@ -69,6 +69,9 @@ def get_asset_categories_for_grouped_by_category(filters): condition = "" if filters.get("asset_category"): condition += " and asset_category = %(asset_category)s" + if filters.get("finance_book"): + condition += " and exists (select 1 from `tabAsset Depreciation Schedule` ads where ads.asset = a.name and ads.finance_book = %(finance_book)s)" + # nosemgrep return frappe.db.sql( f""" @@ -119,6 +122,7 @@ def get_asset_categories_for_grouped_by_category(filters): "from_date": filters.from_date, "company": filters.company, "asset_category": filters.get("asset_category"), + "finance_book": filters.get("finance_book"), }, as_dict=1, ) @@ -128,6 +132,8 @@ def get_asset_details_for_grouped_by_category(filters): condition = "" if filters.get("asset"): condition += " and name = %(asset)s" + if filters.get("finance_book"): + condition += " and exists (select 1 from `tabAsset Depreciation Schedule` ads where ads.asset = `tabAsset`.name and ads.finance_book = %(finance_book)s)" return frappe.db.sql( f""" SELECT name, @@ -176,6 +182,7 @@ def get_asset_details_for_grouped_by_category(filters): "from_date": filters.from_date, "company": filters.company, "asset": filters.get("asset"), + "finance_book": filters.get("finance_book"), }, as_dict=1, ) From 9fc0ac1a926a7977cc840b1116a7e007f93e42be Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Tue, 27 Aug 2024 15:27:37 +0530 Subject: [PATCH 083/734] chore: resolved linter warnings with #nosemgrep (cherry picked from commit adf1e487e1ecb9ae1e85cb08654eeb105f982ac8) --- .../asset_depreciations_and_balances.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py index 4a6318382cc..34cced2ca17 100644 --- a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py +++ b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py @@ -134,6 +134,8 @@ def get_asset_details_for_grouped_by_category(filters): condition += " and name = %(asset)s" if filters.get("finance_book"): condition += " and exists (select 1 from `tabAsset Depreciation Schedule` ads where ads.asset = `tabAsset`.name and ads.finance_book = %(finance_book)s)" + + # nosemgrep return frappe.db.sql( f""" SELECT name, From c54e97b89a09f6cecd7380cccca71ece07fed5f6 Mon Sep 17 00:00:00 2001 From: venkat102 Date: Mon, 26 Aug 2024 20:04:51 +0530 Subject: [PATCH 084/734] fix: get amount with taxes and charges from payment entry (cherry picked from commit b3a901b631e800f52554214261a59608b2fe8a4e) --- .../bank_reconciliation_statement.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py b/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py index 8a8e3a59972..c7dba16492e 100644 --- a/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py +++ b/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py @@ -154,8 +154,8 @@ def get_payment_entries(filters): select "Payment Entry" as payment_document, name as payment_entry, reference_no, reference_date as ref_date, - if(paid_to=%(account)s, received_amount, 0) as debit, - if(paid_from=%(account)s, paid_amount, 0) as credit, + if(paid_to=%(account)s, received_amount_after_tax, 0) as debit, + if(paid_from=%(account)s, paid_amount_after_tax, 0) as credit, posting_date, ifnull(party,if(paid_from=%(account)s,paid_to,paid_from)) as against_account, clearance_date, if(paid_to=%(account)s, paid_to_account_currency, paid_from_account_currency) as account_currency from `tabPayment Entry` From 80244bafa44bdd2198788453bd9bae0973dc16ec Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 27 Aug 2024 14:40:12 +0530 Subject: [PATCH 085/734] fix: use of incorrect attribute (cherry picked from commit fb32d2cafb1757216ec3f7db5f3e745ea5b15906) --- erpnext/accounts/doctype/payment_entry/payment_entry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 454101027e7..13baff9e7a8 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -1740,7 +1740,7 @@ def get_outstanding_reference_documents(args, validate=False): d["bill_no"] = frappe.db.get_value(d.voucher_type, d.voucher_no, "bill_no") # Get negative outstanding sales /purchase invoices - if args.get("party_type") != "Employee" and not args.get("voucher_no"): + if args.get("party_type") != "Employee": negative_outstanding_invoices = get_negative_outstanding_invoices( args.get("party_type"), args.get("party"), From 3d469db47bafbea009ab011787b38758a981c000 Mon Sep 17 00:00:00 2001 From: Nihantra Patel Date: Wed, 19 Jun 2024 14:31:52 +0530 Subject: [PATCH 086/734] refactor: item-wise purchase history (query to script report) (cherry picked from commit 5de91cf55e768abfe419e6e8b6f83fa381769cc1) --- .../item_wise_purchase_history.js | 69 +++++ .../item_wise_purchase_history.json | 45 +-- .../item_wise_purchase_history.py | 292 ++++++++++++++++++ 3 files changed, 385 insertions(+), 21 deletions(-) create mode 100644 erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.js create mode 100644 erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.py diff --git a/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.js b/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.js new file mode 100644 index 00000000000..f4f311cc1dd --- /dev/null +++ b/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.js @@ -0,0 +1,69 @@ +// Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.query_reports["Item-wise Purchase History"] = { + filters: [ + { + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), + reqd: 1, + }, + { + fieldname: "from_date", + reqd: 1, + label: __("From Date"), + fieldtype: "Date", + default: frappe.datetime.add_months( + frappe.datetime.get_today(), + -1, + ), + }, + { + fieldname: "to_date", + reqd: 1, + default: frappe.datetime.get_today(), + label: __("To Date"), + fieldtype: "Date", + }, + { + fieldname: "item_group", + label: __("Item Group"), + fieldtype: "Link", + options: "Item Group", + }, + { + fieldname: "item_code", + label: __("Item"), + fieldtype: "Link", + options: "Item", + get_query: () => { + return { + query: "erpnext.controllers.queries.item_query", + }; + }, + }, + { + fieldname: "supplier", + label: __("Supplier"), + fieldtype: "Link", + options: "Supplier", + }, + ], + + formatter: function (value, row, column, data, default_formatter) { + value = default_formatter(value, row, column, data); + let format_fields = ["received_qty", "billed_amt"]; + + if ( + in_list(format_fields, column.fieldname) && + data && + data[column.fieldname] > 0 + ) { + value = "" + value + ""; + } + return value; + }, +}; diff --git a/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.json b/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.json index 521c68c5329..e6c3c624e01 100644 --- a/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.json +++ b/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.json @@ -1,30 +1,33 @@ { - "add_total_row": 1, - "apply_user_permissions": 1, - "creation": "2013-05-03 14:55:53", - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 3, - "is_standard": "Yes", - "modified": "2017-02-24 20:08:57.446613", - "modified_by": "Administrator", - "module": "Buying", - "name": "Item-wise Purchase History", - "owner": "Administrator", - "query": "select\n po_item.item_code as \"Item Code:Link/Item:120\",\n\tpo_item.item_name as \"Item Name::120\",\n po_item.item_group as \"Item Group:Link/Item Group:120\",\n\tpo_item.description as \"Description::150\",\n\tpo_item.qty as \"Qty:Float:100\",\n\tpo_item.uom as \"UOM:Link/UOM:80\",\n\tpo_item.base_rate as \"Rate:Currency:120\",\n\tpo_item.base_amount as \"Amount:Currency:120\",\n\tpo.name as \"Purchase Order:Link/Purchase Order:120\",\n\tpo.transaction_date as \"Transaction Date:Date:140\",\n\tpo.supplier as \"Supplier:Link/Supplier:130\",\n sup.supplier_name as \"Supplier Name::150\",\n\tpo_item.project as \"Project:Link/Project:130\",\n\tifnull(po_item.received_qty, 0) as \"Received Qty:Float:120\",\n\tpo.company as \"Company:Link/Company:\"\nfrom\n\t`tabPurchase Order` po, `tabPurchase Order Item` po_item, `tabSupplier` sup\nwhere\n\tpo.name = po_item.parent and po.supplier = sup.name and po.docstatus = 1\norder by po.name desc", - "ref_doctype": "Purchase Order", - "report_name": "Item-wise Purchase History", - "report_type": "Query Report", - "roles": [ + "add_total_row": 1, + "columns": [], + "creation": "2013-05-03 14:55:53", + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [], + "idx": 5, + "is_standard": "Yes", + "letterhead": null, + "modified": "2024-06-19 12:12:15.418799", + "modified_by": "Administrator", + "module": "Buying", + "name": "Item-wise Purchase History", + "owner": "Administrator", + "prepared_report": 0, + "query": "", + "ref_doctype": "Purchase Order", + "report_name": "Item-wise Purchase History", + "report_type": "Script Report", +"roles": [ { "role": "Stock User" - }, + }, { "role": "Purchase Manager" - }, + }, { "role": "Purchase User" } - ] + ] } \ No newline at end of file diff --git a/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.py b/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.py new file mode 100644 index 00000000000..6665c7a8033 --- /dev/null +++ b/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.py @@ -0,0 +1,292 @@ +# Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +import frappe +from frappe import _ +from frappe.utils import flt +from frappe.utils.nestedset import get_descendants_of + + +def execute(filters=None): + filters = frappe._dict(filters or {}) + if filters.from_date > filters.to_date: + frappe.throw(_("From Date cannot be greater than To Date")) + + columns = get_columns(filters) + data = get_data(filters) + + chart_data = get_chart_data(data) + + return columns, data, None, chart_data + + +def get_columns(filters): + return [ + { + "label": _("Item Code"), + "fieldtype": "Link", + "fieldname": "item_code", + "options": "Item", + "width": 120, + }, + { + "label": _("Item Name"), + "fieldtype": "Data", + "fieldname": "item_name", + "width": 140, + }, + { + "label": _("Item Group"), + "fieldtype": "Link", + "fieldname": "item_group", + "options": "Item Group", + "width": 120, + }, + { + "label": _("Description"), + "fieldtype": "Data", + "fieldname": "description", + "width": 140, + }, + { + "label": _("Quantity"), + "fieldtype": "Float", + "fieldname": "quantity", + "width": 120, + }, + { + "label": _("UOM"), + "fieldtype": "Link", + "fieldname": "uom", + "options": "UOM", + "width": 90, + }, + { + "label": _("Rate"), + "fieldname": "rate", + "fieldtype": "Currency", + "options": "currency", + "width": 120, + }, + { + "label": _("Amount"), + "fieldname": "amount", + "fieldtype": "Currency", + "options": "currency", + "width": 120, + }, + { + "label": _("Purchase Order"), + "fieldtype": "Link", + "fieldname": "purchase_order", + "options": "Purchase Order", + "width": 160, + }, + { + "label": _("Transaction Date"), + "fieldtype": "Date", + "fieldname": "transaction_date", + "width": 110, + }, + { + "label": _("Supplier"), + "fieldtype": "Link", + "fieldname": "supplier", + "options": "Supplier", + "width": 100, + }, + { + "label": _("Supplier Name"), + "fieldtype": "Data", + "fieldname": "supplier_name", + "width": 140, + }, + { + "label": _("Supplier Group"), + "fieldtype": "Link", + "fieldname": "supplier_group", + "options": "Supplier Group", + "width": 120, + }, + { + "label": _("Project"), + "fieldtype": "Link", + "fieldname": "project", + "options": "Project", + "width": 100, + }, + { + "label": _("Received Quantity"), + "fieldtype": "Float", + "fieldname": "received_qty", + "width": 150, + }, + { + "label": _("Billed Amount"), + "fieldtype": "Currency", + "fieldname": "billed_amt", + "options": "currency", + "width": 120, + }, + { + "label": _("Company"), + "fieldtype": "Link", + "fieldname": "company", + "options": "Company", + "width": 100, + }, + { + "label": _("Currency"), + "fieldtype": "Link", + "fieldname": "currency", + "options": "Currency", + "hidden": 1, + }, + ] + + +def get_data(filters): + data = [] + + company_list = get_descendants_of("Company", filters.get("company")) + company_list.append(filters.get("company")) + + supplier_details = get_supplier_details() + item_details = get_item_details() + purchase_order_records = get_purchase_order_details(company_list, filters) + + for record in purchase_order_records: + supplier_record = supplier_details.get(record.supplier) + item_record = item_details.get(record.item_code) + row = { + "item_code": record.get("item_code"), + "item_name": item_record.get("item_name"), + "item_group": item_record.get("item_group"), + "description": record.get("description"), + "quantity": record.get("qty"), + "uom": record.get("uom"), + "rate": record.get("base_rate"), + "amount": record.get("base_amount"), + "purchase_order": record.get("name"), + "transaction_date": record.get("transaction_date"), + "supplier": record.get("supplier"), + "supplier_name": supplier_record.get("supplier_name"), + "supplier_group": supplier_record.get("supplier_group"), + "project": record.get("project"), + "received_qty": flt(record.get("received_qty")), + "billed_amt": flt(record.get("billed_amt")), + "company": record.get("company"), + } + row["currency"] = frappe.get_cached_value( + "Company", row["company"], "default_currency" + ) + data.append(row) + + return data + + +def get_supplier_details(): + details = frappe.get_all( + "Supplier", fields=["name", "supplier_name", "supplier_group"] + ) + supplier_details = {} + for d in details: + supplier_details.setdefault( + d.name, + frappe._dict( + {"supplier_name": d.supplier_name, "supplier_group": d.supplier_group} + ), + ) + return supplier_details + + +def get_item_details(): + details = frappe.db.get_all("Item", fields=["name", "item_name", "item_group"]) + item_details = {} + for d in details: + item_details.setdefault( + d.name, frappe._dict({"item_name": d.item_name, "item_group": d.item_group}) + ) + return item_details + + +def get_purchase_order_details(company_list, filters): + db_po = frappe.qb.DocType("Purchase Order") + db_po_item = frappe.qb.DocType("Purchase Order Item") + + query = ( + frappe.qb.from_(db_po) + .inner_join(db_po_item) + .on(db_po_item.parent == db_po.name) + .select( + db_po.name, + db_po.supplier, + db_po.transaction_date, + db_po.project, + db_po.company, + db_po_item.item_code, + db_po_item.description, + db_po_item.qty, + db_po_item.uom, + db_po_item.base_rate, + db_po_item.base_amount, + db_po_item.received_qty, + (db_po_item.billed_amt * db_po.conversion_rate).as_("billed_amt"), + ) + .where(db_po.docstatus == 1) + .where(db_po.company.isin(tuple(company_list))) + ) + + if filters.get("item_group"): + query = query.where(db_po_item.item_group == filters.item_group) + + if filters.get("from_date"): + query = query.where(db_po.transaction_date >= filters.from_date) + + if filters.get("to_date"): + query = query.where(db_po.transaction_date <= filters.to_date) + + if filters.get("item_code"): + query = query.where(db_po_item.item_code == filters.item_code) + + if filters.get("supplier"): + query = query.where(db_po.supplier == filters.supplier) + + return query.run(as_dict=1) + + +def get_chart_data(data): + item_wise_purchase_map = {} + labels, datapoints = [], [] + + for row in data: + item_key = row.get("item_code") + + if item_key not in item_wise_purchase_map: + item_wise_purchase_map[item_key] = 0 + + item_wise_purchase_map[item_key] = flt(item_wise_purchase_map[item_key]) + flt( + row.get("amount") + ) + + item_wise_purchase_map = { + item: value + for item, value in ( + sorted(item_wise_purchase_map.items(), key=lambda i: i[1], reverse=True) + ) + } + + for key in item_wise_purchase_map: + labels.append(key) + datapoints.append(item_wise_purchase_map[key]) + + return { + "data": { + "labels": labels[:30], # show max of 30 items in chart + "datasets": [ + {"name": _("Total Purchase Amount"), "values": datapoints[:30]} + ], + }, + "type": "bar", + "fieldtype": "Currency", + } From 76d32ab07ae50ca77a09b86034e9c7da8613f096 Mon Sep 17 00:00:00 2001 From: Nihantra Patel Date: Wed, 19 Jun 2024 14:37:29 +0530 Subject: [PATCH 087/734] refactor: item-wise purchase history (query to script report) (cherry picked from commit 49331e6109fec93619c1fdf02a12f0c228973d3b) --- .../item_wise_purchase_history.json | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.json b/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.json index e6c3c624e01..35045afcf8b 100644 --- a/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.json +++ b/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.json @@ -1,25 +1,22 @@ { "add_total_row": 1, - "columns": [], "creation": "2013-05-03 14:55:53", + "disable_prepared_report": 0, "disabled": 0, "docstatus": 0, "doctype": "Report", - "filters": [], "idx": 5, "is_standard": "Yes", - "letterhead": null, "modified": "2024-06-19 12:12:15.418799", "modified_by": "Administrator", "module": "Buying", "name": "Item-wise Purchase History", "owner": "Administrator", "prepared_report": 0, - "query": "", "ref_doctype": "Purchase Order", "report_name": "Item-wise Purchase History", "report_type": "Script Report", -"roles": [ + "roles": [ { "role": "Stock User" }, From b4171e4bd99487fd4af66a9fe6d5cb2519d461bc Mon Sep 17 00:00:00 2001 From: Nihantra Patel Date: Wed, 19 Jun 2024 14:57:02 +0530 Subject: [PATCH 088/734] refactor: item-wise purchase history (query to script report) -- formatter (cherry picked from commit 003a9608dcecd5f5691c23ff9fb189bf541aea87) --- .../item_wise_purchase_history.js | 120 +++++++++--------- .../item_wise_purchase_history.py | 28 +--- 2 files changed, 65 insertions(+), 83 deletions(-) diff --git a/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.js b/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.js index f4f311cc1dd..fd1cbf7bede 100644 --- a/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.js +++ b/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.js @@ -2,68 +2,64 @@ // For license information, please see license.txt frappe.query_reports["Item-wise Purchase History"] = { - filters: [ - { - fieldname: "company", - label: __("Company"), - fieldtype: "Link", - options: "Company", - default: frappe.defaults.get_user_default("Company"), - reqd: 1, - }, - { - fieldname: "from_date", - reqd: 1, - label: __("From Date"), - fieldtype: "Date", - default: frappe.datetime.add_months( - frappe.datetime.get_today(), - -1, - ), - }, - { - fieldname: "to_date", - reqd: 1, - default: frappe.datetime.get_today(), - label: __("To Date"), - fieldtype: "Date", - }, - { - fieldname: "item_group", - label: __("Item Group"), - fieldtype: "Link", - options: "Item Group", - }, - { - fieldname: "item_code", - label: __("Item"), - fieldtype: "Link", - options: "Item", - get_query: () => { - return { - query: "erpnext.controllers.queries.item_query", - }; - }, - }, - { - fieldname: "supplier", - label: __("Supplier"), - fieldtype: "Link", - options: "Supplier", - }, - ], + filters: [ + { + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), + reqd: 1, + }, + { + fieldname: "from_date", + reqd: 1, + label: __("From Date"), + fieldtype: "Date", + default: frappe.datetime.add_months( + frappe.datetime.get_today(), + -1, + ), + }, + { + fieldname: "to_date", + reqd: 1, + default: frappe.datetime.get_today(), + label: __("To Date"), + fieldtype: "Date", + }, + { + fieldname: "item_group", + label: __("Item Group"), + fieldtype: "Link", + options: "Item Group", + }, + { + fieldname: "item_code", + label: __("Item"), + fieldtype: "Link", + options: "Item", + get_query: () => { + return { + query: "erpnext.controllers.queries.item_query", + }; + }, + }, + { + fieldname: "supplier", + label: __("Supplier"), + fieldtype: "Link", + options: "Supplier", + }, + ], - formatter: function (value, row, column, data, default_formatter) { - value = default_formatter(value, row, column, data); - let format_fields = ["received_qty", "billed_amt"]; + formatter: function (value, row, column, data, default_formatter) { + value = default_formatter(value, row, column, data); + let format_fields = ["received_qty", "billed_amt"]; - if ( - in_list(format_fields, column.fieldname) && - data && - data[column.fieldname] > 0 - ) { - value = "" + value + ""; - } - return value; - }, + if (in_list(format_fields, column.fieldname) && data && data[column.fieldname] > 0) { + value = "" + value + ""; + } + return value; + }, }; diff --git a/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.py b/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.py index 6665c7a8033..27aec9c3abe 100644 --- a/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.py +++ b/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.py @@ -177,25 +177,19 @@ def get_data(filters): "billed_amt": flt(record.get("billed_amt")), "company": record.get("company"), } - row["currency"] = frappe.get_cached_value( - "Company", row["company"], "default_currency" - ) + row["currency"] = frappe.get_cached_value("Company", row["company"], "default_currency") data.append(row) return data def get_supplier_details(): - details = frappe.get_all( - "Supplier", fields=["name", "supplier_name", "supplier_group"] - ) + details = frappe.get_all("Supplier", fields=["name", "supplier_name", "supplier_group"]) supplier_details = {} for d in details: supplier_details.setdefault( d.name, - frappe._dict( - {"supplier_name": d.supplier_name, "supplier_group": d.supplier_group} - ), + frappe._dict({"supplier_name": d.supplier_name, "supplier_group": d.supplier_group}), ) return supplier_details @@ -204,9 +198,7 @@ def get_item_details(): details = frappe.db.get_all("Item", fields=["name", "item_name", "item_group"]) item_details = {} for d in details: - item_details.setdefault( - d.name, frappe._dict({"item_name": d.item_name, "item_group": d.item_group}) - ) + item_details.setdefault(d.name, frappe._dict({"item_name": d.item_name, "item_group": d.item_group})) return item_details @@ -265,15 +257,11 @@ def get_chart_data(data): if item_key not in item_wise_purchase_map: item_wise_purchase_map[item_key] = 0 - item_wise_purchase_map[item_key] = flt(item_wise_purchase_map[item_key]) + flt( - row.get("amount") - ) + item_wise_purchase_map[item_key] = flt(item_wise_purchase_map[item_key]) + flt(row.get("amount")) item_wise_purchase_map = { item: value - for item, value in ( - sorted(item_wise_purchase_map.items(), key=lambda i: i[1], reverse=True) - ) + for item, value in (sorted(item_wise_purchase_map.items(), key=lambda i: i[1], reverse=True)) } for key in item_wise_purchase_map: @@ -283,9 +271,7 @@ def get_chart_data(data): return { "data": { "labels": labels[:30], # show max of 30 items in chart - "datasets": [ - {"name": _("Total Purchase Amount"), "values": datapoints[:30]} - ], + "datasets": [{"name": _("Total Purchase Amount"), "values": datapoints[:30]}], }, "type": "bar", "fieldtype": "Currency", From a4d3934f75fee7599a29c82036cba1a0f130b2bf Mon Sep 17 00:00:00 2001 From: Nihantra Patel Date: Wed, 19 Jun 2024 14:59:24 +0530 Subject: [PATCH 089/734] refactor: item-wise purchase history (query to script report) --prettier (cherry picked from commit f740c943638c5e226e5ef4ee41e434d8ab6ec509) --- .../item_wise_purchase_history/item_wise_purchase_history.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.js b/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.js index fd1cbf7bede..174f2f7cf9d 100644 --- a/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.js +++ b/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.js @@ -16,10 +16,7 @@ frappe.query_reports["Item-wise Purchase History"] = { reqd: 1, label: __("From Date"), fieldtype: "Date", - default: frappe.datetime.add_months( - frappe.datetime.get_today(), - -1, - ), + default: frappe.datetime.add_months(frappe.datetime.get_today(), -1), }, { fieldname: "to_date", From 6f75a3c6172e6f1c5e6b783e58733d1fc76cbcb7 Mon Sep 17 00:00:00 2001 From: Nihantra Patel Date: Wed, 19 Jun 2024 15:06:09 +0530 Subject: [PATCH 090/734] refactor: item-wise purchase history (query to script report) --prettier (cherry picked from commit 7bae18aba81ca9dbe64299a707f6713553d31336) --- .../item_wise_purchase_history/item_wise_purchase_history.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.js b/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.js index 174f2f7cf9d..37870b43b6d 100644 --- a/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.js +++ b/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.js @@ -54,7 +54,7 @@ frappe.query_reports["Item-wise Purchase History"] = { value = default_formatter(value, row, column, data); let format_fields = ["received_qty", "billed_amt"]; - if (in_list(format_fields, column.fieldname) && data && data[column.fieldname] > 0) { + if (format_fields.includes(column.fieldname) && data && data[column.fieldname] > 0) { value = "" + value + ""; } return value; From a0a932a23520852ccf4ed2e8cd78b8a1627bc16e Mon Sep 17 00:00:00 2001 From: Nihantra Patel Date: Wed, 19 Jun 2024 16:09:44 +0530 Subject: [PATCH 091/734] refactor: item-wise purchase history (query to script report) --upd (cherry picked from commit 2851764ed6ba977a0327aa09c32be7a1db27c301) --- .../item_wise_purchase_history.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.py b/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.py index 27aec9c3abe..a8950af3ea3 100644 --- a/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.py +++ b/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.py @@ -229,8 +229,9 @@ def get_purchase_order_details(company_list, filters): .where(db_po.company.isin(tuple(company_list))) ) - if filters.get("item_group"): - query = query.where(db_po_item.item_group == filters.item_group) + for field in ("item_code", "item_group"): + if filters.get(field): + query = query.where(db_po_item[field] == filters[field]) if filters.get("from_date"): query = query.where(db_po.transaction_date >= filters.from_date) @@ -238,9 +239,6 @@ def get_purchase_order_details(company_list, filters): if filters.get("to_date"): query = query.where(db_po.transaction_date <= filters.to_date) - if filters.get("item_code"): - query = query.where(db_po_item.item_code == filters.item_code) - if filters.get("supplier"): query = query.where(db_po.supplier == filters.supplier) From 2c990758992bc3f04be6f2606efb5450b8b89c52 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 27 Aug 2024 17:15:23 +0530 Subject: [PATCH 092/734] fix: Cannot read properties of null (reading 'doctype') (backport #42941) (#42943) fix: Cannot read properties of null (reading 'doctype') (#42941) (cherry picked from commit 86d3a9ab035fed3183eebfcfb3e961bfd9c31ea8) Co-authored-by: rohitwaghchaure --- erpnext/public/js/controllers/transaction.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 4696c7f3b4d..d8bf4a5cf17 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -1421,12 +1421,13 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe let show = cint(this.frm.doc.discount_amount) || ((this.frm.doc.taxes || []).filter(function(d) {return d.included_in_print_rate===1}).length); - if(frappe.meta.get_docfield(cur_frm.doctype, "net_total")) + if(this.frm.doc.doctype && frappe.meta.get_docfield(this.frm.doc.doctype, "net_total")) { this.frm.toggle_display("net_total", show); + } - if(frappe.meta.get_docfield(cur_frm.doctype, "base_net_total")) + if(this.frm.doc.doctype && frappe.meta.get_docfield(this.frm.doc.doctype, "base_net_total")) { this.frm.toggle_display("base_net_total", (show && (me.frm.doc.currency != company_currency))); - + } } change_grid_labels(company_currency) { From f56ee58e8161edd867fa19e9d0beeb8d87354985 Mon Sep 17 00:00:00 2001 From: David Date: Mon, 17 Jun 2024 10:25:48 +0200 Subject: [PATCH 093/734] fix: spec mobile and email fields for notifications (cherry picked from commit 18993a97ce684c231e40a870e1a96d7160d63bdf) --- erpnext/selling/doctype/customer/customer.json | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/erpnext/selling/doctype/customer/customer.json b/erpnext/selling/doctype/customer/customer.json index 59f082431b8..dec09e512fe 100644 --- a/erpnext/selling/doctype/customer/customer.json +++ b/erpnext/selling/doctype/customer/customer.json @@ -308,13 +308,15 @@ "fetch_from": "customer_primary_contact.mobile_no", "fieldname": "mobile_no", "fieldtype": "Read Only", - "label": "Mobile No" + "label": "Mobile No", + "options": "Mobile" }, { "fetch_from": "customer_primary_contact.email_id", "fieldname": "email_id", "fieldtype": "Read Only", - "label": "Email Id" + "label": "Email Id", + "options": "Email" }, { "fieldname": "column_break_26", @@ -592,7 +594,7 @@ "link_fieldname": "party" } ], - "modified": "2024-05-08 18:03:20.716169", + "modified": "2024-06-17 03:24:59.612974", "modified_by": "Administrator", "module": "Selling", "name": "Customer", @@ -677,4 +679,4 @@ "states": [], "title_field": "customer_name", "track_changes": 1 -} \ No newline at end of file +} From 8d8dd0cd2bbb34e23832aa83055e78fa08be2245 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 27 Aug 2024 23:07:37 +0530 Subject: [PATCH 094/734] feat: Disassembly Order (backport #42655) (#42957) * feat: Disassembly Order (#42655) (cherry picked from commit 663a08e4cd73a18e51d6d9e56fa9bf867525f370) # Conflicts: # erpnext/stock/doctype/stock_entry_type/stock_entry_type.json * chore: fix conflicts --------- Co-authored-by: rohitwaghchaure --- .../doctype/work_order/test_work_order.py | 50 +++++++++++ .../doctype/work_order/work_order.js | 85 ++++++++++++++++--- .../doctype/work_order/work_order.py | 11 ++- erpnext/patches.txt | 1 + .../add_disassembly_order_stock_entry_type.py | 13 +++ .../v15_0/set_standard_stock_entry_type.py | 1 + .../operations/install_fixtures.py | 1 + .../doctype/stock_entry/stock_entry.json | 8 +- .../stock/doctype/stock_entry/stock_entry.py | 55 ++++++++++++ .../stock_entry_type/stock_entry_type.json | 5 +- .../stock_entry_type/stock_entry_type.py | 2 + 11 files changed, 212 insertions(+), 20 deletions(-) create mode 100644 erpnext/patches/v15_0/add_disassembly_order_stock_entry_type.py diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py index d1f2cdbd1aa..627e5262dc9 100644 --- a/erpnext/manufacturing/doctype/work_order/test_work_order.py +++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py @@ -2053,6 +2053,55 @@ class TestWorkOrder(FrappeTestCase): "BOM", ) + def test_disassemby_order(self): + fg_item = "Test Disassembly Item" + source_warehouse = "Stores - _TC" + raw_materials = ["Test Disassembly RM Item 1", "Test Disassembly RM Item 2"] + + make_item(fg_item, {"is_stock_item": 1}) + for item in raw_materials: + make_item(item, {"is_stock_item": 1}) + test_stock_entry.make_stock_entry( + item_code=item, + target=source_warehouse, + qty=1, + basic_rate=100, + ) + + make_bom(item=fg_item, source_warehouse=source_warehouse, raw_materials=raw_materials) + + wo = make_wo_order_test_record( + item=fg_item, + qty=1, + source_warehouse=source_warehouse, + skip_transfer=1, + ) + + stock_entry = frappe.get_doc(make_stock_entry(wo.name, "Manufacture", 1)) + for row in stock_entry.items: + if row.item_code in raw_materials: + row.s_warehouse = source_warehouse + + stock_entry.submit() + + wo.reload() + self.assertEqual(wo.status, "Completed") + + stock_entry = frappe.get_doc(make_stock_entry(wo.name, "Disassemble", 1)) + stock_entry.save() + + self.assertEqual(stock_entry.purpose, "Disassemble") + + for row in stock_entry.items: + if row.item_code == fg_item: + self.assertTrue(row.s_warehouse) + self.assertFalse(row.t_warehouse) + else: + self.assertFalse(row.s_warehouse) + self.assertTrue(row.t_warehouse) + + stock_entry.submit() + def make_operation(**kwargs): kwargs = frappe._dict(kwargs) @@ -2370,6 +2419,7 @@ def make_wo_order_test_record(**args): wo_order.batch_size = args.batch_size or 0 if args.source_warehouse: + wo_order.source_warehouse = args.source_warehouse for item in wo_order.get("required_items"): item.source_warehouse = args.source_warehouse diff --git a/erpnext/manufacturing/doctype/work_order/work_order.js b/erpnext/manufacturing/doctype/work_order/work_order.js index 1da33f0ad9b..ae888c79714 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.js +++ b/erpnext/manufacturing/doctype/work_order/work_order.js @@ -183,13 +183,30 @@ frappe.ui.form.on("Work Order", { } } + if (frm.doc.status == "Completed") { + if (frm.doc.__onload.backflush_raw_materials_based_on == "Material Transferred for Manufacture") { + frm.add_custom_button( + __("BOM"), + () => { + frm.trigger("make_bom"); + }, + __("Create") + ); + } + } + if ( - frm.doc.status == "Completed" && - frm.doc.__onload.backflush_raw_materials_based_on == "Material Transferred for Manufacture" + frm.doc.docstatus === 1 && + ["Closed", "Completed"].includes(frm.doc.status) && + frm.doc.produced_qty > 0 ) { - frm.add_custom_button(__("Create BOM"), () => { - frm.trigger("make_bom"); - }); + frm.add_custom_button( + __("Disassembly Order"), + () => { + frm.trigger("make_disassembly_order"); + }, + __("Create") + ); } frm.trigger("add_custom_button_to_return_components"); @@ -337,6 +354,23 @@ frappe.ui.form.on("Work Order", { }); }, + make_disassembly_order(frm) { + erpnext.work_order + .show_prompt_for_qty_input(frm, "Disassemble") + .then((data) => { + return frappe.xcall("erpnext.manufacturing.doctype.work_order.work_order.make_stock_entry", { + work_order_id: frm.doc.name, + purpose: "Disassemble", + qty: data.qty, + target_warehouse: data.target_warehouse, + }); + }) + .then((stock_entry) => { + frappe.model.sync(stock_entry); + frappe.set_route("Form", stock_entry.doctype, stock_entry.name); + }); + }, + show_progress_for_items: function (frm) { var bars = []; var message = ""; @@ -745,6 +779,10 @@ erpnext.work_order = { get_max_transferable_qty: (frm, purpose) => { let max = 0; + if (purpose === "Disassemble") { + return flt(frm.doc.produced_qty); + } + if (frm.doc.skip_transfer) { max = flt(frm.doc.qty) - flt(frm.doc.produced_qty); } else { @@ -759,15 +797,38 @@ erpnext.work_order = { show_prompt_for_qty_input: function (frm, purpose) { let max = this.get_max_transferable_qty(frm, purpose); + + let fields = [ + { + fieldtype: "Float", + label: __("Qty for {0}", [__(purpose)]), + fieldname: "qty", + description: __("Max: {0}", [max]), + default: max, + }, + ]; + + if (purpose === "Disassemble") { + fields.push({ + fieldtype: "Link", + options: "Warehouse", + fieldname: "target_warehouse", + label: __("Target Warehouse"), + default: frm.doc.source_warehouse || frm.doc.wip_warehouse, + get_query() { + return { + filters: { + company: frm.doc.company, + is_group: 0, + }, + }; + }, + }); + } + return new Promise((resolve, reject) => { frappe.prompt( - { - fieldtype: "Float", - label: __("Qty for {0}", [__(purpose)]), - fieldname: "qty", - description: __("Max: {0}", [max]), - default: max, - }, + fields, (data) => { max += (frm.doc.qty * (frm.doc.__onload.overproduction_percentage || 0.0)) / 100; diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index 904e42a6a03..057c49949ec 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -1359,7 +1359,7 @@ def set_work_order_ops(name): @frappe.whitelist() -def make_stock_entry(work_order_id, purpose, qty=None): +def make_stock_entry(work_order_id, purpose, qty=None, target_warehouse=None): work_order = frappe.get_doc("Work Order", work_order_id) if not frappe.db.get_value("Warehouse", work_order.wip_warehouse, "is_group"): wip_warehouse = work_order.wip_warehouse @@ -1389,9 +1389,16 @@ def make_stock_entry(work_order_id, purpose, qty=None): stock_entry.to_warehouse = work_order.fg_warehouse stock_entry.project = work_order.project + if purpose == "Disassemble": + stock_entry.from_warehouse = work_order.fg_warehouse + stock_entry.to_warehouse = target_warehouse or work_order.source_warehouse + stock_entry.set_stock_entry_type() stock_entry.get_items() - stock_entry.set_serial_no_batch_for_finished_good() + + if purpose != "Disassemble": + stock_entry.set_serial_no_batch_for_finished_good() + return stock_entry.as_dict() diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 31ba6b23c01..feaac948099 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -372,4 +372,5 @@ erpnext.patches.v15_0.update_asset_repair_field_in_stock_entry erpnext.patches.v15_0.update_total_number_of_booked_depreciations erpnext.patches.v15_0.do_not_use_batchwise_valuation erpnext.patches.v15_0.drop_index_posting_datetime_from_sle +erpnext.patches.v15_0.add_disassembly_order_stock_entry_type #1 erpnext.patches.v15_0.set_standard_stock_entry_type diff --git a/erpnext/patches/v15_0/add_disassembly_order_stock_entry_type.py b/erpnext/patches/v15_0/add_disassembly_order_stock_entry_type.py new file mode 100644 index 00000000000..1f3413b172b --- /dev/null +++ b/erpnext/patches/v15_0/add_disassembly_order_stock_entry_type.py @@ -0,0 +1,13 @@ +import frappe + + +def execute(): + if not frappe.db.exists("Stock Entry Type", "Disassemble"): + frappe.get_doc( + { + "doctype": "Stock Entry Type", + "name": "Disassemble", + "purpose": "Disassemble", + "is_standard": 1, + } + ).insert(ignore_permissions=True) diff --git a/erpnext/patches/v15_0/set_standard_stock_entry_type.py b/erpnext/patches/v15_0/set_standard_stock_entry_type.py index 7551b0d4afc..4a721abae40 100644 --- a/erpnext/patches/v15_0/set_standard_stock_entry_type.py +++ b/erpnext/patches/v15_0/set_standard_stock_entry_type.py @@ -11,6 +11,7 @@ def execute(): "Manufacture", "Repack", "Send to Subcontractor", + "Disassemble", ]: if frappe.db.exists("Stock Entry Type", stock_entry_type): frappe.db.set_value("Stock Entry Type", stock_entry_type, "is_standard", 1) diff --git a/erpnext/setup/setup_wizard/operations/install_fixtures.py b/erpnext/setup/setup_wizard/operations/install_fixtures.py index a99289416ba..270a9e06054 100644 --- a/erpnext/setup/setup_wizard/operations/install_fixtures.py +++ b/erpnext/setup/setup_wizard/operations/install_fixtures.py @@ -96,6 +96,7 @@ def install(country=None): "purpose": "Repack", "is_standard": 1, }, + {"doctype": "Stock Entry Type", "name": "Disassemble", "purpose": "Disassemble", "is_standard": 1}, { "doctype": "Stock Entry Type", "name": "Send to Subcontractor", diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.json b/erpnext/stock/doctype/stock_entry/stock_entry.json index 3f467d3627a..5ba9f2973a1 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.json +++ b/erpnext/stock/doctype/stock_entry/stock_entry.json @@ -127,7 +127,7 @@ "label": "Purpose", "oldfieldname": "purpose", "oldfieldtype": "Select", - "options": "Material Issue\nMaterial Receipt\nMaterial Transfer\nMaterial Transfer for Manufacture\nMaterial Consumption for Manufacture\nManufacture\nRepack\nSend to Subcontractor", + "options": "Material Issue\nMaterial Receipt\nMaterial Transfer\nMaterial Transfer for Manufacture\nMaterial Consumption for Manufacture\nManufacture\nRepack\nSend to Subcontractor\nDisassemble", "read_only": 1, "search_index": 1 }, @@ -143,7 +143,7 @@ "reqd": 1 }, { - "depends_on": "eval:in_list([\"Material Transfer for Manufacture\", \"Manufacture\", \"Material Consumption for Manufacture\"], doc.purpose)", + "depends_on": "eval:in_list([\"Material Transfer for Manufacture\", \"Manufacture\", \"Material Consumption for Manufacture\", \"Disassemble\"], doc.purpose)", "fieldname": "work_order", "fieldtype": "Link", "label": "Work Order", @@ -242,7 +242,7 @@ }, { "default": "0", - "depends_on": "eval:in_list([\"Material Issue\", \"Material Transfer\", \"Manufacture\", \"Repack\", \"Send to Subcontractor\", \"Material Transfer for Manufacture\", \"Material Consumption for Manufacture\"], doc.purpose)", + "depends_on": "eval:in_list([\"Material Issue\", \"Material Transfer\", \"Manufacture\", \"Repack\", \"Send to Subcontractor\", \"Material Transfer for Manufacture\", \"Material Consumption for Manufacture\", \"Disassemble\"], doc.purpose)", "fieldname": "from_bom", "fieldtype": "Check", "label": "From BOM", @@ -697,7 +697,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2024-08-13 19:02:42.386955", + "modified": "2024-08-13 19:05:42.386955", "modified_by": "Administrator", "module": "Stock", "name": "Stock Entry", diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 6b4164dee5f..0ef54c78555 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -132,6 +132,7 @@ class StockEntry(StockController): "Manufacture", "Repack", "Send to Subcontractor", + "Disassemble", ] remarks: DF.Text | None sales_invoice_no: DF.Link | None @@ -337,6 +338,7 @@ class StockEntry(StockController): "Repack", "Send to Subcontractor", "Material Consumption for Manufacture", + "Disassemble", ] if self.purpose not in valid_purposes: @@ -616,6 +618,7 @@ class StockEntry(StockController): "Manufacture", "Material Transfer for Manufacture", "Material Consumption for Manufacture", + "Disassemble", ): # check if work order is entered @@ -1703,11 +1706,63 @@ class StockEntry(StockController): }, ) + def get_items_for_disassembly(self): + """Get items for Disassembly Order""" + + if not self.work_order: + frappe.throw(_("The Work Order is mandatory for Disassembly Order")) + + items = self.get_items_from_manufacture_entry() + + s_warehouse = "" + if self.work_order: + s_warehouse = frappe.db.get_value("Work Order", self.work_order, "fg_warehouse") + + for row in items: + child_row = self.append("items", {}) + for field, value in row.items(): + if value is not None: + child_row.set(field, value) + + child_row.s_warehouse = (self.from_warehouse or s_warehouse) if row.is_finished_item else "" + child_row.t_warehouse = self.to_warehouse if not row.is_finished_item else "" + child_row.is_finished_item = 0 if row.is_finished_item else 1 + + def get_items_from_manufacture_entry(self): + return frappe.get_all( + "Stock Entry", + fields=[ + "`tabStock Entry Detail`.`item_code`", + "`tabStock Entry Detail`.`item_name`", + "`tabStock Entry Detail`.`description`", + "`tabStock Entry Detail`.`qty`", + "`tabStock Entry Detail`.`transfer_qty`", + "`tabStock Entry Detail`.`stock_uom`", + "`tabStock Entry Detail`.`uom`", + "`tabStock Entry Detail`.`basic_rate`", + "`tabStock Entry Detail`.`conversion_factor`", + "`tabStock Entry Detail`.`is_finished_item`", + "`tabStock Entry Detail`.`batch_no`", + "`tabStock Entry Detail`.`serial_no`", + "`tabStock Entry Detail`.`use_serial_batch_fields`", + ], + filters=[ + ["Stock Entry", "purpose", "=", "Manufacture"], + ["Stock Entry", "work_order", "=", self.work_order], + ["Stock Entry", "docstatus", "=", 1], + ["Stock Entry Detail", "docstatus", "=", 1], + ], + order_by="`tabStock Entry Detail`.`idx` desc, `tabStock Entry Detail`.`is_finished_item` desc", + ) + @frappe.whitelist() def get_items(self): self.set("items", []) self.validate_work_order() + if self.purpose == "Disassemble": + return self.get_items_for_disassembly() + if not self.posting_date or not self.posting_time: frappe.throw(_("Posting date and posting time is mandatory")) diff --git a/erpnext/stock/doctype/stock_entry_type/stock_entry_type.json b/erpnext/stock/doctype/stock_entry_type/stock_entry_type.json index 9b426a6c940..c522df59941 100644 --- a/erpnext/stock/doctype/stock_entry_type/stock_entry_type.json +++ b/erpnext/stock/doctype/stock_entry_type/stock_entry_type.json @@ -17,7 +17,7 @@ "fieldtype": "Select", "in_list_view": 1, "label": "Purpose", - "options": "\nMaterial Issue\nMaterial Receipt\nMaterial Transfer\nMaterial Transfer for Manufacture\nMaterial Consumption for Manufacture\nManufacture\nRepack\nSend to Subcontractor", + "options": "\nMaterial Issue\nMaterial Receipt\nMaterial Transfer\nMaterial Transfer for Manufacture\nMaterial Consumption for Manufacture\nManufacture\nRepack\nSend to Subcontractor\nDisassemble", "reqd": 1, "set_only_once": 1 }, @@ -37,10 +37,11 @@ } ], "links": [], - "modified": "2024-08-20 15:35:45.696958", + "modified": "2024-08-24 16:00:22.696958", "modified_by": "Administrator", "module": "Stock", "name": "Stock Entry Type", + "naming_rule": "Set by user", "owner": "Administrator", "permissions": [ { diff --git a/erpnext/stock/doctype/stock_entry_type/stock_entry_type.py b/erpnext/stock/doctype/stock_entry_type/stock_entry_type.py index efbdd680c69..8d05a43408b 100644 --- a/erpnext/stock/doctype/stock_entry_type/stock_entry_type.py +++ b/erpnext/stock/doctype/stock_entry_type/stock_entry_type.py @@ -27,6 +27,7 @@ class StockEntryType(Document): "Manufacture", "Repack", "Send to Subcontractor", + "Disassemble", ] # end: auto-generated types @@ -45,5 +46,6 @@ class StockEntryType(Document): "Manufacture", "Repack", "Send to Subcontractor", + "Disassemble", ]: frappe.throw(f"Stock Entry Type {self.name} cannot be set as standard") From cb2cb4447a1ede7052c5f929a1e91aea7ab6582d Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 28 Aug 2024 05:04:27 +0000 Subject: [PATCH 095/734] chore(release): Bumped to Version 15.34.0 # [15.34.0](https://github.com/frappe/erpnext/compare/v15.33.5...v15.34.0) (2024-08-28) ### Bug Fixes * calculation correction for annual depreciation ([fa85482](https://github.com/frappe/erpnext/commit/fa8548266290e543e77b8255b3b98645c7f0ed05)) * call 'process' directly instead of creating 'process_subscripti ([7582827](https://github.com/frappe/erpnext/commit/758282739ed6d7ab4f60c4e1fa4aaf752400a681)) * Cannot read properties of null (reading 'doctype') (backport [#42941](https://github.com/frappe/erpnext/issues/42941)) ([#42943](https://github.com/frappe/erpnext/issues/42943)) ([2c99075](https://github.com/frappe/erpnext/commit/2c990758992bc3f04be6f2606efb5450b8b89c52)) * Column 'valuation_rate' cannot be null (backport [#42909](https://github.com/frappe/erpnext/issues/42909)) ([#42913](https://github.com/frappe/erpnext/issues/42913)) ([8c350d4](https://github.com/frappe/erpnext/commit/8c350d43b26fe195824eedd73092bc9345668e3f)) * custom stock entry type issue (backport [#42835](https://github.com/frappe/erpnext/issues/42835)) ([#42846](https://github.com/frappe/erpnext/issues/42846)) ([831e2aa](https://github.com/frappe/erpnext/commit/831e2aaf1840a2350249eefecb10ea29e7567908)) * do not copy date fields in opportunity doctype ([7401dc4](https://github.com/frappe/erpnext/commit/7401dc4015fe15862faf59d0492e63a57cf13d73)) * get amount with taxes and charges from payment entry ([c54e97b](https://github.com/frappe/erpnext/commit/c54e97b89a09f6cecd7380cccca71ece07fed5f6)) * include erpnext in apps page ([7428df8](https://github.com/frappe/erpnext/commit/7428df8778ac770085bda24bdca21bc1959e298a)) * incorrect in and out qty in the Batch-Wise Balance History (backport [#42866](https://github.com/frappe/erpnext/issues/42866)) ([#42876](https://github.com/frappe/erpnext/issues/42876)) ([d9ca680](https://github.com/frappe/erpnext/commit/d9ca680a29eea5a4c8b90fd66309df346c248359)) * incorrect Received Qty Amount in Purchase Order Analysis (backport [#42852](https://github.com/frappe/erpnext/issues/42852)) ([#42854](https://github.com/frappe/erpnext/issues/42854)) ([72c1609](https://github.com/frappe/erpnext/commit/72c16097d64384a4218e7646e2c4efcdc0790d82)) * last purchase rate not updated from purchase invoice (backport [#42847](https://github.com/frappe/erpnext/issues/42847)) ([#42853](https://github.com/frappe/erpnext/issues/42853)) ([2203ea9](https://github.com/frappe/erpnext/commit/2203ea9301eea01714809811ec8f774ebbc59b1e)) * LCV based on purchase invoice amount with multi-currency (backport [#42890](https://github.com/frappe/erpnext/issues/42890)) ([#42894](https://github.com/frappe/erpnext/issues/42894)) ([ff868a9](https://github.com/frappe/erpnext/commit/ff868a9290f272b3dc6a47c5b9f890d392cc8c82)) * make party naming sequential when naming_by set as auto name ([0650c22](https://github.com/frappe/erpnext/commit/0650c22b53cfd801970f88aa3b0230869ace8a00)) * not able to make stock entry against MR (backport [#42874](https://github.com/frappe/erpnext/issues/42874)) ([#42875](https://github.com/frappe/erpnext/issues/42875)) ([08bed61](https://github.com/frappe/erpnext/commit/08bed618f611560635b17fc5d77b6934b31b7551)) * same posting date and time, creation causing incorrect balance qty (backport [#42904](https://github.com/frappe/erpnext/issues/42904)) ([#42920](https://github.com/frappe/erpnext/issues/42920)) ([2624892](https://github.com/frappe/erpnext/commit/26248924b651d466fc9cf7ffe071787d65159af0)) * spec mobile and email fields for notifications ([f56ee58](https://github.com/frappe/erpnext/commit/f56ee58e8161edd867fa19e9d0beeb8d87354985)) * timeout while submitting stock entry (backport [#42929](https://github.com/frappe/erpnext/issues/42929)) ([#42931](https://github.com/frappe/erpnext/issues/42931)) ([ec26c92](https://github.com/frappe/erpnext/commit/ec26c92263ba2c7acdd3941b2569f59af6a6bed9)) * unsupported operand type(s) for *: 'float' and 'NoneType' (backport [#42916](https://github.com/frappe/erpnext/issues/42916)) ([#42918](https://github.com/frappe/erpnext/issues/42918)) ([8d29dc6](https://github.com/frappe/erpnext/commit/8d29dc6a81e95703b1215b2278a7186f565eabe9)) * update dimesions in exchange_gain_loss jv based on base document ([caa6ca1](https://github.com/frappe/erpnext/commit/caa6ca1d0b300e30f08bd431186e31161f8ef0a2)) * Update get_amount to return currency precision grand total ([976abf7](https://github.com/frappe/erpnext/commit/976abf7b3c92cd6387275442f87c784d4adf16e1)) * use of incorrect attribute ([80244ba](https://github.com/frappe/erpnext/commit/80244bafa44bdd2198788453bd9bae0973dc16ec)) ### Features * added finance book filter in depreciation and balances report ([5bdd298](https://github.com/frappe/erpnext/commit/5bdd2989c67293ed0e5d786637f4373ef7bb7c97)) * Disassembly Order (backport [#42655](https://github.com/frappe/erpnext/issues/42655)) ([#42957](https://github.com/frappe/erpnext/issues/42957)) ([8d8dd0c](https://github.com/frappe/erpnext/commit/8d8dd0cd2bbb34e23832aa83055e78fa08be2245)) * report to identify incorrectly cleared cheques ([25193c5](https://github.com/frappe/erpnext/commit/25193c5e92f7423668c696bd5996150299f58288)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 5ff4bdbe8ce..c798ed0a02f 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "15.33.5" +__version__ = "15.34.0" def get_default_company(user=None): From 6d51d14dfd3924e369073fc18e643fcdaaa26e6f Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 28 Aug 2024 12:21:22 +0530 Subject: [PATCH 096/734] fix: typeerror on Payment Entry (cherry picked from commit e9cf8937cd4a6c00666bf5f016b0647121d9838c) --- 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 57107c7a453..5c00aff587d 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -305,7 +305,7 @@ frappe.ui.form.on("Payment Entry", { set_dynamic_labels: function (frm) { var company_currency = frm.doc.company - ? frappe.get_doc(":Company", frm.doc.company).default_currency + ? frappe.get_doc(":Company", frm.doc.company)?.default_currency : ""; frm.set_currency_labels( From a833010d2bb09c7c5798d7aefad9c5dc61394641 Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Thu, 29 Aug 2024 01:51:45 +0530 Subject: [PATCH 097/734] fix: don't allow capitalizing only service item for new composite asset (cherry picked from commit f1d21382589830392154823db0e29b99c758dd89) --- .../asset_capitalization/asset_capitalization.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py index 4c22f7ddd7a..47c80d060e2 100644 --- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py +++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py @@ -317,7 +317,16 @@ class AssetCapitalization(StockController): if not self.target_is_fixed_asset and not self.get("asset_items"): frappe.throw(_("Consumed Asset Items is mandatory for Decapitalization")) - if not (self.get("stock_items") or self.get("asset_items") or self.get("service_items")): + if self.capitalization_method == "Create a new composite asset" and not ( + self.get("stock_items") or self.get("asset_items") + ): + frappe.throw( + _( + "Consumed Stock Items or Consumed Asset Items are mandatory for creating new composite asset" + ) + ) + + elif not (self.get("stock_items") or self.get("asset_items") or self.get("service_items")): frappe.throw( _( "Consumed Stock Items, Consumed Asset Items or Consumed Service Items is mandatory for Capitalization" From db746a4def25f0869708e480db8f1ecef95aba55 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 29 Aug 2024 11:46:29 +0530 Subject: [PATCH 098/734] refactor: better UX on Bank Clearance tool (cherry picked from commit 6a06e26d04b6fa50eab4068c9e26349560eb0fcb) --- .../doctype/bank_clearance/bank_clearance.js | 26 ++++++------------- 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/erpnext/accounts/doctype/bank_clearance/bank_clearance.js b/erpnext/accounts/doctype/bank_clearance/bank_clearance.js index 2993825482c..7ece7c9c2d6 100644 --- a/erpnext/accounts/doctype/bank_clearance/bank_clearance.js +++ b/erpnext/accounts/doctype/bank_clearance/bank_clearance.js @@ -38,6 +38,11 @@ frappe.ui.form.on("Bank Clearance", { frm.add_custom_button(__("Get Payment Entries"), () => frm.trigger("get_payment_entries")); frm.change_custom_button_type(__("Get Payment Entries"), null, "primary"); + if (frm.doc.payment_entries.length) { + frm.add_custom_button(__("Update Clearance Date"), () => frm.trigger("update_clearance_date")); + frm.change_custom_button_type(__("Get Payment Entries"), null, "default"); + frm.change_custom_button_type(__("Update Clearance Date"), null, "primary"); + } }, update_clearance_date: function (frm) { @@ -45,13 +50,7 @@ frappe.ui.form.on("Bank Clearance", { method: "update_clearance_date", doc: frm.doc, callback: function (r, rt) { - frm.refresh_field("payment_entries"); - frm.refresh_fields(); - - if (!frm.doc.payment_entries.length) { - frm.change_custom_button_type(__("Get Payment Entries"), null, "primary"); - frm.change_custom_button_type(__("Update Clearance Date"), null, "default"); - } + frm.refresh(); }, }); }, @@ -60,17 +59,8 @@ frappe.ui.form.on("Bank Clearance", { return frappe.call({ method: "get_payment_entries", doc: frm.doc, - callback: function (r, rt) { - frm.refresh_field("payment_entries"); - - if (frm.doc.payment_entries.length) { - frm.add_custom_button(__("Update Clearance Date"), () => - frm.trigger("update_clearance_date") - ); - - frm.change_custom_button_type(__("Get Payment Entries"), null, "default"); - frm.change_custom_button_type(__("Update Clearance Date"), null, "primary"); - } + callback: function () { + frm.refresh(); }, }); }, From 42e7725442c0c6d4ace34c84a9ca4ec8ae6d6e8f Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 29 Aug 2024 15:41:08 +0530 Subject: [PATCH 099/734] fix: add the company in payment request bcz delete company transactions (backport #42664) (#42982) * fix: add the company in payment request bcz delete company transactions (cherry picked from commit 12834ccf9aebb7c53e3eeafb4a5747b09da5eb77) # Conflicts: # erpnext/accounts/doctype/payment_request/payment_request.json * fix: link company when make payment request (cherry picked from commit e3008843d15ef0301c3171250bfcd81db738e5ae) * fix: add the company in payment request bcz delete company transactions --conflicts --------- Co-authored-by: Nihantra Patel Co-authored-by: Nihantra C. Patel <141945075+Nihantra-Patel@users.noreply.github.com> --- .../doctype/payment_request/payment_request.json | 10 +++++++++- .../doctype/payment_request/payment_request.py | 2 ++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_request/payment_request.json b/erpnext/accounts/doctype/payment_request/payment_request.json index 0537ee6d3a3..d0651f74bdf 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.json +++ b/erpnext/accounts/doctype/payment_request/payment_request.json @@ -9,6 +9,7 @@ "transaction_date", "column_break_2", "naming_series", + "company", "mode_of_payment", "party_details", "party_type", @@ -390,13 +391,20 @@ "options": "Payment Request", "print_hide": 1, "read_only": 1 + }, + { + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "read_only": 1 } ], "in_create": 1, "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2024-06-20 13:54:55.245774", + "modified": "2024-08-07 16:39:54.288002", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Request", diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index da63ccb6ad7..f368c2a9613 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -84,6 +84,7 @@ class PaymentRequest(Document): subscription_plans: DF.Table[SubscriptionPlanDetail] swift_number: DF.ReadOnly | None transaction_date: DF.Date | None + company: DF.Link | None # end: auto-generated types def validate(self): @@ -491,6 +492,7 @@ def make_payment_request(**args): "message": gateway_account.get("message") or get_dummy_message(ref_doc), "reference_doctype": args.dt, "reference_name": args.dn, + "company": ref_doc.get("company"), "party_type": args.get("party_type") or "Customer", "party": args.get("party") or ref_doc.get("customer"), "bank_account": bank_account, From 53e1b57354d3743b29bab2c758d6b26574e3cdc9 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 29 Aug 2024 15:54:55 +0530 Subject: [PATCH 100/734] refactor: link utility report with bank reconciliation statement (cherry picked from commit 00eac65712bc8dfdaf53589c8a4f5abc3d28a9c3) --- .../bank_reconciliation_statement.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.js b/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.js index efcfa7a5ee5..9335a8cd65a 100644 --- a/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.js +++ b/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.js @@ -46,4 +46,20 @@ frappe.query_reports["Bank Reconciliation Statement"] = { fieldtype: "Check", }, ], + formatter: function (value, row, column, data, default_formatter, filter) { + if (column.fieldname == "payment_entry" && value == "Cheques and Deposits incorrectly cleared") { + column.link_onclick = + "frappe.query_reports['Bank Reconciliation Statement'].open_utility_report()"; + } + return default_formatter(value, row, column, data); + }, + open_utility_report: function () { + frappe.route_options = { + company: frappe.query_report.get_filter_value("company"), + account: frappe.query_report.get_filter_value("account"), + report_date: frappe.query_report.get_filter_value("report_date"), + }; + frappe.open_in_new_tab = true; + frappe.set_route("query-report", "Cheques and Deposits Incorrectly cleared"); + }, }; From f3c60ea0a781d0aea185013bda4baa4ec3064525 Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Thu, 29 Aug 2024 15:50:02 +0530 Subject: [PATCH 101/734] fix(capitalization): debit cwip account instead of fixed asset account (#42857) * fix(capitalization): debit cwip account instead of fixed asset account * fix: post entries for capitalized asset through background jobs * chore: run pre-commit * fix: correct GL entries posting for composite assets * fix(minor): resolve failing check * chore: update gl entry check logic * chore: handle none values (cherry picked from commit 5d99f17583bc3aeb82d7b44a97466a4b9f765d96) --- erpnext/assets/doctype/asset/asset.py | 41 ++++++++++++++++++- .../asset_capitalization.py | 19 +++++++-- .../test_asset_capitalization.py | 9 ++-- 3 files changed, 60 insertions(+), 9 deletions(-) diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index db37e54ada8..8ede2670d38 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -691,12 +691,17 @@ class Asset(AccountsController): return cwip_account def make_gl_entries(self): + if self.check_asset_capitalization_gl_entries(): + return + gl_entries = [] purchase_document = self.get_purchase_document() fixed_asset_account, cwip_account = self.get_fixed_asset_account(), self.get_cwip_account() - if purchase_document and self.purchase_amount and getdate(self.available_for_use_date) <= getdate(): + if (self.is_composite_asset or (purchase_document and self.purchase_amount)) and getdate( + self.available_for_use_date + ) <= getdate(): gl_entries.append( self.get_gl_dict( { @@ -733,6 +738,24 @@ class Asset(AccountsController): make_gl_entries(gl_entries) self.db_set("booked_fixed_asset", 1) + def check_asset_capitalization_gl_entries(self): + if self.is_composite_asset: + result = frappe.db.get_value( + "Asset Capitalization", + {"target_asset": self.name, "docstatus": 1}, + ["name", "target_fixed_asset_account"], + ) + + if result: + asset_capitalization, target_fixed_asset_account = result + # Check GL entries for the retrieved Asset Capitalization and target fixed asset account + return has_gl_entries( + "Asset Capitalization", asset_capitalization, target_fixed_asset_account + ) + # return if there are no submitted capitalization for given asset + return True + return False + @frappe.whitelist() def get_depreciation_rate(self, args, on_validate=False): if isinstance(args, str): @@ -779,6 +802,22 @@ class Asset(AccountsController): return flt((100 * (1 - depreciation_rate)), float_precision) +def has_gl_entries(doctype, docname, target_account): + gl_entry = frappe.qb.DocType("GL Entry") + gl_entries = ( + frappe.qb.from_(gl_entry) + .select(gl_entry.account) + .where( + (gl_entry.voucher_type == doctype) + & (gl_entry.voucher_no == docname) + & (gl_entry.debit != 0) + & (gl_entry.account == target_account) + ) + .run(as_dict=True) + ) + return len(gl_entries) > 0 + + def update_maintenance_status(): assets = frappe.get_all( "Asset", filters={"docstatus": 1, "maintenance_required": 1, "disposal_date": ("is", "not set")} diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py index 47c80d060e2..b38bd952bca 100644 --- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py +++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py @@ -469,13 +469,24 @@ class AssetCapitalization(StockController): self.get_gl_entries_for_consumed_asset_items(gl_entries, target_account, target_against, precision) self.get_gl_entries_for_consumed_service_items(gl_entries, target_account, target_against, precision) - self.get_gl_entries_for_target_item(gl_entries, target_against, precision) + self.get_gl_entries_for_target_item(gl_entries, target_account, target_against, precision) return gl_entries def get_target_account(self): if self.target_is_fixed_asset: - return self.target_fixed_asset_account + from erpnext.assets.doctype.asset.asset import is_cwip_accounting_enabled + + asset_category = frappe.get_cached_value("Asset", self.target_asset, "asset_category") + if is_cwip_accounting_enabled(asset_category): + target_account = get_asset_category_account( + "capital_work_in_progress_account", + asset_category=asset_category, + company=self.company, + ) + return target_account if target_account else self.target_fixed_asset_account + else: + return self.target_fixed_asset_account else: return self.warehouse_account[self.target_warehouse]["account"] @@ -563,13 +574,13 @@ class AssetCapitalization(StockController): ) ) - def get_gl_entries_for_target_item(self, gl_entries, target_against, precision): + def get_gl_entries_for_target_item(self, gl_entries, target_account, target_against, precision): if self.target_is_fixed_asset: # Capitalization gl_entries.append( self.get_gl_dict( { - "account": self.target_fixed_asset_account, + "account": target_account, "against": ", ".join(target_against), "remarks": self.get("remarks") or _("Accounting Entry for Asset"), "debit": flt(self.total_value, precision), diff --git a/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py index 5508bdcbef2..37b92552887 100644 --- a/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py +++ b/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py @@ -187,9 +187,10 @@ class TestAssetCapitalization(unittest.TestCase): # Test General Ledger Entries default_expense_account = frappe.db.get_value("Company", company, "default_expense_account") expected_gle = { - "_Test Fixed Asset - _TC": 3000, - "Expenses Included In Asset Valuation - _TC": -1000, - default_expense_account: -2000, + "_Test Fixed Asset - _TC": -100000.0, + default_expense_account: -2000.0, + "CWIP Account - _TC": 103000.0, + "Expenses Included In Asset Valuation - _TC": -1000.0, } actual_gle = get_actual_gle_dict(asset_capitalization.name) @@ -424,7 +425,7 @@ class TestAssetCapitalization(unittest.TestCase): self.assertEqual(target_asset.purchase_amount, total_amount) expected_gle = { - "_Test Fixed Asset - _TC": 1000.0, + "CWIP Account - _TC": 1000.0, "Expenses Included In Asset Valuation - _TC": -1000.0, } From a35ce12d6000152feef4e87a15046d63a7b85d7e Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Fri, 30 Aug 2024 16:51:26 +0530 Subject: [PATCH 102/734] fix: added app permission check for apps page (cherry picked from commit e8f8fb8a8f68f365a57f870697a5c81933d11c2c) --- erpnext/__init__.py | 11 +++++++++++ erpnext/hooks.py | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 4a9236066cc..40fde01861b 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -2,6 +2,7 @@ import functools import inspect import frappe +from frappe.utils.user import is_website_user __version__ = "15.28.2" @@ -149,3 +150,13 @@ def allow_regional(fn): return frappe.get_attr(overrides[function_path][-1])(*args, **kwargs) return caller + + +def check_app_permission(): + if frappe.session.user == "Administrator": + return True + + if is_website_user(): + return False + + return True \ No newline at end of file diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 448d0d2a57b..038f58bfcb9 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -16,7 +16,7 @@ add_to_apps_screen = [ "logo": "/assets/erpnext/images/erpnext-logo-blue.png", "title": "ERPNext", "route": "/app/home", - # "has_permission": "erpnext.api.permission.has_app_permission" + "has_permission": "erpnext.check_app_permission" } ] From 30f034555b7b677244dc96b1a6e95d6004dbeba5 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Fri, 30 Aug 2024 16:57:17 +0530 Subject: [PATCH 103/734] chore: linter fix (cherry picked from commit 1d9ed27a89fcbe77c86b8fc18940e26fc3b255ac) --- erpnext/__init__.py | 2 +- erpnext/hooks.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 40fde01861b..70fd15175cb 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -159,4 +159,4 @@ def check_app_permission(): if is_website_user(): return False - return True \ No newline at end of file + return True diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 038f58bfcb9..1377288a06e 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -16,7 +16,7 @@ add_to_apps_screen = [ "logo": "/assets/erpnext/images/erpnext-logo-blue.png", "title": "ERPNext", "route": "/app/home", - "has_permission": "erpnext.check_app_permission" + "has_permission": "erpnext.check_app_permission", } ] From 6c8e0fd1fbefca5e69be899ce317ecb10a6c10e7 Mon Sep 17 00:00:00 2001 From: "Nihantra C. Patel" <141945075+Nihantra-Patel@users.noreply.github.com> Date: Mon, 2 Sep 2024 10:40:31 +0530 Subject: [PATCH 104/734] fix: update develop_version in hooks (cherry picked from commit 01b345e046501e273b8405a04b33759e4a3dddbb) --- erpnext/hooks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 1377288a06e..bd367d6d95e 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -20,7 +20,7 @@ add_to_apps_screen = [ } ] -develop_version = "14.x.x-develop" +develop_version = "15.x.x-develop" app_include_js = "erpnext.bundle.js" app_include_css = "erpnext.bundle.css" From fee2255661e6a930a627acca9faa271f7b30c112 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 16:29:30 +0530 Subject: [PATCH 105/734] fix: validate component quantity according to BOM (backport #43011) (#43014) * fix: validate component quantity according to BOM (#43011) (cherry picked from commit f3b91d4d62f6f5748d5eb3e0aa7739899057a3db) # Conflicts: # erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json * chore: fix conflicts --------- Co-authored-by: rohitwaghchaure --- .../manufacturing_settings.json | 59 +++++++++++++++---- .../manufacturing_settings.py | 9 ++- .../doctype/work_order/test_work_order.py | 53 +++++++++++++++++ .../js/utils/serial_no_batch_selector.js | 29 +++++++++ .../stock/doctype/stock_entry/stock_entry.py | 29 +++++++++ 5 files changed, 165 insertions(+), 14 deletions(-) diff --git a/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json b/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json index 63e3fa3e9ff..26cbc03eeb2 100644 --- a/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json +++ b/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json @@ -5,18 +5,17 @@ "document_type": "Document", "engine": "InnoDB", "field_order": [ + "bom_and_work_order_tab", "raw_materials_consumption_section", "material_consumption", "get_rm_cost_from_consumption_entry", "column_break_3", "backflush_raw_materials_based_on", - "capacity_planning", - "disable_capacity_planning", - "allow_overtime", - "allow_production_on_holidays", - "column_break_5", - "capacity_planning_for_days", - "mins_between_operations", + "validate_components_quantities_per_bom", + "bom_section", + "update_bom_costs_automatically", + "column_break_lhyt", + "manufacture_sub_assembly_in_operation", "section_break_6", "default_wip_warehouse", "default_fg_warehouse", @@ -30,8 +29,14 @@ "add_corrective_operation_cost_in_finished_good_valuation", "column_break_24", "job_card_excess_transfer", + "capacity_planning", + "disable_capacity_planning", + "allow_overtime", + "allow_production_on_holidays", + "column_break_5", + "capacity_planning_for_days", + "mins_between_operations", "other_settings_section", - "update_bom_costs_automatically", "set_op_cost_and_scrape_from_sub_assemblies", "column_break_23", "make_serial_no_batch_from_work_order" @@ -149,7 +154,7 @@ { "fieldname": "raw_materials_consumption_section", "fieldtype": "Section Break", - "label": "Raw Materials Consumption" + "label": "Raw Materials Consumption " }, { "fieldname": "column_break_16", @@ -183,8 +188,8 @@ }, { "fieldname": "job_card_section", - "fieldtype": "Section Break", - "label": "Job Card" + "fieldtype": "Tab Break", + "label": "Job Card and Capacity Planning" }, { "fieldname": "column_break_24", @@ -210,13 +215,41 @@ "fieldname": "get_rm_cost_from_consumption_entry", "fieldtype": "Check", "label": "Get Raw Materials Cost from Consumption Entry" + }, + { + "fieldname": "bom_and_work_order_tab", + "fieldtype": "Tab Break", + "label": "BOM and Production" + }, + { + "fieldname": "bom_section", + "fieldtype": "Section Break", + "label": "BOM" + }, + { + "fieldname": "column_break_lhyt", + "fieldtype": "Column Break" + }, + { + "default": "0", + "description": "If enabled then system will manufacture Sub-assembly against the Job Card (operation).", + "fieldname": "manufacture_sub_assembly_in_operation", + "fieldtype": "Check", + "label": "Manufacture Sub-assembly in Operation" + }, + { + "default": "0", + "depends_on": "eval:doc.backflush_raw_materials_based_on == \"BOM\"", + "fieldname": "validate_components_quantities_per_bom", + "fieldtype": "Check", + "label": "Validate Components Quantities Per BOM" } ], "icon": "icon-wrench", "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2024-02-08 19:00:37.561244", + "modified": "2024-09-02 12:12:03.132567", "modified_by": "Administrator", "module": "Manufacturing", "name": "Manufacturing Settings", @@ -234,4 +267,4 @@ "sort_order": "DESC", "states": [], "track_changes": 1 -} \ No newline at end of file +} diff --git a/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.py b/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.py index 84dbce2b83f..cb2916c8ac5 100644 --- a/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.py +++ b/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.py @@ -29,15 +29,22 @@ class ManufacturingSettings(Document): get_rm_cost_from_consumption_entry: DF.Check job_card_excess_transfer: DF.Check make_serial_no_batch_from_work_order: DF.Check + manufacture_sub_assembly_in_operation: DF.Check material_consumption: DF.Check mins_between_operations: DF.Int overproduction_percentage_for_sales_order: DF.Percent overproduction_percentage_for_work_order: DF.Percent set_op_cost_and_scrape_from_sub_assemblies: DF.Check update_bom_costs_automatically: DF.Check + validate_components_quantities_per_bom: DF.Check # end: auto-generated types - pass + def before_save(self): + self.reset_values() + + def reset_values(self): + if self.backflush_raw_materials_based_on != "BOM" and self.validate_components_quantities_per_bom: + self.validate_components_quantities_per_bom = 0 def get_mins_between_operations(): diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py index 627e5262dc9..ebc400979b6 100644 --- a/erpnext/manufacturing/doctype/work_order/test_work_order.py +++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py @@ -2102,6 +2102,59 @@ class TestWorkOrder(FrappeTestCase): stock_entry.submit() + def test_components_qty_for_bom_based_manufacture_entry(self): + frappe.db.set_single_value("Manufacturing Settings", "backflush_raw_materials_based_on", "BOM") + frappe.db.set_single_value("Manufacturing Settings", "validate_components_quantities_per_bom", 1) + + fg_item = "Test FG Item For Component Validation" + source_warehouse = "Stores - _TC" + raw_materials = ["Test Component Validation RM Item 1", "Test Component Validation RM Item 2"] + + make_item(fg_item, {"is_stock_item": 1}) + for item in raw_materials: + make_item(item, {"is_stock_item": 1}) + test_stock_entry.make_stock_entry( + item_code=item, + target=source_warehouse, + qty=10, + basic_rate=100, + ) + + make_bom(item=fg_item, source_warehouse=source_warehouse, raw_materials=raw_materials) + + wo = make_wo_order_test_record( + item=fg_item, + qty=10, + source_warehouse=source_warehouse, + ) + + transfer_entry = frappe.get_doc(make_stock_entry(wo.name, "Material Transfer for Manufacture", 10)) + transfer_entry.save() + for row in transfer_entry.items: + row.qty = 5 + + self.assertRaises(frappe.ValidationError, transfer_entry.save) + + transfer_entry.reload() + for row in transfer_entry.items: + self.assertEqual(row.qty, 10) + + transfer_entry.submit() + + manufacture_entry = frappe.get_doc(make_stock_entry(wo.name, "Manufacture", 10)) + manufacture_entry.save() + for row in manufacture_entry.items: + if not row.s_warehouse: + continue + + row.qty = 5 + + self.assertRaises(frappe.ValidationError, manufacture_entry.save) + manufacture_entry.reload() + manufacture_entry.submit() + + frappe.db.set_single_value("Manufacturing Settings", "validate_components_quantities_per_bom", 0) + def make_operation(**kwargs): kwargs = frappe._dict(kwargs) diff --git a/erpnext/public/js/utils/serial_no_batch_selector.js b/erpnext/public/js/utils/serial_no_batch_selector.js index 78efb46f4c3..2ddfc24adb1 100644 --- a/erpnext/public/js/utils/serial_no_batch_selector.js +++ b/erpnext/public/js/utils/serial_no_batch_selector.js @@ -368,8 +368,28 @@ erpnext.SerialBatchPackageSelector = class SerialNoBatchBundleUpdate { ]; } + get_batch_qty(batch_no, callback) { + let warehouse = this.item.s_warehouse || this.item.t_warehouse || this.item.warehouse; + frappe.call({ + method: "erpnext.stock.doctype.batch.batch.get_batch_qty", + args: { + batch_no: batch_no, + warehouse: warehouse, + item_code: this.item.item_code, + posting_date: this.frm.doc.posting_date, + posting_time: this.frm.doc.posting_time, + }, + callback: (r) => { + if (r.message) { + callback(flt(r.message)); + } + }, + }); + } + get_dialog_table_fields() { let fields = []; + let me = this; if (this.item.has_serial_no) { fields.push({ @@ -395,6 +415,15 @@ erpnext.SerialBatchPackageSelector = class SerialNoBatchBundleUpdate { fieldname: "batch_no", label: __("Batch No"), in_list_view: 1, + change() { + let doc = this.doc; + if (!doc.qty && me.item.type_of_transaction === "Outward") { + me.get_batch_qty(doc.batch_no, (qty) => { + doc.qty = qty; + this.grid.set_value("qty", qty, doc); + }); + } + }, get_query: () => { let is_inward = false; if ( diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 0ef54c78555..4ee4c6b2930 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -232,6 +232,7 @@ class StockEntry(StockController): self.validate_serialized_batch() self.calculate_rate_and_amount() self.validate_putaway_capacity() + self.validate_component_quantities() if not self.get("purpose") == "Manufacture": # ignore scrap item wh difference and empty source/target wh @@ -747,6 +748,34 @@ class StockEntry(StockController): title=_("Insufficient Stock"), ) + def validate_component_quantities(self): + if self.purpose not in ["Manufacture", "Material Transfer for Manufacture"]: + return + + if not frappe.db.get_single_value("Manufacturing Settings", "validate_components_quantities_per_bom"): + return + + if not self.fg_completed_qty: + return + + raw_materials = self.get_bom_raw_materials(self.fg_completed_qty) + + precision = frappe.get_precision("Stock Entry Detail", "qty") + for row in self.items: + if not row.s_warehouse: + continue + + if details := raw_materials.get(row.item_code): + if flt(details.get("qty"), precision) != flt(row.qty, precision): + frappe.throw( + _("For the item {0}, the quantity should be {1} according to the BOM {2}.").format( + frappe.bold(row.item_code), + flt(details.get("qty"), precision), + get_link_to_form("BOM", self.bom_no), + ), + title=_("Incorrect Component Quantity"), + ) + @frappe.whitelist() def get_stock_and_rate(self): """ From 88e5ed79988596adad658d9571b9b77137468a59 Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Mon, 2 Sep 2024 16:50:28 +0530 Subject: [PATCH 106/734] chore: test case failing issue (cherry picked from commit 0bdffdfa9835780149a09d06e498386cc6f6a7ff) --- .../test_asset_capitalization.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py index 37b92552887..86293fac765 100644 --- a/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py +++ b/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py @@ -31,6 +31,12 @@ class TestAssetCapitalization(unittest.TestCase): def test_capitalization_with_perpetual_inventory(self): company = "_Test Company with perpetual inventory" set_depreciation_settings_in_company(company=company) + name = frappe.db.get_value( + "Asset Category Account", + filters={"parent": "Computers", "company_name": company}, + fieldname=["name"], + ) + frappe.db.set_value("Asset Category Account", name, "capital_work_in_progress_account", "") # Variables consumed_asset_value = 100000 @@ -215,6 +221,12 @@ class TestAssetCapitalization(unittest.TestCase): def test_capitalization_with_wip_composite_asset(self): company = "_Test Company with perpetual inventory" set_depreciation_settings_in_company(company=company) + name = frappe.db.get_value( + "Asset Category Account", + filters={"parent": "Computers", "company_name": company}, + fieldname=["name"], + ) + frappe.db.set_value("Asset Category Account", name, "capital_work_in_progress_account", "") stock_rate = 1000 stock_qty = 2 From f0e3fb466a7deeb438b0ff7d9e932dea2298951d Mon Sep 17 00:00:00 2001 From: vishnu Date: Wed, 28 Aug 2024 15:10:08 +0000 Subject: [PATCH 107/734] fix: retain date filter when redirecting in Profit and Loss report (cherry picked from commit bb29fc4c3d9e55a5dff14529b37d1bc78b0fb3cc) --- erpnext/public/js/financial_statements.js | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/erpnext/public/js/financial_statements.js b/erpnext/public/js/financial_statements.js index bd8b70af02e..31abef62700 100644 --- a/erpnext/public/js/financial_statements.js +++ b/erpnext/public/js/financial_statements.js @@ -114,16 +114,17 @@ erpnext.financial_statements = { onload: function (report) { // dropdown for links to other financial statements erpnext.financial_statements.filters = get_filters(); + var filters = report.get_values(); - let fiscal_year = erpnext.utils.get_fiscal_year(frappe.datetime.get_today()); - - frappe.model.with_doc("Fiscal Year", fiscal_year, function (r) { - var fy = frappe.model.get_doc("Fiscal Year", fiscal_year); - frappe.query_report.set_filter_value({ - period_start_date: fy.year_start_date, - period_end_date: fy.year_end_date, + if (!filters.period_start_date || !filters.period_end_date) { + frappe.model.with_doc("Fiscal Year", fiscal_year, function (r) { + var fy = frappe.model.get_doc("Fiscal Year", fiscal_year); + frappe.query_report.set_filter_value({ + period_start_date: fy.year_start_date, + period_end_date: fy.year_end_date, + }); }); - }); + } if (report.page) { const views_menu = report.page.add_custom_button_group(__("Financial Statements")); From 4d7c0c004a2677be6fad1154ff1ff80eadfe7482 Mon Sep 17 00:00:00 2001 From: vishnu Date: Wed, 28 Aug 2024 15:12:22 +0000 Subject: [PATCH 108/734] fix: indentation (cherry picked from commit 598e9c1390d756ed0f1f0afa80fa13efe94af820) --- erpnext/public/js/financial_statements.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/public/js/financial_statements.js b/erpnext/public/js/financial_statements.js index 31abef62700..bd80c254889 100644 --- a/erpnext/public/js/financial_statements.js +++ b/erpnext/public/js/financial_statements.js @@ -114,6 +114,8 @@ erpnext.financial_statements = { onload: function (report) { // dropdown for links to other financial statements erpnext.financial_statements.filters = get_filters(); + + let fiscal_year = erpnext.utils.get_fiscal_year(frappe.datetime.get_today()); var filters = report.get_values(); if (!filters.period_start_date || !filters.period_end_date) { From b99cdb5be7be2d31acadc252404b792ba1d31561 Mon Sep 17 00:00:00 2001 From: "Nihantra C. Patel" <141945075+Nihantra-Patel@users.noreply.github.com> Date: Tue, 3 Sep 2024 15:54:42 +0530 Subject: [PATCH 109/734] fix: company accounts setup_queries (cherry picked from commit 80ace72541df3eb7b4e4daa6377f945308078751) --- erpnext/setup/doctype/company/company.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js index 939eaf25712..f14057a272e 100644 --- a/erpnext/setup/doctype/company/company.js +++ b/erpnext/setup/doctype/company/company.js @@ -20,7 +20,6 @@ frappe.ui.form.on("Company", { }, setup: function (frm) { frm.__rename_queue = "long"; - erpnext.company.setup_queries(frm); frm.set_query("parent_company", function () { return { @@ -81,6 +80,8 @@ frappe.ui.form.on("Company", { }, refresh: function (frm) { + erpnext.company.setup_queries(frm); + frm.toggle_display("address_html", !frm.is_new()); if (!frm.is_new()) { From 7d6984c87331d27705f9a92be13d84ed46afa946 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 3 Sep 2024 15:54:39 +0530 Subject: [PATCH 110/734] fix: typerror on default_currency (cherry picked from commit 4a7cc4da8743599755f13588cfa25f9d58ad4998) --- 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 5c00aff587d..b50dcd217e7 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -658,7 +658,7 @@ frappe.ui.form.on("Payment Entry", { frm.set_value("source_exchange_rate", 1); } else if (frm.doc.paid_from) { if (["Internal Transfer", "Pay"].includes(frm.doc.payment_type)) { - let company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency; + let company_currency = frappe.get_doc(":Company", frm.doc.company)?.default_currency; frappe.call({ method: "erpnext.setup.utils.get_exchange_rate", args: { From daa75eea00d46c565380c4303eba1e939fe3579a Mon Sep 17 00:00:00 2001 From: Lakshit Jain <108322669+ljain112@users.noreply.github.com> Date: Wed, 4 Sep 2024 12:56:04 +0530 Subject: [PATCH 111/734] fix: adjust price insertion logic for internal suppliers/customers (#42988) * fix: adjust price insertion logic for internal suppliers/customers * refactor: correct indentation, specify conditions within function * fix: typo --------- Co-authored-by: Smit Vora (cherry picked from commit 38f925b376d4dc95c99c19b11a27ade2db6dc81f) --- erpnext/stock/get_item_details.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 6485c5f6022..5cfa306066d 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -807,14 +807,10 @@ def get_price_list_rate(args, item_doc, out=None): if price_list_rate is None or frappe.db.get_single_value( "Stock Settings", "update_existing_price_list_rate" ): - if args.get("is_internal_supplier") or args.get("is_internal_customer"): - return out + insert_item_price(args) - if args.price_list and args.rate: - insert_item_price(args) - - if not price_list_rate: - return out + if price_list_rate is None: + return out out.price_list_rate = flt(price_list_rate) * flt(args.plc_conversion_rate) / flt(args.conversion_rate) @@ -835,6 +831,14 @@ def get_price_list_rate(args, item_doc, out=None): def insert_item_price(args): """Insert Item Price if Price List and Price List Rate are specified and currency is the same""" + if ( + not args.price_list + or not args.rate + or args.get("is_internal_supplier") + or args.get("is_internal_customer") + ): + return + if frappe.db.get_value("Price List", args.price_list, "currency", cache=True) == args.currency and cint( frappe.db.get_single_value("Stock Settings", "auto_insert_price_list_rate_if_missing") ): From 1121c6663f325502e867eea76a144dfe55c522b3 Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Mon, 26 Aug 2024 23:33:10 +0530 Subject: [PATCH 112/734] fix: link Purchase Invoice and Receipt Items to Asset --- .../purchase_invoice/purchase_invoice.py | 6 +- erpnext/assets/doctype/asset/asset.js | 5 ++ erpnext/assets/doctype/asset/asset.json | 24 +++++-- erpnext/assets/doctype/asset/asset.py | 2 + erpnext/controllers/buying_controller.py | 2 + erpnext/patches.txt | 1 + .../v15_0/link_purchase_item_to_asset_doc.py | 68 +++++++++++++++++++ .../purchase_receipt/purchase_receipt.py | 6 +- 8 files changed, 108 insertions(+), 6 deletions(-) create mode 100644 erpnext/patches/v15_0/link_purchase_item_to_asset_doc.py diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 78fc02999ef..de99c35e27e 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -1262,7 +1262,11 @@ class PurchaseInvoice(BuyingController): def update_gross_purchase_amount_for_linked_assets(self, item): assets = frappe.db.get_all( "Asset", - filters={"purchase_invoice": self.name, "item_code": item.item_code}, + filters={ + "purchase_invoice": self.name, + "item_code": item.item_code, + "purchase_invoice_item": ("in", [item.name, ""]), + }, fields=["name", "asset_quantity"], ) for asset in assets: diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js index 145e2ba3b3e..2477e443c4a 100644 --- a/erpnext/assets/doctype/asset/asset.js +++ b/erpnext/assets/doctype/asset/asset.js @@ -670,6 +670,11 @@ frappe.ui.form.on("Asset", { if (item.asset_location) { frm.set_value("location", item.asset_location); } + if (doctype === "Purchase Receipt") { + frm.set_value("purchase_receipt_item", item.name); + } else if (doctype === "Purchase Invoice") { + frm.set_value("purchase_invoice_item", item.name); + } }); }, diff --git a/erpnext/assets/doctype/asset/asset.json b/erpnext/assets/doctype/asset/asset.json index e408cc24d1e..55932e9b787 100644 --- a/erpnext/assets/doctype/asset/asset.json +++ b/erpnext/assets/doctype/asset/asset.json @@ -33,14 +33,16 @@ "dimension_col_break", "purchase_details_section", "purchase_receipt", + "purchase_receipt_item", "purchase_invoice", + "purchase_invoice_item", + "purchase_date", "available_for_use_date", - "total_asset_cost", - "additional_asset_cost", "column_break_23", "gross_purchase_amount", "asset_quantity", - "purchase_date", + "additional_asset_cost", + "total_asset_cost", "section_break_23", "calculate_depreciation", "column_break_33", @@ -536,6 +538,20 @@ "fieldname": "opening_number_of_booked_depreciations", "fieldtype": "Int", "label": "Opening Number of Booked Depreciations" + }, + { + "fieldname": "purchase_receipt_item", + "fieldtype": "Link", + "hidden": 1, + "label": "Purchase Receipt Item", + "options": "Purchase Receipt Item" + }, + { + "fieldname": "purchase_invoice_item", + "fieldtype": "Link", + "hidden": 1, + "label": "Purchase Invoice Item", + "options": "Purchase Invoice Item" } ], "idx": 72, @@ -579,7 +595,7 @@ "link_fieldname": "target_asset" } ], - "modified": "2024-08-01 16:39:09.340973", + "modified": "2024-08-26 23:28:29.095139", "modified_by": "Administrator", "module": "Assets", "name": "Asset", diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 8ede2670d38..f6846ea3cee 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -94,7 +94,9 @@ class Asset(AccountsController): purchase_amount: DF.Currency purchase_date: DF.Date | None purchase_invoice: DF.Link | None + purchase_invoice_item: DF.Link | None purchase_receipt: DF.Link | None + purchase_receipt_item: DF.Link | None split_from: DF.Link | None status: DF.Literal[ "Draft", diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index f797374d64e..31b6f391ba4 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -822,6 +822,8 @@ class BuyingController(SubcontractingController): "asset_quantity": asset_quantity, "purchase_receipt": self.name if self.doctype == "Purchase Receipt" else None, "purchase_invoice": self.name if self.doctype == "Purchase Invoice" else None, + "purchase_receipt_item": row.name if self.doctype == "Purchase Receipt" else None, + "purchase_invoice_item": row.name if self.doctype == "Purchase Invoice" else None, } ) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index feaac948099..2a97061a6a2 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -374,3 +374,4 @@ erpnext.patches.v15_0.do_not_use_batchwise_valuation erpnext.patches.v15_0.drop_index_posting_datetime_from_sle erpnext.patches.v15_0.add_disassembly_order_stock_entry_type #1 erpnext.patches.v15_0.set_standard_stock_entry_type +erpnext.patches.v15_0.link_purchase_item_to_asset_doc diff --git a/erpnext/patches/v15_0/link_purchase_item_to_asset_doc.py b/erpnext/patches/v15_0/link_purchase_item_to_asset_doc.py new file mode 100644 index 00000000000..e4311a8879d --- /dev/null +++ b/erpnext/patches/v15_0/link_purchase_item_to_asset_doc.py @@ -0,0 +1,68 @@ +import frappe + + +def execute(): + if frappe.db.has_column("Asset", "purchase_invoice_item") and frappe.db.has_column( + "Asset", "purchase_receipt_item" + ): + # Get all assets with their related Purchase Invoice and Purchase Receipt + assets = frappe.get_all( + "Asset", + filters={"docstatus": 0}, + fields=[ + "name", + "item_code", + "purchase_invoice", + "purchase_receipt", + "gross_purchase_amount", + "asset_quantity", + "purchase_invoice_item", + "purchase_receipt_item", + ], + ) + + for asset in assets: + # Get Purchase Invoice Items + if asset.purchase_invoice and not asset.purchase_invoice_item: + purchase_invoice_item = get_linked_item( + "Purchase Invoice Item", + asset.purchase_invoice, + asset.item_code, + asset.gross_purchase_amount, + asset.asset_quantity, + ) + frappe.db.set_value("Asset", asset.name, "purchase_invoice_item", purchase_invoice_item) + + # Get Purchase Receipt Items + if asset.purchase_receipt and not asset.purchase_receipt_item: + purchase_receipt_item = get_linked_item( + "Purchase Receipt Item", + asset.purchase_receipt, + asset.item_code, + asset.gross_purchase_amount, + asset.asset_quantity, + ) + frappe.db.set_value("Asset", asset.name, "purchase_receipt_item", purchase_receipt_item) + + +def get_linked_item(doctype, parent, item_code, amount, quantity): + items = frappe.get_all( + doctype, + filters={ + "parenttype": doctype.replace(" Item", ""), + "parent": parent, + "item_code": item_code, + }, + fields=["name", "amount", "qty", "landed_cost_voucher_amount"], + ) + if len(items) == 1: + # If only one item exists, return it directly + return items[0].name + + for item in items: + landed_cost = item.get("landed_cost_voucher_amount", 0) + if item.amount + landed_cost == amount and item.qty == quantity: + return item.name + + # If no exact match, return None + return None diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index afcb00141a1..42b70a08222 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -837,7 +837,11 @@ class PurchaseReceipt(BuyingController): def update_assets(self, item, valuation_rate): assets = frappe.db.get_all( "Asset", - filters={"purchase_receipt": self.name, "item_code": item.item_code}, + filters={ + "purchase_receipt": self.name, + "item_code": item.item_code, + "purchase_receipt_item": ("in", [item.name, ""]), + }, fields=["name", "asset_quantity"], ) From 3bb186736d8adbf83cdb1d75bb4f5f9db8af8532 Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Thu, 29 Aug 2024 17:10:12 +0530 Subject: [PATCH 113/734] fix: improve asset item matching logic --- .../patches/v15_0/link_purchase_item_to_asset_doc.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/erpnext/patches/v15_0/link_purchase_item_to_asset_doc.py b/erpnext/patches/v15_0/link_purchase_item_to_asset_doc.py index e4311a8879d..e5386c1de0a 100644 --- a/erpnext/patches/v15_0/link_purchase_item_to_asset_doc.py +++ b/erpnext/patches/v15_0/link_purchase_item_to_asset_doc.py @@ -53,7 +53,7 @@ def get_linked_item(doctype, parent, item_code, amount, quantity): "parent": parent, "item_code": item_code, }, - fields=["name", "amount", "qty", "landed_cost_voucher_amount"], + fields=["name", "rate", "amount", "qty", "landed_cost_voucher_amount"], ) if len(items) == 1: # If only one item exists, return it directly @@ -61,8 +61,13 @@ def get_linked_item(doctype, parent, item_code, amount, quantity): for item in items: landed_cost = item.get("landed_cost_voucher_amount", 0) - if item.amount + landed_cost == amount and item.qty == quantity: - return item.name + # Check if the asset is grouped + if quantity > 1: + if item.amount + landed_cost == amount and item.qty == quantity: + return item.name + else: + if item.rate + landed_cost == amount: + return item.name # If no exact match, return None return None From 957eabf53edc962393fa6c03acfebd1bcd587ff7 Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Fri, 30 Aug 2024 00:30:47 +0530 Subject: [PATCH 114/734] chore: resolved linter warnings with #nosemgrep --- erpnext/stock/stock_ledger.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 2aacd86e3d2..07d401de8c1 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -1532,7 +1532,7 @@ def get_previous_sle_of_current_voucher(args, operator="<", exclude_current_vouc operator = "<=" voucher_condition = f"and creation < '{creation}'" - sle = frappe.db.sql( + sle = frappe.db.sql( # nosemgrep f""" select *, posting_datetime as "timestamp" from `tabStock Ledger Entry` @@ -1629,6 +1629,7 @@ def get_stock_ledger_entries( if extra_cond: conditions += f"{extra_cond}" + # nosemgrep return frappe.db.sql( """ select *, posting_datetime as "timestamp" From 193d7981eabeea0b57c83255e0a76fa01fc20b43 Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Fri, 30 Aug 2024 00:40:01 +0530 Subject: [PATCH 115/734] chore: linters/semgrep check --- erpnext/stock/stock_ledger.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 07d401de8c1..8f9f39c7a7b 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -1745,7 +1745,7 @@ def get_valuation_rate( return batch_obj.get_incoming_rate() # Get valuation rate from last sle for the same item and warehouse - if last_valuation_rate := frappe.db.sql( + if last_valuation_rate := frappe.db.sql( # nosemgrep """select valuation_rate from `tabStock Ledger Entry` force index (item_warehouse) where @@ -1825,7 +1825,7 @@ def update_qty_in_future_sle(args, allow_negative_stock=False): detail = next_stock_reco_detail[0] datetime_limit_condition = get_datetime_limit_condition(detail) - frappe.db.sql( + frappe.db.sql( # nosemgrep f""" update `tabStock Ledger Entry` set qty_after_transaction = qty_after_transaction + {qty_shift} @@ -1992,7 +1992,7 @@ def is_negative_with_precision(neg_sle, is_batch=False): def get_future_sle_with_negative_qty(args): - return frappe.db.sql( + return frappe.db.sql( # nosemgrep """ select qty_after_transaction, posting_date, posting_time, @@ -2014,7 +2014,7 @@ def get_future_sle_with_negative_qty(args): def get_future_sle_with_negative_batch_qty(args): - return frappe.db.sql( + return frappe.db.sql( # nosemgrep """ with batch_ledger as ( select From e185a06a1519214269fa3941e8d38c2609001321 Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Mon, 2 Sep 2024 11:48:41 +0530 Subject: [PATCH 116/734] refactor: rename to in SLE query functions --- erpnext/stock/stock_ledger.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 8f9f39c7a7b..556aac09abd 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -1991,7 +1991,7 @@ def is_negative_with_precision(neg_sle, is_batch=False): return qty_deficit < 0 and abs(qty_deficit) > 0.0001 -def get_future_sle_with_negative_qty(args): +def get_future_sle_with_negative_qty(sle_args): return frappe.db.sql( # nosemgrep """ select @@ -2008,12 +2008,12 @@ def get_future_sle_with_negative_qty(args): order by posting_date asc, posting_time asc limit 1 """, - args, + sle_args, as_dict=1, ) -def get_future_sle_with_negative_batch_qty(args): +def get_future_sle_with_negative_batch_qty(sle_args): return frappe.db.sql( # nosemgrep """ with batch_ledger as ( @@ -2034,7 +2034,7 @@ def get_future_sle_with_negative_batch_qty(args): and posting_datetime >= %(posting_datetime)s limit 1 """, - args, + sle_args, as_dict=1, ) From f5a4ec129be3233bd98832900531414d31dd86b1 Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Tue, 3 Sep 2024 03:35:19 +0530 Subject: [PATCH 117/734] chore: patch correction --- erpnext/patches.txt | 1 + erpnext/patches/v15_0/link_purchase_item_to_asset_doc.py | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 2a97061a6a2..637ce3dbed1 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -374,4 +374,5 @@ erpnext.patches.v15_0.do_not_use_batchwise_valuation erpnext.patches.v15_0.drop_index_posting_datetime_from_sle erpnext.patches.v15_0.add_disassembly_order_stock_entry_type #1 erpnext.patches.v15_0.set_standard_stock_entry_type +erpnext.patches.v15_0.set_difference_amount_in_asset_value_adjustment erpnext.patches.v15_0.link_purchase_item_to_asset_doc diff --git a/erpnext/patches/v15_0/link_purchase_item_to_asset_doc.py b/erpnext/patches/v15_0/link_purchase_item_to_asset_doc.py index e5386c1de0a..662858e52a4 100644 --- a/erpnext/patches/v15_0/link_purchase_item_to_asset_doc.py +++ b/erpnext/patches/v15_0/link_purchase_item_to_asset_doc.py @@ -65,9 +65,10 @@ def get_linked_item(doctype, parent, item_code, amount, quantity): if quantity > 1: if item.amount + landed_cost == amount and item.qty == quantity: return item.name + elif item.qty == quantity: + return item.name else: - if item.rate + landed_cost == amount: + if item.rate + (landed_cost / item.qty) == amount: return item.name - # If no exact match, return None - return None + return items[0].name if items else None From 5d5ec2ab7c2ecee1e616ca3c54e66b9f48243806 Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Wed, 4 Sep 2024 03:08:30 +0530 Subject: [PATCH 118/734] chore: resolve test failing --- erpnext/patches.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 637ce3dbed1..2a97061a6a2 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -374,5 +374,4 @@ erpnext.patches.v15_0.do_not_use_batchwise_valuation erpnext.patches.v15_0.drop_index_posting_datetime_from_sle erpnext.patches.v15_0.add_disassembly_order_stock_entry_type #1 erpnext.patches.v15_0.set_standard_stock_entry_type -erpnext.patches.v15_0.set_difference_amount_in_asset_value_adjustment erpnext.patches.v15_0.link_purchase_item_to_asset_doc From 56dad7d365a7debf98b8739352002adb3294bc79 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 5 Sep 2024 16:00:33 +0530 Subject: [PATCH 119/734] fix: disabled batches showing in the list (backport #43024) (#43069) fix: disabled batches showing in the list (#43024) (cherry picked from commit c13a147df1057db8a5fac86b1a81dfd970f570bc) Co-authored-by: rohitwaghchaure --- erpnext/controllers/queries.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index a07a00dd03e..919bbb477f4 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -366,7 +366,7 @@ def get_batch_no(doctype, txt, searchfield, start, page_len, filters): def get_empty_batches(filters, start, page_len, filtered_batches=None, txt=None): - query_filter = {"item": filters.get("item_code")} + query_filter = {"item": filters.get("item_code"), "disabled": 0} if txt: query_filter["name"] = ("like", f"%{txt}%") From d2b200266461b9eaafab2f8e349189f7e017022d Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 5 Sep 2024 16:00:47 +0530 Subject: [PATCH 120/734] fix: auto reorder material request mail issue (backport #43066) (#43068) fix: auto reorder material request mail issue (#43066) fix: auto reorder matreial request mail issue (cherry picked from commit a8055a6da975a4a59ce17052d31d7672f3b46ba7) Co-authored-by: rohitwaghchaure --- erpnext/stock/reorder_item.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/reorder_item.py b/erpnext/stock/reorder_item.py index 7bc8b71917c..ed87906731e 100644 --- a/erpnext/stock/reorder_item.py +++ b/erpnext/stock/reorder_item.py @@ -354,9 +354,14 @@ def get_email_list(company): def get_comapny_wise_users(company): + companies = [company] + + if parent_company := frappe.db.get_value("Company", company, "parent_company"): + companies.append(parent_company) + users = frappe.get_all( "User Permission", - filters={"allow": "Company", "for_value": company, "apply_to_all_doctypes": 1}, + filters={"allow": "Company", "for_value": ("in", companies), "apply_to_all_doctypes": 1}, fields=["user"], ) From 99828d945fe13540b1ccbfb68b86c623c9832940 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 5 Sep 2024 14:38:22 +0530 Subject: [PATCH 121/734] refactor: Handle Emp Advance as separate row in AP report (cherry picked from commit eedf22b07a86c0c8ff6763e94f7f541482714fa5) --- .../accounts_receivable.py | 48 ++++++++++++------- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index f8511d2f497..59087a7c2d0 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -112,6 +112,26 @@ class ReceivablePayableReport: self.build_data() + def build_voucher_dict(self, ple): + return frappe._dict( + voucher_type=ple.voucher_type, + voucher_no=ple.voucher_no, + party=ple.party, + party_account=ple.account, + posting_date=ple.posting_date, + account_currency=ple.account_currency, + remarks=ple.remarks, + invoiced=0.0, + paid=0.0, + credit_note=0.0, + outstanding=0.0, + invoiced_in_account_currency=0.0, + paid_in_account_currency=0.0, + credit_note_in_account_currency=0.0, + outstanding_in_account_currency=0.0, + cost_center=ple.cost_center, + ) + def init_voucher_balance(self): # build all keys, since we want to exclude vouchers beyond the report date for ple in self.ple_entries: @@ -123,24 +143,8 @@ class ReceivablePayableReport: key = (ple.account, ple.voucher_type, ple.voucher_no, ple.party) if key not in self.voucher_balance: - self.voucher_balance[key] = frappe._dict( - voucher_type=ple.voucher_type, - voucher_no=ple.voucher_no, - party=ple.party, - party_account=ple.account, - posting_date=ple.posting_date, - account_currency=ple.account_currency, - remarks=ple.remarks, - invoiced=0.0, - paid=0.0, - credit_note=0.0, - outstanding=0.0, - invoiced_in_account_currency=0.0, - paid_in_account_currency=0.0, - credit_note_in_account_currency=0.0, - outstanding_in_account_currency=0.0, - cost_center=ple.cost_center, - ) + self.voucher_balance[key] = self.build_voucher_dict(ple) + self.get_invoices(ple) if self.filters.get("group_by_party"): @@ -208,6 +212,14 @@ class ReceivablePayableReport: row = self.voucher_balance.get(key) + # Build and use a separate row for Employee Advances. + # This allows Payments or Journals made against Emp Advance to be processed. + if not row and ple.against_voucher_type == "Employee Advance": + _d = self.build_voucher_dict(ple) + _d.voucher_type = ple.against_voucher_type + _d.voucher_no = ple.against_voucher_no + row = self.voucher_balance[key] = _d + if not row: # no invoice, this is an invoice / stand-alone payment / credit note if self.filters.get("ignore_accounts"): From efdc2173b233261d99fb8e7134158bd4805359a2 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 5 Sep 2024 16:12:52 +0530 Subject: [PATCH 122/734] refactor: filter to toggle employee advance scenario in AP (cherry picked from commit 257e13c2997a0d2fc7682ae9b7b5feca837a63dc) # Conflicts: # erpnext/accounts/report/accounts_payable/accounts_payable.js --- .../accounts/report/accounts_payable/accounts_payable.js | 5 +++++ .../report/accounts_receivable/accounts_receivable.py | 6 +++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/report/accounts_payable/accounts_payable.js b/erpnext/accounts/report/accounts_payable/accounts_payable.js index 86463f1f1f2..ee37c1a2153 100644 --- a/erpnext/accounts/report/accounts_payable/accounts_payable.js +++ b/erpnext/accounts/report/accounts_payable/accounts_payable.js @@ -158,8 +158,13 @@ frappe.query_reports["Accounts Payable"] = { fieldtype: "Check", }, { +<<<<<<< HEAD fieldname: "in_party_currency", label: __("In Party Currency"), +======= + fieldname: "handle_employee_advances", + label: __("Handle Employee Advances"), +>>>>>>> 257e13c299 (refactor: filter to toggle employee advance scenario in AP) fieldtype: "Check", }, ], diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index 59087a7c2d0..6303b317e78 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -214,7 +214,11 @@ class ReceivablePayableReport: # Build and use a separate row for Employee Advances. # This allows Payments or Journals made against Emp Advance to be processed. - if not row and ple.against_voucher_type == "Employee Advance": + if ( + not row + and ple.against_voucher_type == "Employee Advance" + and self.filters.handle_employee_advances + ): _d = self.build_voucher_dict(ple) _d.voucher_type = ple.against_voucher_type _d.voucher_no = ple.against_voucher_no From 7f261f34483aa3f8ebb38e55f480cc7d80d655c9 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Thu, 5 Sep 2024 11:44:28 +0000 Subject: [PATCH 123/734] chore(release): Bumped to Version 15.34.1 ## [15.34.1](https://github.com/frappe/erpnext/compare/v15.34.0...v15.34.1) (2024-09-05) ### Bug Fixes * add the company in payment request bcz delete company transactions (backport [#42664](https://github.com/frappe/erpnext/issues/42664)) ([#42982](https://github.com/frappe/erpnext/issues/42982)) ([42e7725](https://github.com/frappe/erpnext/commit/42e7725442c0c6d4ace34c84a9ca4ec8ae6d6e8f)) * added app permission check for apps page ([a35ce12](https://github.com/frappe/erpnext/commit/a35ce12d6000152feef4e87a15046d63a7b85d7e)) * adjust price insertion logic for internal suppliers/customers ([#42988](https://github.com/frappe/erpnext/issues/42988)) ([daa75ee](https://github.com/frappe/erpnext/commit/daa75eea00d46c565380c4303eba1e939fe3579a)) * auto reorder material request mail issue (backport [#43066](https://github.com/frappe/erpnext/issues/43066)) ([#43068](https://github.com/frappe/erpnext/issues/43068)) ([d2b2002](https://github.com/frappe/erpnext/commit/d2b200266461b9eaafab2f8e349189f7e017022d)) * **capitalization:** debit cwip account instead of fixed asset account ([#42857](https://github.com/frappe/erpnext/issues/42857)) ([f3c60ea](https://github.com/frappe/erpnext/commit/f3c60ea0a781d0aea185013bda4baa4ec3064525)) * company accounts setup_queries ([b99cdb5](https://github.com/frappe/erpnext/commit/b99cdb5be7be2d31acadc252404b792ba1d31561)) * disabled batches showing in the list (backport [#43024](https://github.com/frappe/erpnext/issues/43024)) ([#43069](https://github.com/frappe/erpnext/issues/43069)) ([56dad7d](https://github.com/frappe/erpnext/commit/56dad7d365a7debf98b8739352002adb3294bc79)) * don't allow capitalizing only service item for new composite asset ([a833010](https://github.com/frappe/erpnext/commit/a833010d2bb09c7c5798d7aefad9c5dc61394641)) * improve asset item matching logic ([3bb1867](https://github.com/frappe/erpnext/commit/3bb186736d8adbf83cdb1d75bb4f5f9db8af8532)) * indentation ([4d7c0c0](https://github.com/frappe/erpnext/commit/4d7c0c004a2677be6fad1154ff1ff80eadfe7482)) * link Purchase Invoice and Receipt Items to Asset ([1121c66](https://github.com/frappe/erpnext/commit/1121c6663f325502e867eea76a144dfe55c522b3)) * retain date filter when redirecting in Profit and Loss report ([f0e3fb4](https://github.com/frappe/erpnext/commit/f0e3fb466a7deeb438b0ff7d9e932dea2298951d)) * typeerror on Payment Entry ([6d51d14](https://github.com/frappe/erpnext/commit/6d51d14dfd3924e369073fc18e643fcdaaa26e6f)) * typerror on default_currency ([7d6984c](https://github.com/frappe/erpnext/commit/7d6984c87331d27705f9a92be13d84ed46afa946)) * update develop_version in hooks ([6c8e0fd](https://github.com/frappe/erpnext/commit/6c8e0fd1fbefca5e69be899ce317ecb10a6c10e7)) * validate component quantity according to BOM (backport [#43011](https://github.com/frappe/erpnext/issues/43011)) ([#43014](https://github.com/frappe/erpnext/issues/43014)) ([fee2255](https://github.com/frappe/erpnext/commit/fee2255661e6a930a627acca9faa271f7b30c112)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index eab2a96b941..e782ce5e145 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.34.0" +__version__ = "15.34.1" def get_default_company(user=None): From cb64f90d7d3a9e350b74773c9841b5dad39f1afe Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 5 Sep 2024 17:22:47 +0530 Subject: [PATCH 124/734] chore: resolve conflict --- .../accounts/report/accounts_payable/accounts_payable.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/report/accounts_payable/accounts_payable.js b/erpnext/accounts/report/accounts_payable/accounts_payable.js index ee37c1a2153..4f3ffd6b839 100644 --- a/erpnext/accounts/report/accounts_payable/accounts_payable.js +++ b/erpnext/accounts/report/accounts_payable/accounts_payable.js @@ -158,13 +158,13 @@ frappe.query_reports["Accounts Payable"] = { fieldtype: "Check", }, { -<<<<<<< HEAD fieldname: "in_party_currency", label: __("In Party Currency"), -======= + fieldtype: "Check", + }, + { fieldname: "handle_employee_advances", label: __("Handle Employee Advances"), ->>>>>>> 257e13c299 (refactor: filter to toggle employee advance scenario in AP) fieldtype: "Check", }, ], From 75cb29890d5f275dc172c4528fb1ecf41e02d8ea Mon Sep 17 00:00:00 2001 From: ljain112 Date: Tue, 27 Aug 2024 16:53:34 +0530 Subject: [PATCH 125/734] fix: `default_advance_account` field in Process Payment Reconciliation (cherry picked from commit 143209f91a201c58004408cd04812aefa49e9f94) # Conflicts: # erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.json --- .../process_payment_reconciliation.js | 20 ++++++++++++++++++- .../process_payment_reconciliation.json | 18 ++++++++++++++++- .../process_payment_reconciliation.py | 2 ++ 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.js b/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.js index 0f52a4d998e..9c0a14bd177 100644 --- a/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.js +++ b/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.js @@ -20,6 +20,17 @@ frappe.ui.form.on("Process Payment Reconciliation", { }, }; }); + + frm.set_query("default_advance_account", function (doc) { + return { + filters: { + company: doc.company, + is_group: 0, + account_type: doc.party_type == "Customer" ? "Receivable" : "Payable", + root_type: doc.party_type == "Customer" ? "Liability" : "Asset", + }, + }; + }); frm.set_query("cost_center", function (doc) { return { filters: { @@ -102,6 +113,7 @@ frappe.ui.form.on("Process Payment Reconciliation", { company(frm) { frm.set_value("party", ""); frm.set_value("receivable_payable_account", ""); + frm.set_value("default_advance_account", ""); }, party_type(frm) { frm.set_value("party", ""); @@ -109,6 +121,7 @@ frappe.ui.form.on("Process Payment Reconciliation", { party(frm) { frm.set_value("receivable_payable_account", ""); + frm.set_value("default_advance_account", ""); if (!frm.doc.receivable_payable_account && frm.doc.party_type && frm.doc.party) { return frappe.call({ method: "erpnext.accounts.party.get_party_account", @@ -119,7 +132,12 @@ frappe.ui.form.on("Process Payment Reconciliation", { }, callback: (r) => { if (!r.exc && r.message) { - frm.set_value("receivable_payable_account", r.message); + if (typeof r.message === "string") { + frm.set_value("receivable_payable_account", r.message); + } else if (Array.isArray(r.message)) { + frm.set_value("receivable_payable_account", r.message[0]); + frm.set_value("default_advance_account", r.message[1]); + } } frm.refresh(); }, diff --git a/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.json b/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.json index 1a1ab4d800e..b0584dd4c4d 100644 --- a/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.json +++ b/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.json @@ -13,6 +13,7 @@ "column_break_io6c", "party", "receivable_payable_account", + "default_advance_account", "filter_section", "from_invoice_date", "to_invoice_date", @@ -141,12 +142,27 @@ { "fieldname": "section_break_a8yx", "fieldtype": "Section Break" + }, + { + "depends_on": "eval:doc.party", + "description": "Only 'Payment Entries' made against this advance account are supported.", + "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/advance-in-separate-party-account", + "fieldname": "default_advance_account", + "fieldtype": "Link", + "label": "Default Advance Account", + "mandatory_depends_on": "doc.party_type", + "options": "Account", + "reqd": 1 } ], "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], +<<<<<<< HEAD "modified": "2023-08-11 10:56:51.699137", +======= + "modified": "2024-08-27 14:48:56.715320", +>>>>>>> 143209f91a (fix: `default_advance_account` field in Process Payment Reconciliation) "modified_by": "Administrator", "module": "Accounts", "name": "Process Payment Reconciliation", @@ -180,4 +196,4 @@ "sort_order": "DESC", "states": [], "title_field": "company" -} \ No newline at end of file +} diff --git a/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.py b/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.py index 5048fc5e25e..882a2c4ad1c 100644 --- a/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.py +++ b/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.py @@ -23,6 +23,7 @@ class ProcessPaymentReconciliation(Document): bank_cash_account: DF.Link | None company: DF.Link cost_center: DF.Link | None + default_advance_account: DF.Link error_log: DF.LongText | None from_invoice_date: DF.Date | None from_payment_date: DF.Date | None @@ -101,6 +102,7 @@ def get_pr_instance(doc: str): "party_type", "party", "receivable_payable_account", + "default_advance_account", "from_invoice_date", "to_invoice_date", "from_payment_date", From 84b0fa38d5b99b6c2b646852e0081dd8fee4f3d1 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 3 Sep 2024 17:27:50 +0530 Subject: [PATCH 126/734] refactor: fetch advance account on party seleection (cherry picked from commit c4ed04cb31a80e694a3c27ec92e4e574f2035d39) --- .../process_payment_reconciliation.js | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.js b/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.js index 9c0a14bd177..d72c4724690 100644 --- a/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.js +++ b/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.js @@ -129,6 +129,7 @@ frappe.ui.form.on("Process Payment Reconciliation", { company: frm.doc.company, party_type: frm.doc.party_type, party: frm.doc.party, + include_advance: 1, }, callback: (r) => { if (!r.exc && r.message) { From 9bd3d7a020ad8ce4e90da1ffdfddfc71510aab74 Mon Sep 17 00:00:00 2001 From: venkat102 Date: Thu, 5 Sep 2024 19:13:31 +0530 Subject: [PATCH 127/734] fix: cancel common party advance jv while canceling the invoice (cherry picked from commit 6a928b92dfa90de8290a3e5829deb69a80c6ccdb) --- erpnext/accounts/utils.py | 40 ++++++++++++++++++++++ erpnext/controllers/accounts_controller.py | 3 ++ 2 files changed, 43 insertions(+) diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 0450221222d..e462f749b54 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -781,6 +781,46 @@ def cancel_exchange_gain_loss_journal( gain_loss_je.cancel() +def cancel_common_party_journal(self): + if self.doctype not in ["Sales Invoice", "Purchase Invoice"]: + return + + if not frappe.db.get_single_value("Accounts Settings", "enable_common_party_accounting"): + return + + party_link = self.get_common_party_link() + if not party_link: + return + + journal_entry = frappe.db.get_value( + "Journal Entry Account", + filters={ + "reference_type": self.doctype, + "reference_name": self.name, + "docstatus": 1, + }, + fieldname="parent", + ) + + if not journal_entry: + return + + common_party_journal = frappe.db.get_value( + "Journal Entry", + filters={ + "name": journal_entry, + "is_system_generated": True, + "docstatus": 1, + }, + ) + + if not common_party_journal: + return + + common_party_je = frappe.get_doc("Journal Entry", common_party_journal) + common_party_je.cancel() + + def update_accounting_ledgers_after_reference_removal( ref_type: str | None = None, ref_no: str | None = None, payment_name: str | None = None ): diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index ce5d813b801..fbd717db153 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1580,6 +1580,7 @@ class AccountsController(TransactionBase): remove_from_bank_transaction, ) from erpnext.accounts.utils import ( + cancel_common_party_journal, cancel_exchange_gain_loss_journal, unlink_ref_doc_from_payment_entries, ) @@ -1591,6 +1592,7 @@ class AccountsController(TransactionBase): # Cancel Exchange Gain/Loss Journal before unlinking cancel_exchange_gain_loss_journal(self) + cancel_common_party_journal(self) if frappe.db.get_single_value("Accounts Settings", "unlink_payment_on_cancellation_of_invoice"): unlink_ref_doc_from_payment_entries(self) @@ -2424,6 +2426,7 @@ class AccountsController(TransactionBase): jv.posting_date = self.posting_date jv.company = self.company jv.remark = f"Adjustment for {self.doctype} {self.name}" + jv.is_system_generated = True reconcilation_entry = frappe._dict() advance_entry = frappe._dict() From 6c74180e1c2fae184ae3300d0bfb9b932e8b9a13 Mon Sep 17 00:00:00 2001 From: venkat102 Date: Thu, 5 Sep 2024 23:30:35 +0530 Subject: [PATCH 128/734] test: add unit test for canceling the common party advance jv created from sales invoice (cherry picked from commit 8c6e3f3c12164f30f044b3bf1fa0b71b66ff837d) --- .../sales_invoice/test_sales_invoice.py | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index fef30bdfecd..ce8627be27f 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -3162,6 +3162,50 @@ class TestSalesInvoice(FrappeTestCase): party_link.delete() frappe.db.set_single_value("Accounts Settings", "enable_common_party_accounting", 0) + def test_sales_invoice_cancel_with_common_party_advance_jv(self): + from erpnext.accounts.doctype.opening_invoice_creation_tool.test_opening_invoice_creation_tool import ( + make_customer, + ) + from erpnext.accounts.doctype.party_link.party_link import create_party_link + from erpnext.buying.doctype.supplier.test_supplier import create_supplier + + # create a customer + customer = make_customer(customer="_Test Common Supplier") + # create a supplier + supplier = create_supplier(supplier_name="_Test Common Supplier").name + + # create a party link between customer & supplier + party_link = create_party_link("Supplier", supplier, customer) + + # enable common party accounting + frappe.db.set_single_value("Accounts Settings", "enable_common_party_accounting", 1) + + # create a sales invoice + si = create_sales_invoice(customer=customer) + + # check creation of journal entry + jv = frappe.db.get_value( + "Journal Entry Account", + filters={ + "reference_type": si.doctype, + "reference_name": si.name, + "docstatus": 1, + }, + fieldname="parent", + ) + + self.assertTrue(jv) + + # cancel sales invoice + si.cancel() + + # check cancellation of journal entry + jv_status = frappe.db.get_value("Journal Entry", jv, "docstatus") + self.assertEqual(jv_status, 2) + + party_link.delete() + frappe.db.set_single_value("Accounts Settings", "enable_common_party_accounting", 0) + def test_payment_statuses(self): from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry From 6fde07da0e5a477f375429ace53067f1ab8eb0c8 Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Fri, 6 Sep 2024 23:46:29 +0530 Subject: [PATCH 129/734] fix(minor): reorder expected value validation (cherry picked from commit 0a6bf1559bd21f94b903b8304d90fd727382a91a) --- erpnext/assets/doctype/asset/asset.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index f6846ea3cee..abb615ff277 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -125,7 +125,6 @@ class Asset(AccountsController): self.validate_cost_center() self.set_missing_values() self.validate_gross_and_purchase_amount() - self.validate_expected_value_after_useful_life() self.validate_finance_books() if not self.split_from: @@ -146,6 +145,7 @@ class Asset(AccountsController): "Asset Depreciation Schedules created:
{0}

Please check, edit if needed, and submit the Asset." ).format(asset_depr_schedules_links) ) + self.validate_expected_value_after_useful_life() self.set_total_booked_depreciations() self.total_asset_cost = self.gross_purchase_amount self.status = self.get_status() From 8447bf34f07c230f3084a2fe66e0295db10a566f Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sat, 7 Sep 2024 17:32:42 +0530 Subject: [PATCH 130/734] fix: incorrect qty after transaction in SLE (backport #43103) (#43105) fix: incorrect qty after transaction in SLE (#43103) (cherry picked from commit 5ff87edc85b6bf1a355a44650440055abbb75ef5) Co-authored-by: rohitwaghchaure --- erpnext/stock/stock_ledger.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 556aac09abd..49a3a3335d1 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -1527,7 +1527,7 @@ def get_previous_sle_of_current_voucher(args, operator="<", exclude_current_vouc voucher_no = args.get("voucher_no") voucher_condition = f"and voucher_no != '{voucher_no}'" - elif args.get("creation"): + elif args.get("creation") and args.get("sle_id"): creation = args.get("creation") operator = "<=" voucher_condition = f"and creation < '{creation}'" From 0bc947f30db9ae0f5d99d3f44bb85c6568fef472 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sat, 7 Sep 2024 17:32:42 +0530 Subject: [PATCH 131/734] fix: incorrect qty after transaction in SLE (backport #43103) (#43105) fix: incorrect qty after transaction in SLE (#43103) (cherry picked from commit 5ff87edc85b6bf1a355a44650440055abbb75ef5) Co-authored-by: rohitwaghchaure (cherry picked from commit 8447bf34f07c230f3084a2fe66e0295db10a566f) --- erpnext/stock/stock_ledger.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 556aac09abd..49a3a3335d1 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -1527,7 +1527,7 @@ def get_previous_sle_of_current_voucher(args, operator="<", exclude_current_vouc voucher_no = args.get("voucher_no") voucher_condition = f"and voucher_no != '{voucher_no}'" - elif args.get("creation"): + elif args.get("creation") and args.get("sle_id"): creation = args.get("creation") operator = "<=" voucher_condition = f"and creation < '{creation}'" From 829660e7f39cb5895a0df54e4ed3c2f335a400cd Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Sat, 7 Sep 2024 12:40:39 +0000 Subject: [PATCH 132/734] chore(release): Bumped to Version 15.34.2 ## [15.34.2](https://github.com/frappe/erpnext/compare/v15.34.1...v15.34.2) (2024-09-07) ### Bug Fixes * incorrect qty after transaction in SLE (backport [#43103](https://github.com/frappe/erpnext/issues/43103)) ([#43105](https://github.com/frappe/erpnext/issues/43105)) ([0bc947f](https://github.com/frappe/erpnext/commit/0bc947f30db9ae0f5d99d3f44bb85c6568fef472)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index e782ce5e145..10c7018c9c1 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.34.1" +__version__ = "15.34.2" def get_default_company(user=None): From b832b60b28e9cd015f061b65f56e220551e42b08 Mon Sep 17 00:00:00 2001 From: Sanket322 <113279972+Sanket322@users.noreply.github.com> Date: Mon, 9 Sep 2024 06:40:17 +0530 Subject: [PATCH 133/734] refactor: age range in one field (#42736) * fix: age range in one field * fix: patch for custom reports * refactor: stock ageing and account payable report * fix: fixing the test cases * fix: common patch for reports with ageing * refactor: rename variable and minor refactor * fix: fixing the test case (cherry picked from commit 05de8994b09cf7e5c1364631316d6b5d8bd2cf2e) --- .../accounts_payable/accounts_payable.js | 30 +----- .../accounts_payable/test_accounts_payable.py | 5 +- .../accounts_payable_summary.js | 30 +----- .../accounts_receivable.js | 30 +----- .../accounts_receivable.py | 65 +++++-------- .../test_accounts_receivable.py | 95 ++++--------------- .../accounts_receivable_summary.js | 30 +----- .../accounts_receivable_summary.py | 54 ++++------- .../test_accounts_receivable_summary.py | 10 +- erpnext/accounts/test/test_reports.py | 4 +- erpnext/patches.txt | 1 + .../v14_0/update_reports_with_range.py | 36 +++++++ .../stock/report/stock_ageing/stock_ageing.js | 23 +---- .../stock/report/stock_ageing/stock_ageing.py | 41 ++++---- .../report/stock_ageing/test_stock_ageing.py | 4 +- erpnext/stock/report/test_reports.py | 2 +- 16 files changed, 142 insertions(+), 318 deletions(-) create mode 100644 erpnext/patches/v14_0/update_reports_with_range.py diff --git a/erpnext/accounts/report/accounts_payable/accounts_payable.js b/erpnext/accounts/report/accounts_payable/accounts_payable.js index 4f3ffd6b839..445e532183b 100644 --- a/erpnext/accounts/report/accounts_payable/accounts_payable.js +++ b/erpnext/accounts/report/accounts_payable/accounts_payable.js @@ -61,32 +61,10 @@ frappe.query_reports["Accounts Payable"] = { default: "Due Date", }, { - fieldname: "range1", - label: __("Ageing Range 1"), - fieldtype: "Int", - default: "30", - reqd: 1, - }, - { - fieldname: "range2", - label: __("Ageing Range 2"), - fieldtype: "Int", - default: "60", - reqd: 1, - }, - { - fieldname: "range3", - label: __("Ageing Range 3"), - fieldtype: "Int", - default: "90", - reqd: 1, - }, - { - fieldname: "range4", - label: __("Ageing Range 4"), - fieldtype: "Int", - default: "120", - reqd: 1, + fieldname: "range", + label: __("Ageing Range"), + fieldtype: "Data", + default: "30, 60, 90, 120", }, { fieldname: "payment_terms_template", diff --git a/erpnext/accounts/report/accounts_payable/test_accounts_payable.py b/erpnext/accounts/report/accounts_payable/test_accounts_payable.py index 43856bf569f..8971dc3d37b 100644 --- a/erpnext/accounts/report/accounts_payable/test_accounts_payable.py +++ b/erpnext/accounts/report/accounts_payable/test_accounts_payable.py @@ -30,10 +30,7 @@ class TestAccountsPayable(AccountsTestMixin, FrappeTestCase): "party_type": "Supplier", "party": [self.supplier], "report_date": today(), - "range1": 30, - "range2": 60, - "range3": 90, - "range4": 120, + "range": "30, 60, 90, 120", "in_party_currency": 1, } 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 92ea9e8f598..cf7a62c6b69 100644 --- a/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js +++ b/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js @@ -24,32 +24,10 @@ frappe.query_reports["Accounts Payable Summary"] = { default: "Due Date", }, { - fieldname: "range1", - label: __("Ageing Range 1"), - fieldtype: "Int", - default: "30", - reqd: 1, - }, - { - fieldname: "range2", - label: __("Ageing Range 2"), - fieldtype: "Int", - default: "60", - reqd: 1, - }, - { - fieldname: "range3", - label: __("Ageing Range 3"), - fieldtype: "Int", - default: "90", - reqd: 1, - }, - { - fieldname: "range4", - label: __("Ageing Range 4"), - fieldtype: "Int", - default: "120", - reqd: 1, + fieldname: "range", + label: __("Ageing Range"), + fieldtype: "Data", + default: "30, 60, 90, 120", }, { fieldname: "finance_book", diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js index 7e795fbe3c1..9f15bbc333d 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js @@ -89,32 +89,10 @@ frappe.query_reports["Accounts Receivable"] = { default: "Due Date", }, { - fieldname: "range1", - label: __("Ageing Range 1"), - fieldtype: "Int", - default: "30", - reqd: 1, - }, - { - fieldname: "range2", - label: __("Ageing Range 2"), - fieldtype: "Int", - default: "60", - reqd: 1, - }, - { - fieldname: "range3", - label: __("Ageing Range 3"), - fieldtype: "Int", - default: "90", - reqd: 1, - }, - { - fieldname: "range4", - label: __("Ageing Range 4"), - fieldtype: "Int", - default: "120", - reqd: 1, + fieldname: "range", + label: __("Ageing Range"), + fieldtype: "Data", + default: "30, 60, 90, 120", }, { fieldname: "customer_group", diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index 6303b317e78..1a7638aaae4 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -50,6 +50,11 @@ class ReceivablePayableReport: getdate(nowdate()) if self.filters.report_date > getdate(nowdate()) else self.filters.report_date ) + if not self.filters.range: + self.filters.range = "30, 60, 90, 120" + self.ranges = [num.strip() for num in self.filters.range.split(",") if num.strip().isdigit()] + self.range_numbers = [num for num in range(1, len(self.ranges) + 2)] + def run(self, args): self.filters.update(args) self.set_defaults() @@ -733,37 +738,22 @@ class ReceivablePayableReport: # ageing buckets should not have amounts if due date is not reached if getdate(entry_date) > getdate(self.filters.report_date): - row.range1 = row.range2 = row.range3 = row.range4 = row.range5 = 0.0 + [setattr(row, f"range{i}", 0.0) for i in self.range_numbers] - row.total_due = row.range1 + row.range2 + row.range3 + row.range4 + row.range5 + row.total_due = sum(row[f"range{i}"] for i in self.range_numbers) def get_ageing_data(self, entry_date, row): # [0-30, 30-60, 60-90, 90-120, 120-above] - row.range1 = row.range2 = row.range3 = row.range4 = row.range5 = 0.0 + [setattr(row, f"range{i}", 0.0) for i in self.range_numbers] if not (self.age_as_on and entry_date): return row.age = (getdate(self.age_as_on) - getdate(entry_date)).days or 0 - index = None - if not (self.filters.range1 and self.filters.range2 and self.filters.range3 and self.filters.range4): - self.filters.range1, self.filters.range2, self.filters.range3, self.filters.range4 = ( - 30, - 60, - 90, - 120, - ) - - for i, days in enumerate( - [self.filters.range1, self.filters.range2, self.filters.range3, self.filters.range4] - ): - if cint(row.age) <= cint(days): - index = i - break - - if index is None: - index = 4 + index = next( + (i for i, days in enumerate(self.ranges) if cint(row.age) <= cint(days)), len(self.ranges) + ) row["range" + str(index + 1)] = row.outstanding def get_ple_entries(self): @@ -1075,6 +1065,7 @@ class ReceivablePayableReport: self.add_column(_("Debit Note"), fieldname="credit_note") self.add_column(_("Outstanding Amount"), fieldname="outstanding") + self.add_column(label=_("Age (Days)"), fieldname="age", fieldtype="Int", width=80) self.setup_ageing_columns() self.add_column( @@ -1133,34 +1124,26 @@ class ReceivablePayableReport: def setup_ageing_columns(self): # for charts self.ageing_column_labels = [] - self.add_column(label=_("Age (Days)"), fieldname="age", fieldtype="Int", width=80) + ranges = [*self.ranges, "Above"] + + prev_range_value = 0 + for idx, curr_range_value in enumerate(ranges): + label = f"{prev_range_value}-{curr_range_value}" + self.add_column(label=label, fieldname="range" + str(idx + 1)) - for i, label in enumerate( - [ - "0-{range1}".format(range1=self.filters["range1"]), - "{range1}-{range2}".format( - range1=cint(self.filters["range1"]) + 1, range2=self.filters["range2"] - ), - "{range2}-{range3}".format( - range2=cint(self.filters["range2"]) + 1, range3=self.filters["range3"] - ), - "{range3}-{range4}".format( - range3=cint(self.filters["range3"]) + 1, range4=self.filters["range4"] - ), - _("{range4}-Above").format(range4=cint(self.filters["range4"]) + 1), - ] - ): - self.add_column(label=label, fieldname="range" + str(i + 1)) self.ageing_column_labels.append(label) + if curr_range_value.isdigit(): + prev_range_value = cint(curr_range_value) + 1 + def get_chart_data(self): + precision = cint(frappe.db.get_default("float_precision")) or 2 rows = [] for row in self.data: row = frappe._dict(row) if not cint(row.bold): - values = [row.range1, row.range2, row.range3, row.range4, row.range5] - precision = cint(frappe.db.get_default("float_precision")) or 2 - rows.append({"values": [flt(val, precision) for val in values]}) + values = [flt(row.get(f"range{i}", None), precision) for i in self.range_numbers] + rows.append({"values": values}) self.chart = { "data": {"labels": self.ageing_column_labels, "datasets": rows}, diff --git a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py index c4baa4e4842..39ca78153c3 100644 --- a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py @@ -83,10 +83,7 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase): "party": [self.customer], "report_date": add_days(today(), 2), "based_on_payment_terms": 0, - "range1": 30, - "range2": 60, - "range3": 90, - "range4": 120, + "range": "30, 60, 90, 120", "show_remarks": False, } @@ -116,10 +113,7 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase): "company": self.company, "based_on_payment_terms": 1, "report_date": today(), - "range1": 30, - "range2": 60, - "range3": 90, - "range4": 120, + "range": "30, 60, 90, 120", "show_remarks": True, } @@ -172,10 +166,7 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase): filters = { "company": self.company, "report_date": today(), - "range1": 30, - "range2": 60, - "range3": 90, - "range4": 120, + "range": "30, 60, 90, 120", "show_remarks": True, } @@ -266,10 +257,7 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase): "company": self.company, "based_on_payment_terms": 0, "report_date": today(), - "range1": 30, - "range2": 60, - "range3": 90, - "range4": 120, + "range": "30, 60, 90, 120", } report = execute(filters) @@ -328,10 +316,7 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase): filters = { "company": self.company, "report_date": today(), - "range1": 30, - "range2": 60, - "range3": 90, - "range4": 120, + "range": "30, 60, 90, 120", } report = execute(filters) @@ -397,10 +382,7 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase): filters = { "company": self.company, "report_date": today(), - "range1": 30, - "range2": 60, - "range3": 90, - "range4": 120, + "range": "30, 60, 90, 120", } report = execute(filters) self.assertEqual(report[1], []) @@ -416,10 +398,7 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase): filters = { "company": self.company, "report_date": today(), - "range1": 30, - "range2": 60, - "range3": 90, - "range4": 120, + "range": "30, 60, 90, 120", "group_by_party": True, } report = execute(filters)[1] @@ -493,10 +472,7 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase): filters = { "company": self.company, "report_date": today(), - "range1": 30, - "range2": 60, - "range3": 90, - "range4": 120, + "range": "30, 60, 90, 120", "show_future_payments": True, } report = execute(filters)[1] @@ -555,10 +531,7 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase): filters = { "company": self.company, "report_date": today(), - "range1": 30, - "range2": 60, - "range3": 90, - "range4": 120, + "range": "30, 60, 90, 120", "sales_person": sales_person.name, "show_sales_person": True, } @@ -575,10 +548,7 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase): filters = { "company": self.company, "report_date": today(), - "range1": 30, - "range2": 60, - "range3": 90, - "range4": 120, + "range": "30, 60, 90, 120", "cost_center": self.cost_center, } report = execute(filters)[1] @@ -593,10 +563,7 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase): filters = { "company": self.company, "report_date": today(), - "range1": 30, - "range2": 60, - "range3": 90, - "range4": 120, + "range": "30, 60, 90, 120", "customer_group": cus_group, } report = execute(filters)[1] @@ -618,10 +585,7 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase): filters = { "company": self.company, "report_date": today(), - "range1": 30, - "range2": 60, - "range3": 90, - "range4": 120, + "range": "30, 60, 90, 120", "customer_group": cus_groups_list, # Use the list of customer groups } report = execute(filters)[1] @@ -660,10 +624,7 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase): filters = { "company": self.company, "report_date": today(), - "range1": 30, - "range2": 60, - "range3": 90, - "range4": 120, + "range": "30, 60, 90, 120", "party_account": self.debit_to, } report = execute(filters)[1] @@ -711,10 +672,7 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase): "party_type": "Customer", "party": [self.customer], "report_date": today(), - "range1": 30, - "range2": 60, - "range3": 90, - "range4": 120, + "range": "30, 60, 90, 120", "in_party_currency": 1, } @@ -754,10 +712,7 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase): "party_type": "Customer", "party": [self.customer1, self.customer3], "report_date": today(), - "range1": 30, - "range2": 60, - "range3": 90, - "range4": 120, + "range": "30, 60, 90, 120", } si1 = self.create_sales_invoice(no_payment_schedule=True, do_not_submit=True) @@ -837,10 +792,7 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase): filters = { "company": self.company, "report_date": today(), - "range1": 30, - "range2": 60, - "range3": 90, - "range4": 120, + "range": "30, 60, 90, 120", } report_ouput = execute(filters)[1] @@ -903,10 +855,7 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase): { "company": self.company, "report_date": today(), - "range1": 30, - "range2": 60, - "range3": 90, - "range4": 120, + "range": "30, 60, 90, 120", "show_future_payments": True, "in_party_currency": False, } @@ -965,10 +914,7 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase): filters = { "company": self.company, "report_date": today(), - "range1": 30, - "range2": 60, - "range3": 90, - "range4": 120, + "range": "30, 60, 90, 120", } # check invoice grand total and invoiced column's value for 3 payment terms @@ -991,10 +937,7 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase): filters = { "company": self.company, "report_date": today(), - "range1": 30, - "range2": 60, - "range3": 90, - "range4": 120, + "range": "30, 60, 90, 120", } # check invoice grand total and invoiced column's value for 3 payment terms 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 964abc23747..e36f40169b3 100644 --- a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js +++ b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js @@ -24,32 +24,10 @@ frappe.query_reports["Accounts Receivable Summary"] = { default: "Due Date", }, { - fieldname: "range1", - label: __("Ageing Range 1"), - fieldtype: "Int", - default: "30", - reqd: 1, - }, - { - fieldname: "range2", - label: __("Ageing Range 2"), - fieldtype: "Int", - default: "60", - reqd: 1, - }, - { - fieldname: "range3", - label: __("Ageing Range 3"), - fieldtype: "Int", - default: "90", - reqd: 1, - }, - { - fieldname: "range4", - label: __("Ageing Range 4"), - fieldtype: "Int", - default: "120", - reqd: 1, + fieldname: "range", + label: __("Ageing Range"), + fieldtype: "Data", + default: "30, 60, 90, 120", }, { fieldname: "finance_book", diff --git a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py index 6a1b1057724..87fc7ea68be 100644 --- a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py +++ b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py @@ -104,25 +104,23 @@ class AccountsReceivableSummary(ReceivablePayableReport): self.set_party_details(d) def init_party_total(self, row): + default_dict = { + "invoiced": 0.0, + "paid": 0.0, + "credit_note": 0.0, + "outstanding": 0.0, + "total_due": 0.0, + "future_amount": 0.0, + "sales_person": [], + "party_type": row.party_type, + } + for i in self.range_numbers: + range_key = f"range{i}" + default_dict[range_key] = 0.0 + self.party_total.setdefault( row.party, - frappe._dict( - { - "invoiced": 0.0, - "paid": 0.0, - "credit_note": 0.0, - "outstanding": 0.0, - "range1": 0.0, - "range2": 0.0, - "range3": 0.0, - "range4": 0.0, - "range5": 0.0, - "total_due": 0.0, - "future_amount": 0.0, - "sales_person": [], - "party_type": row.party_type, - } - ), + frappe._dict(default_dict), ) def set_party_details(self, row): @@ -173,6 +171,7 @@ class AccountsReceivableSummary(ReceivablePayableReport): self.add_column(_("Difference"), fieldname="diff") self.setup_ageing_columns() + self.add_column(label="Total Amount Due", fieldname="total_due") if self.filters.show_future_payments: self.add_column(label=_("Future Payment Amount"), fieldname="future_amount") @@ -206,27 +205,6 @@ class AccountsReceivableSummary(ReceivablePayableReport): label=_("Currency"), fieldname="currency", fieldtype="Link", options="Currency", width=80 ) - def setup_ageing_columns(self): - for i, label in enumerate( - [ - "0-{range1}".format(range1=self.filters["range1"]), - "{range1}-{range2}".format( - range1=cint(self.filters["range1"]) + 1, range2=self.filters["range2"] - ), - "{range2}-{range3}".format( - range2=cint(self.filters["range2"]) + 1, range3=self.filters["range3"] - ), - "{range3}-{range4}".format( - range3=cint(self.filters["range3"]) + 1, range4=self.filters["range4"] - ), - "{range4}-{above}".format(range4=cint(self.filters["range4"]) + 1, above=_("Above")), - ] - ): - self.add_column(label=label, fieldname="range" + str(i + 1)) - - # Add column for total due amount - self.add_column(label="Total Amount Due", fieldname="total_due") - def get_gl_balance(report_date, company): return frappe._dict( diff --git a/erpnext/accounts/report/accounts_receivable_summary/test_accounts_receivable_summary.py b/erpnext/accounts/report/accounts_receivable_summary/test_accounts_receivable_summary.py index 4ef607bab28..a98cc6af7a3 100644 --- a/erpnext/accounts/report/accounts_receivable_summary/test_accounts_receivable_summary.py +++ b/erpnext/accounts/report/accounts_receivable_summary/test_accounts_receivable_summary.py @@ -27,10 +27,7 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase): "company": self.company, "customer": self.customer, "posting_date": today(), - "range1": 30, - "range2": 60, - "range3": 90, - "range4": 120, + "range": "30, 60, 90, 120", } si = create_sales_invoice( @@ -121,10 +118,7 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase): "company": self.company, "customer": self.customer, "posting_date": today(), - "range1": 30, - "range2": 60, - "range3": 90, - "range4": 120, + "range": "30, 60, 90, 120", } si = create_sales_invoice( diff --git a/erpnext/accounts/test/test_reports.py b/erpnext/accounts/test/test_reports.py index c2e10f8fd47..56b7832a32e 100644 --- a/erpnext/accounts/test/test_reports.py +++ b/erpnext/accounts/test/test_reports.py @@ -14,8 +14,8 @@ DEFAULT_FILTERS = { REPORT_FILTER_TEST_CASES: list[tuple[ReportName, ReportFilters]] = [ ("General Ledger", {"group_by": "Group by Voucher (Consolidated)"}), ("General Ledger", {"group_by": "Group by Voucher (Consolidated)", "include_dimensions": 1}), - ("Accounts Payable", {"range1": 30, "range2": 60, "range3": 90, "range4": 120}), - ("Accounts Receivable", {"range1": 30, "range2": 60, "range3": 90, "range4": 120}), + ("Accounts Payable", {"range": "30, 60, 90, 120"}), + ("Accounts Receivable", {"range": "30, 60, 90, 120"}), ("Consolidated Financial Statement", {"report": "Balance Sheet"}), ("Consolidated Financial Statement", {"report": "Profit and Loss Statement"}), ("Consolidated Financial Statement", {"report": "Cash Flow"}), diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 2a97061a6a2..7350fa77e1c 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -371,6 +371,7 @@ erpnext.patches.v15_0.update_warehouse_field_in_asset_repair_consumed_item_docty erpnext.patches.v15_0.update_asset_repair_field_in_stock_entry erpnext.patches.v15_0.update_total_number_of_booked_depreciations erpnext.patches.v15_0.do_not_use_batchwise_valuation +erpnext.patches.v14_0.update_reports_with_range erpnext.patches.v15_0.drop_index_posting_datetime_from_sle erpnext.patches.v15_0.add_disassembly_order_stock_entry_type #1 erpnext.patches.v15_0.set_standard_stock_entry_type diff --git a/erpnext/patches/v14_0/update_reports_with_range.py b/erpnext/patches/v14_0/update_reports_with_range.py new file mode 100644 index 00000000000..2bda265ca66 --- /dev/null +++ b/erpnext/patches/v14_0/update_reports_with_range.py @@ -0,0 +1,36 @@ +import json + +import frappe + +REFERENCE_REPORTS = [ + "Accounts Receivable", + "Accounts Receivable Summary", + "Accounts Payable", + "Accounts Payable Summary", + "Stock Ageing", +] + + +def execute(): + for report in REFERENCE_REPORTS: + update_reference_reports(report) + + +def update_reference_reports(reference_report): + reports = frappe.get_all( + "Report", filters={"reference_report": reference_report}, fields={"json", "name"} + ) + + for report in reports: + update_report_json(report) + update_reference_reports(report.name) + + +def update_report_json(report): + report_json = json.loads(report.json) + report_filter = report_json.get("filters") + + keys_to_pop = [key for key in report_filter if key.startswith("range")] + report_filter["range"] = ", ".join(str(report_filter.pop(key)) for key in keys_to_pop) + + frappe.db.set_value("Report", report.name, "json", json.dumps(report_json)) diff --git a/erpnext/stock/report/stock_ageing/stock_ageing.js b/erpnext/stock/report/stock_ageing/stock_ageing.js index 578869b6e93..a3ebf14571d 100644 --- a/erpnext/stock/report/stock_ageing/stock_ageing.js +++ b/erpnext/stock/report/stock_ageing/stock_ageing.js @@ -54,25 +54,10 @@ frappe.query_reports["Stock Ageing"] = { options: "Brand", }, { - fieldname: "range1", - label: __("Ageing Range 1"), - fieldtype: "Int", - default: "30", - reqd: 1, - }, - { - fieldname: "range2", - label: __("Ageing Range 2"), - fieldtype: "Int", - default: "60", - reqd: 1, - }, - { - fieldname: "range3", - label: __("Ageing Range 3"), - fieldtype: "Int", - default: "90", - reqd: 1, + fieldname: "range", + label: __("Ageing Range"), + fieldtype: "Data", + default: "30, 60, 90", }, { fieldname: "show_warehouse_wise_stock", diff --git a/erpnext/stock/report/stock_ageing/stock_ageing.py b/erpnext/stock/report/stock_ageing/stock_ageing.py index 09af3f224a3..c7e0af6cd38 100644 --- a/erpnext/stock/report/stock_ageing/stock_ageing.py +++ b/erpnext/stock/report/stock_ageing/stock_ageing.py @@ -16,6 +16,7 @@ Filters = frappe._dict def execute(filters: Filters = None) -> tuple: to_date = filters["to_date"] + filters.ranges = [num.strip() for num in filters.range.split(",") if num.strip().isdigit()] columns = get_columns(filters) item_details = FIFOSlots(filters).generate() @@ -48,7 +49,7 @@ def format_report_data(filters: Filters, item_details: dict, to_date: str) -> li average_age = get_average_age(fifo_queue, to_date) earliest_age = date_diff(to_date, fifo_queue[0][1]) latest_age = date_diff(to_date, fifo_queue[-1][1]) - range1, range2, range3, above_range3 = get_range_age(filters, fifo_queue, to_date, item_dict) + range_values = get_range_age(filters, fifo_queue, to_date, item_dict) row = [details.name, details.item_name, details.description, details.item_group, details.brand] @@ -59,10 +60,7 @@ def format_report_data(filters: Filters, item_details: dict, to_date: str) -> li [ flt(item_dict.get("total_qty"), precision), average_age, - range1, - range2, - range3, - above_range3, + *range_values, earliest_age, latest_age, details.stock_uom, @@ -89,25 +87,22 @@ def get_average_age(fifo_queue: list, to_date: str) -> float: return flt(age_qty / total_qty, 2) if total_qty else 0.0 -def get_range_age(filters: Filters, fifo_queue: list, to_date: str, item_dict: dict) -> tuple: +def get_range_age(filters: Filters, fifo_queue: list, to_date: str, item_dict: dict) -> list: precision = cint(frappe.db.get_single_value("System Settings", "float_precision", cache=True)) - - range1 = range2 = range3 = above_range3 = 0.0 + range_values = [0.0] * (len(filters.ranges) + 1) for item in fifo_queue: age = flt(date_diff(to_date, item[1])) qty = flt(item[0]) if not item_dict["has_serial_no"] else 1.0 - if age <= flt(filters.range1): - range1 = flt(range1 + qty, precision) - elif age <= flt(filters.range2): - range2 = flt(range2 + qty, precision) - elif age <= flt(filters.range3): - range3 = flt(range3 + qty, precision) + for i, age_limit in enumerate(filters.ranges): + if age <= flt(age_limit): + range_values[i] = flt(range_values[i] + qty, precision) + break else: - above_range3 = flt(above_range3 + qty, precision) + range_values[-1] = flt(range_values[-1] + qty, precision) - return range1, range2, range3, above_range3 + return range_values def get_columns(filters: Filters) -> list[dict]: @@ -193,12 +188,14 @@ def get_chart_data(data: list, filters: Filters) -> dict: def setup_ageing_columns(filters: Filters, range_columns: list): - ranges = [ - f"0 - {filters['range1']}", - f"{cint(filters['range1']) + 1} - {cint(filters['range2'])}", - f"{cint(filters['range2']) + 1} - {cint(filters['range3'])}", - _("{0} - Above").format(cint(filters["range3"]) + 1), - ] + prev_range_value = 0 + ranges = [] + for range in filters.ranges: + ranges.append(f"{prev_range_value} - {range}") + prev_range_value = cint(range) + 1 + + ranges.append(f"{prev_range_value} - Above") + for i, label in enumerate(ranges): fieldname = "range" + str(i + 1) add_column(range_columns, label=_("Age ({0})").format(label), fieldname=fieldname) diff --git a/erpnext/stock/report/stock_ageing/test_stock_ageing.py b/erpnext/stock/report/stock_ageing/test_stock_ageing.py index fb363606233..c0c007e5015 100644 --- a/erpnext/stock/report/stock_ageing/test_stock_ageing.py +++ b/erpnext/stock/report/stock_ageing/test_stock_ageing.py @@ -9,9 +9,7 @@ from erpnext.stock.report.stock_ageing.stock_ageing import FIFOSlots, format_rep class TestStockAgeing(FrappeTestCase): def setUp(self) -> None: - self.filters = frappe._dict( - company="_Test Company", to_date="2021-12-10", range1=30, range2=60, range3=90 - ) + self.filters = frappe._dict(company="_Test Company", to_date="2021-12-10", ranges=["30", "60", "90"]) def test_normal_inward_outward_queue(self): "Reference: Case 1 in stock_ageing_fifo_logic.md (same wh)" diff --git a/erpnext/stock/report/test_reports.py b/erpnext/stock/report/test_reports.py index 74c6afa204b..85337a3bf54 100644 --- a/erpnext/stock/report/test_reports.py +++ b/erpnext/stock/report/test_reports.py @@ -62,7 +62,7 @@ REPORT_FILTER_TEST_CASES: list[tuple[ReportName, ReportFilters]] = [ ("Item Prices", {"items": "Enabled Items only"}), ("Delayed Item Report", {"based_on": "Sales Invoice"}), ("Delayed Item Report", {"based_on": "Delivery Note"}), - ("Stock Ageing", {"range1": 30, "range2": 60, "range3": 90, "_optional": True}), + ("Stock Ageing", {"range": "30, 60, 90", "_optional": True}), ("Stock Ledger Invariant Check", {"warehouse": "_Test Warehouse - _TC", "item": "_Test Item"}), ("FIFO Queue vs Qty After Transaction Comparison", {"warehouse": "_Test Warehouse - _TC"}), ("FIFO Queue vs Qty After Transaction Comparison", {"item_group": "All Item Groups"}), From 39150184008bcf7accdbddae2d6b9276ed3fdf5b Mon Sep 17 00:00:00 2001 From: Smit Vora Date: Mon, 9 Sep 2024 07:00:32 +0530 Subject: [PATCH 134/734] chore: resolve conflicts with backport --- .../process_payment_reconciliation.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.json b/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.json index b0584dd4c4d..0511571d754 100644 --- a/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.json +++ b/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.json @@ -158,11 +158,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], -<<<<<<< HEAD - "modified": "2023-08-11 10:56:51.699137", -======= "modified": "2024-08-27 14:48:56.715320", ->>>>>>> 143209f91a (fix: `default_advance_account` field in Process Payment Reconciliation) "modified_by": "Administrator", "module": "Accounts", "name": "Process Payment Reconciliation", From 2c1f72e44cac422f5f2ab94b7b2934a5b0979c83 Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Mon, 19 Aug 2024 08:16:11 +0530 Subject: [PATCH 135/734] fix: ensure `SellingController.onload` gets called for SO & DN (cherry picked from commit 8431e3c275cc8cad8406f8a27436ecf31140106d) --- erpnext/selling/doctype/sales_order/sales_order.py | 2 ++ erpnext/stock/doctype/delivery_note/delivery_note.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 570677aad78..2f2d840cce8 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -185,6 +185,8 @@ class SalesOrder(SellingController): super().__init__(*args, **kwargs) def onload(self) -> None: + super().onload() + if frappe.db.get_single_value("Stock Settings", "enable_stock_reservation"): if self.has_unreserved_stock(): self.set_onload("has_unreserved_stock", True) diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index 8a096aca80c..847a57baa72 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -208,6 +208,8 @@ class DeliveryNote(SellingController): ) def onload(self): + super().onload() + if self.docstatus == 0: self.set_onload("has_unpacked_items", self.has_unpacked_items()) From f45b1db1a402d57b7a6312d39f6ca975098a6ae5 Mon Sep 17 00:00:00 2001 From: Smit Vora Date: Mon, 9 Sep 2024 07:23:44 +0530 Subject: [PATCH 136/734] fix: multiple fixes related to remarks for GL Report (#42753) * fix: show remarks in report only if it exists * fix: additional fixes to reduce redundancy in report print format * fix: revert changes for supplier invoice reference * fix: update remarks before submit to ensure all available details before submit are used * fix: patch to update invoice remarks where it's not set * fix: update remarks in payment ledger entry (cherry picked from commit e5a49f738bb3593a42180491114cc9f5424f8685) --- .../purchase_invoice/purchase_invoice.py | 13 +++-- .../doctype/sales_invoice/sales_invoice.py | 13 +++-- .../report/general_ledger/general_ledger.html | 5 +- erpnext/patches.txt | 1 + .../patches/v15_0/update_invoice_remarks.py | 51 +++++++++++++++++++ 5 files changed, 71 insertions(+), 12 deletions(-) create mode 100644 erpnext/patches/v15_0/update_invoice_remarks.py diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index de99c35e27e..623fb941b89 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -285,7 +285,6 @@ class PurchaseInvoice(BuyingController): self.set_against_expense_account() self.validate_write_off_account() self.validate_multiple_billing("Purchase Receipt", "pr_detail", "amount") - self.create_remarks() self.set_status() self.validate_purchase_receipt_if_update_stock() validate_inter_company_party( @@ -322,10 +321,11 @@ class PurchaseInvoice(BuyingController): def create_remarks(self): if not self.remarks: - if self.bill_no and self.bill_date: - self.remarks = _("Against Supplier Invoice {0} dated {1}").format( - self.bill_no, formatdate(self.bill_date) - ) + if self.bill_no: + self.remarks = _("Against Supplier Invoice {0}").format(self.bill_no) + if self.bill_date: + self.remarks += " " + _("dated {0}").format(formatdate(self.bill_date)) + else: self.remarks = _("No Remarks") @@ -747,6 +747,9 @@ class PurchaseInvoice(BuyingController): validate_docs_for_voucher_types(["Purchase Invoice"]) validate_docs_for_deferred_accounting([], [self.name]) + def before_submit(self): + self.create_remarks() + def on_submit(self): super().on_submit() diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 1966a78ef49..12e26623ad0 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -278,7 +278,6 @@ class SalesInvoice(SellingController): self.check_sales_order_on_hold_or_close("sales_order") self.validate_debit_to_acc() self.clear_unallocated_advances("Sales Invoice Advance", "advances") - self.add_remarks() self.validate_fixed_asset() self.set_income_account_for_fixed_assets() self.validate_item_cost_centers() @@ -422,6 +421,9 @@ class SalesInvoice(SellingController): self.set_account_for_mode_of_payment() self.set_paid_amount() + def before_submit(self): + self.add_remarks() + def on_submit(self): self.validate_pos_paid_amount() @@ -946,10 +948,11 @@ class SalesInvoice(SellingController): def add_remarks(self): if not self.remarks: - if self.po_no and self.po_date: - self.remarks = _("Against Customer Order {0} dated {1}").format( - self.po_no, formatdate(self.po_date) - ) + if self.po_no: + self.remarks = _("Against Customer Order {0}").format(self.po_no) + if self.po_date: + self.remarks += " " + _("dated {0}").format(formatdate(self.po_data)) + else: self.remarks = _("No Remarks") diff --git a/erpnext/accounts/report/general_ledger/general_ledger.html b/erpnext/accounts/report/general_ledger/general_ledger.html index 3c4e1a05c97..bdea568bdf4 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.html +++ b/erpnext/accounts/report/general_ledger/general_ledger.html @@ -48,8 +48,9 @@
{% } %} -
{%= __("Remarks") %}: {%= data[i].remarks %} - {% if(data[i].bill_no) { %} + {% if(data[i].remarks) { %} +
{%= __("Remarks") %}: {%= data[i].remarks %} + {% } else if(data[i].bill_no) { %}
{%= __("Supplier Invoice No") %}: {%= data[i].bill_no %} {% } %} diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 7350fa77e1c..5b1455ef723 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -371,6 +371,7 @@ erpnext.patches.v15_0.update_warehouse_field_in_asset_repair_consumed_item_docty erpnext.patches.v15_0.update_asset_repair_field_in_stock_entry erpnext.patches.v15_0.update_total_number_of_booked_depreciations erpnext.patches.v15_0.do_not_use_batchwise_valuation +erpnext.patches.v15_0.update_invoice_remarks erpnext.patches.v14_0.update_reports_with_range erpnext.patches.v15_0.drop_index_posting_datetime_from_sle erpnext.patches.v15_0.add_disassembly_order_stock_entry_type #1 diff --git a/erpnext/patches/v15_0/update_invoice_remarks.py b/erpnext/patches/v15_0/update_invoice_remarks.py new file mode 100644 index 00000000000..7060fe57e31 --- /dev/null +++ b/erpnext/patches/v15_0/update_invoice_remarks.py @@ -0,0 +1,51 @@ +import frappe +from frappe import _ + + +def execute(): + update_sales_invoice_remarks() + update_purchase_invoice_remarks() + + +def update_sales_invoice_remarks(): + si_list = frappe.db.get_all( + "Sales Invoice", + filters={ + "docstatus": 1, + "remarks": "No Remarks", + "po_no": ["!=", ""], + }, + fields=["name", "po_no"], + ) + + for doc in si_list: + remarks = _("Against Customer Order {0}").format(doc.po_no) + update_remarks("Sales Invoice", doc.name, remarks) + + +def update_purchase_invoice_remarks(): + pi_list = frappe.db.get_all( + "Purchase Invoice", + filters={ + "docstatus": 1, + "remarks": "No Remarks", + "bill_no": ["!=", ""], + }, + fields=["name", "bill_no"], + ) + + for doc in pi_list: + remarks = _("Against Supplier Invoice {0}").format(doc.bill_no) + update_remarks("Purchase Invoice", doc.name, remarks) + + +def update_remarks(doctype, docname, remarks): + filters = { + "voucher_type": doctype, + "remarks": "No Remarks", + "voucher_no": docname, + } + + frappe.db.set_value(doctype, docname, "remarks", remarks) + frappe.db.set_value("GL Entry", filters, "remarks", remarks) + frappe.db.set_value("Payment Ledger Entry", filters, "remarks", remarks) From 80b5c16a2e3183553c5ad95c0a70ecf94310aa12 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2024 09:56:29 +0530 Subject: [PATCH 137/734] fix: Cannot read properties of null (reading 'doc') (backport #43071) (#43118) fix: Cannot read properties of null (reading 'doc') (cherry picked from commit 62c3389bd620f4d31eb8ee4df6fbf3dec03e3ae6) Co-authored-by: Rohit Waghchaure --- erpnext/public/js/controllers/transaction.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index d8bf4a5cf17..91b938acb50 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -1510,8 +1510,8 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe item_grid.set_column_disp(fname, me.frm.doc.currency != company_currency); }); - var show = (cint(cur_frm.doc.discount_amount)) || - ((cur_frm.doc.taxes || []).filter(function(d) {return d.included_in_print_rate===1}).length); + var show = (cint(this.frm.doc.discount_amount)) || + ((this.frm.doc.taxes || []).filter(function(d) {return d.included_in_print_rate===1}).length); $.each(["net_rate", "net_amount"], function(i, fname) { if(frappe.meta.get_docfield(item_grid.doctype, fname)) From d17baddb0d3c13212f8695967ffb971b55306224 Mon Sep 17 00:00:00 2001 From: venkat102 Date: Fri, 6 Sep 2024 19:53:40 +0530 Subject: [PATCH 138/734] fix: check multi-currency on jv for common party accounting with foreign currency (cherry picked from commit 00938bfd4d1243b570e83f13f5774029ad0df3a1) --- erpnext/controllers/accounts_controller.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index fbd717db153..49198be9b48 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -2420,6 +2420,8 @@ class AccountsController(TransactionBase): primary_account = get_party_account(primary_party_type, primary_party, self.company) secondary_account = get_party_account(secondary_party_type, secondary_party, self.company) + primary_account_currency = get_account_currency(primary_account) + secondary_account_currency = get_account_currency(secondary_account) jv = frappe.new_doc("Journal Entry") jv.voucher_type = "Journal Entry" @@ -2460,6 +2462,10 @@ class AccountsController(TransactionBase): advance_entry.credit_in_account_currency = self.outstanding_amount reconcilation_entry.debit_in_account_currency = self.outstanding_amount + default_currency = erpnext.get_company_currency(self.company) + if primary_account_currency != default_currency or secondary_account_currency != default_currency: + jv.multi_currency = 1 + jv.append("accounts", reconcilation_entry) jv.append("accounts", advance_entry) From 47b216373d55bc4b8a1517b32c538ef247306602 Mon Sep 17 00:00:00 2001 From: venkat102 Date: Fri, 6 Sep 2024 20:01:35 +0530 Subject: [PATCH 139/734] test: add unit test for common party with foreign currency (cherry picked from commit 740a04a70437422dac2b824d058b264026228b79) # Conflicts: # erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py --- .../sales_invoice/test_sales_invoice.py | 194 +++++++++++++++++- 1 file changed, 193 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index ce8627be27f..5f9cf6f104b 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -51,7 +51,7 @@ class TestSalesInvoice(FrappeTestCase): from erpnext.stock.doctype.stock_ledger_entry.test_stock_ledger_entry import create_items create_items(["_Test Internal Transfer Item"], uoms=[{"uom": "Box", "conversion_factor": 10}]) - create_internal_parties() + # create_internal_parties() setup_accounts() frappe.db.set_single_value("Accounts Settings", "acc_frozen_upto", None) @@ -3915,6 +3915,198 @@ class TestSalesInvoice(FrappeTestCase): self.assertEqual(len(res), 1) self.assertEqual(res[0][0], pos_return.return_against) +<<<<<<< HEAD +======= + def test_validation_on_opening_invoice_with_rounding(self): + si = create_sales_invoice(qty=1, rate=99.98, do_not_submit=True) + si.is_opening = "Yes" + si.items[0].income_account = "Temporary Opening - _TC" + si.save() + self.assertRaises(frappe.ValidationError, si.submit) + + def _create_opening_roundoff_account(self, company_name): + liability_root = frappe.db.get_all( + "Account", + filters={"company": company_name, "root_type": "Liability", "disabled": 0}, + order_by="lft", + limit=1, + )[0] + + # setup round off account + if acc := frappe.db.exists( + "Account", + { + "account_name": "Round Off for Opening", + "account_type": "Round Off for Opening", + "company": company_name, + }, + ): + frappe.db.set_value("Company", company_name, "round_off_for_opening", acc) + else: + acc = frappe.new_doc("Account") + acc.company = company_name + acc.parent_account = liability_root.name + acc.account_name = "Round Off for Opening" + acc.account_type = "Round Off for Opening" + acc.save() + frappe.db.set_value("Company", company_name, "round_off_for_opening", acc.name) + + def test_opening_invoice_with_rounding_adjustment(self): + si = create_sales_invoice(qty=1, rate=99.98, do_not_submit=True) + si.is_opening = "Yes" + si.items[0].income_account = "Temporary Opening - _TC" + si.save() + + self._create_opening_roundoff_account(si.company) + + si.reload() + si.submit() + res = frappe.db.get_all( + "GL Entry", + filters={"voucher_no": si.name, "is_opening": "Yes"}, + fields=["account", "debit", "credit", "is_opening"], + ) + self.assertEqual(len(res), 3) + + def _create_opening_invoice_with_inclusive_tax(self): + si = create_sales_invoice(qty=1, rate=90, do_not_submit=True) + si.is_opening = "Yes" + si.items[0].income_account = "Temporary Opening - _TC" + item_template = si.items[0].as_dict() + item_template.name = None + item_template.rate = 55 + si.append("items", item_template) + si.append( + "taxes", + { + "charge_type": "On Net Total", + "account_head": "_Test Account Service Tax - _TC", + "cost_center": "_Test Cost Center - _TC", + "description": "Testing...", + "rate": 5, + "included_in_print_rate": True, + }, + ) + # there will be 0.01 precision loss between Dr and Cr + # caused by 'included_in_print_tax' option + si.save() + return si + + def test_rounding_validation_for_opening_with_inclusive_tax(self): + si = self._create_opening_invoice_with_inclusive_tax() + # 'Round Off for Opening' not set in Company master + # Ledger level validation must be thrown + self.assertRaises(frappe.ValidationError, si.submit) + + def test_ledger_entries_on_opening_invoice_with_rounding_loss_by_inclusive_tax(self): + si = self._create_opening_invoice_with_inclusive_tax() + # 'Round Off for Opening' is set in Company master + self._create_opening_roundoff_account(si.company) + + si.submit() + actual = frappe.db.get_all( + "GL Entry", + filters={"voucher_no": si.name, "is_opening": "Yes", "is_cancelled": False}, + fields=["account", "debit", "credit", "is_opening"], + order_by="account,debit", + ) + expected = [ + {"account": "_Test Account Service Tax - _TC", "debit": 0.0, "credit": 6.9, "is_opening": "Yes"}, + {"account": "Debtors - _TC", "debit": 145.0, "credit": 0.0, "is_opening": "Yes"}, + {"account": "Round Off for Opening - _TC", "debit": 0.0, "credit": 0.01, "is_opening": "Yes"}, + {"account": "Temporary Opening - _TC", "debit": 0.0, "credit": 138.09, "is_opening": "Yes"}, + ] + self.assertEqual(len(actual), 4) + self.assertEqual(expected, actual) + + def test_common_party_with_foreign_currency_jv(self): + from erpnext.accounts.doctype.account.test_account import create_account + from erpnext.accounts.doctype.opening_invoice_creation_tool.test_opening_invoice_creation_tool import ( + make_customer, + ) + from erpnext.accounts.doctype.party_link.party_link import create_party_link + from erpnext.buying.doctype.supplier.test_supplier import create_supplier + from erpnext.setup.utils import get_exchange_rate + + creditors = create_account( + account_name="Creditors USD", + parent_account="Accounts Payable - _TC", + company="_Test Company", + account_currency="USD", + account_type="Payable", + ) + debtors = create_account( + account_name="Debtors USD", + parent_account="Accounts Receivable - _TC", + company="_Test Company", + account_currency="USD", + account_type="Receivable", + ) + + # create a customer + customer = make_customer(customer="_Test Common Party USD") + cust_doc = frappe.get_doc("Customer", customer) + cust_doc.default_currency = "USD" + test_account_details = { + "company": "_Test Company", + "account": debtors, + } + cust_doc.append("accounts", test_account_details) + cust_doc.save() + + # create a supplier + supplier = create_supplier(supplier_name="_Test Common Party USD").name + supp_doc = frappe.get_doc("Supplier", supplier) + supp_doc.default_currency = "USD" + test_account_details = { + "company": "_Test Company", + "account": creditors, + } + supp_doc.append("accounts", test_account_details) + supp_doc.save() + + # enable common party accounting + frappe.db.set_single_value("Accounts Settings", "enable_common_party_accounting", 1) + + # create a party link between customer & supplier + party_link = create_party_link("Supplier", supplier, customer) + + # create a sales invoice + si = create_sales_invoice( + customer=customer, + currency="USD", + conversion_rate=get_exchange_rate("USD", "INR"), + debit_to=debtors, + do_not_save=1, + ) + si.party_account_currency = "USD" + si.save() + si.submit() + + # check outstanding of sales invoice + si.reload() + self.assertEqual(si.status, "Paid") + self.assertEqual(flt(si.outstanding_amount), 0.0) + + # check creation of journal entry + jv = frappe.get_all( + "Journal Entry Account", + { + "account": si.debit_to, + "party_type": "Customer", + "party": si.customer, + "reference_type": si.doctype, + "reference_name": si.name, + }, + pluck="credit_in_account_currency", + ) + self.assertTrue(jv) + self.assertEqual(jv[0], si.grand_total) + + party_link.delete() + frappe.db.set_single_value("Accounts Settings", "enable_common_party_accounting", 0) + +>>>>>>> 740a04a704 (test: add unit test for common party with foreign currency) def set_advance_flag(company, flag, default_account): frappe.db.set_value( From 33174b1ba26e7002d3e3286816182dba2223954e Mon Sep 17 00:00:00 2001 From: venkat102 Date: Fri, 6 Sep 2024 20:03:56 +0530 Subject: [PATCH 140/734] fix: uncomment internal parties (cherry picked from commit 454e18ad5fef1ad81aab3efcca1d7886a0d80fbf) --- erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 5f9cf6f104b..58db5bc7059 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -51,7 +51,7 @@ class TestSalesInvoice(FrappeTestCase): from erpnext.stock.doctype.stock_ledger_entry.test_stock_ledger_entry import create_items create_items(["_Test Internal Transfer Item"], uoms=[{"uom": "Box", "conversion_factor": 10}]) - # create_internal_parties() + create_internal_parties() setup_accounts() frappe.db.set_single_value("Accounts Settings", "acc_frozen_upto", None) From a9bd11f59ae5aa1115a440812d87734aeba55c81 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sat, 7 Sep 2024 11:39:06 +0530 Subject: [PATCH 141/734] refactor(test): use change_settings decorator (cherry picked from commit ee94fb37c81a28e89aa2175b933d231a5a6601f7) # Conflicts: # erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py --- .../accounts/doctype/sales_invoice/test_sales_invoice.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 58db5bc7059..c5090815253 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -4019,6 +4019,7 @@ class TestSalesInvoice(FrappeTestCase): self.assertEqual(len(actual), 4) self.assertEqual(expected, actual) + @change_settings("Accounts Settings", {"enable_common_party_accounting": True}) def test_common_party_with_foreign_currency_jv(self): from erpnext.accounts.doctype.account.test_account import create_account from erpnext.accounts.doctype.opening_invoice_creation_tool.test_opening_invoice_creation_tool import ( @@ -4065,11 +4066,8 @@ class TestSalesInvoice(FrappeTestCase): supp_doc.append("accounts", test_account_details) supp_doc.save() - # enable common party accounting - frappe.db.set_single_value("Accounts Settings", "enable_common_party_accounting", 1) - # create a party link between customer & supplier - party_link = create_party_link("Supplier", supplier, customer) + create_party_link("Supplier", supplier, customer) # create a sales invoice si = create_sales_invoice( @@ -4103,10 +4101,13 @@ class TestSalesInvoice(FrappeTestCase): self.assertTrue(jv) self.assertEqual(jv[0], si.grand_total) +<<<<<<< HEAD party_link.delete() frappe.db.set_single_value("Accounts Settings", "enable_common_party_accounting", 0) >>>>>>> 740a04a704 (test: add unit test for common party with foreign currency) +======= +>>>>>>> ee94fb37c8 (refactor(test): use change_settings decorator) def set_advance_flag(company, flag, default_account): frappe.db.set_value( From 354c34e4d8e423486e71161de2f64e4ae8de3fac Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 9 Sep 2024 10:51:45 +0530 Subject: [PATCH 142/734] chore: resolve conflict --- .../sales_invoice/test_sales_invoice.py | 111 ------------------ 1 file changed, 111 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index c5090815253..beb347e07db 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -3915,110 +3915,6 @@ class TestSalesInvoice(FrappeTestCase): self.assertEqual(len(res), 1) self.assertEqual(res[0][0], pos_return.return_against) -<<<<<<< HEAD -======= - def test_validation_on_opening_invoice_with_rounding(self): - si = create_sales_invoice(qty=1, rate=99.98, do_not_submit=True) - si.is_opening = "Yes" - si.items[0].income_account = "Temporary Opening - _TC" - si.save() - self.assertRaises(frappe.ValidationError, si.submit) - - def _create_opening_roundoff_account(self, company_name): - liability_root = frappe.db.get_all( - "Account", - filters={"company": company_name, "root_type": "Liability", "disabled": 0}, - order_by="lft", - limit=1, - )[0] - - # setup round off account - if acc := frappe.db.exists( - "Account", - { - "account_name": "Round Off for Opening", - "account_type": "Round Off for Opening", - "company": company_name, - }, - ): - frappe.db.set_value("Company", company_name, "round_off_for_opening", acc) - else: - acc = frappe.new_doc("Account") - acc.company = company_name - acc.parent_account = liability_root.name - acc.account_name = "Round Off for Opening" - acc.account_type = "Round Off for Opening" - acc.save() - frappe.db.set_value("Company", company_name, "round_off_for_opening", acc.name) - - def test_opening_invoice_with_rounding_adjustment(self): - si = create_sales_invoice(qty=1, rate=99.98, do_not_submit=True) - si.is_opening = "Yes" - si.items[0].income_account = "Temporary Opening - _TC" - si.save() - - self._create_opening_roundoff_account(si.company) - - si.reload() - si.submit() - res = frappe.db.get_all( - "GL Entry", - filters={"voucher_no": si.name, "is_opening": "Yes"}, - fields=["account", "debit", "credit", "is_opening"], - ) - self.assertEqual(len(res), 3) - - def _create_opening_invoice_with_inclusive_tax(self): - si = create_sales_invoice(qty=1, rate=90, do_not_submit=True) - si.is_opening = "Yes" - si.items[0].income_account = "Temporary Opening - _TC" - item_template = si.items[0].as_dict() - item_template.name = None - item_template.rate = 55 - si.append("items", item_template) - si.append( - "taxes", - { - "charge_type": "On Net Total", - "account_head": "_Test Account Service Tax - _TC", - "cost_center": "_Test Cost Center - _TC", - "description": "Testing...", - "rate": 5, - "included_in_print_rate": True, - }, - ) - # there will be 0.01 precision loss between Dr and Cr - # caused by 'included_in_print_tax' option - si.save() - return si - - def test_rounding_validation_for_opening_with_inclusive_tax(self): - si = self._create_opening_invoice_with_inclusive_tax() - # 'Round Off for Opening' not set in Company master - # Ledger level validation must be thrown - self.assertRaises(frappe.ValidationError, si.submit) - - def test_ledger_entries_on_opening_invoice_with_rounding_loss_by_inclusive_tax(self): - si = self._create_opening_invoice_with_inclusive_tax() - # 'Round Off for Opening' is set in Company master - self._create_opening_roundoff_account(si.company) - - si.submit() - actual = frappe.db.get_all( - "GL Entry", - filters={"voucher_no": si.name, "is_opening": "Yes", "is_cancelled": False}, - fields=["account", "debit", "credit", "is_opening"], - order_by="account,debit", - ) - expected = [ - {"account": "_Test Account Service Tax - _TC", "debit": 0.0, "credit": 6.9, "is_opening": "Yes"}, - {"account": "Debtors - _TC", "debit": 145.0, "credit": 0.0, "is_opening": "Yes"}, - {"account": "Round Off for Opening - _TC", "debit": 0.0, "credit": 0.01, "is_opening": "Yes"}, - {"account": "Temporary Opening - _TC", "debit": 0.0, "credit": 138.09, "is_opening": "Yes"}, - ] - self.assertEqual(len(actual), 4) - self.assertEqual(expected, actual) - @change_settings("Accounts Settings", {"enable_common_party_accounting": True}) def test_common_party_with_foreign_currency_jv(self): from erpnext.accounts.doctype.account.test_account import create_account @@ -4101,13 +3997,6 @@ class TestSalesInvoice(FrappeTestCase): self.assertTrue(jv) self.assertEqual(jv[0], si.grand_total) -<<<<<<< HEAD - party_link.delete() - frappe.db.set_single_value("Accounts Settings", "enable_common_party_accounting", 0) - ->>>>>>> 740a04a704 (test: add unit test for common party with foreign currency) -======= ->>>>>>> ee94fb37c8 (refactor(test): use change_settings decorator) def set_advance_flag(company, flag, default_account): frappe.db.set_value( From ea86bc2235acb0712ae7205cff97c3e14614c3c4 Mon Sep 17 00:00:00 2001 From: rahulgupta8848 <147691594+rahulgupta8848@users.noreply.github.com> Date: Mon, 9 Sep 2024 12:12:00 +0530 Subject: [PATCH 143/734] =?UTF-8?q?feat:=20added=20revaluation=20surplus?= =?UTF-8?q?=20and=20impairment=20acc=20in=20standard=20charts=E2=80=A6=20(?= =?UTF-8?q?#43022)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit feat: added revaluation surplus and impairment acc in standard charts of accounts Co-authored-by: ā€œrahulgupta8848ā€ <ā€œrahul.gupta@8848digital.comā€> (cherry picked from commit 8202f505cce9c938b43e6f238129f4641cb1d6e8) --- .../verified/in_standard_chart_of_accounts.json | 6 ++++-- .../verified/standard_chart_of_accounts.py | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/account/chart_of_accounts/verified/in_standard_chart_of_accounts.json b/erpnext/accounts/doctype/account/chart_of_accounts/verified/in_standard_chart_of_accounts.json index 2ec0b7f70c8..4d807b09c33 100644 --- a/erpnext/accounts/doctype/account/chart_of_accounts/verified/in_standard_chart_of_accounts.json +++ b/erpnext/accounts/doctype/account/chart_of_accounts/verified/in_standard_chart_of_accounts.json @@ -109,7 +109,8 @@ "Utility Expenses": {}, "Write Off": {}, "Exchange Gain/Loss": {}, - "Gain/Loss on Asset Disposal": {} + "Gain/Loss on Asset Disposal": {}, + "Impairment": {} }, "root_type": "Expense" }, @@ -132,7 +133,8 @@ "Source of Funds (Liabilities)": { "Capital Account": { "Reserves and Surplus": {}, - "Shareholders Funds": {} + "Shareholders Funds": {}, + "Revaluation Surplus": {} }, "Current Liabilities": { "Accounts Payable": { diff --git a/erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts.py b/erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts.py index e30ad24a374..5a5e232db8d 100644 --- a/erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts.py +++ b/erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts.py @@ -72,6 +72,7 @@ def get(): _("Write Off"): {}, _("Exchange Gain/Loss"): {}, _("Gain/Loss on Asset Disposal"): {}, + _("Impairment"): {}, }, "root_type": "Expense", }, @@ -104,6 +105,7 @@ def get(): _("Dividends Paid"): {"account_type": "Equity"}, _("Opening Balance Equity"): {"account_type": "Equity"}, _("Retained Earnings"): {"account_type": "Equity"}, + _("Revaluation Surplus"): {"account_type": "Equity"}, "root_type": "Equity", }, } From 03e3374a8b0e90a3d84c8a61070510edf1c25b2b Mon Sep 17 00:00:00 2001 From: "Nihantra C. Patel" <141945075+Nihantra-Patel@users.noreply.github.com> Date: Mon, 9 Sep 2024 11:27:59 +0530 Subject: [PATCH 144/734] fix: set today in 'On This Date' in Available Batch Report (cherry picked from commit 9fd55e4c83e4c0b715dd746c5032ac51217641d6) --- .../report/available_batch_report/available_batch_report.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/report/available_batch_report/available_batch_report.js b/erpnext/stock/report/available_batch_report/available_batch_report.js index 011f7e09ca2..a13e4ca82f7 100644 --- a/erpnext/stock/report/available_batch_report/available_batch_report.js +++ b/erpnext/stock/report/available_batch_report/available_batch_report.js @@ -17,7 +17,7 @@ frappe.query_reports["Available Batch Report"] = { fieldtype: "Date", width: "80", reqd: 1, - default: frappe.datetime.add_months(frappe.datetime.get_today(), -1), + default: frappe.datetime.get_today(), }, { fieldname: "item_code", From 8f4dc8048d86c2f103b3ded40074be80df0e5870 Mon Sep 17 00:00:00 2001 From: Bhavan23 Date: Sat, 31 Aug 2024 17:28:03 +0530 Subject: [PATCH 145/734] fix: validate the item code when updating the other item's price rule (cherry picked from commit 45de18069c5e69970115dbd04381c4cd296948d1) --- erpnext/public/js/controllers/transaction.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 91b938acb50..aa4a2ae85a0 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -1777,7 +1777,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe if (data && data.apply_rule_on_other_items && JSON.parse(data.apply_rule_on_other_items)) { me.frm.doc.items.forEach(d => { - if (in_list(JSON.parse(data.apply_rule_on_other_items), d[data.apply_rule_on])) { + if (in_list(JSON.parse(data.apply_rule_on_other_items), d[data.apply_rule_on]) && d.item_code === data.item_code) { for(var k in data) { if (data.pricing_rule_for == "Discount Percentage" && data.apply_rule_on_other_items && k == "discount_amount") { continue; From 2dddd7906bc0468bd8f8eb7b139e19af334b5f2e Mon Sep 17 00:00:00 2001 From: Prashant Kamble <99401472+pra17shant@users.noreply.github.com> Date: Wed, 4 Sep 2024 11:21:05 +0000 Subject: [PATCH 146/734] fix: unreconcile allocation child table redirect url voucher no issue (cherry picked from commit 5d6f6a2fb972e4678fe55f5d3970903501f10a10) --- erpnext/public/js/utils/unreconcile.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/public/js/utils/unreconcile.js b/erpnext/public/js/utils/unreconcile.js index 6864e2865d3..c6ee8a330c7 100644 --- a/erpnext/public/js/utils/unreconcile.js +++ b/erpnext/public/js/utils/unreconcile.js @@ -69,7 +69,7 @@ erpnext.accounts.unreconcile_payment = { { label: __("Voucher Type"), fieldname: "voucher_type", - fieldtype: "Dynamic Link", + fieldtype: "Link", options: "DocType", in_list_view: 1, read_only: 1, @@ -77,7 +77,7 @@ erpnext.accounts.unreconcile_payment = { { label: __("Voucher No"), fieldname: "voucher_no", - fieldtype: "Link", + fieldtype: "Dynamic Link", options: "voucher_type", in_list_view: 1, read_only: 1, From ea4f7365ea8e6dc48af50369c2239b2ba665ab5e Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Sat, 17 Aug 2024 02:27:36 +0200 Subject: [PATCH 147/734] fix(Delivery Note): translatability of validation errors (cherry picked from commit 34df6e39dcca685b6b107c5f0631721d7156768c) --- .../doctype/delivery_note/delivery_note.py | 70 ++++++++----------- 1 file changed, 30 insertions(+), 40 deletions(-) diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index 847a57baa72..d2d10cd9935 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -362,52 +362,42 @@ class DeliveryNote(SellingController): self.validate_sales_invoice_references() def validate_sales_order_references(self): - err_msg = "" + errors = [] for item in self.items: - if (item.against_sales_order and not item.so_detail) or ( - not item.against_sales_order and item.so_detail - ): - if not item.against_sales_order: - err_msg += ( - _("'Sales Order' reference ({1}) is missing in row {0}").format( - frappe.bold(item.idx), frappe.bold("against_sales_order") - ) - + "
" - ) - else: - err_msg += ( - _("'Sales Order Item' reference ({1}) is missing in row {0}").format( - frappe.bold(item.idx), frappe.bold("so_detail") - ) - + "
" - ) + missing_label = None + if item.against_sales_order and not item.so_detail: + missing_label = item.meta.get_label("so_detail") + elif item.so_detail and not item.against_sales_order: + missing_label = item.meta.get_label("against_sales_order") - if err_msg: - frappe.throw(err_msg, title=_("References to Sales Orders are Incomplete")) + if missing_label and missing_label != "No Label": + errors.append( + _("The field {0} in row {1} is not set").format( + frappe.bold(_(missing_label)), frappe.bold(item.idx) + ) + ) + + if errors: + frappe.throw("
".join(errors), title=_("References to Sales Orders are Incomplete")) def validate_sales_invoice_references(self): - err_msg = "" + errors = [] for item in self.items: - if (item.against_sales_invoice and not item.si_detail) or ( - not item.against_sales_invoice and item.si_detail - ): - if not item.against_sales_invoice: - err_msg += ( - _("'Sales Invoice' reference ({1}) is missing in row {0}").format( - frappe.bold(item.idx), frappe.bold("against_sales_invoice") - ) - + "
" - ) - else: - err_msg += ( - _("'Sales Invoice Item' reference ({1}) is missing in row {0}").format( - frappe.bold(item.idx), frappe.bold("si_detail") - ) - + "
" - ) + missing_label = None + if item.against_sales_invoice and not item.si_detail: + missing_label = item.meta.get_label("si_detail") + elif item.si_detail and not item.against_sales_invoice: + missing_label = item.meta.get_label("against_sales_invoice") - if err_msg: - frappe.throw(err_msg, title=_("References to Sales Invoices are Incomplete")) + if missing_label and missing_label != "No Label": + errors.append( + _("The field {0} in row {1} is not set").format( + frappe.bold(_(missing_label)), frappe.bold(item.idx) + ) + ) + + if errors: + frappe.throw("
".join(errors), title=_("References to Sales Invoices are Incomplete")) def validate_proj_cust(self): """check for does customer belong to same project as entered..""" From 0c0f103b836344cc6dbebfbc0fb1d8253a9cffc7 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Sat, 17 Aug 2024 02:36:16 +0200 Subject: [PATCH 148/734] refactor: extract common validation method (cherry picked from commit 08646b7ab7605aa76b1defe691c97070dc54c317) --- .../doctype/delivery_note/delivery_note.py | 35 +++++++------------ 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index d2d10cd9935..5d95e7b66d3 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -362,32 +362,23 @@ class DeliveryNote(SellingController): self.validate_sales_invoice_references() def validate_sales_order_references(self): - errors = [] - for item in self.items: - missing_label = None - if item.against_sales_order and not item.so_detail: - missing_label = item.meta.get_label("so_detail") - elif item.so_detail and not item.against_sales_order: - missing_label = item.meta.get_label("against_sales_order") - - if missing_label and missing_label != "No Label": - errors.append( - _("The field {0} in row {1} is not set").format( - frappe.bold(_(missing_label)), frappe.bold(item.idx) - ) - ) - - if errors: - frappe.throw("
".join(errors), title=_("References to Sales Orders are Incomplete")) + self._validate_dependent_item_fields( + "against_sales_order", "so_detail", _("References to Sales Orders are Incomplete") + ) def validate_sales_invoice_references(self): + self._validate_dependent_item_fields( + "against_sales_invoice", "si_detail", _("References to Sales Invoices are Incomplete") + ) + + def _validate_dependent_item_fields(self, field_a: str, field_b: str, error_title: str): errors = [] for item in self.items: missing_label = None - if item.against_sales_invoice and not item.si_detail: - missing_label = item.meta.get_label("si_detail") - elif item.si_detail and not item.against_sales_invoice: - missing_label = item.meta.get_label("against_sales_invoice") + if item.get(field_a) and not item.get(field_b): + missing_label = item.meta.get_label(field_b) + elif item.get(field_b) and not item.get(field_a): + missing_label = item.meta.get_label(field_a) if missing_label and missing_label != "No Label": errors.append( @@ -397,7 +388,7 @@ class DeliveryNote(SellingController): ) if errors: - frappe.throw("
".join(errors), title=_("References to Sales Invoices are Incomplete")) + frappe.throw("
".join(errors), title=error_title) def validate_proj_cust(self): """check for does customer belong to same project as entered..""" From 3fd9df0d2e6efeef83384e480a0e81dc8e4966c7 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Wed, 4 Sep 2024 18:05:02 +0200 Subject: [PATCH 149/734] fix(Opening Invoice Creation Tool): translatability of messages (cherry picked from commit f3c5803198174f46caf607d1e36f8cb367f72fc0) --- .../opening_invoice_creation_tool.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.js b/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.js index f1efba8a954..4938e6690e5 100644 --- a/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.js +++ b/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.js @@ -28,7 +28,12 @@ frappe.ui.form.on("Opening Invoice Creation Tool", { frm.refresh_fields(); frm.page.clear_indicator(); frm.dashboard.hide_progress(); - frappe.msgprint(__("Opening {0} Invoices created", [frm.doc.invoice_type])); + + if (frm.doc.invoice_type == "Sales") { + frappe.msgprint(__("Opening Sales Invoices have been created.")); + } else { + frappe.msgprint(__("Opening Purchase Invoices have been created.")); + } }, 1500, data.title @@ -48,12 +53,19 @@ frappe.ui.form.on("Opening Invoice Creation Tool", { !frm.doc.import_in_progress && frm.trigger("make_dashboard"); frm.page.set_primary_action(__("Create Invoices"), () => { let btn_primary = frm.page.btn_primary.get(0); + let freeze_message; + if (frm.doc.invoice_type == "Sales") { + freeze_message = __("Creating Sales Invoices ..."); + } else { + freeze_message = __("Creating Purchase Invoices ..."); + } + return frm.call({ doc: frm.doc, btn: $(btn_primary), method: "make_invoices", freeze: 1, - freeze_message: __("Creating {0} Invoice", [frm.doc.invoice_type]), + freeze_message: freeze_message, }); }); From 5110975c6dc13aa894a60caace1fc4ba33d178cf Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2024 20:34:56 +0530 Subject: [PATCH 150/734] fix: incorrect actual cost in Procurement Tracker report (backport #43109) (#43138) fix: incorrect actual cost in Procurement Tracker report (#43109) (cherry picked from commit 80f101f92e2bd85927450b6adb073c575a327f42) Co-authored-by: rohitwaghchaure --- .../buying/report/procurement_tracker/procurement_tracker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/buying/report/procurement_tracker/procurement_tracker.py b/erpnext/buying/report/procurement_tracker/procurement_tracker.py index a7e03c08fac..bd0798236b3 100644 --- a/erpnext/buying/report/procurement_tracker/procurement_tracker.py +++ b/erpnext/buying/report/procurement_tracker/procurement_tracker.py @@ -175,7 +175,7 @@ def get_data(filters): "purchase_order": po.parent, "supplier": po.supplier, "estimated_cost": flt(mr_record.get("amount")), - "actual_cost": flt(pi_records.get(po.name)), + "actual_cost": flt(pi_records.get(po.name)) or flt(po.amount), "purchase_order_amt": flt(po.amount), "purchase_order_amt_in_company_currency": flt(po.base_amount), "expected_delivery_date": po.schedule_date, From 208bd2b8ff4d55dc9061f90f9b48027bf247a68e Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Sat, 7 Sep 2024 01:33:12 +0530 Subject: [PATCH 151/734] fix: unhide action button after form redirect (cherry picked from commit 5ce5b1b6a2a64cc5a8a19a478f95781886bfff72) --- erpnext/assets/doctype/asset/asset.js | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js index 2477e443c4a..302fe816882 100644 --- a/erpnext/assets/doctype/asset/asset.js +++ b/erpnext/assets/doctype/asset/asset.js @@ -528,6 +528,7 @@ frappe.ui.form.on("Asset", { callback: function (r) { var doclist = frappe.model.sync(r.message); frappe.set_route("Form", doclist[0].doctype, doclist[0].name); + $(".primary-action").prop("hidden", false); }, }); }, From 9e72a844f702d0e3e032765777ec1780d8a6b6ed Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Sun, 8 Sep 2024 23:23:54 +0530 Subject: [PATCH 152/734] fix: pass company from asset to asset capitalization (cherry picked from commit f3445d645def648fdcd285125520dab13315beef) --- erpnext/assets/doctype/asset/asset.js | 2 ++ erpnext/assets/doctype/asset/asset.py | 7 ++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js index 302fe816882..0a21516c177 100644 --- a/erpnext/assets/doctype/asset/asset.js +++ b/erpnext/assets/doctype/asset/asset.js @@ -506,6 +506,7 @@ frappe.ui.form.on("Asset", { create_asset_repair: function (frm) { frappe.call({ args: { + company: frm.doc.company, asset: frm.doc.name, asset_name: frm.doc.asset_name, }, @@ -520,6 +521,7 @@ frappe.ui.form.on("Asset", { create_asset_capitalization: function (frm) { frappe.call({ args: { + company: frm.doc.company, asset: frm.doc.name, asset_name: frm.doc.asset_name, item_code: frm.doc.item_code, diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index abb615ff277..58f813247f0 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -895,18 +895,19 @@ def create_asset_maintenance(asset, item_code, item_name, asset_category, compan @frappe.whitelist() -def create_asset_repair(asset, asset_name): +def create_asset_repair(company, asset, asset_name): asset_repair = frappe.new_doc("Asset Repair") - asset_repair.update({"asset": asset, "asset_name": asset_name}) + asset_repair.update({"company": company, "asset": asset, "asset_name": asset_name}) return asset_repair @frappe.whitelist() -def create_asset_capitalization(asset, asset_name, item_code): +def create_asset_capitalization(company, asset, asset_name, item_code): asset_capitalization = frappe.new_doc("Asset Capitalization") asset_capitalization.update( { "target_asset": asset, + "company": company, "capitalization_method": "Choose a WIP composite asset", "target_asset_name": asset_name, "target_item_code": item_code, From d2923bae8582f2c2bc7b126ff6a41746f2f61262 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2024 19:26:38 +0200 Subject: [PATCH 153/734] fix: return type of `get_party_details` (backport #43131) (#43134) Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com> fix: return type of `get_party_details` (#43131) --- erpnext/accounts/party.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index e6015e081d6..06e285e4ada 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -68,7 +68,7 @@ def get_party_details( pos_profile=None, ): if not party: - return {} + return frappe._dict() if not frappe.db.exists(party_type, party): frappe.throw(_("{0}: {1} does not exists").format(party_type, party)) return _get_party_details( From 5929d50c72085a4a875b802b26f9364548342e49 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 9 Sep 2024 12:31:32 +0530 Subject: [PATCH 154/734] feat: utility report to identify invalid ledger entries (cherry picked from commit 832c4aaf82e6556a83429a3757efb3449b53de2a) --- .../report/invalid_ledger_entries/__init__.py | 0 .../invalid_ledger_entries.js | 13 +++++ .../invalid_ledger_entries.json | 23 +++++++++ .../invalid_ledger_entries.py | 48 +++++++++++++++++++ 4 files changed, 84 insertions(+) create mode 100644 erpnext/accounts/report/invalid_ledger_entries/__init__.py create mode 100644 erpnext/accounts/report/invalid_ledger_entries/invalid_ledger_entries.js create mode 100644 erpnext/accounts/report/invalid_ledger_entries/invalid_ledger_entries.json create mode 100644 erpnext/accounts/report/invalid_ledger_entries/invalid_ledger_entries.py diff --git a/erpnext/accounts/report/invalid_ledger_entries/__init__.py b/erpnext/accounts/report/invalid_ledger_entries/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/accounts/report/invalid_ledger_entries/invalid_ledger_entries.js b/erpnext/accounts/report/invalid_ledger_entries/invalid_ledger_entries.js new file mode 100644 index 00000000000..548a6f7d951 --- /dev/null +++ b/erpnext/accounts/report/invalid_ledger_entries/invalid_ledger_entries.js @@ -0,0 +1,13 @@ +// Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.query_reports["Invalid Ledger Entries"] = { + filters: [ + // { + // "fieldname": "my_filter", + // "label": __("My Filter"), + // "fieldtype": "Data", + // "reqd": 1, + // }, + ], +}; diff --git a/erpnext/accounts/report/invalid_ledger_entries/invalid_ledger_entries.json b/erpnext/accounts/report/invalid_ledger_entries/invalid_ledger_entries.json new file mode 100644 index 00000000000..00dbbfc5056 --- /dev/null +++ b/erpnext/accounts/report/invalid_ledger_entries/invalid_ledger_entries.json @@ -0,0 +1,23 @@ +{ + "add_total_row": 0, + "columns": [], + "creation": "2024-09-09 12:31:25.295976", + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [], + "idx": 0, + "is_standard": "Yes", + "letterhead": null, + "modified": "2024-09-09 12:31:25.295976", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Invalid Ledger Entries", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "GL Entry", + "report_name": "Invalid Ledger Entries", + "report_type": "Script Report", + "roles": [], + "timeout": 0 +} \ No newline at end of file diff --git a/erpnext/accounts/report/invalid_ledger_entries/invalid_ledger_entries.py b/erpnext/accounts/report/invalid_ledger_entries/invalid_ledger_entries.py new file mode 100644 index 00000000000..4f4b1835227 --- /dev/null +++ b/erpnext/accounts/report/invalid_ledger_entries/invalid_ledger_entries.py @@ -0,0 +1,48 @@ +# Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +# import frappe +from frappe import _ + + +def execute(filters: dict | None = None): + """Return columns and data for the report. + + This is the main entry point for the report. It accepts the filters as a + dictionary and should return columns and data. It is called by the framework + every time the report is refreshed or a filter is updated. + """ + columns = get_columns() + data = get_data() + + return columns, data + + +def get_columns() -> list[dict]: + """Return columns for the report. + + One field definition per column, just like a DocType field definition. + """ + return [ + { + "label": _("Column 1"), + "fieldname": "column_1", + "fieldtype": "Data", + }, + { + "label": _("Column 2"), + "fieldname": "column_2", + "fieldtype": "Int", + }, + ] + + +def get_data() -> list[list]: + """Return data for the report. + + The report data is a list of rows, with each row being a list of cell values. + """ + return [ + ["Row 1", 1], + ["Row 2", 2], + ] From 14e30d12b4d556ec6be3ea8cf70fbbd6afac8192 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 9 Sep 2024 13:55:10 +0530 Subject: [PATCH 155/734] refactor: standard filters (cherry picked from commit dccbc1f432a83f07ef46582ac36df8fd776d4a4d) --- .../invalid_ledger_entries.js | 55 ++++++++++++++++--- 1 file changed, 47 insertions(+), 8 deletions(-) diff --git a/erpnext/accounts/report/invalid_ledger_entries/invalid_ledger_entries.js b/erpnext/accounts/report/invalid_ledger_entries/invalid_ledger_entries.js index 548a6f7d951..ffaaf5d7cbf 100644 --- a/erpnext/accounts/report/invalid_ledger_entries/invalid_ledger_entries.js +++ b/erpnext/accounts/report/invalid_ledger_entries/invalid_ledger_entries.js @@ -1,13 +1,52 @@ // Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt +function get_filters() { + let filters = [ + { + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), + reqd: 1, + }, + { + fieldname: "from_date", + label: __("Start Date"), + fieldtype: "Date", + reqd: 1, + default: frappe.datetime.add_months(frappe.datetime.get_today(), -1), + }, + { + fieldname: "to_date", + label: __("End Date"), + fieldtype: "Date", + reqd: 1, + default: frappe.datetime.get_today(), + }, + { + fieldname: "account", + label: __("Account"), + fieldtype: "MultiSelectList", + options: "Account", + get_data: function (txt) { + return frappe.db.get_link_options("Account", txt, { + company: frappe.query_report.get_filter_value("company"), + account_type: ["in", ["Receivable", "Payable"]], + }); + }, + }, + { + fieldname: "voucher_no", + label: __("Voucher No"), + fieldtype: "Data", + width: 100, + }, + ]; + return filters; +} + frappe.query_reports["Invalid Ledger Entries"] = { - filters: [ - // { - // "fieldname": "my_filter", - // "label": __("My Filter"), - // "fieldtype": "Data", - // "reqd": 1, - // }, - ], + filters: get_filters(), }; From 710d30074d6d65e18b70cae80d197d81b54242be Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 9 Sep 2024 17:53:59 +0530 Subject: [PATCH 156/734] refactor: barebones methods with basic logic (cherry picked from commit b05b378ef0fcd4c08404fd541857921702751676) --- .../invalid_ledger_entries.py | 121 +++++++++++++++--- 1 file changed, 105 insertions(+), 16 deletions(-) diff --git a/erpnext/accounts/report/invalid_ledger_entries/invalid_ledger_entries.py b/erpnext/accounts/report/invalid_ledger_entries/invalid_ledger_entries.py index 4f4b1835227..e1599a6758e 100644 --- a/erpnext/accounts/report/invalid_ledger_entries/invalid_ledger_entries.py +++ b/erpnext/accounts/report/invalid_ledger_entries/invalid_ledger_entries.py @@ -1,8 +1,10 @@ # Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt -# import frappe -from frappe import _ +import frappe +from frappe import _, qb +from frappe.query_builder import Criterion +from frappe.query_builder.custom import ConstantColumn def execute(filters: dict | None = None): @@ -12,8 +14,10 @@ def execute(filters: dict | None = None): dictionary and should return columns and data. It is called by the framework every time the report is refreshed or a filter is updated. """ + validate_filters(filters) + columns = get_columns() - data = get_data() + data = get_data(filters) return columns, data @@ -24,25 +28,110 @@ def get_columns() -> list[dict]: One field definition per column, just like a DocType field definition. """ return [ + {"label": _("Voucher Type"), "fieldname": "voucher_type", "fieldtype": "Link", "options": "DocType"}, { - "label": _("Column 1"), - "fieldname": "column_1", - "fieldtype": "Data", - }, - { - "label": _("Column 2"), - "fieldname": "column_2", - "fieldtype": "Int", + "label": _("Voucher No"), + "fieldname": "voucher_no", + "fieldtype": "Dynamic Link", + "options": "voucher_type", }, ] -def get_data() -> list[list]: +def get_data(filters) -> list[list]: """Return data for the report. The report data is a list of rows, with each row being a list of cell values. """ - return [ - ["Row 1", 1], - ["Row 2", 2], - ] + active_vouchers = get_active_vouchers_for_period(filters) + invalid_vouchers = identify_cancelled_vouchers(active_vouchers) + + return invalid_vouchers + + +def identify_cancelled_vouchers(active_vouchers: list[dict] | list | None = None) -> list[dict]: + cancelled_vouchers = [] + if active_vouchers: + # Group by voucher types and use single query to identify cancelled vouchers + vtypes = set([x.voucher_type for x in active_vouchers]) + + for _t in vtypes: + _names = [x.voucher_no for x in active_vouchers if x.voucher_type == _t] + dt = qb.DocType(_t) + non_active_vouchers = ( + qb.from_(dt) + .select(ConstantColumn(_t).as_("doctype"), dt.name) + .where(dt.docstatus.ne(1) & dt.name.isin(_names)) + .run() + ) + if non_active_vouchers: + cancelled_vouchers.extend(non_active_vouchers) + return cancelled_vouchers + + +def validate_filters(filters: dict | None = None): + if not filters: + frappe.throw(_("Filters missing")) + + if not filters.company: + frappe.throw(_("Company is mandatory")) + + if filters.from_date > filters.to_date: + frappe.throw(_("Start Date should be lower than End Date")) + + +def build_query_filters(filters: dict | None = None) -> list: + qb_filters = [] + if filters: + if filters.account: + qb_filters.append(qb.Field("account").isin(filters.account)) + + if filters.voucher_no: + qb_filters.append(qb.Field("voucher_no").eq(filters.voucher_no)) + + return qb_filters + + +def get_active_vouchers_for_period(filters: dict | None = None) -> list[dict]: + uniq_vouchers = [] + + if filters: + gle = qb.DocType("GL Entry") + ple = qb.DocType("Payment Ledger Entry") + + qb_filters = build_query_filters(filters) + + gl_vouchers = ( + qb.from_(gle) + .select(gle.voucher_type) + .distinct() + .select(gle.voucher_no) + .distinct() + .where( + gle.is_cancelled.eq(0) + & gle.company.eq(filters.company) + & gle.posting_date[filters.from_date : filters.to_date] + ) + .where(Criterion.all(qb_filters)) + .run(as_dict=True) + ) + + pl_vouchers = ( + qb.from_(ple) + .select(ple.voucher_type) + .distinct() + .select(ple.voucher_no) + .distinct() + .where( + ple.delinked.eq(0) + & ple.company.eq(filters.company) + & ple.posting_date[filters.from_date : filters.to_date] + ) + .where(Criterion.all(qb_filters)) + .run(as_dict=True) + ) + + uniq_vouchers.extend(gl_vouchers) + uniq_vouchers.extend(pl_vouchers) + + return uniq_vouchers From 5413372aebdab062c0433b3658ad92b7ba6df55e Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 10 Sep 2024 12:58:05 +0530 Subject: [PATCH 157/734] refactor: fetch as dictionary (cherry picked from commit 2126b10a92c38aa12a35bc78b577fc2212d5da6a) --- .../report/invalid_ledger_entries/invalid_ledger_entries.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/invalid_ledger_entries/invalid_ledger_entries.py b/erpnext/accounts/report/invalid_ledger_entries/invalid_ledger_entries.py index e1599a6758e..33fda705cf2 100644 --- a/erpnext/accounts/report/invalid_ledger_entries/invalid_ledger_entries.py +++ b/erpnext/accounts/report/invalid_ledger_entries/invalid_ledger_entries.py @@ -60,9 +60,9 @@ def identify_cancelled_vouchers(active_vouchers: list[dict] | list | None = None dt = qb.DocType(_t) non_active_vouchers = ( qb.from_(dt) - .select(ConstantColumn(_t).as_("doctype"), dt.name) + .select(ConstantColumn(_t).as_("voucher_type"), dt.name.as_("voucher_no")) .where(dt.docstatus.ne(1) & dt.name.isin(_names)) - .run() + .run(as_dict=True) ) if non_active_vouchers: cancelled_vouchers.extend(non_active_vouchers) From 9f09bf14cb4d2a0ab3a8de58e3cd0985f4b5ce73 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 10 Sep 2024 13:03:49 +0530 Subject: [PATCH 158/734] refactor: allow all accounts (cherry picked from commit 43198c946b498ab76ebbcd39de3d5cc031d897db) --- .../report/invalid_ledger_entries/invalid_ledger_entries.js | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/accounts/report/invalid_ledger_entries/invalid_ledger_entries.js b/erpnext/accounts/report/invalid_ledger_entries/invalid_ledger_entries.js index ffaaf5d7cbf..47d478f2865 100644 --- a/erpnext/accounts/report/invalid_ledger_entries/invalid_ledger_entries.js +++ b/erpnext/accounts/report/invalid_ledger_entries/invalid_ledger_entries.js @@ -33,7 +33,6 @@ function get_filters() { get_data: function (txt) { return frappe.db.get_link_options("Account", txt, { company: frappe.query_report.get_filter_value("company"), - account_type: ["in", ["Receivable", "Payable"]], }); }, }, From a23e8b13bebf83e05363c9074c84e36f835e6d04 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 10 Sep 2024 14:29:13 +0530 Subject: [PATCH 159/734] fix: permission on guest PR creation (cherry picked from commit ea02e5f15ab58ad2f346e8f698ab5c63da5c5f1b) --- erpnext/accounts/doctype/payment_request/payment_request.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index f368c2a9613..83b43a15987 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -516,6 +516,8 @@ def make_payment_request(**args): if frappe.db.get_single_value("Accounts Settings", "create_pr_in_draft_status", cache=True): pr.insert(ignore_permissions=True) if args.submit_doc: + if pr.get("__unsaved"): + pr.insert(ignore_permissions=True) pr.submit() if args.order_type == "Shopping Cart": From cd57e009dd3023e419e7d9114ac32e5408b48881 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 10 Sep 2024 17:01:53 +0530 Subject: [PATCH 160/734] fix: concurrency issue while picking materials (backport #43087) (#43152) fix: concurrency issue while picking materials (#43087) (cherry picked from commit 5c7dff0e846b3f28cd66a79ebec2d5cc18d68470) Co-authored-by: rohitwaghchaure --- erpnext/stock/doctype/pick_list/pick_list.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index 0c93fd6c402..27a37ef1bab 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -650,6 +650,8 @@ class PickList(Document): if self.name: query = query.where(pi_item.parent != self.name) + query = query.for_update() + return query.run(as_dict=True) def _get_product_bundles(self) -> dict[str, str]: From c9f49caeccb7b04cc20f43768b72a8238122c893 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 10 Sep 2024 23:06:38 +0530 Subject: [PATCH 161/734] perf: timeout error (backport #43154) (#43158) perf: timeout error (#43154) (cherry picked from commit 1bf60248d9db816bb528e9500572a15c242f2025) Co-authored-by: rohitwaghchaure --- erpnext/public/js/controllers/transaction.js | 4 +++ .../serial_and_batch_bundle.py | 30 +++++++++++++++---- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index aa4a2ae85a0..3dcd36b2cff 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -1879,6 +1879,10 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe const fields = ["discount_percentage", "discount_amount", "margin_rate_or_amount", "rate_with_margin"]; + if (!item) { + return; + } + if(item.remove_free_item) { let items = []; diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py index ef4d475dca4..956eb08d9ff 100644 --- a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py +++ b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py @@ -1558,13 +1558,14 @@ def get_serial_nos_based_on_posting_date(kwargs, ignore_serial_nos): serial_nos = set() data = get_stock_ledgers_for_serial_nos(kwargs) + bundle_wise_serial_nos = get_bundle_wise_serial_nos(data) for d in data: if d.serial_and_batch_bundle: - sns = get_serial_nos_from_bundle(d.serial_and_batch_bundle, kwargs.get("serial_nos", [])) - if d.actual_qty > 0: - serial_nos.update(sns) - else: - serial_nos.difference_update(sns) + if sns := bundle_wise_serial_nos.get(d.serial_and_batch_bundle): + if d.actual_qty > 0: + serial_nos.update(sns) + else: + serial_nos.difference_update(sns) elif d.serial_no: sns = get_serial_nos(d.serial_no) @@ -1581,6 +1582,25 @@ def get_serial_nos_based_on_posting_date(kwargs, ignore_serial_nos): return serial_nos +def get_bundle_wise_serial_nos(data): + bundle_wise_serial_nos = defaultdict(list) + bundles = [d.serial_and_batch_bundle for d in data if d.serial_and_batch_bundle] + if not bundles: + return bundle_wise_serial_nos + + bundle_data = frappe.get_all( + "Serial and Batch Entry", + fields=["serial_no", "parent"], + filters={"parent": ("in", bundles), "docstatus": 1, "serial_no": ("is", "set")}, + ) + + for d in bundle_data: + if d.parent: + bundle_wise_serial_nos[d.parent].append(d.serial_no) + + return bundle_wise_serial_nos + + def get_reserved_serial_nos(kwargs) -> list: """Returns a list of `Serial No` reserved in POS Invoice and Stock Reservation Entry.""" From 8c8dc241e50ba72775c7651b0c19636d3f5a2182 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 10 Sep 2024 23:07:07 +0530 Subject: [PATCH 162/734] fix: bom cost update is not working (backport #43155) (#43157) fix: bom cost update is not working (#43155) (cherry picked from commit 05f9015c0bf341f25c340a69cfd95cebd7777115) Co-authored-by: rohitwaghchaure --- .../doctype/bom_update_log/bom_update_log.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom_update_log/bom_update_log.py b/erpnext/manufacturing/doctype/bom_update_log/bom_update_log.py index 12d52ea51fd..34a3900015d 100644 --- a/erpnext/manufacturing/doctype/bom_update_log/bom_update_log.py +++ b/erpnext/manufacturing/doctype/bom_update_log/bom_update_log.py @@ -8,7 +8,7 @@ from frappe import _ from frappe.model.document import Document from frappe.query_builder import DocType, Interval from frappe.query_builder.functions import Now -from frappe.utils import cint, cstr +from frappe.utils import cint, cstr, date_diff, today from erpnext.manufacturing.doctype.bom_update_log.bom_updation_utils import ( get_leaf_boms, @@ -88,10 +88,12 @@ class BOMUpdateLog(Document): wip_log = frappe.get_all( "BOM Update Log", - {"update_type": "Update Cost", "status": ["in", ["Queued", "In Progress"]]}, + fields=["name", "modified"], + filters={"update_type": "Update Cost", "status": ["in", ["Queued", "In Progress"]]}, limit_page_length=1, ) - if wip_log: + + if wip_log and date_diff(today(), wip_log[0].modified) < 1: log_link = frappe.utils.get_link_to_form("BOM Update Log", wip_log[0].name) frappe.throw( _("BOM Updation already in progress. Please wait until {0} is complete.").format(log_link), From d9d86dae35f3b07a54c5fc2916fcd4d32daac990 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 11 Sep 2024 05:11:52 +0000 Subject: [PATCH 163/734] chore(release): Bumped to Version 15.35.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # [15.35.0](https://github.com/frappe/erpnext/compare/v15.34.2...v15.35.0) (2024-09-11) ### Bug Fixes * `default_advance_account` field in Process Payment Reconciliation ([75cb298](https://github.com/frappe/erpnext/commit/75cb29890d5f275dc172c4528fb1ecf41e02d8ea)) * bom cost update is not working (backport [#43155](https://github.com/frappe/erpnext/issues/43155)) ([#43157](https://github.com/frappe/erpnext/issues/43157)) ([8c8dc24](https://github.com/frappe/erpnext/commit/8c8dc241e50ba72775c7651b0c19636d3f5a2182)) * cancel common party advance jv while canceling the invoice ([9bd3d7a](https://github.com/frappe/erpnext/commit/9bd3d7a020ad8ce4e90da1ffdfddfc71510aab74)) * Cannot read properties of null (reading 'doc') (backport [#43071](https://github.com/frappe/erpnext/issues/43071)) ([#43118](https://github.com/frappe/erpnext/issues/43118)) ([80b5c16](https://github.com/frappe/erpnext/commit/80b5c16a2e3183553c5ad95c0a70ecf94310aa12)) * check multi-currency on jv for common party accounting with foreign currency ([d17badd](https://github.com/frappe/erpnext/commit/d17baddb0d3c13212f8695967ffb971b55306224)) * concurrency issue while picking materials (backport [#43087](https://github.com/frappe/erpnext/issues/43087)) ([#43152](https://github.com/frappe/erpnext/issues/43152)) ([cd57e00](https://github.com/frappe/erpnext/commit/cd57e009dd3023e419e7d9114ac32e5408b48881)) * **Delivery Note:** translatability of validation errors ([ea4f736](https://github.com/frappe/erpnext/commit/ea4f7365ea8e6dc48af50369c2239b2ba665ab5e)) * ensure `SellingController.onload` gets called for SO & DN ([2c1f72e](https://github.com/frappe/erpnext/commit/2c1f72e44cac422f5f2ab94b7b2934a5b0979c83)) * incorrect actual cost in Procurement Tracker report (backport [#43109](https://github.com/frappe/erpnext/issues/43109)) ([#43138](https://github.com/frappe/erpnext/issues/43138)) ([5110975](https://github.com/frappe/erpnext/commit/5110975c6dc13aa894a60caace1fc4ba33d178cf)) * incorrect qty after transaction in SLE (backport [#43103](https://github.com/frappe/erpnext/issues/43103)) ([#43105](https://github.com/frappe/erpnext/issues/43105)) ([8447bf3](https://github.com/frappe/erpnext/commit/8447bf34f07c230f3084a2fe66e0295db10a566f)) * **minor:** reorder expected value validation ([6fde07d](https://github.com/frappe/erpnext/commit/6fde07da0e5a477f375429ace53067f1ab8eb0c8)) * multiple fixes related to remarks for GL Report ([#42753](https://github.com/frappe/erpnext/issues/42753)) ([f45b1db](https://github.com/frappe/erpnext/commit/f45b1db1a402d57b7a6312d39f6ca975098a6ae5)) * **Opening Invoice Creation Tool:** translatability of messages ([3fd9df0](https://github.com/frappe/erpnext/commit/3fd9df0d2e6efeef83384e480a0e81dc8e4966c7)) * pass company from asset to asset capitalization ([9e72a84](https://github.com/frappe/erpnext/commit/9e72a844f702d0e3e032765777ec1780d8a6b6ed)) * permission on guest PR creation ([a23e8b1](https://github.com/frappe/erpnext/commit/a23e8b13bebf83e05363c9074c84e36f835e6d04)) * return type of `get_party_details` (backport [#43131](https://github.com/frappe/erpnext/issues/43131)) ([#43134](https://github.com/frappe/erpnext/issues/43134)) ([d2923ba](https://github.com/frappe/erpnext/commit/d2923bae8582f2c2bc7b126ff6a41746f2f61262)) * set today in 'On This Date' in Available Batch Report ([03e3374](https://github.com/frappe/erpnext/commit/03e3374a8b0e90a3d84c8a61070510edf1c25b2b)) * uncomment internal parties ([33174b1](https://github.com/frappe/erpnext/commit/33174b1ba26e7002d3e3286816182dba2223954e)) * unhide action button after form redirect ([208bd2b](https://github.com/frappe/erpnext/commit/208bd2b8ff4d55dc9061f90f9b48027bf247a68e)) * unreconcile allocation child table redirect url voucher no issue ([2dddd79](https://github.com/frappe/erpnext/commit/2dddd7906bc0468bd8f8eb7b139e19af334b5f2e)) * validate the item code when updating the other item's price rule ([8f4dc80](https://github.com/frappe/erpnext/commit/8f4dc8048d86c2f103b3ded40074be80df0e5870)) ### Features * added revaluation surplus and impairment acc in standard charts… ([#43022](https://github.com/frappe/erpnext/issues/43022)) ([ea86bc2](https://github.com/frappe/erpnext/commit/ea86bc2235acb0712ae7205cff97c3e14614c3c4)) * utility report to identify invalid ledger entries ([5929d50](https://github.com/frappe/erpnext/commit/5929d50c72085a4a875b802b26f9364548342e49)) ### Performance Improvements * timeout error (backport [#43154](https://github.com/frappe/erpnext/issues/43154)) ([#43158](https://github.com/frappe/erpnext/issues/43158)) ([c9f49ca](https://github.com/frappe/erpnext/commit/c9f49caeccb7b04cc20f43768b72a8238122c893)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 10c7018c9c1..32c572e92c3 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.34.2" +__version__ = "15.35.0" def get_default_company(user=None): From 0d9741fdd77be8621e5194f1391fc3d3d2c5a515 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 11 Sep 2024 12:59:46 +0530 Subject: [PATCH 164/734] refactor: enable no-copy on Purchase Invoice status --- .../accounts/doctype/purchase_invoice/purchase_invoice.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index 3751c027c97..c2ee4166ed1 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -1271,6 +1271,7 @@ "fieldtype": "Select", "in_standard_filter": 1, "label": "Status", + "no_copy": 1, "options": "\nDraft\nReturn\nDebit Note Issued\nSubmitted\nPaid\nPartly Paid\nUnpaid\nOverdue\nCancelled\nInternal Transfer", "print_hide": 1 }, @@ -1630,7 +1631,7 @@ "idx": 204, "is_submittable": 1, "links": [], - "modified": "2024-07-25 19:42:36.931278", + "modified": "2024-09-11 12:59:19.130593", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice", From 1657a83151fb002e0606c36923e722aa638cc6f2 Mon Sep 17 00:00:00 2001 From: Smit Vora Date: Thu, 12 Sep 2024 10:36:32 +0530 Subject: [PATCH 165/734] fix: typo with po_date when creating remarks (cherry picked from commit a55502e0f14133d8e6dfca9e0741c07d20056b08) --- .../accounts/doctype/sales_invoice/sales_invoice.py | 2 +- .../doctype/sales_invoice/test_sales_invoice.py | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 12e26623ad0..1214ddee8cd 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -951,7 +951,7 @@ class SalesInvoice(SellingController): if self.po_no: self.remarks = _("Against Customer Order {0}").format(self.po_no) if self.po_date: - self.remarks += " " + _("dated {0}").format(formatdate(self.po_data)) + self.remarks += " " + _("dated {0}").format(formatdate(self.po_date)) else: self.remarks = _("No Remarks") diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index beb347e07db..7ac0d34e671 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -8,7 +8,7 @@ import frappe from frappe import qb from frappe.model.dynamic_links import get_dynamic_link_map from frappe.tests.utils import FrappeTestCase, change_settings -from frappe.utils import add_days, flt, getdate, nowdate, today +from frappe.utils import add_days, flt, format_date, getdate, nowdate, today import erpnext from erpnext.accounts.doctype.account.test_account import create_account, get_inventory_account @@ -3997,6 +3997,14 @@ class TestSalesInvoice(FrappeTestCase): self.assertTrue(jv) self.assertEqual(jv[0], si.grand_total) + def test_invoice_remarks(self): + si = frappe.copy_doc(test_records[0]) + si.po_no = "Test PO" + si.po_date = nowdate() + si.save() + si.submit() + self.assertEqual(si.remarks, f"Against Customer Order Test PO dated {format_date(nowdate())}") + def set_advance_flag(company, flag, default_account): frappe.db.set_value( From 31e0bb477ecda7145771e53d642b4fa34a8d7f42 Mon Sep 17 00:00:00 2001 From: Smit Vora Date: Thu, 12 Sep 2024 10:36:32 +0530 Subject: [PATCH 166/734] fix: typo with po_date when creating remarks (cherry picked from commit a55502e0f14133d8e6dfca9e0741c07d20056b08) --- .../accounts/doctype/sales_invoice/sales_invoice.py | 2 +- .../doctype/sales_invoice/test_sales_invoice.py | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 12e26623ad0..1214ddee8cd 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -951,7 +951,7 @@ class SalesInvoice(SellingController): if self.po_no: self.remarks = _("Against Customer Order {0}").format(self.po_no) if self.po_date: - self.remarks += " " + _("dated {0}").format(formatdate(self.po_data)) + self.remarks += " " + _("dated {0}").format(formatdate(self.po_date)) else: self.remarks = _("No Remarks") diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index beb347e07db..7ac0d34e671 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -8,7 +8,7 @@ import frappe from frappe import qb from frappe.model.dynamic_links import get_dynamic_link_map from frappe.tests.utils import FrappeTestCase, change_settings -from frappe.utils import add_days, flt, getdate, nowdate, today +from frappe.utils import add_days, flt, format_date, getdate, nowdate, today import erpnext from erpnext.accounts.doctype.account.test_account import create_account, get_inventory_account @@ -3997,6 +3997,14 @@ class TestSalesInvoice(FrappeTestCase): self.assertTrue(jv) self.assertEqual(jv[0], si.grand_total) + def test_invoice_remarks(self): + si = frappe.copy_doc(test_records[0]) + si.po_no = "Test PO" + si.po_date = nowdate() + si.save() + si.submit() + self.assertEqual(si.remarks, f"Against Customer Order Test PO dated {format_date(nowdate())}") + def set_advance_flag(company, flag, default_account): frappe.db.set_value( From 7f95e42becfff89b31c8b6964fa237ba49445580 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Thu, 12 Sep 2024 06:11:04 +0000 Subject: [PATCH 167/734] chore(release): Bumped to Version 15.35.1 ## [15.35.1](https://github.com/frappe/erpnext/compare/v15.35.0...v15.35.1) (2024-09-12) ### Bug Fixes * typo with po_date when creating remarks ([31e0bb4](https://github.com/frappe/erpnext/commit/31e0bb477ecda7145771e53d642b4fa34a8d7f42)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 32c572e92c3..50acf5dd033 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.35.0" +__version__ = "15.35.1" def get_default_company(user=None): From 45ff8fa296b47c809ecb643a4b67fc775d9e9848 Mon Sep 17 00:00:00 2001 From: Vishakh Desai Date: Wed, 11 Sep 2024 14:21:48 +0530 Subject: [PATCH 168/734] fix: set party_type null when payment_type is changed to Internal Transfer (cherry picked from commit 502cf0eb8d8001925b5c00583083a8f29a2d2115) --- .../accounts/doctype/payment_entry/payment_entry.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index b50dcd217e7..f46c782112c 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -385,7 +385,15 @@ frappe.ui.form.on("Payment Entry", { payment_type: function (frm) { if (frm.doc.payment_type == "Internal Transfer") { $.each( - ["party", "party_balance", "paid_from", "paid_to", "references", "total_allocated_amount"], + [ + "party", + "party_type", + "party_balance", + "paid_from", + "paid_to", + "references", + "total_allocated_amount", + ], function (i, field) { frm.set_value(field, null); } From 5789de25b94258c8e521885cde47c5706632cc98 Mon Sep 17 00:00:00 2001 From: Navin-S-R Date: Thu, 12 Sep 2024 15:25:54 +0530 Subject: [PATCH 169/734] fix: delete exchange gain loss journal entry while deleting payment entry (cherry picked from commit 9886cf0d46f68caec45467d5dbcd018084a65088) --- erpnext/accounts/utils.py | 98 +++++++++++++++------- erpnext/controllers/accounts_controller.py | 5 ++ 2 files changed, 71 insertions(+), 32 deletions(-) diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index e462f749b54..32eeb1e07a6 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -745,40 +745,74 @@ def cancel_exchange_gain_loss_journal( Cancel Exchange Gain/Loss for Sales/Purchase Invoice, if they have any. """ if parent_doc.doctype in ["Sales Invoice", "Purchase Invoice", "Payment Entry", "Journal Entry"]: - journals = frappe.db.get_all( - "Journal Entry Account", - filters={ - "reference_type": parent_doc.doctype, - "reference_name": parent_doc.name, - "docstatus": 1, - }, - fields=["parent"], - as_list=1, + gain_loss_journals = get_linked_exchange_gain_loss_journal( + referenced_dt=parent_doc.doctype, referenced_dn=parent_doc.name, je_docstatus=1 ) - - if journals: - gain_loss_journals = frappe.db.get_all( - "Journal Entry", - filters={ - "name": ["in", [x[0] for x in journals]], - "voucher_type": "Exchange Gain Or Loss", - "docstatus": 1, - }, - as_list=1, - ) - for doc in gain_loss_journals: - gain_loss_je = frappe.get_doc("Journal Entry", doc[0]) - if referenced_dt and referenced_dn: - references = [(x.reference_type, x.reference_name) for x in gain_loss_je.accounts] - if ( - len(references) == 2 - and (referenced_dt, referenced_dn) in references - and (parent_doc.doctype, parent_doc.name) in references - ): - # only cancel JE generated against parent_doc and referenced_dn - gain_loss_je.cancel() - else: + for doc in gain_loss_journals: + gain_loss_je = frappe.get_doc("Journal Entry", doc) + if referenced_dt and referenced_dn: + references = [(x.reference_type, x.reference_name) for x in gain_loss_je.accounts] + if ( + len(references) == 2 + and (referenced_dt, referenced_dn) in references + and (parent_doc.doctype, parent_doc.name) in references + ): + # only cancel JE generated against parent_doc and referenced_dn gain_loss_je.cancel() + else: + gain_loss_je.cancel() + + +def delete_exchange_gain_loss_journal( + parent_doc: dict | object, referenced_dt: str | None = None, referenced_dn: str | None = None +) -> None: + """ + Delete Exchange Gain/Loss for Sales/Purchase Invoice, if they have any. + """ + if parent_doc.doctype in ["Sales Invoice", "Purchase Invoice", "Payment Entry", "Journal Entry"]: + gain_loss_journals = get_linked_exchange_gain_loss_journal( + referenced_dt=parent_doc.doctype, referenced_dn=parent_doc.name, je_docstatus=2 + ) + for doc in gain_loss_journals: + gain_loss_je = frappe.get_doc("Journal Entry", doc) + if referenced_dt and referenced_dn: + references = [(x.reference_type, x.reference_name) for x in gain_loss_je.accounts] + if ( + len(references) == 2 + and (referenced_dt, referenced_dn) in references + and (parent_doc.doctype, parent_doc.name) in references + ): + # only delete JE generated against parent_doc and referenced_dn + gain_loss_je.delete() + else: + gain_loss_je.delete() + + +def get_linked_exchange_gain_loss_journal(referenced_dt: str, referenced_dn: str, je_docstatus: int) -> list: + """ + Get all the linked exchange gain/loss journal entries for a given document. + """ + gain_loss_journals = [] + if journals := frappe.db.get_all( + "Journal Entry Account", + { + "reference_type": referenced_dt, + "reference_name": referenced_dn, + "docstatus": je_docstatus, + }, + pluck="parent", + ): + gain_loss_journals = frappe.db.get_all( + "Journal Entry", + { + "name": ["in", journals], + "voucher_type": "Exchange Gain Or Loss", + "is_system_generated": 1, + "docstatus": je_docstatus, + }, + pluck="name", + ) + return gain_loss_journals def cancel_common_party_journal(self): diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 49198be9b48..92efa5168f3 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -346,12 +346,17 @@ class AccountsController(TransactionBase): repost_doc.save(ignore_permissions=True) def on_trash(self): + from erpnext.accounts.utils import delete_exchange_gain_loss_journal + self._remove_references_in_repost_doctypes() self._remove_references_in_unreconcile() self.remove_serial_and_batch_bundle() # delete sl and gl entries on deletion of transaction if frappe.db.get_single_value("Accounts Settings", "delete_linked_ledger_entries"): + # delete linked exchange gain/loss journal + delete_exchange_gain_loss_journal(self) + ple = frappe.qb.DocType("Payment Ledger Entry") frappe.qb.from_(ple).delete().where( (ple.voucher_type == self.doctype) & (ple.voucher_no == self.name) From d618c9a481270515922a493867c68ea61354e9ac Mon Sep 17 00:00:00 2001 From: Navin-S-R Date: Thu, 12 Sep 2024 15:26:55 +0530 Subject: [PATCH 170/734] test: add unit test for deletion of gain loss jv while deleting payment entry (cherry picked from commit 7855d3034b1cc8f6385548643a8820293dfd33eb) --- .../payment_entry/test_payment_entry.py | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py index cc03dc260bb..771c91a462c 100644 --- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py @@ -1791,6 +1791,79 @@ class TestPaymentEntry(FrappeTestCase): # 'Is Opening' should always be 'No' for normal advance payments self.assertEqual(gl_with_opening_set, []) + @change_settings("Accounts Settings", {"delete_linked_ledger_entries": 1}) + def test_delete_linked_exchange_gain_loss_journal(self): + from erpnext.accounts.doctype.account.test_account import create_account + from erpnext.accounts.doctype.opening_invoice_creation_tool.test_opening_invoice_creation_tool import ( + make_customer, + ) + + debtors = create_account( + account_name="Debtors USD", + parent_account="Accounts Receivable - _TC", + company="_Test Company", + account_currency="USD", + account_type="Receivable", + ) + + # create a customer + customer = make_customer(customer="_Test Party USD") + cust_doc = frappe.get_doc("Customer", customer) + cust_doc.default_currency = "USD" + test_account_details = { + "company": "_Test Company", + "account": debtors, + } + cust_doc.append("accounts", test_account_details) + cust_doc.save() + + # create a sales invoice + si = create_sales_invoice( + customer=customer, + currency="USD", + conversion_rate=83.970000000, + debit_to=debtors, + do_not_save=1, + ) + si.party_account_currency = "USD" + si.save() + si.submit() + + # create a payment entry for the invoice + pe = get_payment_entry("Sales Invoice", si.name) + pe.reference_no = "1" + pe.reference_date = frappe.utils.nowdate() + pe.paid_amount = 100 + pe.source_exchange_rate = 90 + pe.append( + "deductions", + { + "account": "_Test Exchange Gain/Loss - _TC", + "cost_center": "_Test Cost Center - _TC", + "amount": 2710, + }, + ) + pe.save() + pe.submit() + + # check creation of journal entry + jv = frappe.get_all( + "Journal Entry Account", + {"reference_type": pe.doctype, "reference_name": pe.name, "docstatus": 1}, + pluck="parent", + ) + self.assertTrue(jv) + + # check cancellation of payment entry and journal entry + pe.cancel() + self.assertTrue(pe.docstatus == 2) + self.assertTrue(frappe.db.get_value("Journal Entry", {"name": jv[0]}, "docstatus") == 2) + + # check deletion of payment entry and journal entry + pe.delete() + self.assertRaises(frappe.DoesNotExistError, frappe.get_doc, pe.doctype, pe.name) + self.assertRaises(frappe.DoesNotExistError, frappe.get_doc, "Journal Entry", jv[0]) + def create_payment_entry(**args): payment_entry = frappe.new_doc("Payment Entry") From 055e7820c85cc9aafb6dff38a22ecc76cef2eac8 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 12 Sep 2024 16:48:25 +0530 Subject: [PATCH 171/734] refactor(test): use test fixture on pricing rule test suite (cherry picked from commit 0ea1d6d9605e31b8af539ec37bc46b64c67ef5c9) --- erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py index 235fddf3ab3..3fece4aeeab 100644 --- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py @@ -5,6 +5,7 @@ import unittest import frappe +from frappe.tests.utils import FrappeTestCase, change_settings 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 @@ -14,7 +15,7 @@ from erpnext.stock.doctype.item.test_item import make_item from erpnext.stock.get_item_details import get_item_details -class TestPricingRule(unittest.TestCase): +class TestPricingRule(FrappeTestCase): def setUp(self): delete_existing_pricing_rules() setup_pricing_rule_data() From c1a6c56217b326f4c948145c507c87bbc987bafc Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 13 Sep 2024 13:29:58 +0530 Subject: [PATCH 172/734] fix: item list view in website (backport #43165) (#43207) fix: item list view in website (#43165) (cherry picked from commit ce34bb97930ea97d4abe4ac9c3227654b5d00a62) Co-authored-by: rohitwaghchaure --- erpnext/stock/doctype/item/templates/item_row.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/item/templates/item_row.html b/erpnext/stock/doctype/item/templates/item_row.html index f81fc1d8743..809ac838ccb 100644 --- a/erpnext/stock/doctype/item/templates/item_row.html +++ b/erpnext/stock/doctype/item/templates/item_row.html @@ -1,4 +1,4 @@ From 61a42ea5d71b9f28c73d9954168bc3e9ba37a3cc Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 13 Sep 2024 13:30:12 +0530 Subject: [PATCH 173/734] fix: batch based item price not working (backport #43172) (#43206) * fix: batch based item price not working (#43172) (cherry picked from commit d9e4ed13cbbae7d318038554e486b67cf0b9ff7c) # Conflicts: # erpnext/stock/get_item_details.py * chore: fix conflicts --------- Co-authored-by: rohitwaghchaure --- erpnext/public/js/controllers/transaction.js | 25 ++++++++++++++++++++ erpnext/stock/get_item_details.py | 25 +++++++++++++++++--- 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 3dcd36b2cff..435ce26c5c1 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -1501,6 +1501,31 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe } } + batch_no(frm, cdt, cdn) { + let row = locals[cdt][cdn]; + if (row.use_serial_batch_fields && row.batch_no) { + var params = this._get_args(row); + params.batch_no = row.batch_no; + params.uom = row.uom; + + frappe.call({ + method: "erpnext.stock.get_item_details.get_batch_based_item_price", + args: { + params: params, + item_code: row.item_code, + }, + callback: function(r) { + if (!r.exc && r.message) { + row.price_list_rate = r.message; + row.rate = r.message; + refresh_field("rate", row.name, row.parentfield); + refresh_field("price_list_rate", row.name, row.parentfield); + } + } + }) + } + } + toggle_item_grid_columns(company_currency) { const me = this; // toggle columns diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 5cfa306066d..50db899433f 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -10,7 +10,7 @@ from frappe.model import child_table_fields, default_fields from frappe.model.meta import get_field_precision from frappe.model.utils import get_fetch_values from frappe.query_builder.functions import IfNull, Sum -from frappe.utils import add_days, add_months, cint, cstr, flt, getdate +from frappe.utils import add_days, add_months, cint, cstr, flt, getdate, parse_json from erpnext import get_company_currency from erpnext.accounts.doctype.pricing_rule.pricing_rule import ( @@ -889,7 +889,7 @@ def insert_item_price(args): ) -def get_item_price(args, item_code, ignore_party=False): +def get_item_price(args, item_code, ignore_party=False, force_batch_no=False) -> list[dict]: """ Get name, price_list_rate from Item Price based on conditions Check if the desired qty is within the increment of the packing list. @@ -906,13 +906,17 @@ def get_item_price(args, item_code, ignore_party=False): (ip.item_code == item_code) & (ip.price_list == args.get("price_list")) & (IfNull(ip.uom, "").isin(["", args.get("uom")])) - & (IfNull(ip.batch_no, "").isin(["", args.get("batch_no")])) ) .orderby(ip.valid_from, order=frappe.qb.desc) .orderby(IfNull(ip.batch_no, ""), order=frappe.qb.desc) .orderby(ip.uom, order=frappe.qb.desc) ) + if force_batch_no: + query = query.where(ip.batch_no == args.get("batch_no")) + else: + query = query.where(IfNull(ip.batch_no, "").isin(["", args.get("batch_no")])) + if not ignore_party: if args.get("customer"): query = query.where(ip.customer == args.get("customer")) @@ -930,6 +934,21 @@ def get_item_price(args, item_code, ignore_party=False): return query.run() +@frappe.whitelist() +def get_batch_based_item_price(params, item_code) -> float: + if isinstance(params, str): + params = parse_json(params) + + item_price = get_item_price(params, item_code, force_batch_no=True) + if not item_price: + item_price = get_item_price(params, item_code, ignore_party=True, force_batch_no=True) + + if item_price and item_price[0].uom == params.get("uom"): + return item_price[0].price_list_rate + + return 0.0 + + def get_price_list_rate_for(args, item_code): """ :param customer: link to Customer DocType From ef6b172616ee473c1b05324d2e6217510cec8cda Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 13 Sep 2024 13:30:36 +0530 Subject: [PATCH 174/734] fix: currency changing while making PO from Supplier Quotation (backport #43187) (#43205) fix: currency changing while making PO from Supplier Quotation (#43187) (cherry picked from commit 2b96e37c3486c5f4e1928cfcb5c3b9f0cad60f6b) Co-authored-by: rohitwaghchaure --- erpnext/public/js/controllers/transaction.js | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 435ce26c5c1..67c61118952 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -1258,6 +1258,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe "Purchase Receipt": ["purchase_order_item", "purchase_invoice_item", "purchase_receipt_item"], "Purchase Invoice": ["purchase_order_item", "pr_detail", "po_detail"], "Sales Order": ["prevdoc_docname", "quotation_item"], + "Purchase Order": ["supplier_quotation_item"], }; const mappped_fields = mapped_item_field_map[this.frm.doc.doctype] || []; From 2f56ba7f428488c8a94160941df9c4d309dee5eb Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 13 Sep 2024 13:30:36 +0530 Subject: [PATCH 175/734] fix: currency changing while making PO from Supplier Quotation (backport #43187) (#43205) fix: currency changing while making PO from Supplier Quotation (#43187) (cherry picked from commit 2b96e37c3486c5f4e1928cfcb5c3b9f0cad60f6b) Co-authored-by: rohitwaghchaure (cherry picked from commit ef6b172616ee473c1b05324d2e6217510cec8cda) --- erpnext/public/js/controllers/transaction.js | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 3dcd36b2cff..4b997b6d8fb 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -1258,6 +1258,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe "Purchase Receipt": ["purchase_order_item", "purchase_invoice_item", "purchase_receipt_item"], "Purchase Invoice": ["purchase_order_item", "pr_detail", "po_detail"], "Sales Order": ["prevdoc_docname", "quotation_item"], + "Purchase Order": ["supplier_quotation_item"], }; const mappped_fields = mapped_item_field_map[this.frm.doc.doctype] || []; From ad2d6a16258235a20d041174458e32bb6ebad8f7 Mon Sep 17 00:00:00 2001 From: sameer Chauhan Date: Fri, 13 Sep 2024 17:32:44 +0530 Subject: [PATCH 176/734] fix: Payment Ledger Report currency fieldtype fix --- erpnext/accounts/report/payment_ledger/payment_ledger.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/payment_ledger/payment_ledger.py b/erpnext/accounts/report/payment_ledger/payment_ledger.py index 9852c6e7ab9..d4f0f0a107d 100644 --- a/erpnext/accounts/report/payment_ledger/payment_ledger.py +++ b/erpnext/accounts/report/payment_ledger/payment_ledger.py @@ -210,7 +210,7 @@ class PaymentLedger: ) ) self.columns.append( - dict(label=_("Currency"), fieldname="currency", fieldtype="Currency", hidden=True) + dict(label=_("Currency"), fieldname="currency", fieldtype="Link", options="Currency", hidden=True) ) def run(self): From f101a1ce3b9e2e579ff200c80204a0231916eeec Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sat, 14 Sep 2024 13:36:26 +0530 Subject: [PATCH 177/734] fix(holiday-list): use same date format for same holiday error message (backport #42606) (#43222) fix(holiday-list): use same date format for same holiday error message (#42606) * fix(holiday-list): use same date format for same holiday error message * chore: fix formatting --------- Co-authored-by: Rucha Mahabal (cherry picked from commit a43544153667f8b15d915fd774b46d737de912d2) Co-authored-by: Ananyobrata Pal <74728797+ananyo141@users.noreply.github.com> --- erpnext/setup/doctype/holiday_list/holiday_list.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/erpnext/setup/doctype/holiday_list/holiday_list.py b/erpnext/setup/doctype/holiday_list/holiday_list.py index 0216f75a628..b7920236ce1 100644 --- a/erpnext/setup/doctype/holiday_list/holiday_list.py +++ b/erpnext/setup/doctype/holiday_list/holiday_list.py @@ -149,7 +149,11 @@ class HolidayList(Document): unique_dates = [] for row in self.holidays: if row.holiday_date in unique_dates: - frappe.throw(_("Holiday Date {0} added multiple times").format(frappe.bold(row.holiday_date))) + frappe.throw( + _("Holiday Date {0} added multiple times").format( + frappe.bold(formatdate(row.holiday_date)) + ) + ) unique_dates.append(row.holiday_date) From f060534625b6a7dcb7b60fd783f041ba3e980650 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 12 Sep 2024 19:38:30 +0530 Subject: [PATCH 178/734] feat: API for crm integration (cherry picked from commit b7bf9f80f2554dc466a108cdcf2e6f929393ac19) --- erpnext/crm/frappe_crm_api.py | 135 ++++++++++++++++++ .../selling/doctype/quotation/quotation.js | 2 +- .../selling/doctype/quotation/quotation.py | 4 + 3 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 erpnext/crm/frappe_crm_api.py diff --git a/erpnext/crm/frappe_crm_api.py b/erpnext/crm/frappe_crm_api.py new file mode 100644 index 00000000000..40c0ce8c9c1 --- /dev/null +++ b/erpnext/crm/frappe_crm_api.py @@ -0,0 +1,135 @@ +import json + +import frappe +from frappe import _ +from frappe.custom.doctype.custom_field.custom_field import create_custom_fields + + +@frappe.whitelist() +def create_custom_fields_for_frappe_crm(): + frappe.only_for("System Manager") + custom_fields = { + "Quotation": [ + { + "fieldname": "crm_deal", + "fieldtype": "Data", + "label": "Frappe CRM Deal", + "insert_after": "party_name", + } + ], + "Customer": [ + { + "fieldname": "crm_deal", + "fieldtype": "Data", + "label": "Frappe CRM Deal", + "insert_after": "prospect_name", + } + ], + } + create_custom_fields(custom_fields, ignore_validate=True) + + +@frappe.whitelist() +def create_prospect_against_crm_deal(): + frappe.only_for("System Manager") + doc = frappe.form_dict + prospect = frappe.get_doc( + { + "doctype": "Prospect", + "company_name": doc.organization or doc.lead_name, + "no_of_employees": doc.no_of_employees, + "prospect_owner": doc.deal_owner, + "company": doc.erpnext_company, + "crm_deal": doc.crm_deal, + "territory": doc.territory, + "industry": doc.industry, + "website": doc.website, + "annual_revenue": doc.annual_revenue, + } + ) + + try: + prospect_name = frappe.db.get_value("Prospect", {"company_name": prospect.company_name}) + if not prospect_name: + prospect.insert() + prospect_name = prospect.name + except Exception: + frappe.log_error( + frappe.get_traceback(), + f"Error while creating prospect against CRM Deal: {frappe.form_dict.get('crm_deal_id')}", + ) + pass + + create_contacts(json.loads(doc.contacts), prospect.company_name, "Prospect", prospect_name) + frappe.response["message"] = prospect_name + + +def create_contacts(contacts, organization=None, link_doctype=None, link_docname=None): + for c in contacts: + c = frappe._dict(c) + existing_contact = contact_exists(c.email, c.mobile_no) + if existing_contact: + contact = frappe.get_doc("Contact", existing_contact) + else: + contact = frappe.get_doc( + { + "doctype": "Contact", + "first_name": c.get("full_name"), + "gender": c.get("gender"), + "company_name": organization, + } + ) + + if c.get("email"): + contact.append("email_ids", {"email_id": c.get("email"), "is_primary": 1}) + + if c.get("mobile_no"): + contact.append("phone_nos", {"phone": c.get("mobile_no"), "is_primary_mobile_no": 1}) + + link_contact_to_prospect(contact, link_doctype, link_docname) + + contact.save(ignore_permissions=True) + + +def link_contact_to_prospect(contact, link_doctype, link_docname): + already_linked = any( + [(link.link_doctype == link_doctype and link.link_name == link_docname) for link in contact.links] + ) + if not already_linked: + contact.append( + "links", {"link_doctype": link_doctype, "link_name": link_docname, "link_title": link_docname} + ) + + +def contact_exists(email, mobile_no): + email_exist = frappe.db.exists("Contact Email", {"email_id": email}) + mobile_exist = frappe.db.exists("Contact Phone", {"phone": mobile_no}) + + doctype = "Contact Email" if email_exist else "Contact Phone" + name = email_exist or mobile_exist + + if name: + return frappe.db.get_value(doctype, name, "parent") + + return False + + +@frappe.whitelist() +def create_customer(customer_data=None): + frappe.only_for("System Manager") + if not customer_data: + customer_data = frappe.form_dict + + try: + customer_name = frappe.db.exists("Customer", {"customer_name": customer_data.get("customer_name")}) + if not customer_name: + customer = frappe.get_doc({"doctype": "Customer", **customer_data}).insert( + ignore_permissions=True + ) + customer_name = customer.name + + contacts = json.loads(customer_data.get("contacts")) + create_contacts(contacts, customer_name, "Customer", customer_name) + except Exception: + frappe.log_error(frappe.get_traceback(), "Error while creating customer against Frappe CRM Deal") + pass diff --git a/erpnext/selling/doctype/quotation/quotation.js b/erpnext/selling/doctype/quotation/quotation.js index 3044d865c0c..a4c70d7f50f 100644 --- a/erpnext/selling/doctype/quotation/quotation.js +++ b/erpnext/selling/doctype/quotation/quotation.js @@ -71,7 +71,7 @@ frappe.ui.form.on("Quotation", { frm.trigger("set_label"); frm.trigger("toggle_reqd_lead_customer"); frm.trigger("set_dynamic_field_label"); - frm.set_value("party_name", ""); + // frm.set_value("party_name", ""); // removed to set party_name from url for crm integration frm.set_value("customer_name", ""); }, diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index 2a4b04b9db5..7ebcb329193 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -220,6 +220,10 @@ class Quotation(SellingController): "Lead", self.party_name, ["lead_name", "company_name"] ) self.customer_name = company_name or lead_name + elif self.party_name and self.quotation_to == "Prospect": + self.customer_name = self.party_name + elif self.party_name and self.quotation_to == "CRM Deal": + self.customer_name = frappe.db.get_value("CRM Deal", self.party_name, "organization") def update_opportunity(self, status): for opportunity in set(d.prevdoc_docname for d in self.get("items")): From 984acb661d4314960bf2f1eb3542830b182441cc Mon Sep 17 00:00:00 2001 From: ljain112 Date: Fri, 13 Sep 2024 17:30:52 +0530 Subject: [PATCH 179/734] fix: prevent KeyError by checking `report_filter` existence (cherry picked from commit c1d2cc2c14da36508ef1cd72f0ae04c961c1edbf) --- erpnext/patches/v14_0/update_reports_with_range.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/patches/v14_0/update_reports_with_range.py b/erpnext/patches/v14_0/update_reports_with_range.py index 2bda265ca66..014fba883fc 100644 --- a/erpnext/patches/v14_0/update_reports_with_range.py +++ b/erpnext/patches/v14_0/update_reports_with_range.py @@ -30,6 +30,9 @@ def update_report_json(report): report_json = json.loads(report.json) report_filter = report_json.get("filters") + if not report_filter: + return + keys_to_pop = [key for key in report_filter if key.startswith("range")] report_filter["range"] = ", ".join(str(report_filter.pop(key)) for key in keys_to_pop) From d8d4cd23a518ec94b30f69dfc67dd042582fcbec Mon Sep 17 00:00:00 2001 From: venkat102 Date: Sat, 14 Sep 2024 16:00:53 +0530 Subject: [PATCH 180/734] fix(ux): set amount on foreign currency when foreign currency account is selected on last row of journal (cherry picked from commit 2b66842d3492fae20c87eb6969c1c39c33aa8b0e) --- .../doctype/journal_entry/journal_entry.js | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js index d290d794df1..999db883e98 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.js +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js @@ -680,6 +680,7 @@ $.extend(erpnext.journal_entry, { callback: function (r) { if (r.message) { $.extend(d, r.message); + erpnext.journal_entry.set_amount_on_last_row(frm, dt, dn); erpnext.journal_entry.set_debit_credit_in_company_currency(frm, dt, dn); refresh_field("accounts"); } @@ -687,4 +688,26 @@ $.extend(erpnext.journal_entry, { }); } }, + set_amount_on_last_row: function (frm, dt, dn) { + let row = locals[dt][dn]; + let length = frm.doc.accounts.length; + if (row.idx != length) return; + + let difference = frm.doc.accounts.reduce((total, row) => { + if (row.idx == length) return total; + + return total + row.debit - row.credit; + }, 0); + + if (difference) { + if (difference > 0) { + row.credit_in_account_currency = difference / row.exchange_rate; + row.credit = difference; + } else { + row.debit_in_account_currency = -difference / row.exchange_rate; + row.debit = -difference; + } + } + refresh_field("accounts"); + }, }); From 2ad6d637ee9193253fbade6ab35769636906c8d1 Mon Sep 17 00:00:00 2001 From: ljain112 Date: Tue, 20 Aug 2024 10:58:45 +0530 Subject: [PATCH 181/734] fix: revert 091c5496b20864577d133b0804e957ff8995606f (cherry picked from commit eeb6e75dcf4a79d8ea87c4538f3424348088117d) --- .../doctype/purchase_invoice/purchase_invoice.js | 2 +- .../doctype/purchase_invoice/purchase_invoice.py | 16 ---------------- 2 files changed, 1 insertion(+), 17 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index 9c8c8cdfb03..54153b5c19f 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -652,7 +652,7 @@ frappe.ui.form.on("Purchase Invoice", { if (frm.doc.supplier) { frm.doc.apply_tds = frm.doc.__onload.supplier_tds ? 1 : 0; } - if (!frm.doc.__onload.enable_apply_tds) { + if (!frm.doc.__onload.supplier_tds) { frm.set_df_property("apply_tds", "read_only", 1); } } diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 623fb941b89..c9f3b220cc6 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -346,22 +346,6 @@ class PurchaseInvoice(BuyingController): self.tax_withholding_category = tds_category self.set_onload("supplier_tds", tds_category) - # If Linked Purchase Order has TDS applied, enable 'apply_tds' checkbox - if purchase_orders := [x.purchase_order for x in self.items if x.purchase_order]: - po = qb.DocType("Purchase Order") - po_with_tds = ( - qb.from_(po) - .select(po.name) - .where( - po.docstatus.eq(1) - & (po.name.isin(purchase_orders)) - & (po.apply_tds.eq(1)) - & (po.tax_withholding_category.notnull()) - ) - .run() - ) - self.set_onload("enable_apply_tds", True if po_with_tds else False) - super().set_missing_values(for_validate) def validate_credit_to_acc(self): From 11359bd2353e7804e226ae000464093d8d5aa71c Mon Sep 17 00:00:00 2001 From: ljain112 Date: Fri, 23 Aug 2024 12:34:07 +0530 Subject: [PATCH 182/734] fix: tds workflow in purchase order (cherry picked from commit a7888b26a73ab61240adf22392bb2ef7b4f93927) --- .../doctype/purchase_order/purchase_order.js | 36 +++++++++++++++++++ .../doctype/purchase_order/purchase_order.py | 7 ++++ 2 files changed, 43 insertions(+) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js index bd92ebef3d7..cab375ea557 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.js +++ b/erpnext/buying/doctype/purchase_order/purchase_order.js @@ -65,6 +65,33 @@ frappe.ui.form.on("Purchase Order", { } }, + supplier: function (frm) { + // Do not update if inter company reference is there as the details will already be updated + if (frm.updating_party_details || frm.doc.inter_company_invoice_reference) return; + + if (frm.doc.__onload && frm.doc.__onload.load_after_mapping) return; + + erpnext.utils.get_party_details( + frm, + "erpnext.accounts.party.get_party_details", + { + posting_date: frm.doc.transaction_date, + bill_date: frm.doc.bill_date, + party: frm.doc.supplier, + party_type: "Supplier", + account: frm.doc.credit_to, + price_list: frm.doc.buying_price_list, + fetch_payment_terms_template: cint(!frm.doc.ignore_default_payment_terms_template), + }, + function () { + frm.doc.apply_tds = frm.supplier_tds ? 1 : 0; + frm.doc.tax_withholding_category = frm.supplier_tds; + frm.set_df_property("apply_tds", "read_only", frm.supplier_tds ? 0 : 1); + frm.set_df_property("tax_withholding_category", "hidden", frm.supplier_tds ? 0 : 1); + } + ); + }, + get_materials_from_supplier: function (frm) { let po_details = []; @@ -108,6 +135,15 @@ frappe.ui.form.on("Purchase Order", { frm.set_value("transaction_date", frappe.datetime.get_today()); } + if (frm.doc.__onload && frm.is_new()) { + if (frm.doc.supplier) { + frm.doc.apply_tds = frm.doc.__onload.supplier_tds ? 1 : 0; + } + if (!frm.doc.__onload.supplier_tds) { + frm.set_df_property("apply_tds", "read_only", 1); + } + } + erpnext.queries.setup_queries(frm, "Warehouse", function () { return erpnext.queries.warehouse(frm.doc); }); diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index 0508483a0fc..32968bc6a56 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -648,6 +648,13 @@ class PurchaseOrder(BuyingController): if sco: update_sco_status(sco, "Closed" if self.status == "Closed" else None) + def set_missing_values(self, for_validate=False): + tds_category = frappe.db.get_value("Supplier", self.supplier, "tax_withholding_category") + if tds_category and not for_validate: + self.apply_tds = 1 + self.tax_withholding_category = tds_category + self.set_onload("supplier_tds", tds_category) + @frappe.request_cache def item_last_purchase_rate(name, conversion_rate, item_code, conversion_factor=1.0): From 5edebb28a5bcbeacc1084c9da3cb99e156d95b8d Mon Sep 17 00:00:00 2001 From: ljain112 Date: Tue, 3 Sep 2024 11:44:55 +0530 Subject: [PATCH 183/734] fix: do not check appy_tds in Purchase Order Automatically (cherry picked from commit be6c174b439c6a3729f89583c74723921e1fb9f5) --- erpnext/buying/doctype/purchase_order/purchase_order.js | 2 -- erpnext/buying/doctype/purchase_order/purchase_order.py | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js index cab375ea557..a40ef47afcf 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.js +++ b/erpnext/buying/doctype/purchase_order/purchase_order.js @@ -84,8 +84,6 @@ frappe.ui.form.on("Purchase Order", { fetch_payment_terms_template: cint(!frm.doc.ignore_default_payment_terms_template), }, function () { - frm.doc.apply_tds = frm.supplier_tds ? 1 : 0; - frm.doc.tax_withholding_category = frm.supplier_tds; frm.set_df_property("apply_tds", "read_only", frm.supplier_tds ? 0 : 1); frm.set_df_property("tax_withholding_category", "hidden", frm.supplier_tds ? 0 : 1); } diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index 32968bc6a56..6c311953aea 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -655,6 +655,8 @@ class PurchaseOrder(BuyingController): self.tax_withholding_category = tds_category self.set_onload("supplier_tds", tds_category) + super().set_missing_values(for_validate) + @frappe.request_cache def item_last_purchase_rate(name, conversion_rate, item_code, conversion_factor=1.0): From 01f30682ee9e15a51161a4f2a91681484e6ac8bf Mon Sep 17 00:00:00 2001 From: ljain112 Date: Tue, 3 Sep 2024 13:58:30 +0530 Subject: [PATCH 184/734] fix: consistent behaviour on refresh (cherry picked from commit b216d7127816a9b61c3ca20c645e5b78e9984be6) --- erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js | 4 ++-- erpnext/buying/doctype/purchase_order/purchase_order.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index 54153b5c19f..2d5cbb9e6c3 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -648,8 +648,8 @@ frappe.ui.form.on("Purchase Invoice", { }, onload: function (frm) { - if (frm.doc.__onload && frm.is_new()) { - if (frm.doc.supplier) { + if (frm.doc.__onload && frm.doc.supplier) { + if (frm.is_new()) { frm.doc.apply_tds = frm.doc.__onload.supplier_tds ? 1 : 0; } if (!frm.doc.__onload.supplier_tds) { diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js index a40ef47afcf..ac2aa43f23d 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.js +++ b/erpnext/buying/doctype/purchase_order/purchase_order.js @@ -133,8 +133,8 @@ frappe.ui.form.on("Purchase Order", { frm.set_value("transaction_date", frappe.datetime.get_today()); } - if (frm.doc.__onload && frm.is_new()) { - if (frm.doc.supplier) { + if (frm.doc.__onload && frm.doc.supplier) { + if (frm.is_new()) { frm.doc.apply_tds = frm.doc.__onload.supplier_tds ? 1 : 0; } if (!frm.doc.__onload.supplier_tds) { From 7027be8fbcf51282ea470299e4edb3d9ce89dd6f Mon Sep 17 00:00:00 2001 From: ljain112 Date: Wed, 4 Sep 2024 13:57:03 +0530 Subject: [PATCH 185/734] fix: set tax_withholding_category from Purchase Order while creating pi form po (cherry picked from commit b9048ca6faad26837626f42a9cc475f96d4a4894) --- erpnext/buying/doctype/purchase_order/purchase_order.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index 6c311953aea..afb43e30c7b 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -769,6 +769,11 @@ def get_mapped_purchase_invoice(source_name, target_doc=None, ignore_permissions def postprocess(source, target): target.flags.ignore_permissions = ignore_permissions set_missing_values(source, target) + + # set tax_withholding_category from Purchase Order + if source.apply_tds and source.tax_withholding_category and target.apply_tds: + target.tax_withholding_category = source.tax_withholding_category + # Get the advance paid Journal Entries in Purchase Invoice Advance if target.get("allocate_advances_automatically"): target.set_advances() From 741c18b14468f29586cc9c19c293b6f6518249e1 Mon Sep 17 00:00:00 2001 From: ljain112 Date: Tue, 10 Sep 2024 13:11:09 +0530 Subject: [PATCH 186/734] fix: do not auto apply tds in purchase order (cherry picked from commit 0b942a06144977a052b48d6b61daf9c61816c82d) --- erpnext/buying/doctype/purchase_order/purchase_order.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index afb43e30c7b..14424dfdf4a 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -651,8 +651,6 @@ class PurchaseOrder(BuyingController): def set_missing_values(self, for_validate=False): tds_category = frappe.db.get_value("Supplier", self.supplier, "tax_withholding_category") if tds_category and not for_validate: - self.apply_tds = 1 - self.tax_withholding_category = tds_category self.set_onload("supplier_tds", tds_category) super().set_missing_values(for_validate) From 42494db3c77db1ec590bf7e73bb89828cd06b029 Mon Sep 17 00:00:00 2001 From: ljain112 Date: Wed, 11 Sep 2024 18:54:55 +0530 Subject: [PATCH 187/734] fix: hide and reset discount control on new POS order (cherry picked from commit 5b0053f8ddbb93e2f5e6921b72ea6cbccb265f5c) --- .../page/point_of_sale/pos_item_cart.js | 41 ++++++++----------- 1 file changed, 16 insertions(+), 25 deletions(-) diff --git a/erpnext/selling/page/point_of_sale/pos_item_cart.js b/erpnext/selling/page/point_of_sale/pos_item_cart.js index 694f70d4db5..b808b4f8828 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_cart.js +++ b/erpnext/selling/page/point_of_sale/pos_item_cart.js @@ -389,28 +389,14 @@ erpnext.PointOfSale.ItemCart = class { placeholder: discount ? discount + "%" : __("Enter discount percentage."), input_class: "input-xs", onchange: function () { - if (flt(this.value) != 0) { - frappe.model.set_value( - frm.doc.doctype, - frm.doc.name, - "additional_discount_percentage", - flt(this.value) - ); - me.hide_discount_control(this.value); - } else { - frappe.model.set_value( - frm.doc.doctype, - frm.doc.name, - "additional_discount_percentage", - 0 - ); - me.$add_discount_elem.css({ - border: "1px dashed var(--gray-500)", - padding: "var(--padding-sm) var(--padding-md)", - }); - me.$add_discount_elem.html(`${me.get_discount_icon()} ${__("Add Discount")}`); - me.discount_field = undefined; - } + this.value = flt(this.value); + frappe.model.set_value( + frm.doc.doctype, + frm.doc.name, + "additional_discount_percentage", + flt(this.value) + ); + me.hide_discount_control(this.value); }, }, parent: this.$add_discount_elem.find(".add-discount-field"), @@ -421,9 +407,13 @@ erpnext.PointOfSale.ItemCart = class { } hide_discount_control(discount) { - if (!discount) { - this.$add_discount_elem.css({ padding: "0px", border: "none" }); - this.$add_discount_elem.html(`
`); + if (!flt(discount)) { + this.$add_discount_elem.css({ + border: "1px dashed var(--gray-500)", + padding: "var(--padding-sm) var(--padding-md)", + }); + this.$add_discount_elem.html(`${this.get_discount_icon()} ${__("Add Discount")}`); + this.discount_field = undefined; } else { this.$add_discount_elem.css({ border: "1px dashed var(--dark-green-500)", @@ -1051,6 +1041,7 @@ erpnext.PointOfSale.ItemCart = class { this.highlight_checkout_btn(false); } + this.hide_discount_control(frm.doc.additional_discount_percentage); this.update_totals_section(frm); if (frm.doc.docstatus === 1) { From d6a3d0d46835559dffb50c502bfec10d15492031 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Mon, 16 Sep 2024 18:47:34 +0530 Subject: [PATCH 188/734] fix: create and link address while creating prospect & customer (cherry picked from commit 035c15794cd86407fffd2dcc89cc436ba3a3dd6a) --- erpnext/crm/frappe_crm_api.py | 43 +++++++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/erpnext/crm/frappe_crm_api.py b/erpnext/crm/frappe_crm_api.py index 40c0ce8c9c1..53f58ed57eb 100644 --- a/erpnext/crm/frappe_crm_api.py +++ b/erpnext/crm/frappe_crm_api.py @@ -61,6 +61,7 @@ def create_prospect_against_crm_deal(): pass create_contacts(json.loads(doc.contacts), prospect.company_name, "Prospect", prospect_name) + create_address("Prospect", prospect_name, doc.address) frappe.response["message"] = prospect_name @@ -86,17 +87,49 @@ def create_contacts(contacts, organization=None, link_doctype=None, link_docname if c.get("mobile_no"): contact.append("phone_nos", {"phone": c.get("mobile_no"), "is_primary_mobile_no": 1}) - link_contact_to_prospect(contact, link_doctype, link_docname) + link_doc(contact, link_doctype, link_docname) contact.save(ignore_permissions=True) -def link_contact_to_prospect(contact, link_doctype, link_docname): +def create_address(doctype, docname, address): + if not address: + return + try: + _address = frappe.db.exists("Address", address.get("name")) + if not _address: + new_address_doc = frappe.new_doc("Address") + for field in [ + "address_title", + "address_type", + "address_line1", + "address_line2", + "city", + "state", + "pincode", + "country", + ]: + if address.get(field): + new_address_doc.set(field, address.get(field)) + + new_address_doc.append("links", {"link_doctype": doctype, "link_name": docname}) + new_address_doc.insert(ignore_mandatory=True) + return new_address_doc.name + else: + address = frappe.get_doc("Address", _address) + link_doc(address, doctype, docname) + address.save(ignore_permissions=True) + return address.name + except Exception: + frappe.log_error(frappe.get_traceback(), f"Error while creating address for {docname}") + + +def link_doc(doc, link_doctype, link_docname): already_linked = any( - [(link.link_doctype == link_doctype and link.link_name == link_docname) for link in contact.links] + [(link.link_doctype == link_doctype and link.link_name == link_docname) for link in doc.links] ) if not already_linked: - contact.append( + doc.append( "links", {"link_doctype": link_doctype, "link_name": link_docname, "link_title": link_docname} ) @@ -130,6 +163,8 @@ def create_customer(customer_data=None): contacts = json.loads(customer_data.get("contacts")) create_contacts(contacts, customer_name, "Customer", customer_name) + create_address("Customer", customer_name, customer_data.get("address")) + return customer_name except Exception: frappe.log_error(frappe.get_traceback(), "Error while creating customer against Frappe CRM Deal") pass From c5051561e4eb14374e85245c1068e807ef8b15a0 Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Mon, 16 Sep 2024 12:35:35 +0530 Subject: [PATCH 189/734] fix: do not validate purchase document for composite asset (cherry picked from commit 5fd058dde9c5afe1e8b3148ac22bcf5f28aa5023) --- erpnext/assets/doctype/asset/asset.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 58f813247f0..6c77a1b9b0e 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -623,6 +623,9 @@ class Asset(AccountsController): return records def validate_make_gl_entry(self): + if self.is_composite_asset: + return True + purchase_document = self.get_purchase_document() if not purchase_document: return False From f7cedac5260d832aa6b95b9f0341c94d7ccd6203 Mon Sep 17 00:00:00 2001 From: Navin-S-R Date: Sat, 14 Sep 2024 17:39:50 +0530 Subject: [PATCH 190/734] fix(ux): set amount based on account currency while adding new row (cherry picked from commit 0ff04f774d5e54ce83e239896e7ab51d083021fe) --- erpnext/accounts/doctype/journal_entry/journal_entry.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js index 999db883e98..faa38763b80 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.js +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js @@ -360,21 +360,23 @@ erpnext.accounts.JournalEntry = class JournalEntry extends frappe.ui.form.Contro accounts_add(doc, cdt, cdn) { var row = frappe.get_doc(cdt, cdn); + row.exchange_rate = 1; $.each(doc.accounts, function (i, d) { if (d.account && d.party && d.party_type) { row.account = d.account; row.party = d.party; row.party_type = d.party_type; + row.exchange_rate = d.exchange_rate; } }); // set difference if (doc.difference) { if (doc.difference > 0) { - row.credit_in_account_currency = doc.difference; + row.credit_in_account_currency = doc.difference / row.exchange_rate; row.credit = doc.difference; } else { - row.debit_in_account_currency = -doc.difference; + row.debit_in_account_currency = -doc.difference / row.exchange_rate; row.debit = -doc.difference; } } From dea735de4df2ca427fce8803d3d4f8acdc89c789 Mon Sep 17 00:00:00 2001 From: "lukas.brandhoff" Date: Wed, 28 Aug 2024 09:17:28 +0000 Subject: [PATCH 191/734] fix: A project without tasks should be able to complete (cherry picked from commit 268962c25f0571ded4c46c381906c870df6aab91) --- erpnext/projects/doctype/project/project.py | 7 +++++ .../projects/doctype/project/test_project.py | 28 +++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/erpnext/projects/doctype/project/project.py b/erpnext/projects/doctype/project/project.py index b09735e5644..7870a2ace73 100644 --- a/erpnext/projects/doctype/project/project.py +++ b/erpnext/projects/doctype/project/project.py @@ -213,6 +213,13 @@ class Project(Document): frappe.db.set_value("Sales Order", {"project": self.name}, "project", "") def update_percent_complete(self): + if self.status == "Completed": + if ( + len(frappe.get_all("Task", dict(project=self.name))) == 0 + ): # A project without tasks should be able to complete + self.percent_complete_method = "Manual" + self.percent_complete = 100 + if self.percent_complete_method == "Manual": if self.status == "Completed": self.percent_complete = 100 diff --git a/erpnext/projects/doctype/project/test_project.py b/erpnext/projects/doctype/project/test_project.py index 1b7460f7a2a..f7140c211e0 100644 --- a/erpnext/projects/doctype/project/test_project.py +++ b/erpnext/projects/doctype/project/test_project.py @@ -199,6 +199,34 @@ class TestProject(FrappeTestCase): if not pt.is_group: self.assertIsNotNone(pt.parent_task) + def test_project_having_no_tasks_complete(self): + project_name = "Test Project - No Tasks Completion" + frappe.db.sql(""" delete from tabTask where project = %s """, project_name) + frappe.delete_doc("Project", project_name) + + project = frappe.get_doc( + dict( + doctype="Project", + project_name=project_name, + status="Open", + expected_start_date=nowdate(), + company="_Test Company", + ) + ).insert() + + tasks = frappe.get_all( + "Task", + ["subject", "exp_end_date", "depends_on_tasks", "name", "parent_task"], + dict(project=project.name), + order_by="creation asc", + ) + + self.assertEqual(project.status, "Open") + self.assertEqual(len(tasks), 0) + project.status = "Completed" + project.save() + self.assertEqual(project.status, "Completed") + def get_project(name, template): project = frappe.get_doc( From c9b6b0d8683ef2328ba7892b295c3ca05256cd94 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 16 Sep 2024 11:14:54 +0530 Subject: [PATCH 192/734] refactor(test): fix linter (cherry picked from commit 4eeae8011ea51dc5621cef302255c2b54da1537a) --- erpnext/projects/doctype/project/test_project.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/erpnext/projects/doctype/project/test_project.py b/erpnext/projects/doctype/project/test_project.py index f7140c211e0..e5996c2da9d 100644 --- a/erpnext/projects/doctype/project/test_project.py +++ b/erpnext/projects/doctype/project/test_project.py @@ -205,13 +205,13 @@ class TestProject(FrappeTestCase): frappe.delete_doc("Project", project_name) project = frappe.get_doc( - dict( - doctype="Project", - project_name=project_name, - status="Open", - expected_start_date=nowdate(), - company="_Test Company", - ) + { + "doctype": "Project", + "project_name": project_name, + "status": "Open", + "expected_start_date": nowdate(), + "company": "_Test Company", + } ).insert() tasks = frappe.get_all( From 28f1f9355da9ad92733de78a141571c404755be1 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 17 Sep 2024 05:00:07 +0000 Subject: [PATCH 193/734] chore(release): Bumped to Version 15.35.2 ## [15.35.2](https://github.com/frappe/erpnext/compare/v15.35.1...v15.35.2) (2024-09-17) ### Bug Fixes * currency changing while making PO from Supplier Quotation (backport [#43187](https://github.com/frappe/erpnext/issues/43187)) ([#43205](https://github.com/frappe/erpnext/issues/43205)) ([2f56ba7](https://github.com/frappe/erpnext/commit/2f56ba7f428488c8a94160941df9c4d309dee5eb)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 50acf5dd033..8d6e2cc0d18 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.35.1" +__version__ = "15.35.2" def get_default_company(user=None): From 291f0a580be176a4c87c03e54372dd7a7cca9cb1 Mon Sep 17 00:00:00 2001 From: Dany Robert Date: Thu, 12 Sep 2024 09:18:19 +0000 Subject: [PATCH 194/734] fix: invalid gp calculation (cherry picked from commit c79851239cf3fa663dff2e10a5d5738e7bfa7abf) --- erpnext/controllers/selling_controller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index 766e67be0f6..7ad12b54eff 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -640,7 +640,7 @@ class SellingController(StockController): if self.doctype in ["Sales Order", "Quotation"]: for item in self.items: item.gross_profit = flt( - ((item.base_rate - flt(item.valuation_rate)) * item.stock_qty), + ((flt(item.stock_uom_rate) - flt(item.valuation_rate)) * item.stock_qty), self.precision("amount", item), ) From 86e1818420a3152cf47ef65943216c6a9bd0d29e Mon Sep 17 00:00:00 2001 From: Navin-S-R Date: Sat, 14 Sep 2024 15:58:02 +0530 Subject: [PATCH 195/734] fix: map rows on journal entry by validating account, party, debit and credit value (cherry picked from commit b634aa9cfb60f78531da4a02efec458a3d294a78) --- erpnext/controllers/accounts_controller.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 92efa5168f3..b35be7950fd 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -3535,6 +3535,13 @@ def check_if_child_table_updated(child_table_before_update, child_table_after_up # Check if any field affecting accounting entry is altered for index, item in enumerate(child_table_before_update): + if item.parenttype == "Journal Entry" and any( + [ + child_table_after_update[index].get(i) != item.get(i) + for i in ["account", "party_type", "party", "debit", "credit"] + ] + ): + continue for field in fields_to_check: if child_table_after_update[index].get(field) != item.get(field): return True From 310b1314698710c1f82c820cdf6a78aaaebe5fdc Mon Sep 17 00:00:00 2001 From: Navin-S-R Date: Sat, 14 Sep 2024 17:31:53 +0530 Subject: [PATCH 196/734] test: reconcile payment jv from closed fiscal year (cherry picked from commit f47ea468066c25db8d6765205733e549c6dce190) --- .../test_payment_reconciliation.py | 162 +++++++++++++++++- 1 file changed, 161 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py index 5aa411158a8..65d7d42ef3d 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py @@ -5,7 +5,7 @@ import frappe from frappe import qb 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, today from erpnext import get_default_cost_center from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry @@ -13,6 +13,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.accounts.utils import get_fiscal_year from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order from erpnext.stock.doctype.item.test_item import create_item @@ -1845,6 +1846,107 @@ class TestPaymentReconciliation(FrappeTestCase): self.assertEqual(len(pr.invoices), 1) self.assertEqual(len(pr.payments), 1) + def test_reconciliation_on_closed_period_payment(self): + from erpnext.accounts.doctype.account.test_account import create_account + + self.create_company() + self.create_cost_center() + + # create bank account + parent_account = frappe.db.get_value( + "Account", {"company": self.company, "account_name": "Bank Accounts", "is_group": 1}, "name" + ) + bank_account = create_account( + account_name="Bank Account", + account_type="Bank", + is_group=0, + company=self.company, + root_type="Asset", + report_type="Balance Sheet", + account_currency="INR", + parent_account=parent_account, + doctype="Account", + ) + + # create backdated fiscal year + create_fiscal_year(company=self.company, year_start_date="1990-04-01", year_end_date="1991-03-31") + + # make journal entry for previous year + je_1 = frappe.new_doc("Journal Entry") + je_1.posting_date = "1990-06-01" + je_1.company = self.company + je_1.user_remark = "test" + je_1.set( + "accounts", + [ + { + "account": self.debit_to, + "cost_center": self.cost_center, + "party_type": "Customer", + "party": self.customer, + "debit_in_account_currency": 0, + "credit_in_account_currency": 1000, + }, + { + "account": bank_account, + "cost_center": self.sub_cc, + "credit_in_account_currency": 0, + "debit_in_account_currency": 500, + }, + { + "account": "Cash - _PR", + "cost_center": self.sub_cc, + "credit_in_account_currency": 0, + "debit_in_account_currency": 500, + }, + ], + ) + je_1.save() + je_1.submit() + je_1.reload() + # check journal entry is submitted + self.assertTrue(je_1.docstatus == 1) + + # make period closing voucher + pcv = make_period_closing_voucher( + company=self.company, cost_center=self.cost_center, posting_date="1991-03-31" + ) + pcv.reload() + # check if period closing voucher is completed + self.assertEqual(pcv.gle_processing_status, "Completed") + + # make journal entry for active year + je_2 = self.create_journal_entry( + acc1=self.debit_to, acc2=self.income_account, amount=1000, posting_date=today() + ) + je_2.accounts[0].party_type = "Customer" + je_2.accounts[0].party = self.customer + je_2.save() + je_2.submit() + je_2.reload() + # check journal entry is submitted + self.assertTrue(je_2.docstatus == 1) + + # process reconciliation on closed period payment + pr = self.create_payment_reconciliation(party_is_customer=True) + pr.from_invoice_date = pr.to_invoice_date = pr.from_payment_date = pr.to_payment_date = None + pr.get_unreconciled_entries() + invoices = [invoice.as_dict() for invoice in pr.invoices] + payments = [payment.as_dict() for payment in pr.payments] + pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments})) + pr.reconcile() + je_1.reload() + je_2.reload() + + # check whether the payment reconciliation is done on the closed period + self.assertEqual(pr.get("invoices"), []) + self.assertEqual(pr.get("payments"), []) + + # cancel created entires during test + pcv.cancel() + je_1.cancel() + je_2.cancel() + def make_customer(customer_name, currency=None): if not frappe.db.exists("Customer", customer_name): @@ -1872,3 +1974,61 @@ def make_supplier(supplier_name, currency=None): return supplier.name else: return supplier_name + + +def create_fiscal_year(company, year_start_date, year_end_date): + fy_docname = frappe.db.exists( + "Fiscal Year", {"year_start_date": year_start_date, "year_end_date": year_end_date} + ) + if not fy_docname: + fy_doc = frappe.get_doc( + { + "doctype": "Fiscal Year", + "year": f"{getdate(year_start_date).year}-{getdate(year_end_date).year}", + "year_start_date": year_start_date, + "year_end_date": year_end_date, + "companies": [{"company": company}], + } + ).save() + return fy_doc + else: + fy_doc = frappe.get_doc("Fiscal Year", fy_docname) + if not frappe.db.exists("Fiscal Year Company", {"parent": fy_docname, "company": company}): + fy_doc.append("companies", {"company": company}) + fy_doc.save() + return fy_doc + + +def make_period_closing_voucher(company, cost_center, posting_date=None, submit=True): + from erpnext.accounts.doctype.account.test_account import create_account + + parent_account = frappe.db.get_value( + "Account", {"company": company, "account_name": "Current Liabilities", "is_group": 1}, "name" + ) + surplus_account = create_account( + account_name="Reserve and Surplus", + is_group=0, + company=company, + root_type="Liability", + report_type="Balance Sheet", + account_currency="INR", + parent_account=parent_account, + doctype="Account", + ) + pcv = frappe.get_doc( + { + "doctype": "Period Closing Voucher", + "transaction_date": posting_date or today(), + "posting_date": posting_date or today(), + "company": company, + "fiscal_year": get_fiscal_year(today(), company=company)[0], + "cost_center": cost_center, + "closing_account_head": surplus_account, + "remarks": "test", + } + ) + pcv.insert() + if submit: + pcv.submit() + + return pcv From d91013a46707095f62a009a2fbdcf84bd1303c62 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 17 Sep 2024 14:38:26 +0530 Subject: [PATCH 197/734] fix: ignore repost logic on Payment Reconciliation (cherry picked from commit 75babd4c18fc555046750a2b640d0ac1f4f435bd) --- erpnext/accounts/doctype/journal_entry/journal_entry.py | 5 +++++ erpnext/accounts/utils.py | 2 ++ erpnext/controllers/accounts_controller.py | 7 ------- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index 6adc8be3f7d..593fa48e856 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -195,6 +195,11 @@ class JournalEntry(AccountsController): self.update_booked_depreciation() def on_update_after_submit(self): + # Flag will be set on Reconciliation + # Reconciliation tool will anyways repost ledger entries. So, no need to check and do implicit repost. + if self.flags.get("ignore_reposting_on_reconciliation"): + return + self.needs_repost = self.check_if_fields_updated(fields_to_check=[], child_tables={"accounts": []}) if self.needs_repost: self.validate_for_repost() diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 32eeb1e07a6..8e47ddc3652 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -665,6 +665,8 @@ def update_reference_in_journal_entry(d, journal_entry, do_not_save=False): # will work as update after submit journal_entry.flags.ignore_validate_update_after_submit = True + # Ledgers will be reposted by Reconciliation tool + journal_entry.flags.ignore_reposting_on_reconciliation = True if not do_not_save: journal_entry.save(ignore_permissions=True) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index b35be7950fd..92efa5168f3 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -3535,13 +3535,6 @@ def check_if_child_table_updated(child_table_before_update, child_table_after_up # Check if any field affecting accounting entry is altered for index, item in enumerate(child_table_before_update): - if item.parenttype == "Journal Entry" and any( - [ - child_table_after_update[index].get(i) != item.get(i) - for i in ["account", "party_type", "party", "debit", "credit"] - ] - ): - continue for field in fields_to_check: if child_table_after_update[index].get(field) != item.get(field): return True From 861edb438bb11e246c2eb9f8e1953a84bcdb0461 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 17 Sep 2024 16:49:03 +0530 Subject: [PATCH 198/734] refactor(test): make use existing test data and dynamic fy creation (cherry picked from commit f45638015f8bce4efe7cea3f0035c060d42a3426) --- .../test_payment_reconciliation.py | 51 +++++-------------- 1 file changed, 13 insertions(+), 38 deletions(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py index 65d7d42ef3d..b229c9c2c40 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py @@ -1849,31 +1849,19 @@ class TestPaymentReconciliation(FrappeTestCase): def test_reconciliation_on_closed_period_payment(self): from erpnext.accounts.doctype.account.test_account import create_account - self.create_company() - self.create_cost_center() - - # create bank account - parent_account = frappe.db.get_value( - "Account", {"company": self.company, "account_name": "Bank Accounts", "is_group": 1}, "name" - ) - bank_account = create_account( - account_name="Bank Account", - account_type="Bank", - is_group=0, - company=self.company, - root_type="Asset", - report_type="Balance Sheet", - account_currency="INR", - parent_account=parent_account, - doctype="Account", - ) + # Get current fiscal year + current_fy_start_date = get_fiscal_year(today())[1] # create backdated fiscal year - create_fiscal_year(company=self.company, year_start_date="1990-04-01", year_end_date="1991-03-31") + prev_fy_start_date = add_days(current_fy_start_date, -366) + prev_fy_end_date = add_days(current_fy_start_date, -1) + create_fiscal_year( + company=self.company, year_start_date=prev_fy_start_date, year_end_date=prev_fy_end_date + ) # make journal entry for previous year je_1 = frappe.new_doc("Journal Entry") - je_1.posting_date = "1990-06-01" + je_1.posting_date = add_days(prev_fy_start_date, 20) je_1.company = self.company je_1.user_remark = "test" je_1.set( @@ -1888,28 +1876,24 @@ class TestPaymentReconciliation(FrappeTestCase): "credit_in_account_currency": 1000, }, { - "account": bank_account, - "cost_center": self.sub_cc, + "account": self.bank, + "cost_center": self.sub_cc.name, "credit_in_account_currency": 0, "debit_in_account_currency": 500, }, { - "account": "Cash - _PR", - "cost_center": self.sub_cc, + "account": self.cash, + "cost_center": self.sub_cc.name, "credit_in_account_currency": 0, "debit_in_account_currency": 500, }, ], ) - je_1.save() je_1.submit() - je_1.reload() - # check journal entry is submitted - self.assertTrue(je_1.docstatus == 1) # make period closing voucher pcv = make_period_closing_voucher( - company=self.company, cost_center=self.cost_center, posting_date="1991-03-31" + company=self.company, cost_center=self.cost_center, posting_date=prev_fy_end_date ) pcv.reload() # check if period closing voucher is completed @@ -1921,11 +1905,7 @@ class TestPaymentReconciliation(FrappeTestCase): ) je_2.accounts[0].party_type = "Customer" je_2.accounts[0].party = self.customer - je_2.save() je_2.submit() - je_2.reload() - # check journal entry is submitted - self.assertTrue(je_2.docstatus == 1) # process reconciliation on closed period payment pr = self.create_payment_reconciliation(party_is_customer=True) @@ -1942,11 +1922,6 @@ class TestPaymentReconciliation(FrappeTestCase): self.assertEqual(pr.get("invoices"), []) self.assertEqual(pr.get("payments"), []) - # cancel created entires during test - pcv.cancel() - je_1.cancel() - je_2.cancel() - def make_customer(customer_name, currency=None): if not frappe.db.exists("Customer", customer_name): From edcdfdd194be8d84b5009c18f725868a7bb572d9 Mon Sep 17 00:00:00 2001 From: Navin-S-R Date: Tue, 17 Sep 2024 17:21:26 +0530 Subject: [PATCH 199/734] refactor: update formatting changes (cherry picked from commit 768bb0312aae3ad67191778f09fe75f4c914a019) --- .../payment_reconciliation/test_payment_reconciliation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py index b229c9c2c40..f317f3399db 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py @@ -1996,7 +1996,7 @@ def make_period_closing_voucher(company, cost_center, posting_date=None, submit= "transaction_date": posting_date or today(), "posting_date": posting_date or today(), "company": company, - "fiscal_year": get_fiscal_year(today(), company=company)[0], + "fiscal_year": get_fiscal_year(posting_date or today(), company=company)[0], "cost_center": cost_center, "closing_account_head": surplus_account, "remarks": "test", From 78768f883c09ec91de2dfee101923808ccd6fa55 Mon Sep 17 00:00:00 2001 From: Navin-S-R Date: Tue, 17 Sep 2024 18:55:28 +0530 Subject: [PATCH 200/734] fix: create fiscal year without overlapping existing Fiscal Years (cherry picked from commit 720a330617c7cb5410bee715dbb4a4111af879f7) --- .../test_payment_reconciliation.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py index f317f3399db..883c638398c 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py @@ -5,7 +5,7 @@ import frappe from frappe import qb from frappe.tests.utils import FrappeTestCase, change_settings -from frappe.utils import add_days, flt, getdate, nowdate, today +from frappe.utils import add_days, add_years, flt, getdate, nowdate, today from erpnext import get_default_cost_center from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry @@ -1847,14 +1847,10 @@ class TestPaymentReconciliation(FrappeTestCase): self.assertEqual(len(pr.payments), 1) def test_reconciliation_on_closed_period_payment(self): - from erpnext.accounts.doctype.account.test_account import create_account - - # Get current fiscal year - current_fy_start_date = get_fiscal_year(today())[1] - # create backdated fiscal year - prev_fy_start_date = add_days(current_fy_start_date, -366) - prev_fy_end_date = add_days(current_fy_start_date, -1) + first_fy_start_date = frappe.db.get_value("Fiscal Year", {"disabled": 0}, "min(year_start_date)") + prev_fy_start_date = add_years(first_fy_start_date, -1) + prev_fy_end_date = add_days(first_fy_start_date, -1) create_fiscal_year( company=self.company, year_start_date=prev_fy_start_date, year_end_date=prev_fy_end_date ) From 78c68397d9a5147970f4b19435c5352ced2a6a44 Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Fri, 13 Sep 2024 13:26:21 +0530 Subject: [PATCH 201/734] fix: updated filtering in depreciation and balances report (cherry picked from commit 3a34eecdcfe394d336cd5abd1734ff701eac32b5) --- .../asset_depreciations_and_balances.py | 58 +++++++++++++++---- 1 file changed, 46 insertions(+), 12 deletions(-) diff --git a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py index 34cced2ca17..caddaf91f82 100644 --- a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py +++ b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py @@ -232,9 +232,14 @@ def get_group_by_asset_data(filters): def get_assets_for_grouped_by_category(filters): condition = "" if filters.get("asset_category"): - condition = " and a.asset_category = '{}'".format(filters.get("asset_category")) + condition = f" and a.asset_category = '{filters.get('asset_category')}'" + finance_book_filter = "" + if filters.get("finance_book"): + finance_book_filter += " and ifnull(gle.finance_book, '')=%(finance_book)s" + condition += " and exists (select 1 from `tabAsset Depreciation Schedule` ads where ads.asset = a.name and ads.finance_book = %(finance_book)s)" + return frappe.db.sql( - """ + f""" SELECT results.asset_category, sum(results.accumulated_depreciation_as_on_from_date) as accumulated_depreciation_as_on_from_date, sum(results.depreciation_eliminated_during_the_period) as depreciation_eliminated_during_the_period, @@ -264,7 +269,14 @@ def get_assets_for_grouped_by_category(filters): aca.parent = a.asset_category and aca.company_name = %(company)s join `tabCompany` company on company.name = %(company)s - where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s and gle.debit != 0 and gle.is_cancelled = 0 and gle.account = ifnull(aca.depreciation_expense_account, company.depreciation_expense_account) {0} + where + a.docstatus=1 + and a.company=%(company)s + and a.purchase_date <= %(to_date)s + and gle.debit != 0 + and gle.is_cancelled = 0 + and gle.account = ifnull(aca.depreciation_expense_account, company.depreciation_expense_account) + {condition} {finance_book_filter} group by a.asset_category union SELECT a.asset_category, @@ -280,11 +292,16 @@ def get_assets_for_grouped_by_category(filters): end), 0) as depreciation_eliminated_during_the_period, 0 as depreciation_amount_during_the_period from `tabAsset` a - where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s {0} + where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s {condition} group by a.asset_category) as results group by results.asset_category - """.format(condition), - {"to_date": filters.to_date, "from_date": filters.from_date, "company": filters.company}, + """, + { + "to_date": filters.to_date, + "from_date": filters.from_date, + "company": filters.company, + "finance_book": filters.get("finance_book", ""), + }, as_dict=1, ) @@ -292,9 +309,14 @@ def get_assets_for_grouped_by_category(filters): def get_assets_for_grouped_by_asset(filters): condition = "" if filters.get("asset"): - condition = " and a.name = '{}'".format(filters.get("asset")) + condition = f" and a.name = '{filters.get('asset')}'" + finance_book_filter = "" + if filters.get("finance_book"): + finance_book_filter += " and ifnull(gle.finance_book, '')=%(finance_book)s" + condition += " and exists (select 1 from `tabAsset Depreciation Schedule` ads where ads.asset = a.name and ads.finance_book = %(finance_book)s)" + return frappe.db.sql( - """ + f""" SELECT results.name as asset, sum(results.accumulated_depreciation_as_on_from_date) as accumulated_depreciation_as_on_from_date, sum(results.depreciation_eliminated_during_the_period) as depreciation_eliminated_during_the_period, @@ -324,7 +346,14 @@ def get_assets_for_grouped_by_asset(filters): aca.parent = a.asset_category and aca.company_name = %(company)s join `tabCompany` company on company.name = %(company)s - where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s and gle.debit != 0 and gle.is_cancelled = 0 and gle.account = ifnull(aca.depreciation_expense_account, company.depreciation_expense_account) {0} + where + a.docstatus=1 + and a.company=%(company)s + and a.purchase_date <= %(to_date)s + and gle.debit != 0 + and gle.is_cancelled = 0 + and gle.account = ifnull(aca.depreciation_expense_account, company.depreciation_expense_account) + {finance_book_filter} {condition} group by a.name union SELECT a.name as name, @@ -340,11 +369,16 @@ def get_assets_for_grouped_by_asset(filters): end), 0) as depreciation_eliminated_during_the_period, 0 as depreciation_amount_during_the_period from `tabAsset` a - where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s {0} + where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s {condition} group by a.name) as results group by results.name - """.format(condition), - {"to_date": filters.to_date, "from_date": filters.from_date, "company": filters.company}, + """, + { + "to_date": filters.to_date, + "from_date": filters.from_date, + "company": filters.company, + "finance_book": filters.get("finance_book", ""), + }, as_dict=1, ) From 1e89c007ed8d6e38f8b3b9d8bcc2c1690599b25b Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Fri, 13 Sep 2024 14:45:29 +0530 Subject: [PATCH 202/734] chore: resolved linter check with #nosemgrep (cherry picked from commit 8c8e25214c3042346d8e31514b27f48089ce2948) --- .../asset_depreciations_and_balances.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py index caddaf91f82..2d80808a7e6 100644 --- a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py +++ b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py @@ -238,6 +238,7 @@ def get_assets_for_grouped_by_category(filters): finance_book_filter += " and ifnull(gle.finance_book, '')=%(finance_book)s" condition += " and exists (select 1 from `tabAsset Depreciation Schedule` ads where ads.asset = a.name and ads.finance_book = %(finance_book)s)" + # nosemgrep return frappe.db.sql( f""" SELECT results.asset_category, @@ -306,6 +307,7 @@ def get_assets_for_grouped_by_category(filters): ) +# nosemgrep def get_assets_for_grouped_by_asset(filters): condition = "" if filters.get("asset"): From 4ba37e49d8eee249b9f33255c82a5390625af158 Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Fri, 13 Sep 2024 14:49:01 +0530 Subject: [PATCH 203/734] chore: resolved failing check (cherry picked from commit af52f0e71fa992f89b55795eb14f12e125e56720) --- .../asset_depreciations_and_balances.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py index 2d80808a7e6..b288e8e5ac7 100644 --- a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py +++ b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py @@ -307,7 +307,6 @@ def get_assets_for_grouped_by_category(filters): ) -# nosemgrep def get_assets_for_grouped_by_asset(filters): condition = "" if filters.get("asset"): @@ -317,6 +316,7 @@ def get_assets_for_grouped_by_asset(filters): finance_book_filter += " and ifnull(gle.finance_book, '')=%(finance_book)s" condition += " and exists (select 1 from `tabAsset Depreciation Schedule` ads where ads.asset = a.name and ads.finance_book = %(finance_book)s)" + # nosemgrep return frappe.db.sql( f""" SELECT results.name as asset, From 0fe901a137c1b4d3ab5caee046ad4cc8911d7444 Mon Sep 17 00:00:00 2001 From: venkat102 Date: Fri, 13 Sep 2024 16:48:32 +0530 Subject: [PATCH 204/734] fix: fetch cost center allocation percentage only from the applicable allocation (cherry picked from commit 36e5945c667595811688bd9eeaa07af9ecc5ab08) --- erpnext/accounts/general_ledger.py | 63 ++++++++++++++++-------------- 1 file changed, 33 insertions(+), 30 deletions(-) diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py index a4d128a5845..ad8cc97e101 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -179,50 +179,53 @@ def process_gl_map(gl_map, merge_entries=True, precision=None): def distribute_gl_based_on_cost_center_allocation(gl_map, precision=None): - cost_center_allocation = get_cost_center_allocation_data(gl_map[0]["company"], gl_map[0]["posting_date"]) - if not cost_center_allocation: - return gl_map - new_gl_map = [] for d in gl_map: cost_center = d.get("cost_center") # Validate budget against main cost center validate_expense_against_budget(d, expense_amount=flt(d.debit, precision) - flt(d.credit, precision)) - - if cost_center and cost_center_allocation.get(cost_center): - for sub_cost_center, percentage in cost_center_allocation.get(cost_center, {}).items(): - gle = copy.deepcopy(d) - gle.cost_center = sub_cost_center - for field in ("debit", "credit", "debit_in_account_currency", "credit_in_account_currency"): - gle[field] = flt(flt(d.get(field)) * percentage / 100, precision) - new_gl_map.append(gle) - else: + cost_center_allocation = get_cost_center_allocation_data( + gl_map[0]["company"], gl_map[0]["posting_date"], cost_center + ) + if not cost_center_allocation: new_gl_map.append(d) + continue + + for sub_cost_center, percentage in cost_center_allocation: + gle = copy.deepcopy(d) + gle.cost_center = sub_cost_center + for field in ("debit", "credit", "debit_in_account_currency", "credit_in_account_currency"): + gle[field] = flt(flt(d.get(field)) * percentage / 100, precision) + new_gl_map.append(gle) return new_gl_map -def get_cost_center_allocation_data(company, posting_date): - par = frappe.qb.DocType("Cost Center Allocation") - child = frappe.qb.DocType("Cost Center Allocation Percentage") +def get_cost_center_allocation_data(company, posting_date, cost_center): + cost_center_allocation = frappe.db.get_value( + "Cost Center Allocation", + { + "docstatus": 1, + "company": company, + "valid_from": ("<=", posting_date), + "main_cost_center": cost_center, + }, + pluck="name", + order_by="valid_from desc", + ) - records = ( - frappe.qb.from_(par) - .inner_join(child) - .on(par.name == child.parent) - .select(par.main_cost_center, child.cost_center, child.percentage) - .where(par.docstatus == 1) - .where(par.company == company) - .where(par.valid_from <= posting_date) - .orderby(par.valid_from, order=frappe.qb.desc) - ).run(as_dict=True) + if not cost_center_allocation: + return [] - cc_allocation = frappe._dict() - for d in records: - cc_allocation.setdefault(d.main_cost_center, frappe._dict()).setdefault(d.cost_center, d.percentage) + records = frappe.db.get_all( + "Cost Center Allocation Percentage", + {"parent": cost_center_allocation}, + ["cost_center", "percentage"], + as_list=True, + ) - return cc_allocation + return records def merge_similar_entries(gl_map, precision=None): From 52a161f076b26cbf46811fbf14eff18072657422 Mon Sep 17 00:00:00 2001 From: venkat102 Date: Fri, 13 Sep 2024 16:54:29 +0530 Subject: [PATCH 205/734] test: add unit test for validating multiple cost center allocation with different child cost center (cherry picked from commit 4d5d6150e134a3af99cbd4a9b5e3eb61c87dd636) --- .../test_cost_center_allocation.py | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/erpnext/accounts/doctype/cost_center_allocation/test_cost_center_allocation.py b/erpnext/accounts/doctype/cost_center_allocation/test_cost_center_allocation.py index 65784dbb6c7..c333bbbf130 100644 --- a/erpnext/accounts/doctype/cost_center_allocation/test_cost_center_allocation.py +++ b/erpnext/accounts/doctype/cost_center_allocation/test_cost_center_allocation.py @@ -24,6 +24,7 @@ class TestCostCenterAllocation(unittest.TestCase): "Main Cost Center 2", "Sub Cost Center 1", "Sub Cost Center 2", + "Sub Cost Center 3", ] for cc in cost_centers: create_cost_center(cost_center_name=cc, company="_Test Company") @@ -141,6 +142,49 @@ class TestCostCenterAllocation(unittest.TestCase): jv.cancel() + def test_multiple_cost_center_allocation_on_same_main_cost_center(self): + create_cost_center_allocation( + "_Test Company", + "Main Cost Center 1 - _TC", + {"Sub Cost Center 1 - _TC": 30, "Sub Cost Center 2 - _TC": 30, "Sub Cost Center 3 - _TC": 40}, + valid_from=add_days(today(), -5), + ) + + create_cost_center_allocation( + "_Test Company", + "Main Cost Center 1 - _TC", + {"Sub Cost Center 1 - _TC": 50, "Sub Cost Center 2 - _TC": 50}, + valid_from=add_days(today(), -1), + ) + + jv = make_journal_entry( + "Cash - _TC", + "Sales - _TC", + 100, + cost_center="Main Cost Center 1 - _TC", + posting_date=today(), + submit=True, + ) + + expected_values = {"Sub Cost Center 1 - _TC": 50, "Sub Cost Center 2 - _TC": 50} + + gle = frappe.qb.DocType("GL Entry") + gl_entries = ( + frappe.qb.from_(gle) + .select(gle.cost_center, gle.debit, gle.credit) + .where(gle.voucher_type == "Journal Entry") + .where(gle.voucher_no == jv.name) + .where(gle.account == "Sales - _TC") + .orderby(gle.cost_center) + ).run(as_dict=1) + + self.assertTrue(gl_entries) + + for gle in gl_entries: + self.assertTrue(gle.cost_center in expected_values) + self.assertEqual(gle.debit, 0) + self.assertEqual(gle.credit, expected_values[gle.cost_center]) + def create_cost_center_allocation( company, From 3d29007aeb053fad0b8040fa1753305def7a4948 Mon Sep 17 00:00:00 2001 From: venkat102 Date: Fri, 13 Sep 2024 18:46:10 +0530 Subject: [PATCH 206/734] fix: cancel cost center allocation and journal entry after test (cherry picked from commit 3c65b98b49d2e7760c4bc01fcc0221f77a529a2b) --- .../test_cost_center_allocation.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/erpnext/accounts/doctype/cost_center_allocation/test_cost_center_allocation.py b/erpnext/accounts/doctype/cost_center_allocation/test_cost_center_allocation.py index c333bbbf130..4abc82d8bec 100644 --- a/erpnext/accounts/doctype/cost_center_allocation/test_cost_center_allocation.py +++ b/erpnext/accounts/doctype/cost_center_allocation/test_cost_center_allocation.py @@ -22,6 +22,7 @@ class TestCostCenterAllocation(unittest.TestCase): cost_centers = [ "Main Cost Center 1", "Main Cost Center 2", + "Main Cost Center 3", "Sub Cost Center 1", "Sub Cost Center 2", "Sub Cost Center 3", @@ -37,7 +38,7 @@ class TestCostCenterAllocation(unittest.TestCase): ) jv = make_journal_entry( - "_Test Cash - _TC", "Sales - _TC", 100, cost_center="Main Cost Center 1 - _TC", submit=True + "Cash - _TC", "Sales - _TC", 100, cost_center="Main Cost Center 1 - _TC", submit=True ) expected_values = [["Sub Cost Center 1 - _TC", 0.0, 60], ["Sub Cost Center 2 - _TC", 0.0, 40]] @@ -121,7 +122,7 @@ class TestCostCenterAllocation(unittest.TestCase): def test_valid_from_based_on_existing_gle(self): # GLE posted against Sub Cost Center 1 on today jv = make_journal_entry( - "_Test Cash - _TC", + "Cash - _TC", "Sales - _TC", 100, cost_center="Main Cost Center 1 - _TC", @@ -143,16 +144,16 @@ class TestCostCenterAllocation(unittest.TestCase): jv.cancel() def test_multiple_cost_center_allocation_on_same_main_cost_center(self): - create_cost_center_allocation( + coa1 = create_cost_center_allocation( "_Test Company", - "Main Cost Center 1 - _TC", + "Main Cost Center 3 - _TC", {"Sub Cost Center 1 - _TC": 30, "Sub Cost Center 2 - _TC": 30, "Sub Cost Center 3 - _TC": 40}, valid_from=add_days(today(), -5), ) - create_cost_center_allocation( + coa2 = create_cost_center_allocation( "_Test Company", - "Main Cost Center 1 - _TC", + "Main Cost Center 3 - _TC", {"Sub Cost Center 1 - _TC": 50, "Sub Cost Center 2 - _TC": 50}, valid_from=add_days(today(), -1), ) @@ -161,7 +162,7 @@ class TestCostCenterAllocation(unittest.TestCase): "Cash - _TC", "Sales - _TC", 100, - cost_center="Main Cost Center 1 - _TC", + cost_center="Main Cost Center 3 - _TC", posting_date=today(), submit=True, ) @@ -185,6 +186,10 @@ class TestCostCenterAllocation(unittest.TestCase): self.assertEqual(gle.debit, 0) self.assertEqual(gle.credit, expected_values[gle.cost_center]) + coa1.cancel() + coa2.cancel() + jv.cancel() + def create_cost_center_allocation( company, From 782c9dda1a7af57d330294d3b5ef51fd3e3904ef Mon Sep 17 00:00:00 2001 From: krishna Date: Mon, 16 Sep 2024 18:44:58 +0530 Subject: [PATCH 207/734] fix: add currency in options for rate field in pricing rule (cherry picked from commit 636c0131fa7f43a2902e18e68663418e2bf625f5) # Conflicts: # erpnext/accounts/doctype/pricing_rule/pricing_rule.json --- erpnext/accounts/doctype/pricing_rule/pricing_rule.json | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.json b/erpnext/accounts/doctype/pricing_rule/pricing_rule.json index 6f191c106c9..ae76a7eebe1 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.json +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.json @@ -419,7 +419,8 @@ "depends_on": "eval:doc.rate_or_discount==\"Rate\"", "fieldname": "rate", "fieldtype": "Currency", - "label": "Rate" + "label": "Rate", + "options": "currency" }, { "default": "0", @@ -647,7 +648,11 @@ "icon": "fa fa-gift", "idx": 1, "links": [], +<<<<<<< HEAD "modified": "2024-05-17 13:16:34.496704", +======= + "modified": "2024-09-16 18:14:51.314765", +>>>>>>> 636c0131fa (fix: add currency in options for rate field in pricing rule) "modified_by": "Administrator", "module": "Accounts", "name": "Pricing Rule", @@ -709,4 +714,4 @@ "sort_order": "DESC", "states": [], "title_field": "title" -} +} \ No newline at end of file From 82982e25c6758d2fdcf22c62bd61dcd065759fb4 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 18 Sep 2024 12:10:42 +0530 Subject: [PATCH 208/734] chore: resolve conflict --- erpnext/accounts/doctype/pricing_rule/pricing_rule.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.json b/erpnext/accounts/doctype/pricing_rule/pricing_rule.json index ae76a7eebe1..ee9dd2be8c3 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.json +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.json @@ -648,11 +648,7 @@ "icon": "fa fa-gift", "idx": 1, "links": [], -<<<<<<< HEAD - "modified": "2024-05-17 13:16:34.496704", -======= "modified": "2024-09-16 18:14:51.314765", ->>>>>>> 636c0131fa (fix: add currency in options for rate field in pricing rule) "modified_by": "Administrator", "module": "Accounts", "name": "Pricing Rule", From 479e8573c2f12b1022a3b6b1e318bcd6ea787d58 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 18 Sep 2024 07:32:30 +0000 Subject: [PATCH 209/734] chore(release): Bumped to Version 15.36.0 # [15.36.0](https://github.com/frappe/erpnext/compare/v15.35.2...v15.36.0) (2024-09-18) ### Bug Fixes * A project without tasks should be able to complete ([dea735d](https://github.com/frappe/erpnext/commit/dea735de4df2ca427fce8803d3d4f8acdc89c789)) * add currency in options for rate field in pricing rule ([782c9dd](https://github.com/frappe/erpnext/commit/782c9dda1a7af57d330294d3b5ef51fd3e3904ef)) * batch based item price not working (backport [#43172](https://github.com/frappe/erpnext/issues/43172)) ([#43206](https://github.com/frappe/erpnext/issues/43206)) ([61a42ea](https://github.com/frappe/erpnext/commit/61a42ea5d71b9f28c73d9954168bc3e9ba37a3cc)) * cancel cost center allocation and journal entry after test ([3d29007](https://github.com/frappe/erpnext/commit/3d29007aeb053fad0b8040fa1753305def7a4948)) * consistent behaviour on refresh ([01f3068](https://github.com/frappe/erpnext/commit/01f30682ee9e15a51161a4f2a91681484e6ac8bf)) * create and link address while creating prospect & customer ([d6a3d0d](https://github.com/frappe/erpnext/commit/d6a3d0d46835559dffb50c502bfec10d15492031)) * create fiscal year without overlapping existing Fiscal Years ([78768f8](https://github.com/frappe/erpnext/commit/78768f883c09ec91de2dfee101923808ccd6fa55)) * currency changing while making PO from Supplier Quotation (backport [#43187](https://github.com/frappe/erpnext/issues/43187)) ([#43205](https://github.com/frappe/erpnext/issues/43205)) ([ef6b172](https://github.com/frappe/erpnext/commit/ef6b172616ee473c1b05324d2e6217510cec8cda)) * delete exchange gain loss journal entry while deleting payment entry ([5789de2](https://github.com/frappe/erpnext/commit/5789de25b94258c8e521885cde47c5706632cc98)) * do not auto apply tds in purchase order ([741c18b](https://github.com/frappe/erpnext/commit/741c18b14468f29586cc9c19c293b6f6518249e1)) * do not check appy_tds in Purchase Order Automatically ([5edebb2](https://github.com/frappe/erpnext/commit/5edebb28a5bcbeacc1084c9da3cb99e156d95b8d)) * do not validate purchase document for composite asset ([c505156](https://github.com/frappe/erpnext/commit/c5051561e4eb14374e85245c1068e807ef8b15a0)) * fetch cost center allocation percentage only from the applicable allocation ([0fe901a](https://github.com/frappe/erpnext/commit/0fe901a137c1b4d3ab5caee046ad4cc8911d7444)) * hide and reset discount control on new POS order ([42494db](https://github.com/frappe/erpnext/commit/42494db3c77db1ec590bf7e73bb89828cd06b029)) * **holiday-list:** use same date format for same holiday error message (backport [#42606](https://github.com/frappe/erpnext/issues/42606)) ([#43222](https://github.com/frappe/erpnext/issues/43222)) ([f101a1c](https://github.com/frappe/erpnext/commit/f101a1ce3b9e2e579ff200c80204a0231916eeec)) * ignore repost logic on Payment Reconciliation ([d91013a](https://github.com/frappe/erpnext/commit/d91013a46707095f62a009a2fbdcf84bd1303c62)) * invalid gp calculation ([291f0a5](https://github.com/frappe/erpnext/commit/291f0a580be176a4c87c03e54372dd7a7cca9cb1)) * item list view in website (backport [#43165](https://github.com/frappe/erpnext/issues/43165)) ([#43207](https://github.com/frappe/erpnext/issues/43207)) ([c1a6c56](https://github.com/frappe/erpnext/commit/c1a6c56217b326f4c948145c507c87bbc987bafc)) * map rows on journal entry by validating account, party, debit and credit value ([86e1818](https://github.com/frappe/erpnext/commit/86e1818420a3152cf47ef65943216c6a9bd0d29e)) * prevent KeyError by checking `report_filter` existence ([984acb6](https://github.com/frappe/erpnext/commit/984acb661d4314960bf2f1eb3542830b182441cc)) * revert 091c5496b20864577d133b0804e957ff8995606f ([2ad6d63](https://github.com/frappe/erpnext/commit/2ad6d637ee9193253fbade6ab35769636906c8d1)) * set party_type null when payment_type is changed to Internal Transfer ([45ff8fa](https://github.com/frappe/erpnext/commit/45ff8fa296b47c809ecb643a4b67fc775d9e9848)) * set tax_withholding_category from Purchase Order while creating pi form po ([7027be8](https://github.com/frappe/erpnext/commit/7027be8fbcf51282ea470299e4edb3d9ce89dd6f)) * tds workflow in purchase order ([11359bd](https://github.com/frappe/erpnext/commit/11359bd2353e7804e226ae000464093d8d5aa71c)) * typo with po_date when creating remarks ([1657a83](https://github.com/frappe/erpnext/commit/1657a83151fb002e0606c36923e722aa638cc6f2)) * updated filtering in depreciation and balances report ([78c6839](https://github.com/frappe/erpnext/commit/78c68397d9a5147970f4b19435c5352ced2a6a44)) * **ux:** set amount based on account currency while adding new row ([f7cedac](https://github.com/frappe/erpnext/commit/f7cedac5260d832aa6b95b9f0341c94d7ccd6203)) * **ux:** set amount on foreign currency when foreign currency account is selected on last row of journal ([d8d4cd2](https://github.com/frappe/erpnext/commit/d8d4cd23a518ec94b30f69dfc67dd042582fcbec)) ### Features * API for crm integration ([f060534](https://github.com/frappe/erpnext/commit/f060534625b6a7dcb7b60fd783f041ba3e980650)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 8d6e2cc0d18..46129bf0b24 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.35.2" +__version__ = "15.36.0" def get_default_company(user=None): From 73d98addbcbdb236cda9da130ff9a59a1f942a37 Mon Sep 17 00:00:00 2001 From: "Nihantra C. Patel" <141945075+Nihantra-Patel@users.noreply.github.com> Date: Wed, 18 Sep 2024 21:36:02 +0530 Subject: [PATCH 210/734] fix: Translation for button SO to PO (cherry picked from commit a5275e9f28bb54c286cad7ae697ab37f9e5e8794) --- erpnext/selling/doctype/sales_order/sales_order.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index cdcd1047bd8..5387286c709 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -1251,7 +1251,7 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex ], }, ], - primary_action_label: "Create Purchase Order", + primary_action_label: __("Create Purchase Order"), primary_action(args) { if (!args) return; From 557ef5d214abee8b7e7a7838a940a40e6f8d1477 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Wed, 18 Sep 2024 21:53:27 +0530 Subject: [PATCH 211/734] fix: create_address is failing (cherry picked from commit acc1d52ac8e51a4fd87cdfcfc7f5c0639157fad9) --- erpnext/crm/frappe_crm_api.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/crm/frappe_crm_api.py b/erpnext/crm/frappe_crm_api.py index 53f58ed57eb..a00f0ba798d 100644 --- a/erpnext/crm/frappe_crm_api.py +++ b/erpnext/crm/frappe_crm_api.py @@ -95,6 +95,8 @@ def create_contacts(contacts, organization=None, link_doctype=None, link_docname def create_address(doctype, docname, address): if not address: return + if isinstance(address, str): + address = json.loads(address) try: _address = frappe.db.exists("Address", address.get("name")) if not _address: @@ -105,6 +107,7 @@ def create_address(doctype, docname, address): "address_line1", "address_line2", "city", + "county", "state", "pincode", "country", From 17ad40269555d6a9c559dc925a414e2b6cee273c Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Wed, 18 Sep 2024 21:53:27 +0530 Subject: [PATCH 212/734] fix: create_address is failing (cherry picked from commit acc1d52ac8e51a4fd87cdfcfc7f5c0639157fad9) --- erpnext/crm/frappe_crm_api.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/crm/frappe_crm_api.py b/erpnext/crm/frappe_crm_api.py index 53f58ed57eb..a00f0ba798d 100644 --- a/erpnext/crm/frappe_crm_api.py +++ b/erpnext/crm/frappe_crm_api.py @@ -95,6 +95,8 @@ def create_contacts(contacts, organization=None, link_doctype=None, link_docname def create_address(doctype, docname, address): if not address: return + if isinstance(address, str): + address = json.loads(address) try: _address = frappe.db.exists("Address", address.get("name")) if not _address: @@ -105,6 +107,7 @@ def create_address(doctype, docname, address): "address_line1", "address_line2", "city", + "county", "state", "pincode", "country", From 34ca0c3bb6a61949c66f4d2aee5c7f06e2766244 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Thu, 19 Sep 2024 01:43:11 +0000 Subject: [PATCH 213/734] chore(release): Bumped to Version 15.36.1 ## [15.36.1](https://github.com/frappe/erpnext/compare/v15.36.0...v15.36.1) (2024-09-19) ### Bug Fixes * create_address is failing ([17ad402](https://github.com/frappe/erpnext/commit/17ad40269555d6a9c559dc925a414e2b6cee273c)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 46129bf0b24..d5db7ca59a6 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.36.0" +__version__ = "15.36.1" def get_default_company(user=None): From 84e26e21abbbe0c8a8ab508d4d408e89e2e79467 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Do=C4=9Fancan?= <37839267+doancan@users.noreply.github.com> Date: Wed, 18 Sep 2024 17:06:07 +0300 Subject: [PATCH 214/734] fix: translate in js (cherry picked from commit 25faec5662b317a19e99ad41b589380067b9fa53) --- erpnext/assets/doctype/asset/asset.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js index 0a21516c177..c6e76abb0b5 100644 --- a/erpnext/assets/doctype/asset/asset.js +++ b/erpnext/assets/doctype/asset/asset.js @@ -213,7 +213,7 @@ frappe.ui.form.on("Asset", {
- Failed to post depreciation entries + ${__("Failed to post depreciation entries")}
`; From f6725e2eed39c326bd031ebc969bd40adf6235e1 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 19 Sep 2024 09:35:54 +0530 Subject: [PATCH 215/734] fix: incorrect stock balance for inventory dimension (backport #43284) (#43290) fix: incorrect stock balance for inventory dimension (#43284) (cherry picked from commit 3e7a7a54bf0cda871e0814deb8776238270769b2) Co-authored-by: rohitwaghchaure --- erpnext/stock/report/stock_balance/stock_balance.js | 6 ++++++ erpnext/stock/report/stock_balance/stock_balance.py | 5 ++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/report/stock_balance/stock_balance.js b/erpnext/stock/report/stock_balance/stock_balance.js index d80261895aa..1d86634fd95 100644 --- a/erpnext/stock/report/stock_balance/stock_balance.js +++ b/erpnext/stock/report/stock_balance/stock_balance.js @@ -107,6 +107,12 @@ frappe.query_reports["Stock Balance"] = { fieldtype: "Check", default: 0, }, + { + fieldname: "show_dimension_wise_stock", + label: __("Show Dimension Wise Stock"), + fieldtype: "Check", + default: 0, + }, ], formatter: function (value, row, column, data, default_formatter) { diff --git a/erpnext/stock/report/stock_balance/stock_balance.py b/erpnext/stock/report/stock_balance/stock_balance.py index 2694ba03c8b..8ea07338cf1 100644 --- a/erpnext/stock/report/stock_balance/stock_balance.py +++ b/erpnext/stock/report/stock_balance/stock_balance.py @@ -252,7 +252,10 @@ class StockBalanceReport: group_by_key = [row.company, row.item_code, row.warehouse] for fieldname in self.inventory_dimensions: - if self.filters.get(fieldname): + if not row.get(fieldname): + continue + + if self.filters.get(fieldname) or self.filters.get("show_dimension_wise_stock"): group_by_key.append(row.get(fieldname)) return tuple(group_by_key) From ef10c4ea4f969a0c6ef12f3733324ae43e689675 Mon Sep 17 00:00:00 2001 From: Nihantra Patel Date: Mon, 24 Jun 2024 15:54:01 +0530 Subject: [PATCH 216/734] fix: creation of contact, customer, opportunity, quotation and prospect from lead (cherry picked from commit 8304d19e8b4b4a16f674627ab64f27aa2b9b4915) # Conflicts: # erpnext/crm/doctype/lead/lead.js --- erpnext/crm/doctype/lead/lead.js | 117 ++++++++++++++++++++++++++++++- 1 file changed, 116 insertions(+), 1 deletion(-) diff --git a/erpnext/crm/doctype/lead/lead.js b/erpnext/crm/doctype/lead/lead.js index 609eab7f9a2..448aa858a4c 100644 --- a/erpnext/crm/doctype/lead/lead.js +++ b/erpnext/crm/doctype/lead/lead.js @@ -28,6 +28,7 @@ erpnext.LeadController = class LeadController extends frappe.ui.form.Controller erpnext.toggle_naming_series(); if (!this.frm.is_new() && doc.__onload && !doc.__onload.is_customer) { +<<<<<<< HEAD this.frm.add_custom_button(__("Customer"), this.make_customer, __("Create")); this.frm.add_custom_button( __("Opportunity"), @@ -37,8 +38,29 @@ erpnext.LeadController = class LeadController extends frappe.ui.form.Controller __("Create") ); this.frm.add_custom_button(__("Quotation"), this.make_quotation, __("Create")); +======= + this.frm.add_custom_button( + __("Customer"), + this.make_customer.bind(this), + __("Create") + ); + this.frm.add_custom_button( + __("Opportunity"), + this.make_opportunity.bind(this), + __("Create") + ); + this.frm.add_custom_button( + __("Quotation"), + this.make_quotation.bind(this), + __("Create") + ); +>>>>>>> 8304d19e8b (fix: creation of contact, customer, opportunity, quotation and prospect from lead) if (!doc.__onload.linked_prospects.length) { - this.frm.add_custom_button(__("Prospect"), this.make_prospect, __("Create")); + this.frm.add_custom_button( + __("Prospect"), + this.make_prospect.bind(this), + __("Create") + ); this.frm.add_custom_button(__("Add to Prospect"), this.add_lead_to_prospect, __("Action")); } } @@ -100,6 +122,99 @@ erpnext.LeadController = class LeadController extends frappe.ui.form.Controller }); } +<<<<<<< HEAD +======= + async make_opportunity() { + const frm = this.frm; + let existing_prospect = ( + await frappe.db.get_value( + "Prospect Lead", + { + lead: frm.doc.name, + }, + "name", + null, + "Prospect" + ) + ).message?.name; + + let fields = []; + if (!existing_prospect) { + fields.push( + { + label: "Create Prospect", + fieldname: "create_prospect", + fieldtype: "Check", + default: 1, + }, + { + label: "Prospect Name", + fieldname: "prospect_name", + fieldtype: "Data", + default: frm.doc.company_name, + reqd: 1, + depends_on: "create_prospect", + } + ); + } + + await frm.reload_doc(); + + let existing_contact = ( + await frappe.db.get_value( + "Contact", + { + first_name: frm.doc.first_name || frm.doc.lead_name, + last_name: frm.doc.last_name, + }, + "name" + ) + ).message?.name; + + if (!existing_contact) { + fields.push({ + label: "Create Contact", + fieldname: "create_contact", + fieldtype: "Check", + default: "1", + }); + } + + if (fields.length) { + const d = new frappe.ui.Dialog({ + title: __("Create Opportunity"), + fields: fields, + primary_action: function(data) { + frappe.call({ + method: "create_prospect_and_contact", + doc: frm.doc, + args: { + data: data, + }, + freeze: true, + callback: function(r) { + if (!r.exc) { + frappe.model.open_mapped_doc({ + method: "erpnext.crm.doctype.lead.lead.make_opportunity", + frm: frm, + }); + } + d.hide(); + }, + }); + }, + primary_action_label: __("Create"), + }); + d.show(); + } else { + frappe.model.open_mapped_doc({ + method: "erpnext.crm.doctype.lead.lead.make_opportunity", + frm: frm, + }); + } + } + +>>>>>>> 8304d19e8b (fix: creation of contact, customer, opportunity, quotation and prospect from lead) make_prospect() { frappe.model.with_doctype("Prospect", function () { let prospect = frappe.model.get_new_doc("Prospect"); From 5a2a404a50ebb40507abbd34dc6a1975db4940ca Mon Sep 17 00:00:00 2001 From: Nihantra Patel Date: Mon, 24 Jun 2024 16:03:39 +0530 Subject: [PATCH 217/734] fix: creation of contact, customer, opportunity, quotation and prospect from lead --prettier (cherry picked from commit 5844897c34df7a727c7b14cdb67a136d33a70a18) # Conflicts: # erpnext/crm/doctype/lead/lead.js --- erpnext/crm/doctype/lead/lead.js | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/erpnext/crm/doctype/lead/lead.js b/erpnext/crm/doctype/lead/lead.js index 448aa858a4c..f27187924af 100644 --- a/erpnext/crm/doctype/lead/lead.js +++ b/erpnext/crm/doctype/lead/lead.js @@ -28,6 +28,7 @@ erpnext.LeadController = class LeadController extends frappe.ui.form.Controller erpnext.toggle_naming_series(); if (!this.frm.is_new() && doc.__onload && !doc.__onload.is_customer) { +<<<<<<< HEAD <<<<<<< HEAD this.frm.add_custom_button(__("Customer"), this.make_customer, __("Create")); this.frm.add_custom_button( @@ -55,12 +56,13 @@ erpnext.LeadController = class LeadController extends frappe.ui.form.Controller __("Create") ); >>>>>>> 8304d19e8b (fix: creation of contact, customer, opportunity, quotation and prospect from lead) +======= + this.frm.add_custom_button(__("Customer"), this.make_customer.bind(this), __("Create")); + this.frm.add_custom_button(__("Opportunity"), this.make_opportunity.bind(this), __("Create")); + this.frm.add_custom_button(__("Quotation"), this.make_quotation.bind(this), __("Create")); +>>>>>>> 5844897c34 (fix: creation of contact, customer, opportunity, quotation and prospect from lead --prettier) if (!doc.__onload.linked_prospects.length) { - this.frm.add_custom_button( - __("Prospect"), - this.make_prospect.bind(this), - __("Create") - ); + this.frm.add_custom_button(__("Prospect"), this.make_prospect.bind(this), __("Create")); this.frm.add_custom_button(__("Add to Prospect"), this.add_lead_to_prospect, __("Action")); } } @@ -184,7 +186,7 @@ erpnext.LeadController = class LeadController extends frappe.ui.form.Controller const d = new frappe.ui.Dialog({ title: __("Create Opportunity"), fields: fields, - primary_action: function(data) { + primary_action: function (data) { frappe.call({ method: "create_prospect_and_contact", doc: frm.doc, @@ -192,7 +194,7 @@ erpnext.LeadController = class LeadController extends frappe.ui.form.Controller data: data, }, freeze: true, - callback: function(r) { + callback: function (r) { if (!r.exc) { frappe.model.open_mapped_doc({ method: "erpnext.crm.doctype.lead.lead.make_opportunity", From 86ae64457474108ba4575a93aad4bc1380b9d1e9 Mon Sep 17 00:00:00 2001 From: HENRY Florian Date: Sat, 21 Sep 2024 03:16:57 +0200 Subject: [PATCH 218/734] refactor: use common functionality to validate account number (#42842) feat: Allow unique Account number by root type (not unique for accros all Accounts) (cherry picked from commit 40d97f4fe9da3b16e8c67d0d06ccdb41bdc63c99) --- erpnext/accounts/doctype/account/account.py | 35 +++++++++++---------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/erpnext/accounts/doctype/account/account.py b/erpnext/accounts/doctype/account/account.py index 1a5ee36b95a..2c876e09725 100644 --- a/erpnext/accounts/doctype/account/account.py +++ b/erpnext/accounts/doctype/account/account.py @@ -103,14 +103,12 @@ class Account(NestedSet): self.name = get_autoname_with_number(self.account_number, self.account_name, self.company) def validate(self): - from erpnext.accounts.utils import validate_field_number - if frappe.local.flags.allow_unverified_charts: return self.validate_parent() self.validate_parent_child_account_type() self.validate_root_details() - validate_field_number("Account", self.name, self.account_number, self.company, "account_number") + self.validate_account_number() self.validate_group_or_ledger() self.set_root_and_report_type() self.validate_mandatory() @@ -311,6 +309,22 @@ class Account(NestedSet): if frappe.db.get_value("GL Entry", {"account": self.name}): frappe.throw(_("Currency can not be changed after making entries using some other currency")) + def validate_account_number(self, account_number=None): + if not account_number: + account_number = self.account_number + + if account_number: + account_with_same_number = frappe.db.get_value( + "Account", + {"account_number": account_number, "company": self.company, "name": ["!=", self.name]}, + ) + if account_with_same_number: + frappe.throw( + _("Account Number {0} already used in account {1}").format( + account_number, account_with_same_number + ) + ) + def create_account_for_child_company(self, parent_acc_name_map, descendants, parent_acc_name): for company in descendants: company_bold = frappe.bold(company) @@ -464,19 +478,6 @@ def get_account_autoname(account_number, account_name, company): return " - ".join(parts) -def validate_account_number(name, account_number, company): - if account_number: - account_with_same_number = frappe.db.get_value( - "Account", {"account_number": account_number, "company": company, "name": ["!=", name]} - ) - if account_with_same_number: - frappe.throw( - _("Account Number {0} already used in account {1}").format( - account_number, account_with_same_number - ) - ) - - @frappe.whitelist() def update_account_number(name, account_name, account_number=None, from_descendant=False): account = frappe.get_cached_doc("Account", name) @@ -517,7 +518,7 @@ def update_account_number(name, account_name, account_number=None, from_descenda frappe.throw(message, title=_("Rename Not Allowed")) - validate_account_number(name, account_number, account.company) + account.validate_account_number(account_number) if account_number: frappe.db.set_value("Account", name, "account_number", account_number.strip()) else: From 7e6d6f08a20a03ff5603d9b991d4ea69960780c5 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sun, 22 Sep 2024 11:18:42 +0530 Subject: [PATCH 219/734] fix: ui clean-up (backport #43305) (#43312) * fix: ui clean-up (#43305) fix: ui cleanup (cherry picked from commit b127a0c8b7fd4e1dede7c23c3f59e0562c4945b2) # Conflicts: # erpnext/manufacturing/doctype/bom_creator/bom_creator.json # erpnext/manufacturing/doctype/plant_floor/plant_floor.json # erpnext/public/js/templates/visual_plant_floor_template.html * chore: fix conflicts * chore: fix conflicts * chore: fix conflicts --------- Co-authored-by: rohitwaghchaure --- .../doctype/bom_creator/bom_creator.json | 3 +- .../doctype/plant_floor/plant_floor.js | 21 ++++++++++ .../doctype/plant_floor/plant_floor.json | 5 ++- .../doctype/work_order/work_order.js | 2 +- .../bom_configurator.bundle.js | 12 +++++- .../visual_plant_floor_template.html | 20 +++++++-- erpnext/public/scss/erpnext.scss | 41 +++++++++++++++++++ 7 files changed, 95 insertions(+), 9 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom_creator/bom_creator.json b/erpnext/manufacturing/doctype/bom_creator/bom_creator.json index 9d2384e31a0..65664be2bea 100644 --- a/erpnext/manufacturing/doctype/bom_creator/bom_creator.json +++ b/erpnext/manufacturing/doctype/bom_creator/bom_creator.json @@ -280,6 +280,7 @@ "read_only": 1 } ], + "hide_toolbar": 1, "icon": "fa fa-sitemap", "is_submittable": 1, "links": [ @@ -288,7 +289,7 @@ "link_fieldname": "bom_creator" } ], - "modified": "2024-04-02 16:30:59.779190", + "modified": "2024-09-21 09:05:52.945112", "modified_by": "Administrator", "module": "Manufacturing", "name": "BOM Creator", diff --git a/erpnext/manufacturing/doctype/plant_floor/plant_floor.js b/erpnext/manufacturing/doctype/plant_floor/plant_floor.js index becdbdb9c95..3d18900b70c 100644 --- a/erpnext/manufacturing/doctype/plant_floor/plant_floor.js +++ b/erpnext/manufacturing/doctype/plant_floor/plant_floor.js @@ -6,6 +6,22 @@ frappe.ui.form.on("Plant Floor", { frm.trigger("setup_queries"); }, + add_workstation(frm) { + frm.add_custom_button(__("Create Workstation"), () => { + var doc = frappe.model.get_new_doc("Workstation"); + doc.plant_floor = frm.doc.name; + doc.status = "Off"; + frappe.ui.form.make_quick_entry( + "Workstation", + () => { + frm.trigger("prepare_workstation_dashboard"); + }, + null, + doc + ); + }).addClass("btn-primary"); + }, + setup_queries(frm) { frm.set_query("warehouse", (doc) => { if (!doc.company) { @@ -24,6 +40,11 @@ frappe.ui.form.on("Plant Floor", { refresh(frm) { frm.trigger("prepare_stock_dashboard"); frm.trigger("prepare_workstation_dashboard"); + + if (!frm.is_new()) { + frm.trigger("add_workstation"); + frm.disable_save(); + } }, prepare_workstation_dashboard(frm) { diff --git a/erpnext/manufacturing/doctype/plant_floor/plant_floor.json b/erpnext/manufacturing/doctype/plant_floor/plant_floor.json index be0052c47bf..c1c167c395b 100644 --- a/erpnext/manufacturing/doctype/plant_floor/plant_floor.json +++ b/erpnext/manufacturing/doctype/plant_floor/plant_floor.json @@ -69,9 +69,10 @@ "options": "Company" } ], + "hide_toolbar": 1, "index_web_pages_for_search": 1, "links": [], - "modified": "2024-01-30 11:59:07.508535", + "modified": "2024-09-19 19:06:36.481625", "modified_by": "Administrator", "module": "Manufacturing", "name": "Plant Floor", @@ -94,4 +95,4 @@ "sort_field": "modified", "sort_order": "DESC", "states": [] -} \ No newline at end of file +} diff --git a/erpnext/manufacturing/doctype/work_order/work_order.js b/erpnext/manufacturing/doctype/work_order/work_order.js index ae888c79714..df72b1e6b51 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.js +++ b/erpnext/manufacturing/doctype/work_order/work_order.js @@ -201,7 +201,7 @@ frappe.ui.form.on("Work Order", { frm.doc.produced_qty > 0 ) { frm.add_custom_button( - __("Disassembly Order"), + __("Disassemble Order"), () => { frm.trigger("make_disassembly_order"); }, diff --git a/erpnext/public/js/bom_configurator/bom_configurator.bundle.js b/erpnext/public/js/bom_configurator/bom_configurator.bundle.js index 5061be9d20a..21aa70fe7ae 100644 --- a/erpnext/public/js/bom_configurator/bom_configurator.bundle.js +++ b/erpnext/public/js/bom_configurator/bom_configurator.bundle.js @@ -25,6 +25,9 @@ class BOMConfigurator { }; frappe.views.trees["BOM Configurator"] = new frappe.views.TreeView(options); + let node = frappe.views.trees["BOM Configurator"].tree.root_node; + frappe.views.trees["BOM Configurator"].tree.show_toolbar(node); + frappe.views.trees["BOM Configurator"].tree.load_children(node, true); this.tree_view = frappe.views.trees["BOM Configurator"]; } @@ -137,7 +140,7 @@ class BOMConfigurator { btnClass: "hidden-xs", }, { - label: __("Expand All"), + label: __("Collapse All"), click: function (node) { let view = frappe.views.trees["BOM Configurator"]; @@ -283,6 +286,13 @@ class BOMConfigurator { fieldtype: "Float", reqd: 1, read_only: read_only, + change() { + this.layout.fields_dict.items.grid.data.forEach((row) => { + row.qty = flt(this.value); + }); + + this.layout.fields_dict.items.grid.refresh(); + }, }, { fieldtype: "Section Break" }, { diff --git a/erpnext/public/js/templates/visual_plant_floor_template.html b/erpnext/public/js/templates/visual_plant_floor_template.html index 2e67085c022..a1639f07370 100644 --- a/erpnext/public/js/templates/visual_plant_floor_template.html +++ b/erpnext/public/js/templates/visual_plant_floor_template.html @@ -1,5 +1,16 @@ {% $.each(workstations, (idx, row) => { %}
+
+ {% if(row.status == "Production") { %} +
+
+
+
+ {% } %} + + {{row.status}} + +
-
-

{{row.status}}

-
{{row.workstation_name}}
+
+ + {{row.workstation_name}} +
-{% }); %} \ No newline at end of file +{% }); %} diff --git a/erpnext/public/scss/erpnext.scss b/erpnext/public/scss/erpnext.scss index 03dd31104e1..29a2696470f 100644 --- a/erpnext/public/scss/erpnext.scss +++ b/erpnext/public/scss/erpnext.scss @@ -507,6 +507,47 @@ body[data-route="pos"] { position: relative; } +.ring-container { + position: relative; +} + +.circle { + width: 9px; + height: 9px; + background-color: #278f5e; + border-radius: 50%; + position: absolute; + left: 9px; + top: 8px; +} + +@keyframes pulsate { + 0% { + -webkit-transform: scale(0.1, 0.1); + opacity: 0; + } + 50% { + opacity: 1; + } + 100% { + -webkit-transform: scale(1.2, 1.2); + opacity: 0; + } +} + +.ringring { + border: 2px solid #62bd19; + -webkit-border-radius: 40px; + height: 15px; + width: 15px; + position: absolute; + left: 6px; + top: 5px; + -webkit-animation: pulsate 3s ease-out; + -webkit-animation-iteration-count: infinite; + opacity: 0; +} + .plant-floor { padding-bottom: 25px; } From 63d4fddb493e4ba1ec1b178464f5fd671da1fd8c Mon Sep 17 00:00:00 2001 From: venkat102 Date: Mon, 23 Sep 2024 12:37:40 +0530 Subject: [PATCH 220/734] fix: make to tax category on tax rule to filter with percent (cherry picked from commit 3aaa13cb2925cadae926ab7241559b80b0a9d0bd) --- erpnext/accounts/doctype/tax_rule/tax_rule.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/tax_rule/tax_rule.py b/erpnext/accounts/doctype/tax_rule/tax_rule.py index ed623c6635e..1c0c0a3c1d6 100644 --- a/erpnext/accounts/doctype/tax_rule/tax_rule.py +++ b/erpnext/accounts/doctype/tax_rule/tax_rule.py @@ -185,7 +185,7 @@ def get_tax_template(posting_date, args): conditions.append("(from_date is null) and (to_date is null)") conditions.append( - "ifnull(tax_category, '') = {}".format(frappe.db.escape(cstr(args.get("tax_category")))) + "ifnull(tax_category, '') = {}".format(frappe.db.escape(cstr(args.get("tax_category")), False)) ) if "tax_category" in args.keys(): del args["tax_category"] From 40459288f6c4bdcb88dc284c6874b1294a6b88a5 Mon Sep 17 00:00:00 2001 From: ljain112 Date: Sat, 21 Sep 2024 15:02:40 +0530 Subject: [PATCH 221/734] fix: handle missing liability account scenario in `set_liability_account` (cherry picked from commit ee7ab4b065a2a76cbfa5e5f78ffd3c0b0275dacf) --- .../doctype/payment_entry/payment_entry.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 13baff9e7a8..625608b5374 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -145,9 +145,21 @@ class PaymentEntry(AccountsController): self.is_opening = "No" return - liability_account = get_party_account( - self.party_type, self.party, self.company, include_advance=True - )[1] + accounts = get_party_account(self.party_type, self.party, self.company, include_advance=True) + + liability_account = accounts[1] if len(accounts) > 1 else None + fieldname = ( + "default_advance_received_account" + if self.party_type == "Customer" + else "default_advance_paid_account" + ) + + if not liability_account: + throw( + _("Please set default {0} in Company {1}").format( + frappe.bold(frappe.get_meta("Company").get_label(fieldname)), frappe.bold(self.company) + ) + ) self.set(self.party_account_field, liability_account) From a7ccc9420ba8fa30b7809b299a91ae3501d0b4fb Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 23 Sep 2024 14:37:12 +0530 Subject: [PATCH 222/734] fix: transaction exchange rate on GL's for Multi currency Journals (cherry picked from commit 8cd9ad53617f6e5bb7a35e8f05fa9fd0cb4d3330) --- erpnext/controllers/accounts_controller.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 92efa5168f3..44a9813c89e 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1036,7 +1036,9 @@ class AccountsController(TransactionBase): gl_dict.update( { "transaction_currency": self.get("currency") or self.company_currency, - "transaction_exchange_rate": self.get("conversion_rate", 1), + "transaction_exchange_rate": item.get("exchange_rate", 1) + if self.doctype == "Journal Entry" and item + else self.get("conversion_rate", 1), "debit_in_transaction_currency": self.get_value_in_transaction_currency( account_currency, gl_dict, "debit" ), From 2d9142832ded129093f2418c7e3f6f7cf887f113 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 23 Sep 2024 17:12:31 +0530 Subject: [PATCH 223/734] test: transaction exchange rate on multi-currency journals (cherry picked from commit c524825d2d7f1a385defb679c2e0df978dade742) --- .../doctype/journal_entry/test_journal_entry.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py index c53faf9ff39..3d4b182d52d 100644 --- a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py @@ -515,6 +515,23 @@ class TestJournalEntry(unittest.TestCase): self.assertEqual(row.debit_in_account_currency, 100) self.assertEqual(row.credit_in_account_currency, 100) + def test_transaction_exchange_rate_on_journals(self): + jv = make_journal_entry("_Test Bank - _TC", "_Test Receivable USD - _TC", 100, save=False) + jv.accounts[0].update({"debit_in_account_currency": 8500, "exchange_rate": 1}) + jv.accounts[1].update({"party_type": "Customer", "party": "_Test Customer USD", "exchange_rate": 85}) + jv.submit() + actual = frappe.db.get_all( + "GL Entry", + filters={"voucher_no": jv.name, "is_cancelled": 0}, + fields=["account", "transaction_exchange_rate"], + order_by="account", + ) + expected = [ + {"account": "_Test Bank - _TC", "transaction_exchange_rate": 1.0}, + {"account": "_Test Receivable USD - _TC", "transaction_exchange_rate": 85.0}, + ] + self.assertEqual(expected, actual) + def make_journal_entry( account1, From f89a3dbb6562bed6fa22088a63cd363f612ffbfd Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sat, 21 Sep 2024 12:27:53 +0530 Subject: [PATCH 224/734] fix: incorrect outstanding on non-pos invoice with write_off_account (cherry picked from commit d5e2906e5974117d91dd265319186a99b2835023) --- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 1214ddee8cd..010d3b8bcd5 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -340,6 +340,7 @@ class SalesInvoice(SellingController): ): validate_loyalty_points(self, self.loyalty_points) + self.allow_write_off_only_on_pos() self.reset_default_field_value("set_warehouse", "items", "warehouse") def validate_accounts(self): @@ -1021,6 +1022,10 @@ class SalesInvoice(SellingController): raise_exception=1, ) + def allow_write_off_only_on_pos(self): + if not self.is_pos and self.write_off_account: + self.write_off_account = None + def validate_write_off_account(self): if flt(self.write_off_amount) and not self.write_off_account: self.write_off_account = frappe.get_cached_value("Company", self.company, "write_off_account") From e5ae828580693d0f4b370868f39d70b649de4a7c Mon Sep 17 00:00:00 2001 From: venkat102 Date: Fri, 20 Sep 2024 11:43:00 +0530 Subject: [PATCH 225/734] fix: show chart tool tip in report currency (cherry picked from commit 827b3f454241a6e2b9e4a0bcc8bb15bfb4e7ef3e) --- erpnext/accounts/report/balance_sheet/balance_sheet.py | 8 ++++++-- erpnext/accounts/report/cash_flow/cash_flow.py | 6 ++++-- .../profit_and_loss_statement.py | 8 +++++--- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/erpnext/accounts/report/balance_sheet/balance_sheet.py b/erpnext/accounts/report/balance_sheet/balance_sheet.py index e89a177a867..274c8a7a371 100644 --- a/erpnext/accounts/report/balance_sheet/balance_sheet.py +++ b/erpnext/accounts/report/balance_sheet/balance_sheet.py @@ -95,7 +95,7 @@ def execute(filters=None): filters.periodicity, period_list, filters.accumulated_values, company=filters.company ) - chart = get_chart_data(filters, columns, asset, liability, equity) + chart = get_chart_data(filters, columns, asset, liability, equity, currency) report_summary, primitive_summary = get_report_summary( period_list, asset, liability, equity, provisional_profit_loss, currency, filters @@ -221,7 +221,7 @@ def get_report_summary( ], (net_asset - net_liability + net_equity) -def get_chart_data(filters, columns, asset, liability, equity): +def get_chart_data(filters, columns, asset, liability, equity, currency): labels = [d.get("label") for d in columns[2:]] asset_data, liability_data, equity_data = [], [], [] @@ -249,4 +249,8 @@ def get_chart_data(filters, columns, asset, liability, equity): else: chart["type"] = "line" + chart["fieldtype"] = "Currency" + chart["options"] = "currency" + chart["currency"] = currency + return chart diff --git a/erpnext/accounts/report/cash_flow/cash_flow.py b/erpnext/accounts/report/cash_flow/cash_flow.py index c034f95ec00..9b441671415 100644 --- a/erpnext/accounts/report/cash_flow/cash_flow.py +++ b/erpnext/accounts/report/cash_flow/cash_flow.py @@ -111,7 +111,7 @@ def execute(filters=None): ) columns = get_columns(filters.periodicity, period_list, filters.accumulated_values, filters.company) - chart = get_chart_data(columns, data) + chart = get_chart_data(columns, data, company_currency) report_summary = get_report_summary(summary_data, company_currency) @@ -252,7 +252,7 @@ def get_report_summary(summary_data, currency): return report_summary -def get_chart_data(columns, data): +def get_chart_data(columns, data, currency): labels = [d.get("label") for d in columns[2:]] datasets = [ { @@ -267,5 +267,7 @@ def get_chart_data(columns, data): chart = {"data": {"labels": labels, "datasets": datasets}, "type": "bar"} chart["fieldtype"] = "Currency" + chart["options"] = "currency" + chart["currency"] = currency return chart diff --git a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py index 58610b22a93..35453f2ec44 100644 --- a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py +++ b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py @@ -59,11 +59,11 @@ def execute(filters=None): columns = get_columns(filters.periodicity, period_list, filters.accumulated_values, filters.company) - chart = get_chart_data(filters, columns, income, expense, net_profit_loss) - currency = filters.presentation_currency or frappe.get_cached_value( "Company", filters.company, "default_currency" ) + chart = get_chart_data(filters, columns, income, expense, net_profit_loss, currency) + report_summary, primitive_summary = get_report_summary( period_list, filters.periodicity, income, expense, net_profit_loss, currency, filters ) @@ -152,7 +152,7 @@ def get_net_profit_loss(income, expense, period_list, company, currency=None, co return net_profit_loss -def get_chart_data(filters, columns, income, expense, net_profit_loss): +def get_chart_data(filters, columns, income, expense, net_profit_loss, currency): labels = [d.get("label") for d in columns[2:]] income_data, expense_data, net_profit = [], [], [] @@ -181,5 +181,7 @@ def get_chart_data(filters, columns, income, expense, net_profit_loss): chart["type"] = "line" chart["fieldtype"] = "Currency" + chart["options"] = "currency" + chart["currency"] = currency return chart From 927f80035d4796bb1f75cede6c7aba16035bb5b7 Mon Sep 17 00:00:00 2001 From: venkat102 Date: Fri, 20 Sep 2024 12:22:03 +0530 Subject: [PATCH 226/734] fix: add currency in financial statement (cherry picked from commit 91a27bda842cc6d86f65daa35bcc9d68921ab805) --- .../consolidated_financial_statement.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py index 2931b728a42..e6aa215924d 100644 --- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py +++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py @@ -115,7 +115,7 @@ def get_balance_sheet_data(fiscal_year, companies, columns, filters): True, ) - chart = get_chart_data(filters, columns, asset, liability, equity) + chart = get_chart_data(filters, columns, asset, liability, equity, company_currency) return data, message, chart, report_summary @@ -173,7 +173,7 @@ def get_profit_loss_data(fiscal_year, companies, columns, filters): if net_profit_loss: data.append(net_profit_loss) - chart = get_pl_chart_data(filters, columns, income, expense, net_profit_loss) + chart = get_pl_chart_data(filters, columns, income, expense, net_profit_loss, company_currency) report_summary, primitive_summary = get_pl_summary( companies, "", income, expense, net_profit_loss, company_currency, filters, True From 05c92cce71152f944086c272199540a2d323703b Mon Sep 17 00:00:00 2001 From: venkat102 Date: Fri, 20 Sep 2024 10:53:45 +0530 Subject: [PATCH 227/734] fix: change dynamic link doctype fieldtype to data (cherry picked from commit 1e46f7344af08a89b8daf82c0dba394290b1bf7b) --- .../general_and_payment_ledger_comparison.py | 6 ++---- 1 file changed, 2 insertions(+), 4 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 89cf7e504f0..9d079eb9ebd 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 @@ -199,8 +199,7 @@ class General_Payment_Ledger_Comparison: dict( label=_("Voucher Type"), fieldname="voucher_type", - fieldtype="Link", - options="DocType", + fieldtype="Data", width="100", ) ) @@ -219,8 +218,7 @@ class General_Payment_Ledger_Comparison: dict( label=_("Party Type"), fieldname="party_type", - fieldtype="Link", - options="DocType", + fieldtype="Data", width="100", ) ) From b99ca7d9e988a09e32a9f85848184c829b805648 Mon Sep 17 00:00:00 2001 From: "Nihantra C. Patel" <141945075+Nihantra-Patel@users.noreply.github.com> Date: Tue, 17 Sep 2024 14:51:26 +0530 Subject: [PATCH 228/734] fix: partial return on POS invoice (cherry picked from commit 76289fa8dcf3ee670d9c6976e4d53e7ce230cb6c) --- erpnext/accounts/doctype/pos_invoice/pos_invoice.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py index d517c9d6da2..ce3da4ac7dd 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py @@ -452,8 +452,8 @@ class POSInvoice(SalesInvoice): if self.is_return: invoice_total = self.rounded_total or self.grand_total total_amount_in_payments = flt(total_amount_in_payments, self.precision("grand_total")) - if total_amount_in_payments and total_amount_in_payments < invoice_total: - frappe.throw(_("Total payments amount can't be greater than {}").format(-invoice_total)) + if total_amount_in_payments and total_amount_in_payments > invoice_total: + frappe.throw(_("Total payments amount can't be greater than {}").format(invoice_total)) def validate_company_with_pos_company(self): if self.company != frappe.db.get_value("POS Profile", self.pos_profile, "company"): From 998fef779bc2aba5ebd7e3b7d98189c1a8f388f3 Mon Sep 17 00:00:00 2001 From: "Nihantra C. Patel" <141945075+Nihantra-Patel@users.noreply.github.com> Date: Tue, 17 Sep 2024 15:35:07 +0530 Subject: [PATCH 229/734] fix: partial return on POS invoice (cherry picked from commit 18bdd06652ff3cc772214fa7c6ed620554eddbb7) --- erpnext/accounts/doctype/pos_invoice/pos_invoice.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py index ce3da4ac7dd..17e777197cb 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py @@ -449,11 +449,11 @@ class POSInvoice(SalesInvoice): if self.is_return and entry.amount > 0: frappe.throw(_("Row #{0} (Payment Table): Amount must be negative").format(entry.idx)) - if self.is_return: + if self.is_return and self.docstatus != 0: invoice_total = self.rounded_total or self.grand_total total_amount_in_payments = flt(total_amount_in_payments, self.precision("grand_total")) - if total_amount_in_payments and total_amount_in_payments > invoice_total: - frappe.throw(_("Total payments amount can't be greater than {}").format(invoice_total)) + if total_amount_in_payments and total_amount_in_payments < invoice_total: + frappe.throw(_("Total payments amount can't be greater than {}").format(-invoice_total)) def validate_company_with_pos_company(self): if self.company != frappe.db.get_value("POS Profile", self.pos_profile, "company"): From e06a01fae5dd5b42f54fcccf9c8c38106087b37e Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Thu, 19 Sep 2024 13:57:55 +0200 Subject: [PATCH 230/734] fix(Payment Reconciliation): German translations (cherry picked from commit 32d4f96e025875492fadfdd2c776f2230d5b93b3) --- erpnext/translations/de.csv | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/erpnext/translations/de.csv b/erpnext/translations/de.csv index b9d790d2428..ed05b8d6695 100644 --- a/erpnext/translations/de.csv +++ b/erpnext/translations/de.csv @@ -4480,10 +4480,23 @@ Payment Reconciliation,Zahlungsabgleich, Receivable / Payable Account,Forderungen-/Verbindlichkeiten-Konto, Bank / Cash Account,Bank / Geldkonto, From Invoice Date,Ab Rechnungsdatum, -To Invoice Date,Um Datum Rechnung, -Minimum Invoice Amount,Mindestabrechnung, +To Invoice Date,Bis Rechnungsdatum, +Invoice Limit,Max. Anzahl Rechnungen, +From Payment Date,Ab Zahlungsdatum, +To Payment Date,Bis Zahlungsdatum, +Payment Limit,Max. Anzahl Zahlungen, +Minimum Invoice Amount,Minimaler Rechnungsbetrag, Maximum Invoice Amount,Maximaler Rechnungsbetrag, -System will fetch all the entries if limit value is zero.,"Das System ruft alle Einträge ab, wenn der Grenzwert Null ist.", +Minimum Payment Amount,Minimaler Zahlungsbetrag, +Maximum Payment Amount,Maximaler Zahlungsbetrag, +Filter on Invoice,Filter auf Rechnungsnr., +Filter on Payment,Filter auf Zahlungsnr., +"If you need to reconcile particular transactions against each other, then please select accordingly. If not, all the transactions will be allocated in FIFO order.","Wenn Sie bestimmte Transaktionen gegeneinander abgleichen müssen, wählen Sie diese bitte entsprechend aus. Andernfalls werden alle Transaktionen in FIFO-Reihenfolge zugewiesen.", +System will fetch all the entries if limit value is zero.,"Das System ruft alle Einträge ab, wenn die max. Anzahl Null ist.", +This filter will be applied to Journal Entry.,Dieser Filter wird auf Buchungssätze angewendet., +Unreconciled Entries,Nicht zugeordnete Buchungen, +Allocated Entries,Zugewiesene Buchungen, +Accounting Dimensions Filter,Filetr nach Buchhaltungsdimensionen, Get Unreconciled Entries,Nicht zugeordnete Buchungen aufrufen, Unreconciled Payment Details,Nicht abgeglichene Zahlungen, Invoice/Journal Entry Details,Einzelheiten zu Rechnungs-/Journalbuchungen, From 085a4c61ac3ebef34b16f7e713d03809594fd455 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Wed, 18 Sep 2024 19:39:51 +0200 Subject: [PATCH 231/734] fix: shipping rule must match the company (cherry picked from commit df8f4086f6df9dcb3eed012c3b9865fef3388584) --- erpnext/public/js/utils/sales_common.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/public/js/utils/sales_common.js b/erpnext/public/js/utils/sales_common.js index 91a85aa02a5..e503fb3339c 100644 --- a/erpnext/public/js/utils/sales_common.js +++ b/erpnext/public/js/utils/sales_common.js @@ -15,10 +15,11 @@ erpnext.sales_common = { onload() { super.onload(); this.setup_queries(); - this.frm.set_query("shipping_rule", function () { + this.frm.set_query("shipping_rule", function (doc) { return { filters: { shipping_rule_type: "Selling", + company: doc.company, }, }; }); From 99e004b61962c4463ba8b18985835295d5711b03 Mon Sep 17 00:00:00 2001 From: ljain112 Date: Tue, 17 Sep 2024 19:10:31 +0530 Subject: [PATCH 232/734] fix: item_query in pos_invoice (cherry picked from commit 7f82a06e657d71c7205189c197a46394e78fd8d0) --- .../doctype/pos_invoice/pos_invoice.js | 13 +++++++++ .../doctype/pos_invoice/pos_invoice.py | 28 +++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.js b/erpnext/accounts/doctype/pos_invoice/pos_invoice.js index a6e8bfa6286..8707ee88860 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.js +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.js @@ -40,6 +40,19 @@ erpnext.selling.POSInvoiceController = class POSInvoiceController extends erpnex }; }); + this.frm.set_query("item_code", "items", function (doc) { + return { + query: "erpnext.accounts.doctype.pos_invoice.pos_invoice.item_query", + filters: { + has_variants: ["=", 0], + is_sales_item: ["=", 1], + disabled: ["=", 0], + is_fixed_asset: ["=", 0], + pos_profile: ["=", doc.pos_profile], + }, + }; + }); + erpnext.accounts.dimensions.setup_dimension_filters(this.frm, this.frm.doctype); } diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py index 17e777197cb..742af7dd0fe 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py @@ -6,6 +6,7 @@ import frappe from frappe import _, bold from frappe.query_builder.functions import IfNull, Sum from frappe.utils import cint, flt, get_link_to_form, getdate, nowdate +from frappe.utils.nestedset import get_descendants_of from erpnext.accounts.doctype.loyalty_program.loyalty_program import validate_loyalty_points from erpnext.accounts.doctype.payment_request.payment_request import make_payment_request @@ -15,6 +16,7 @@ from erpnext.accounts.doctype.sales_invoice.sales_invoice import ( update_multi_mode_option, ) from erpnext.accounts.party import get_due_date, get_party_account +from erpnext.controllers.queries import item_query as _item_query from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos @@ -837,3 +839,29 @@ def add_return_modes(doc, pos_profile): ]: payment_mode = get_mode_of_payment_info(mode_of_payment, doc.company) append_payment(payment_mode[0]) + + +@frappe.whitelist() +@frappe.validate_and_sanitize_search_inputs +def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=False): + if pos_profile := filters.get("pos_profile")[1]: + pos_profile = frappe.get_cached_doc("POS Profile", pos_profile) + if item_groups := get_item_group(pos_profile): + filters["item_group"] = ["in", tuple(item_groups)] + + del filters["pos_profile"] + + else: + filters.pop("pos_profile", None) + + return _item_query(doctype, txt, searchfield, start, page_len, filters, as_dict) + + +def get_item_group(pos_profile): + item_groups = [] + if pos_profile.get("item_groups"): + # Get items based on the item groups defined in the POS profile + for row in pos_profile.get("item_groups"): + item_groups.extend(get_descendants_of("Item Group", row.item_group)) + + return list(set(item_groups)) From 10ecdb99fe565d5f4ebedb034f5cbad1c246a865 Mon Sep 17 00:00:00 2001 From: Kavin <78342682+kavin0411@users.noreply.github.com> Date: Fri, 20 Sep 2024 14:29:10 +0530 Subject: [PATCH 233/734] fix: update clearance date in invoice payment table (cherry picked from commit 487c2a29a6bf265d59dc3331938c48b943adfd65) --- .../doctype/bank_clearance/bank_clearance.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/bank_clearance/bank_clearance.py b/erpnext/accounts/doctype/bank_clearance/bank_clearance.py index 85713c6c9a6..bdc2464da27 100644 --- a/erpnext/accounts/doctype/bank_clearance/bank_clearance.py +++ b/erpnext/accounts/doctype/bank_clearance/bank_clearance.py @@ -108,8 +108,17 @@ class BankClearance(Document): if not d.clearance_date: d.clearance_date = None - payment_entry = frappe.get_doc(d.payment_document, d.payment_entry) - payment_entry.db_set("clearance_date", d.clearance_date) + if d.payment_document == "Sales Invoice": + frappe.db.set_value( + "Sales Invoice Payment", + {"parent": d.payment_entry, "account": self.get("account"), "amount": [">", 0]}, + "clearance_date", + d.clearance_date, + ) + + else: + payment_entry = frappe.get_doc(d.payment_document, d.payment_entry) + payment_entry.db_set("clearance_date", d.clearance_date) clearance_date_updated = True From 9d2cbccff2d82456a86c91f86e78e290621035f6 Mon Sep 17 00:00:00 2001 From: Kavin <78342682+kavin0411@users.noreply.github.com> Date: Fri, 20 Sep 2024 14:33:03 +0530 Subject: [PATCH 234/734] test: add test case for updating clearance date on pos invoice (cherry picked from commit ce8600520f56960dffd50bbf04e471037f7ee13c) --- .../bank_clearance/test_bank_clearance.py | 81 ++++++++++++++++++- 1 file changed, 80 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/bank_clearance/test_bank_clearance.py b/erpnext/accounts/doctype/bank_clearance/test_bank_clearance.py index d785bfbfef2..658a69a4803 100644 --- a/erpnext/accounts/doctype/bank_clearance/test_bank_clearance.py +++ b/erpnext/accounts/doctype/bank_clearance/test_bank_clearance.py @@ -6,16 +6,29 @@ import unittest import frappe from frappe.utils import add_months, getdate +from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry 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.stock.doctype.item.test_item import create_item +from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse from erpnext.tests.utils import if_lending_app_installed, if_lending_app_not_installed class TestBankClearance(unittest.TestCase): @classmethod def setUpClass(cls): + create_warehouse( + warehouse_name="_Test Warehouse", + properties={"parent_warehouse": "All Warehouses - _TC"}, + company="_Test Company", + ) + create_item("_Test Item") + create_cost_center(cost_center_name="_Test Cost Center", company="_Test Company") + clear_payment_entries() clear_loan_transactions() + clear_pos_sales_invoices() make_bank_account() add_transactions() @@ -83,11 +96,41 @@ class TestBankClearance(unittest.TestCase): bank_clearance.get_payment_entries() self.assertEqual(len(bank_clearance.payment_entries), 3) + def test_update_clearance_date_on_si(self): + sales_invoice = make_pos_sales_invoice() + + date = getdate() + bank_clearance = frappe.get_doc("Bank Clearance") + bank_clearance.account = "_Test Bank Clearance - _TC" + bank_clearance.from_date = add_months(date, -1) + bank_clearance.to_date = date + bank_clearance.include_pos_transactions = 1 + bank_clearance.get_payment_entries() + + self.assertNotEqual(len(bank_clearance.payment_entries), 0) + for payment in bank_clearance.payment_entries: + if payment.payment_entry == sales_invoice.name: + payment.clearance_date = date + + bank_clearance.update_clearance_date() + + si_clearance_date = frappe.db.get_value( + "Sales Invoice Payment", + {"parent": sales_invoice.name, "account": bank_clearance.account}, + "clearance_date", + ) + + self.assertEqual(si_clearance_date, date) + def clear_payment_entries(): frappe.db.delete("Payment Entry") +def clear_pos_sales_invoices(): + frappe.db.delete("Sales Invoice", {"is_pos": 1}) + + @if_lending_app_installed def clear_loan_transactions(): for dt in [ @@ -115,9 +158,45 @@ def add_transactions(): def make_payment_entry(): - pi = make_purchase_invoice(supplier="_Test Supplier", qty=1, rate=690) + from erpnext.buying.doctype.supplier.test_supplier import create_supplier + + supplier = create_supplier(supplier_name="_Test Supplier") + pi = make_purchase_invoice( + supplier=supplier, + supplier_warehouse="_Test Warehouse - _TC", + expense_account="Cost of Goods Sold - _TC", + uom="Nos", + qty=1, + rate=690, + ) pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank Clearance - _TC") pe.reference_no = "Conrad Oct 18" pe.reference_date = "2018-10-24" pe.insert() pe.submit() + + +def make_pos_sales_invoice(): + from erpnext.accounts.doctype.opening_invoice_creation_tool.test_opening_invoice_creation_tool import ( + make_customer, + ) + + mode_of_payment = frappe.get_doc({"doctype": "Mode of Payment", "name": "Cash"}) + + if not frappe.db.get_value("Mode of Payment Account", {"company": "_Test Company", "parent": "Cash"}): + mode_of_payment.append( + "accounts", {"company": "_Test Company", "default_account": "_Test Bank Clearance - _TC"} + ) + mode_of_payment.save() + + customer = make_customer(customer="_Test Customer") + + si = create_sales_invoice(customer=customer, item="_Test Item", is_pos=1, qty=1, rate=1000, do_not_save=1) + si.set("payments", []) + si.append( + "payments", {"mode_of_payment": "Cash", "account": "_Test Bank Clearance - _TC", "amount": 1000} + ) + si.insert() + si.submit() + + return si From 8bc76bae9cfdf40e59acaa3e8b3bbe03699125ab Mon Sep 17 00:00:00 2001 From: Kavin <78342682+kavin0411@users.noreply.github.com> Date: Fri, 20 Sep 2024 18:24:00 +0530 Subject: [PATCH 235/734] refactor: update clearance date in payment entry (cherry picked from commit c218f7527f77e24236dee41091e0a47dbb826b6d) --- erpnext/accounts/doctype/bank_clearance/bank_clearance.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/bank_clearance/bank_clearance.py b/erpnext/accounts/doctype/bank_clearance/bank_clearance.py index bdc2464da27..92abb8cea89 100644 --- a/erpnext/accounts/doctype/bank_clearance/bank_clearance.py +++ b/erpnext/accounts/doctype/bank_clearance/bank_clearance.py @@ -117,8 +117,9 @@ class BankClearance(Document): ) else: - payment_entry = frappe.get_doc(d.payment_document, d.payment_entry) - payment_entry.db_set("clearance_date", d.clearance_date) + frappe.db.set_value( + d.payment_document, d.payment_entry, "clearance_date", d.clearance_date + ) clearance_date_updated = True From f36a68b42b70cf8a9905aeb090f8e54d827b9733 Mon Sep 17 00:00:00 2001 From: Smit Vora Date: Wed, 4 Sep 2024 13:13:57 +0530 Subject: [PATCH 236/734] Merge pull request #42925 from Ninad1306/mapping_docs_fix fix: Replace `add_if_empty` with `reset_value` flag --- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 2 +- erpnext/buying/doctype/purchase_order/purchase_order.py | 4 ++-- erpnext/selling/doctype/quotation/quotation.py | 4 ++-- erpnext/selling/doctype/sales_order/sales_order.py | 7 +++++-- erpnext/stock/doctype/delivery_note/delivery_note.py | 2 +- erpnext/stock/doctype/purchase_receipt/purchase_receipt.py | 2 +- 6 files changed, 12 insertions(+), 9 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 010d3b8bcd5..529086228de 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -2123,7 +2123,7 @@ def make_delivery_note(source_name, target_doc=None): "postprocess": update_item, "condition": lambda doc: doc.delivered_by_supplier != 1, }, - "Sales Taxes and Charges": {"doctype": "Sales Taxes and Charges", "add_if_empty": True}, + "Sales Taxes and Charges": {"doctype": "Sales Taxes and Charges", "reset_value": True}, "Sales Team": { "doctype": "Sales Team", "field_map": {"incentives": "incentives"}, diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index 14424dfdf4a..0ae10e4b5cd 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -738,7 +738,7 @@ def make_purchase_receipt(source_name, target_doc=None): "condition": lambda doc: abs(doc.received_qty) < abs(doc.qty) and doc.delivered_by_supplier != 1, }, - "Purchase Taxes and Charges": {"doctype": "Purchase Taxes and Charges", "add_if_empty": True}, + "Purchase Taxes and Charges": {"doctype": "Purchase Taxes and Charges", "reset_value": True}, }, target_doc, set_missing_values, @@ -819,7 +819,7 @@ def get_mapped_purchase_invoice(source_name, target_doc=None, ignore_permissions "postprocess": update_item, "condition": lambda doc: (doc.base_amount == 0 or abs(doc.billed_amt) < abs(doc.amount)), }, - "Purchase Taxes and Charges": {"doctype": "Purchase Taxes and Charges", "add_if_empty": True}, + "Purchase Taxes and Charges": {"doctype": "Purchase Taxes and Charges", "reset_value": True}, } doc = get_mapped_doc( diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index 7ebcb329193..2d7fef2d6e0 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -430,7 +430,7 @@ def _make_sales_order(source_name, target_doc=None, ignore_permissions=False): "postprocess": update_item, "condition": can_map_row, }, - "Sales Taxes and Charges": {"doctype": "Sales Taxes and Charges", "add_if_empty": True}, + "Sales Taxes and Charges": {"doctype": "Sales Taxes and Charges", "reset_value": True}, "Sales Team": {"doctype": "Sales Team", "add_if_empty": True}, "Payment Schedule": {"doctype": "Payment Schedule", "add_if_empty": True}, }, @@ -495,7 +495,7 @@ def _make_sales_invoice(source_name, target_doc=None, ignore_permissions=False): "postprocess": update_item, "condition": lambda row: not row.is_alternative, }, - "Sales Taxes and Charges": {"doctype": "Sales Taxes and Charges", "add_if_empty": True}, + "Sales Taxes and Charges": {"doctype": "Sales Taxes and Charges", "reset_value": True}, "Sales Team": {"doctype": "Sales Team", "add_if_empty": True}, }, target_doc, diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 2f2d840cce8..4804080be38 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -933,7 +933,7 @@ def make_delivery_note(source_name, target_doc=None, kwargs=None): mapper = { "Sales Order": {"doctype": "Delivery Note", "validation": {"docstatus": ["=", 1]}}, - "Sales Taxes and Charges": {"doctype": "Sales Taxes and Charges", "add_if_empty": True}, + "Sales Taxes and Charges": {"doctype": "Sales Taxes and Charges", "reset_value": True}, "Sales Team": {"doctype": "Sales Team", "add_if_empty": True}, } @@ -1125,7 +1125,10 @@ def make_sales_invoice(source_name, target_doc=None, ignore_permissions=False): "condition": lambda doc: doc.qty and (doc.base_amount == 0 or abs(doc.billed_amt) < abs(doc.amount)), }, - "Sales Taxes and Charges": {"doctype": "Sales Taxes and Charges", "add_if_empty": True}, + "Sales Taxes and Charges": { + "doctype": "Sales Taxes and Charges", + "reset_value": True, + }, "Sales Team": {"doctype": "Sales Team", "add_if_empty": True}, }, target_doc, diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index 5d95e7b66d3..76323c0a7e6 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -1030,7 +1030,7 @@ def make_sales_invoice(source_name, target_doc=None, args=None): }, "Sales Taxes and Charges": { "doctype": "Sales Taxes and Charges", - "add_if_empty": True, + "reset_value": True, "ignore": args.get("merge_taxes") if args else 0, }, "Sales Team": { diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 42b70a08222..67ee01b0d8f 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -1214,7 +1214,7 @@ def make_purchase_invoice(source_name, target_doc=None, args=None): }, "Purchase Taxes and Charges": { "doctype": "Purchase Taxes and Charges", - "add_if_empty": True, + "reset_value": True, "ignore": args.get("merge_taxes") if args else 0, }, }, From 4278b081479fec9056f3bc3991977ba427d90da0 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 5 Sep 2024 14:32:41 +0530 Subject: [PATCH 237/734] Merge pull request #43065 from Ninad1306/merge_taxes_fix fix: Reset Value Conditionally Based on Merge Taxes --- erpnext/stock/doctype/delivery_note/delivery_note.py | 2 +- erpnext/stock/doctype/purchase_receipt/purchase_receipt.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index 76323c0a7e6..d203b979c61 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -1030,7 +1030,7 @@ def make_sales_invoice(source_name, target_doc=None, args=None): }, "Sales Taxes and Charges": { "doctype": "Sales Taxes and Charges", - "reset_value": True, + "reset_value": not (args and args.get("merge_taxes")), "ignore": args.get("merge_taxes") if args else 0, }, "Sales Team": { diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 67ee01b0d8f..8b046203eee 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -1214,7 +1214,7 @@ def make_purchase_invoice(source_name, target_doc=None, args=None): }, "Purchase Taxes and Charges": { "doctype": "Purchase Taxes and Charges", - "reset_value": True, + "reset_value": not (args and args.get("merge_taxes")), "ignore": args.get("merge_taxes") if args else 0, }, }, From 0a70b3ffccf9367eb0a650b18e78fbc3cf793f90 Mon Sep 17 00:00:00 2001 From: Ninad1306 Date: Tue, 24 Sep 2024 11:24:31 +0530 Subject: [PATCH 238/734] fix: frappe dependency update --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index aaac05d7ed0..d891b186d89 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,7 +29,7 @@ requires = ["flit_core >=3.4,<4"] build-backend = "flit_core.buildapi" [tool.bench.frappe-dependencies] -frappe = ">=15.10.0,<16.0.0" +frappe = ">=15.40.4,<16.0.0" [tool.ruff] line-length = 110 From ec27077d9cd5f3d5e832e35a32451cee7f5e3e6d Mon Sep 17 00:00:00 2001 From: Vishakh Desai Date: Mon, 16 Sep 2024 15:40:49 +0530 Subject: [PATCH 239/734] fix: set group_by condition if empty and voucher_no is set (cherry picked from commit a6b0cb6cac78a415caebaa9fbfd913f123842f55) --- erpnext/accounts/report/general_ledger/general_ledger.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py index 2564eb0800f..1c1d10c087b 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.py +++ b/erpnext/accounts/report/general_ledger/general_ledger.py @@ -35,6 +35,9 @@ def execute(filters=None): if filters.get("party"): filters.party = frappe.parse_json(filters.get("party")) + if filters.get("voucher_no") and not filters.get("group_by"): + filters.group_by = "Group by Voucher (Consolidated)" + validate_filters(filters, account_details) validate_party(filters) From 176feb20ad62e4a137e5babcd363968af59b160a Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 24 Sep 2024 15:25:42 +0530 Subject: [PATCH 240/734] fix: stock dashboard (backport #43347) (#43349) fix: stock dashboard (#43347) (cherry picked from commit 9e8be8db511cdddc48cfe13218e202f1e4c31d6d) Co-authored-by: rohitwaghchaure --- .../dashboard_chart/oldest_items/oldest_items.json | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/erpnext/stock/dashboard_chart/oldest_items/oldest_items.json b/erpnext/stock/dashboard_chart/oldest_items/oldest_items.json index 9c10a5346be..46ad308f230 100644 --- a/erpnext/stock/dashboard_chart/oldest_items/oldest_items.json +++ b/erpnext/stock/dashboard_chart/oldest_items/oldest_items.json @@ -1,22 +1,23 @@ { "chart_name": "Oldest Items", "chart_type": "Report", - "creation": "2020-07-20 21:01:04.336845", + "creation": "2022-03-30 00:58:02.041721", "custom_options": "{\"colors\": [\"#5e64ff\"]}", "docstatus": 0, "doctype": "Dashboard Chart", - "dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\",\"to_date\":\"frappe.datetime.nowdate()\"}", - "filters_json": "{\"range1\":30,\"range2\":60,\"range3\":90,\"show_warehouse_wise_stock\":0}", + "dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\",\"to_date\":\"frappe.datetime.nowdate()\",\"range1\":\"30\",\"range2\":\"60\",\"range3\":\"90\"}", + "filters_json": "{\"range\":\"30, 60, 90\",\"show_warehouse_wise_stock\":0}", "idx": 0, "is_public": 1, "is_standard": 1, - "modified": "2020-07-29 14:50:26.846482", + "modified": "2024-09-23 22:37:51.392999", "modified_by": "Administrator", "module": "Stock", "name": "Oldest Items", "number_of_groups": 0, - "owner": "Administrator", + "owner": "rohitw1991@gmail.com", "report_name": "Stock Ageing", + "roles": [], "timeseries": 0, "type": "Bar", "use_report_chart": 1, From 979d801de5e2213474690a860be04db28c102d7b Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 24 Sep 2024 12:13:43 +0530 Subject: [PATCH 241/734] fix: AR / AP report to ignore 0.0 outstanding (cherry picked from commit 6e2cf79e2cf047c89367a5b446f338e65a484ba6) --- .../report/accounts_receivable/accounts_receivable.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index 1a7638aaae4..43100c81285 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -310,8 +310,8 @@ class ReceivablePayableReport: must_consider = False if self.filters.get("for_revaluation_journals"): - if (abs(row.outstanding) >= 0.0 / 10**self.currency_precision) or ( - abs(row.outstanding_in_account_currency) >= 0.0 / 10**self.currency_precision + 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: From 0e1884539ebfddd67c10f83f515bae23074fccf6 Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Mon, 23 Sep 2024 23:47:48 +0530 Subject: [PATCH 242/734] fix: added date condition --- .../asset_depreciations_and_balances.py | 53 +++++++++++-------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py index b288e8e5ac7..686aa22d6f6 100644 --- a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py +++ b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py @@ -114,7 +114,11 @@ def get_asset_categories_for_grouped_by_category(filters): end), 0) as cost_of_scrapped_asset from `tabAsset` a where docstatus=1 and company=%(company)s and purchase_date <= %(to_date)s {condition} - and not exists(select name from `tabAsset Capitalization Asset Item` where asset = a.name) + and not exists( + select 1 from `tabAsset Capitalization Asset Item` acai join `tabAsset Capitalization` ac on acai.parent=ac.name + where acai.asset = a.name + and ac.posting_date between %(from_date)s AND %(to_date)s + ) group by a.asset_category """, { @@ -131,53 +135,58 @@ def get_asset_categories_for_grouped_by_category(filters): def get_asset_details_for_grouped_by_category(filters): condition = "" if filters.get("asset"): - condition += " and name = %(asset)s" + condition += " and a.name = %(asset)s" if filters.get("finance_book"): - condition += " and exists (select 1 from `tabAsset Depreciation Schedule` ads where ads.asset = `tabAsset`.name and ads.finance_book = %(finance_book)s)" + condition += " and exists (select 1 from `tabAsset Depreciation Schedule` ads where ads.asset = a.name and ads.finance_book = %(finance_book)s)" # nosemgrep return frappe.db.sql( f""" - SELECT name, - ifnull(sum(case when purchase_date < %(from_date)s then - case when ifnull(disposal_date, 0) = 0 or disposal_date >= %(from_date)s then - gross_purchase_amount + SELECT a.name, + ifnull(sum(case when a.purchase_date < %(from_date)s then + case when ifnull(a.disposal_date, 0) = 0 or a.disposal_date >= %(from_date)s then + a.gross_purchase_amount else 0 end else 0 end), 0) as cost_as_on_from_date, - ifnull(sum(case when purchase_date >= %(from_date)s then - gross_purchase_amount + ifnull(sum(case when a.purchase_date >= %(from_date)s then + a.gross_purchase_amount else 0 end), 0) as cost_of_new_purchase, - ifnull(sum(case when ifnull(disposal_date, 0) != 0 - and disposal_date >= %(from_date)s - and disposal_date <= %(to_date)s then - case when status = "Sold" then - gross_purchase_amount + ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 + and a.disposal_date >= %(from_date)s + and a.disposal_date <= %(to_date)s then + case when a.status = "Sold" then + a.gross_purchase_amount else 0 end else 0 end), 0) as cost_of_sold_asset, - ifnull(sum(case when ifnull(disposal_date, 0) != 0 - and disposal_date >= %(from_date)s - and disposal_date <= %(to_date)s then - case when status = "Scrapped" then - gross_purchase_amount + ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 + and a.disposal_date >= %(from_date)s + and a.disposal_date <= %(to_date)s then + case when a.status = "Scrapped" then + a.gross_purchase_amount else 0 end else 0 end), 0) as cost_of_scrapped_asset - from `tabAsset` - where docstatus=1 and company=%(company)s and purchase_date <= %(to_date)s {condition} - group by name + from `tabAsset` a + where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s {condition} + and not exists( + select 1 from `tabAsset Capitalization Asset Item` acai join `tabAsset Capitalization` ac on acai.parent=ac.name + where acai.asset = a.name + and ac.posting_date between %(from_date)s AND %(to_date)s + ) + group by a.name """, { "to_date": filters.to_date, From 1f423029970cb911c5a97d8b3ce92a02b151ea7e Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Tue, 24 Sep 2024 01:41:54 +0530 Subject: [PATCH 243/734] fix(minor): include condition to check docstatus --- .../asset_depreciations_and_balances.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py index 686aa22d6f6..5e399029e31 100644 --- a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py +++ b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py @@ -68,7 +68,7 @@ def get_group_by_asset_category_data(filters): def get_asset_categories_for_grouped_by_category(filters): condition = "" if filters.get("asset_category"): - condition += " and asset_category = %(asset_category)s" + condition += " and a.asset_category = %(asset_category)s" if filters.get("finance_book"): condition += " and exists (select 1 from `tabAsset Depreciation Schedule` ads where ads.asset = a.name and ads.finance_book = %(finance_book)s)" @@ -113,11 +113,12 @@ def get_asset_categories_for_grouped_by_category(filters): 0 end), 0) as cost_of_scrapped_asset from `tabAsset` a - where docstatus=1 and company=%(company)s and purchase_date <= %(to_date)s {condition} + where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s {condition} and not exists( select 1 from `tabAsset Capitalization Asset Item` acai join `tabAsset Capitalization` ac on acai.parent=ac.name where acai.asset = a.name - and ac.posting_date between %(from_date)s AND %(to_date)s + and ac.posting_date <= %(to_date)s + and ac.docstatus=1 ) group by a.asset_category """, @@ -184,7 +185,8 @@ def get_asset_details_for_grouped_by_category(filters): and not exists( select 1 from `tabAsset Capitalization Asset Item` acai join `tabAsset Capitalization` ac on acai.parent=ac.name where acai.asset = a.name - and ac.posting_date between %(from_date)s AND %(to_date)s + and ac.posting_date <= %(to_date)s + and ac.docstatus=1 ) group by a.name """, From 4e621b09ba48bf9aed11d5a5d2bbe0553eaf3eb6 Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Tue, 24 Sep 2024 17:40:33 +0530 Subject: [PATCH 244/734] style: added comment --- .../asset_depreciations_and_balances.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py index 5e399029e31..64e6a340464 100644 --- a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py +++ b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py @@ -37,7 +37,7 @@ def get_group_by_asset_category_data(filters): - flt(row.cost_of_sold_asset) - flt(row.cost_of_scrapped_asset) ) - + # Update row with corresponding asset data row.update( next( asset From cfea2de131fb5a32d3716d55a46cb1285266da2c Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 24 Sep 2024 16:20:50 +0200 Subject: [PATCH 245/734] fix(Bank Account): dashboard connections (backport #43365) (#43367) Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com> Co-authored-by: rohitwaghchaure fix(Bank Account): dashboard connections (#43365) --- .../doctype/bank_account/bank_account.json | 52 +++++++++++++++++-- .../bank_account/bank_account_dashboard.py | 20 ------- 2 files changed, 49 insertions(+), 23 deletions(-) delete mode 100644 erpnext/accounts/doctype/bank_account/bank_account_dashboard.py diff --git a/erpnext/accounts/doctype/bank_account/bank_account.json b/erpnext/accounts/doctype/bank_account/bank_account.json index 32f1c675d3b..a5a7691eb76 100644 --- a/erpnext/accounts/doctype/bank_account/bank_account.json +++ b/erpnext/accounts/doctype/bank_account/bank_account.json @@ -208,8 +208,54 @@ "label": "Disabled" } ], - "links": [], - "modified": "2023-09-22 21:31:34.763977", + "links": [ + { + "group": "Transactions", + "link_doctype": "Payment Request", + "link_fieldname": "bank_account" + }, + { + "group": "Transactions", + "link_doctype": "Payment Order", + "link_fieldname": "bank_account" + }, + { + "group": "Transactions", + "link_doctype": "Bank Guarantee", + "link_fieldname": "bank_account" + }, + { + "group": "Transactions", + "link_doctype": "Payroll Entry", + "link_fieldname": "bank_account" + }, + { + "group": "Transactions", + "link_doctype": "Bank Transaction", + "link_fieldname": "bank_account" + }, + { + "group": "Accounting", + "link_doctype": "Payment Entry", + "link_fieldname": "bank_account" + }, + { + "group": "Accounting", + "link_doctype": "Journal Entry", + "link_fieldname": "bank_account" + }, + { + "group": "Party", + "link_doctype": "Customer", + "link_fieldname": "default_bank_account" + }, + { + "group": "Party", + "link_doctype": "Supplier", + "link_fieldname": "default_bank_account" + } + ], + "modified": "2024-09-24 06:57:41.292970", "modified_by": "Administrator", "module": "Accounts", "name": "Bank Account", @@ -246,4 +292,4 @@ "sort_order": "DESC", "states": [], "track_changes": 1 -} \ No newline at end of file +} diff --git a/erpnext/accounts/doctype/bank_account/bank_account_dashboard.py b/erpnext/accounts/doctype/bank_account/bank_account_dashboard.py deleted file mode 100644 index 8bf8d8a5cd0..00000000000 --- a/erpnext/accounts/doctype/bank_account/bank_account_dashboard.py +++ /dev/null @@ -1,20 +0,0 @@ -from frappe import _ - - -def get_data(): - return { - "fieldname": "bank_account", - "non_standard_fieldnames": { - "Customer": "default_bank_account", - "Supplier": "default_bank_account", - }, - "transactions": [ - { - "label": _("Payments"), - "items": ["Payment Entry", "Payment Request", "Payment Order", "Payroll Entry"], - }, - {"label": _("Party"), "items": ["Customer", "Supplier"]}, - {"items": ["Bank Guarantee"]}, - {"items": ["Journal Entry"]}, - ], - } From 0722aa5a3f5c318110933fadd38303d690d0db32 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 24 Sep 2024 20:49:42 +0530 Subject: [PATCH 246/734] fix: closing amount reset to expected amount on save (backport #43358) (#43368) fix: closing amount reset to expected amount on save (#43358) (cherry picked from commit 9974b7c4ae53505e85019a29481693e492cc8b17) Co-authored-by: jabir-elat <44110258+jabir-elat@users.noreply.github.com> --- .../accounts/doctype/pos_closing_entry/pos_closing_entry.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js index 68a85f7ee96..c171713dc61 100644 --- a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js +++ b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js @@ -194,7 +194,9 @@ function refresh_payments(d, frm) { } if (payment) { payment.expected_amount += flt(p.amount); - payment.closing_amount = payment.expected_amount; + if (payment.closing_amount === 0) { + payment.closing_amount = payment.expected_amount; + } payment.difference = payment.closing_amount - payment.expected_amount; } else { frm.add_child("payment_reconciliation", { From 40fbb1d6ffaa75a612b64159506ed2409e2afe25 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 25 Sep 2024 00:02:24 +0530 Subject: [PATCH 247/734] fix: not able to cancel Quality Inspection (backport #43374) (#43375) fix: not able to cancel Quality Inspection (#43374) (cherry picked from commit 8c32ebee683bab44c4c94a604b33267ee77ee00c) Co-authored-by: rohitwaghchaure --- erpnext/stock/doctype/quality_inspection/quality_inspection.js | 2 +- erpnext/stock/doctype/quality_inspection/quality_inspection.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/quality_inspection/quality_inspection.js b/erpnext/stock/doctype/quality_inspection/quality_inspection.js index fc487514a2c..99e25bf5eb6 100644 --- a/erpnext/stock/doctype/quality_inspection/quality_inspection.js +++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.js @@ -60,7 +60,7 @@ frappe.ui.form.on("Quality Inspection", { refresh: function (frm) { // Ignore cancellation of reference doctype on cancel all. - frm.ignore_doctypes_on_cancel_all = [frm.doc.reference_type]; + frm.ignore_doctypes_on_cancel_all = [frm.doc.reference_type, "Serial and Batch Bundle"]; }, item_code: function (frm) { diff --git a/erpnext/stock/doctype/quality_inspection/quality_inspection.py b/erpnext/stock/doctype/quality_inspection/quality_inspection.py index 60f048673fb..5dca440adbe 100644 --- a/erpnext/stock/doctype/quality_inspection/quality_inspection.py +++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.py @@ -109,6 +109,8 @@ class QualityInspection(Document): self.update_qc_reference() def on_cancel(self): + self.ignore_linked_doctypes = "Serial and Batch Bundle" + self.update_qc_reference() def on_trash(self): From 47f06dc180ee434ce21eea9587fa43bb20079215 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 25 Sep 2024 10:09:17 +0530 Subject: [PATCH 248/734] fix: Cannot read properties of undefined (reading 'price_list_rate') (backport #43376) (#43377) fix: Cannot read properties of undefined (reading 'price_list_rate') (#43376) (cherry picked from commit a63dca098452e46239878eb90d18a8f4ffeb3d16) Co-authored-by: rohitwaghchaure --- erpnext/public/js/controllers/transaction.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 67c61118952..204c09299ad 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -1102,7 +1102,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe apply_discount_on_item(doc, cdt, cdn, field) { var item = frappe.get_doc(cdt, cdn); - if(!item.price_list_rate) { + if(!item?.price_list_rate) { item[field] = 0.0; } else { this.price_list_rate(doc, cdt, cdn); From 8d188dccd74f2fc442fc386541f4730d14849339 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 25 Sep 2024 04:40:17 +0000 Subject: [PATCH 249/734] chore(release): Bumped to Version 15.36.2 ## [15.36.2](https://github.com/frappe/erpnext/compare/v15.36.1...v15.36.2) (2024-09-25) ### Bug Fixes * add currency in financial statement ([927f800](https://github.com/frappe/erpnext/commit/927f80035d4796bb1f75cede6c7aba16035bb5b7)) * added date condition ([0e18845](https://github.com/frappe/erpnext/commit/0e1884539ebfddd67c10f83f515bae23074fccf6)) * AR / AP report to ignore 0.0 outstanding ([979d801](https://github.com/frappe/erpnext/commit/979d801de5e2213474690a860be04db28c102d7b)) * **Bank Account:** dashboard connections (backport [#43365](https://github.com/frappe/erpnext/issues/43365)) ([#43367](https://github.com/frappe/erpnext/issues/43367)) ([cfea2de](https://github.com/frappe/erpnext/commit/cfea2de131fb5a32d3716d55a46cb1285266da2c)) * change dynamic link doctype fieldtype to data ([05c92cc](https://github.com/frappe/erpnext/commit/05c92cce71152f944086c272199540a2d323703b)) * closing amount reset to expected amount on save (backport [#43358](https://github.com/frappe/erpnext/issues/43358)) ([#43368](https://github.com/frappe/erpnext/issues/43368)) ([0722aa5](https://github.com/frappe/erpnext/commit/0722aa5a3f5c318110933fadd38303d690d0db32)) * create_address is failing ([557ef5d](https://github.com/frappe/erpnext/commit/557ef5d214abee8b7e7a7838a940a40e6f8d1477)) * handle missing liability account scenario in `set_liability_account` ([4045928](https://github.com/frappe/erpnext/commit/40459288f6c4bdcb88dc284c6874b1294a6b88a5)) * incorrect outstanding on non-pos invoice with write_off_account ([f89a3db](https://github.com/frappe/erpnext/commit/f89a3dbb6562bed6fa22088a63cd363f612ffbfd)) * incorrect stock balance for inventory dimension (backport [#43284](https://github.com/frappe/erpnext/issues/43284)) ([#43290](https://github.com/frappe/erpnext/issues/43290)) ([f6725e2](https://github.com/frappe/erpnext/commit/f6725e2eed39c326bd031ebc969bd40adf6235e1)) * item_query in pos_invoice ([99e004b](https://github.com/frappe/erpnext/commit/99e004b61962c4463ba8b18985835295d5711b03)) * make to tax category on tax rule to filter with percent ([63d4fdd](https://github.com/frappe/erpnext/commit/63d4fddb493e4ba1ec1b178464f5fd671da1fd8c)) * **minor:** include condition to check docstatus ([1f42302](https://github.com/frappe/erpnext/commit/1f423029970cb911c5a97d8b3ce92a02b151ea7e)) * not able to cancel Quality Inspection (backport [#43374](https://github.com/frappe/erpnext/issues/43374)) ([#43375](https://github.com/frappe/erpnext/issues/43375)) ([40fbb1d](https://github.com/frappe/erpnext/commit/40fbb1d6ffaa75a612b64159506ed2409e2afe25)) * partial return on POS invoice ([998fef7](https://github.com/frappe/erpnext/commit/998fef779bc2aba5ebd7e3b7d98189c1a8f388f3)) * partial return on POS invoice ([b99ca7d](https://github.com/frappe/erpnext/commit/b99ca7d9e988a09e32a9f85848184c829b805648)) * Payment Ledger Report currency fieldtype fix ([ad2d6a1](https://github.com/frappe/erpnext/commit/ad2d6a16258235a20d041174458e32bb6ebad8f7)) * **Payment Reconciliation:** German translations ([e06a01f](https://github.com/frappe/erpnext/commit/e06a01fae5dd5b42f54fcccf9c8c38106087b37e)) * set group_by condition if empty and voucher_no is set ([ec27077](https://github.com/frappe/erpnext/commit/ec27077d9cd5f3d5e832e35a32451cee7f5e3e6d)) * shipping rule must match the company ([085a4c6](https://github.com/frappe/erpnext/commit/085a4c61ac3ebef34b16f7e713d03809594fd455)) * show chart tool tip in report currency ([e5ae828](https://github.com/frappe/erpnext/commit/e5ae828580693d0f4b370868f39d70b649de4a7c)) * stock dashboard (backport [#43347](https://github.com/frappe/erpnext/issues/43347)) ([#43349](https://github.com/frappe/erpnext/issues/43349)) ([176feb2](https://github.com/frappe/erpnext/commit/176feb20ad62e4a137e5babcd363968af59b160a)) * transaction exchange rate on GL's for Multi currency Journals ([a7ccc94](https://github.com/frappe/erpnext/commit/a7ccc9420ba8fa30b7809b299a91ae3501d0b4fb)) * translate in js ([84e26e2](https://github.com/frappe/erpnext/commit/84e26e21abbbe0c8a8ab508d4d408e89e2e79467)) * Translation for button SO to PO ([73d98ad](https://github.com/frappe/erpnext/commit/73d98addbcbdb236cda9da130ff9a59a1f942a37)) * ui clean-up (backport [#43305](https://github.com/frappe/erpnext/issues/43305)) ([#43312](https://github.com/frappe/erpnext/issues/43312)) ([7e6d6f0](https://github.com/frappe/erpnext/commit/7e6d6f08a20a03ff5603d9b991d4ea69960780c5)) * update clearance date in invoice payment table ([10ecdb9](https://github.com/frappe/erpnext/commit/10ecdb99fe565d5f4ebedb034f5cbad1c246a865)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index d5db7ca59a6..89f9ab1605e 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.36.1" +__version__ = "15.36.2" def get_default_company(user=None): From 74c880c232d6fa1cf4f0d53c5faad16f7e809c16 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 26 Sep 2024 12:06:27 +0530 Subject: [PATCH 250/734] fix: serial and batch no selector (backport #43387) (#43390) fix: serial and batch no selector (#43387) (cherry picked from commit e4e96d2a442b771ff0336be0b52afe359831b919) Co-authored-by: rohitwaghchaure --- erpnext/public/js/controllers/buying.js | 18 +- .../js/utils/serial_no_batch_selector.js | 164 +++++++++++------- .../serial_and_batch_bundle.py | 23 ++- 3 files changed, 132 insertions(+), 73 deletions(-) diff --git a/erpnext/public/js/controllers/buying.js b/erpnext/public/js/controllers/buying.js index b5a8b757706..1d0680de861 100644 --- a/erpnext/public/js/controllers/buying.js +++ b/erpnext/public/js/controllers/buying.js @@ -342,12 +342,15 @@ erpnext.buying = { add_serial_batch_bundle(doc, cdt, cdn) { let item = locals[cdt][cdn]; let me = this; + let fields = ["has_batch_no", "has_serial_no"]; - frappe.db.get_value("Item", item.item_code, ["has_batch_no", "has_serial_no"]) + frappe.db.get_value("Item", item.item_code, fields) .then((r) => { if (r.message && (r.message.has_batch_no || r.message.has_serial_no)) { - item.has_serial_no = r.message.has_serial_no; - item.has_batch_no = r.message.has_batch_no; + fields.forEach((field) => { + item[field] = r.message[field]; + }); + item.type_of_transaction = item.qty > 0 ? "Inward" : "Outward"; item.is_rejected = false; @@ -380,12 +383,15 @@ erpnext.buying = { add_serial_batch_for_rejected_qty(doc, cdt, cdn) { let item = locals[cdt][cdn]; let me = this; + let fields = ["has_batch_no", "has_serial_no"]; - frappe.db.get_value("Item", item.item_code, ["has_batch_no", "has_serial_no"]) + frappe.db.get_value("Item", item.item_code, fields) .then((r) => { if (r.message && (r.message.has_batch_no || r.message.has_serial_no)) { - item.has_serial_no = r.message.has_serial_no; - item.has_batch_no = r.message.has_batch_no; + fields.forEach((field) => { + item[field] = r.message[field]; + }); + item.type_of_transaction = item.rejected_qty > 0 ? "Inward" : "Outward"; item.is_rejected = true; diff --git a/erpnext/public/js/utils/serial_no_batch_selector.js b/erpnext/public/js/utils/serial_no_batch_selector.js index 2ddfc24adb1..46ff6366de8 100644 --- a/erpnext/public/js/utils/serial_no_batch_selector.js +++ b/erpnext/public/js/utils/serial_no_batch_selector.js @@ -16,7 +16,7 @@ erpnext.SerialBatchPackageSelector = class SerialNoBatchBundleUpdate { let label = this.item?.has_serial_no ? __("Serial Nos") : __("Batch Nos"); let primary_label = this.bundle ? __("Update") : __("Add"); - if (this.item?.has_serial_no && this.item?.batch_no) { + if (this.item?.has_serial_no && this.item?.has_batch_no) { label = __("Serial Nos / Batch Nos"); } @@ -24,6 +24,7 @@ erpnext.SerialBatchPackageSelector = class SerialNoBatchBundleUpdate { this.dialog = new frappe.ui.Dialog({ title: this.item?.title || primary_label, + size: "large", fields: this.get_dialog_fields(), primary_action_label: primary_label, primary_action: () => this.update_bundle_entries(), @@ -164,12 +165,14 @@ erpnext.SerialBatchPackageSelector = class SerialNoBatchBundleUpdate { fields.push({ fieldtype: "Section Break", + depends_on: "eval:doc.enter_manually !== 1 || doc.entries?.length > 0", }); fields.push({ fieldname: "entries", fieldtype: "Table", allow_bulk_edit: true, + depends_on: "eval:doc.enter_manually !== 1 || doc.entries?.length > 0", data: [], fields: this.get_dialog_table_fields(), }); @@ -178,6 +181,7 @@ erpnext.SerialBatchPackageSelector = class SerialNoBatchBundleUpdate { } get_attach_field() { + let me = this; let label = this.item?.has_serial_no ? __("Serial Nos") : __("Batch Nos"); let primary_label = this.bundle ? __("Update") : __("Add"); @@ -185,66 +189,41 @@ erpnext.SerialBatchPackageSelector = class SerialNoBatchBundleUpdate { label = __("Serial Nos / Batch Nos"); } - let fields = [ - { - fieldtype: "Section Break", - label: __("{0} {1} via CSV File", [primary_label, label]), - }, - ]; - - if (this.item?.has_serial_no) { - fields = [ - ...fields, - { - fieldtype: "Check", - label: __("Import Using CSV file"), - fieldname: "import_using_csv_file", - default: 0, + let fields = []; + if (this.item.has_serial_no) { + fields.push({ + fieldtype: "Check", + label: __("Enter Manually"), + fieldname: "enter_manually", + default: 1, + depends_on: "eval:doc.import_using_csv_file !== 1", + change() { + if (me.dialog.get_value("enter_manually")) { + me.dialog.set_value("import_using_csv_file", 0); + } }, - { - fieldtype: "Section Break", - label: __("{0} {1} Manually", [primary_label, label]), - depends_on: "eval:doc.import_using_csv_file === 0", - }, - { - fieldtype: "Data", - label: __("Enter Serial No Range"), - fieldname: "serial_no_range", - depends_on: "eval:doc.import_using_csv_file === 0", - description: __('Enter "ABC-001::100" for serial nos "ABC-001" to "ABC-100".'), - onchange: () => { - this.set_serial_nos_from_range(); - }, - }, - { - fieldtype: "Small Text", - label: __("Enter Serial Nos"), - fieldname: "upload_serial_nos", - depends_on: "eval:doc.import_using_csv_file === 0", - description: __("Enter each serial no in a new line"), - }, - { - fieldtype: "Column Break", - depends_on: "eval:doc.import_using_csv_file === 0", - }, - { - fieldtype: "Button", - fieldname: "make_serial_nos", - label: __("Create Serial Nos"), - depends_on: "eval:doc.import_using_csv_file === 0", - click: () => { - this.create_serial_nos(); - }, - }, - { - fieldtype: "Section Break", - depends_on: "eval:doc.import_using_csv_file === 1", - }, - ]; + }); } fields = [ ...fields, + { + fieldtype: "Check", + label: __("Import Using CSV file"), + fieldname: "import_using_csv_file", + depends_on: "eval:doc.enter_manually !== 1", + default: !this.item.has_serial_no ? 1 : 0, + change() { + if (me.dialog.get_value("import_using_csv_file")) { + me.dialog.set_value("enter_manually", 0); + } + }, + }, + { + fieldtype: "Section Break", + depends_on: "eval:doc.import_using_csv_file === 1", + label: __("{0} {1} via CSV File", [primary_label, label]), + }, { fieldtype: "Button", fieldname: "download_csv", @@ -262,9 +241,51 @@ erpnext.SerialBatchPackageSelector = class SerialNoBatchBundleUpdate { }, ]; + if (this.item?.has_serial_no) { + fields = [ + ...fields, + { + fieldtype: "Section Break", + label: __("{0} {1} Manually", [primary_label, label]), + depends_on: "eval:doc.enter_manually === 1", + }, + { + fieldtype: "Data", + label: __("Serial No Range"), + fieldname: "serial_no_range", + depends_on: "eval:doc.enter_manually === 1 && !doc.serial_no_series", + description: __('"SN-01::10" for "SN-01" to "SN-10"'), + onchange: () => { + this.set_serial_nos_from_range(); + }, + }, + ]; + } + + if (this.item?.has_serial_no) { + fields = [ + ...fields, + { + fieldtype: "Column Break", + depends_on: "eval:doc.enter_manually === 1", + }, + { + fieldtype: "Small Text", + label: __("Enter Serial Nos"), + fieldname: "upload_serial_nos", + depends_on: "eval:doc.enter_manually === 1", + description: __("Enter each serial no in a new line"), + }, + ]; + } + return fields; } + set_serial_nos_from_series() {} + + set_batch_nos_from_series() {} + set_serial_nos_from_range() { const serial_no_range = this.dialog.get_value("serial_no_range"); @@ -511,6 +532,8 @@ erpnext.SerialBatchPackageSelector = class SerialNoBatchBundleUpdate { scan_barcode_data() { const { scan_serial_no, scan_batch_no } = this.dialog.get_values(); + this.dialog.set_value("enter_manually", 0); + if (scan_serial_no || scan_batch_no) { frappe.call({ method: "erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle.is_serial_batch_no_exists", @@ -554,14 +577,13 @@ erpnext.SerialBatchPackageSelector = class SerialNoBatchBundleUpdate { serial_no: scan_serial_no, }, callback: (r) => { - if (r.message) { - this.dialog.fields_dict.entries.df.data.push({ - serial_no: scan_serial_no, - batch_no: r.message, - }); + this.dialog.fields_dict.entries.df.data.push({ + serial_no: scan_serial_no, + batch_no: r.message, + }); - this.dialog.fields_dict.scan_serial_no.set_value(""); - } + this.dialog.fields_dict.scan_serial_no.set_value(""); + this.dialog.fields_dict.entries.grid.refresh(); }, }); } @@ -590,6 +612,12 @@ erpnext.SerialBatchPackageSelector = class SerialNoBatchBundleUpdate { update_bundle_entries() { let entries = this.dialog.get_values().entries; let warehouse = this.dialog.get_value("warehouse"); + let upload_serial_nos = this.dialog.get_value("upload_serial_nos"); + + if (!entries?.length && upload_serial_nos) { + this.create_serial_nos(); + return; + } if ((entries && !entries.length) || !entries) { frappe.throw(__("Please add atleast one Serial No / Batch No")); @@ -610,9 +638,13 @@ erpnext.SerialBatchPackageSelector = class SerialNoBatchBundleUpdate { }, }) .then((r) => { - this.callback && this.callback(r.message); - this.frm.save(); - this.dialog.hide(); + frappe.run_serially([ + () => { + this.callback && this.callback(r.message); + }, + () => this.frm.save(), + () => this.dialog.hide(), + ]); }); } diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py index 956eb08d9ff..a1944732fb6 100644 --- a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py +++ b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py @@ -1329,6 +1329,15 @@ def create_serial_batch_no_ledgers( } ) + batch_no = None + + if ( + not entries[0].get("batch_no") + and entries[0].get("serial_no") + and frappe.get_cached_value("Item", child_row.item_code, "has_batch_no") + ): + batch_no = get_batch(child_row.item_code) + for row in entries: row = frappe._dict(row) doc.append( @@ -1336,7 +1345,7 @@ def create_serial_batch_no_ledgers( { "qty": (flt(row.qty) or 1.0) * (1 if type_of_transaction == "Inward" else -1), "warehouse": warehouse, - "batch_no": row.batch_no, + "batch_no": row.batch_no or batch_no, "serial_no": row.serial_no, }, ) @@ -1351,6 +1360,18 @@ def create_serial_batch_no_ledgers( return doc +def get_batch(item_code): + from erpnext.stock.doctype.batch.batch import make_batch + + return make_batch( + frappe._dict( + { + "item": item_code, + } + ) + ) + + def get_type_of_transaction(parent_doc, child_row): type_of_transaction = child_row.get("type_of_transaction") if parent_doc.get("doctype") == "Stock Entry": From ea69ba7cd8967af626d8efee1c4575d980533923 Mon Sep 17 00:00:00 2001 From: Abdeali Chharchhodawala <99460106+Abdeali099@users.noreply.github.com> Date: Sat, 21 Sep 2024 07:04:46 +0530 Subject: [PATCH 251/734] fix: multiple issues in Payment Request (#42427) * fix: multiple issues in Payment Request * chore: minor changes * fix: remove bug * fix: replace `round` with `flt` * fix: update `set_advance_payment_status()` logic * fix: removed bug of `set_advance_payment_status` * fix: changes as per review * refactor: replace sql query of `matched_payment_requests` to query builder * fix: replace `locals` with `get_doc` in set_query * fix: changes during review * fix: minor review changes * fix: remove unnecessary code for setting payment entry received amount * fix: logic for ser payment_request if PE made from transaction * fix: Use rounded total to make Payment Request from `Sales Invoice` or `Purchase Invoice` * refactor: enhance logic of `set_open_payment_requests_to_references` * fix: added one optional arg `created_from_payment_request` * fix: handle multiple allocation of PR at PE's reference * fix: logic for PR if outstanding docs fetch * fix: formatted Link field for `Payment Request` for PE's references * fix: replace `get_all()` with `get_list()` for getting Payment Request for Link field * fix: replace `get_all()` with `get_list()` for getting Payment Request for Link field * chore: format `payment_entry.js` file * style: Show preview popup of `Payment Request` * fix: remove minor bug * fix: add virtual field for Payment Term and Request `outstanding_amount` in PE's reference * fix: get outstanding amount in PE's reference on realtime * fix: move allocation of allocated_amount to server side (no change) * fix: some minor changes to allocation * fix: Split `Payment Request` if PE is created from PR and there are `Payment Terms` * fix: minor logic changes * fix: Allocation of allocated_amount if `paid_amount` is changes * fix: improve logic of allocation * fix: set matched payment request if unset * fix: minor changes * fix: Allocate single Payment Request if PE created from PR * fix: improve code logic * fix: Removed duplication code * fix: proper message title * refactor: Rename method of Allocation Amount to References * refactor: Changing `grand_total` description based on `party_type` * refactor: update Payment Request * fix: Remove virtual property of payment_term_oustanding from references * fix: fetch party account currency for creating payment request * fix: use transaction currency as base in payment request * fix: party amount for creating payment entry * fix: allow for proportional amount paid by bank * fix: Changed field order in Payment Request * fix: Minor refactor in Payment Entry Reference table data * test: Added test cases for allow Payment at `Partially Paid` status for PR * test: Update partial paid status test case * test: Update test case for same currency PR * refactor: Wider the `msgprint` dialog for after save PE * test: Update PR test cases * chore: Remove dirty lines * test: Checking `Advance Payment Status` * fix: formatting update * fix: Use `flt` where doing subtraction * test: PR test case with Payment Term for same currency * fix: remove redundant `flt` * test: Add test cases for PR --------- Co-authored-by: Sagar Vora --- .../doctype/payment_entry/payment_entry.js | 206 +++--- .../doctype/payment_entry/payment_entry.py | 599 +++++++++++++++++- .../payment_entry_reference.json | 28 +- .../payment_entry_reference.py | 12 +- .../payment_request/payment_request.js | 4 +- .../payment_request/payment_request.json | 50 +- .../payment_request/payment_request.py | 359 ++++++++--- .../payment_request/test_payment_request.py | 255 ++++++++ erpnext/controllers/accounts_controller.py | 33 +- 9 files changed, 1317 insertions(+), 229 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index f46c782112c..7ababfec81a 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -174,6 +174,17 @@ frappe.ui.form.on("Payment Entry", { }; }); + frm.set_query("payment_request", "references", function (doc, cdt, cdn) { + const row = frappe.get_doc(cdt, cdn); + return { + query: "erpnext.accounts.doctype.payment_request.payment_request.get_open_payment_requests_query", + filters: { + reference_doctype: row.reference_doctype, + reference_name: row.reference_name, + }, + }; + }); + frm.set_query("sales_taxes_and_charges_template", function () { return { filters: { @@ -191,7 +202,15 @@ frappe.ui.form.on("Payment Entry", { }, }; }); + + frm.add_fetch( + "payment_request", + "outstanding_amount", + "payment_request_outstanding", + "Payment Entry Reference" + ); }, + refresh: function (frm) { erpnext.hide_company(frm); frm.events.hide_unhide_fields(frm); @@ -216,6 +235,7 @@ frappe.ui.form.on("Payment Entry", { ); } erpnext.accounts.unreconcile_payment.add_unreconcile_btn(frm); + frappe.flags.allocate_payment_amount = true; }, validate_company: (frm) => { @@ -797,7 +817,7 @@ frappe.ui.form.on("Payment Entry", { ); if (frm.doc.payment_type == "Pay") - frm.events.allocate_party_amount_against_ref_docs(frm, frm.doc.received_amount, 1); + frm.events.allocate_party_amount_against_ref_docs(frm, frm.doc.received_amount, true); else frm.events.set_unallocated_amount(frm); frm.set_paid_amount_based_on_received_amount = false; @@ -818,7 +838,7 @@ frappe.ui.form.on("Payment Entry", { } if (frm.doc.payment_type == "Receive") - frm.events.allocate_party_amount_against_ref_docs(frm, frm.doc.paid_amount, 1); + frm.events.allocate_party_amount_against_ref_docs(frm, frm.doc.paid_amount, true); else frm.events.set_unallocated_amount(frm); }, @@ -989,6 +1009,7 @@ frappe.ui.form.on("Payment Entry", { c.outstanding_amount = d.outstanding_amount; c.bill_no = d.bill_no; c.payment_term = d.payment_term; + c.payment_term_outstanding = d.payment_term_outstanding; c.allocated_amount = d.allocated_amount; c.account = d.account; @@ -1038,7 +1059,8 @@ frappe.ui.form.on("Payment Entry", { frm.events.allocate_party_amount_against_ref_docs( frm, - frm.doc.payment_type == "Receive" ? frm.doc.paid_amount : frm.doc.received_amount + frm.doc.payment_type == "Receive" ? frm.doc.paid_amount : frm.doc.received_amount, + false ); }, }); @@ -1052,93 +1074,13 @@ frappe.ui.form.on("Payment Entry", { return ["Sales Invoice", "Purchase Invoice"]; }, - allocate_party_amount_against_ref_docs: function (frm, paid_amount, paid_amount_change) { - var total_positive_outstanding_including_order = 0; - var total_negative_outstanding = 0; - var total_deductions = frappe.utils.sum( - $.map(frm.doc.deductions || [], function (d) { - return flt(d.amount); - }) - ); - - paid_amount -= total_deductions; - - $.each(frm.doc.references || [], function (i, row) { - if (flt(row.outstanding_amount) > 0) - total_positive_outstanding_including_order += flt(row.outstanding_amount); - else total_negative_outstanding += Math.abs(flt(row.outstanding_amount)); + allocate_party_amount_against_ref_docs: async function (frm, paid_amount, paid_amount_change) { + await frm.call("allocate_amount_to_references", { + paid_amount: paid_amount, + paid_amount_change: paid_amount_change, + allocate_payment_amount: frappe.flags.allocate_payment_amount ?? false, }); - var allocated_negative_outstanding = 0; - if ( - (frm.doc.payment_type == "Receive" && frm.doc.party_type == "Customer") || - (frm.doc.payment_type == "Pay" && frm.doc.party_type == "Supplier") || - (frm.doc.payment_type == "Pay" && frm.doc.party_type == "Employee") - ) { - if (total_positive_outstanding_including_order > paid_amount) { - var remaining_outstanding = total_positive_outstanding_including_order - paid_amount; - allocated_negative_outstanding = - total_negative_outstanding < remaining_outstanding - ? total_negative_outstanding - : remaining_outstanding; - } - - var allocated_positive_outstanding = paid_amount + allocated_negative_outstanding; - } else if (["Customer", "Supplier"].includes(frm.doc.party_type)) { - total_negative_outstanding = flt(total_negative_outstanding, precision("outstanding_amount")); - if (paid_amount > total_negative_outstanding) { - if (total_negative_outstanding == 0) { - frappe.msgprint( - __("Cannot {0} {1} {2} without any negative outstanding invoice", [ - frm.doc.payment_type, - frm.doc.party_type == "Customer" ? "to" : "from", - frm.doc.party_type, - ]) - ); - return false; - } else { - frappe.msgprint( - __("Paid Amount cannot be greater than total negative outstanding amount {0}", [ - total_negative_outstanding, - ]) - ); - return false; - } - } else { - allocated_positive_outstanding = total_negative_outstanding - paid_amount; - allocated_negative_outstanding = - paid_amount + - (total_positive_outstanding_including_order < allocated_positive_outstanding - ? total_positive_outstanding_including_order - : allocated_positive_outstanding); - } - } - - $.each(frm.doc.references || [], function (i, row) { - if (frappe.flags.allocate_payment_amount == 0) { - //If allocate payment amount checkbox is unchecked, set zero to allocate amount - row.allocated_amount = 0; - } else if ( - frappe.flags.allocate_payment_amount != 0 && - (!row.allocated_amount || paid_amount_change) - ) { - if (row.outstanding_amount > 0 && allocated_positive_outstanding >= 0) { - row.allocated_amount = - row.outstanding_amount >= allocated_positive_outstanding - ? allocated_positive_outstanding - : row.outstanding_amount; - allocated_positive_outstanding -= flt(row.allocated_amount); - } else if (row.outstanding_amount < 0 && allocated_negative_outstanding) { - row.allocated_amount = - Math.abs(row.outstanding_amount) >= allocated_negative_outstanding - ? -1 * allocated_negative_outstanding - : row.outstanding_amount; - allocated_negative_outstanding -= Math.abs(flt(row.allocated_amount)); - } - } - }); - - frm.refresh_fields(); frm.events.set_total_allocated_amount(frm); }, @@ -1686,6 +1628,62 @@ frappe.ui.form.on("Payment Entry", { return current_tax_amount; }, + + cost_center: function (frm) { + if (frm.doc.posting_date && (frm.doc.paid_from || frm.doc.paid_to)) { + return frappe.call({ + method: "erpnext.accounts.doctype.payment_entry.payment_entry.get_party_and_account_balance", + args: { + company: frm.doc.company, + date: frm.doc.posting_date, + paid_from: frm.doc.paid_from, + paid_to: frm.doc.paid_to, + ptype: frm.doc.party_type, + pty: frm.doc.party, + cost_center: frm.doc.cost_center, + }, + callback: function (r, rt) { + if (r.message) { + frappe.run_serially([ + () => { + frm.set_value( + "paid_from_account_balance", + r.message.paid_from_account_balance + ); + frm.set_value("paid_to_account_balance", r.message.paid_to_account_balance); + frm.set_value("party_balance", r.message.party_balance); + }, + ]); + } + }, + }); + } + }, + + after_save: function (frm) { + const { matched_payment_requests } = frappe.last_response; + if (!matched_payment_requests) return; + + const COLUMN_LABEL = [ + [__("Reference DocType"), __("Reference Name"), __("Allocated Amount"), __("Payment Request")], + ]; + + frappe.msgprint({ + title: __("Unset Matched Payment Request"), + message: COLUMN_LABEL.concat(matched_payment_requests), + as_table: true, + wide: true, + primary_action: { + label: __("Allocate Payment Request"), + action() { + frappe.hide_msgprint(); + frm.call("set_matched_payment_requests", { matched_payment_requests }, () => { + frm.dirty(); + }); + }, + }, + }); + }, }); frappe.ui.form.on("Payment Entry Reference", { @@ -1778,35 +1776,3 @@ frappe.ui.form.on("Payment Entry Deduction", { frm.events.set_unallocated_amount(frm); }, }); -frappe.ui.form.on("Payment Entry", { - cost_center: function (frm) { - if (frm.doc.posting_date && (frm.doc.paid_from || frm.doc.paid_to)) { - return frappe.call({ - method: "erpnext.accounts.doctype.payment_entry.payment_entry.get_party_and_account_balance", - args: { - company: frm.doc.company, - date: frm.doc.posting_date, - paid_from: frm.doc.paid_from, - paid_to: frm.doc.paid_to, - ptype: frm.doc.party_type, - pty: frm.doc.party, - cost_center: frm.doc.cost_center, - }, - callback: function (r, rt) { - if (r.message) { - frappe.run_serially([ - () => { - frm.set_value( - "paid_from_account_balance", - r.message.paid_from_account_balance - ); - frm.set_value("paid_to_account_balance", r.message.paid_to_account_balance); - frm.set_value("party_balance", r.message.party_balance); - }, - ]); - } - }, - }); - } - }, -}); diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 625608b5374..9424d722cf5 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -7,8 +7,10 @@ from functools import reduce import frappe from frappe import ValidationError, _, qb, scrub, throw +from frappe.query_builder import Tuple +from frappe.query_builder.functions import Count from frappe.utils import cint, comma_or, flt, getdate, nowdate -from frappe.utils.data import comma_and, fmt_money +from frappe.utils.data import comma_and, fmt_money, get_link_to_form from pypika import Case from pypika.functions import Coalesce, Sum @@ -98,13 +100,17 @@ class PaymentEntry(AccountsController): self.set_status() self.set_total_in_words() + def before_save(self): + self.set_matched_unset_payment_requests_to_response() + def on_submit(self): if self.difference_amount: frappe.throw(_("Difference Amount must be zero")) self.make_gl_entries() self.update_outstanding_amounts() - self.update_advance_paid() self.update_payment_schedule() + self.update_payment_requests() + self.update_advance_paid() # advance_paid_status depends on the payment request amount self.set_status() def set_liability_account(self): @@ -188,30 +194,34 @@ class PaymentEntry(AccountsController): super().on_cancel() self.make_gl_entries(cancel=1) self.update_outstanding_amounts() - self.update_advance_paid() self.delink_advance_entry_references() self.update_payment_schedule(cancel=1) - self.set_payment_req_status() + self.update_payment_requests(cancel=True) + self.update_advance_paid() # advance_paid_status depends on the payment request amount self.set_status() - def set_payment_req_status(self): - from erpnext.accounts.doctype.payment_request.payment_request import update_payment_req_status + def update_payment_requests(self, cancel=False): + from erpnext.accounts.doctype.payment_request.payment_request import ( + update_payment_requests_as_per_pe_references, + ) - update_payment_req_status(self, None) + update_payment_requests_as_per_pe_references(self.references, cancel=cancel) def update_outstanding_amounts(self): self.set_missing_ref_details(force=True) def validate_duplicate_entry(self): - reference_names = [] + reference_names = set() for d in self.get("references"): - if (d.reference_doctype, d.reference_name, d.payment_term) in reference_names: + key = (d.reference_doctype, d.reference_name, d.payment_term, d.payment_request) + if key in reference_names: frappe.throw( _("Row #{0}: Duplicate entry in References {1} {2}").format( d.idx, d.reference_doctype, d.reference_name ) ) - reference_names.append((d.reference_doctype, d.reference_name, d.payment_term)) + + reference_names.add(key) def set_bank_account_data(self): if self.bank_account: @@ -237,6 +247,8 @@ class PaymentEntry(AccountsController): if self.payment_type == "Internal Transfer": return + self.validate_allocated_amount_as_per_payment_request() + if self.party_type in ("Customer", "Supplier"): self.validate_allocated_amount_with_latest_data() else: @@ -249,6 +261,27 @@ class PaymentEntry(AccountsController): if flt(d.allocated_amount) < 0 and flt(d.allocated_amount) < flt(d.outstanding_amount): frappe.throw(fail_message.format(d.idx)) + def validate_allocated_amount_as_per_payment_request(self): + """ + Allocated amount should not be greater than the outstanding amount of the Payment Request. + """ + if not self.references: + return + + pr_outstanding_amounts = get_payment_request_outstanding_set_in_references(self.references) + + if not pr_outstanding_amounts: + return + + for ref in self.references: + if ref.payment_request and ref.allocated_amount > pr_outstanding_amounts[ref.payment_request]: + frappe.throw( + msg=_( + "Row #{0}: Allocated Amount cannot be greater than Outstanding Amount of Payment Request {1}" + ).format(ref.idx, get_link_to_form("Payment Request", ref.payment_request)), + title=_("Invalid Allocated Amount"), + ) + def term_based_allocation_enabled_for_reference( self, reference_doctype: str, reference_name: str ) -> bool: @@ -1606,6 +1639,380 @@ class PaymentEntry(AccountsController): return current_tax_fraction + def set_matched_unset_payment_requests_to_response(self): + """ + Find matched Payment Requests for those references which have no Payment Request set.\n + And set to `frappe.response` to show in the frontend for allocation. + """ + if not self.references: + return + + matched_payment_requests = get_matched_payment_request_of_references( + [row for row in self.references if not row.payment_request] + ) + + if not matched_payment_requests: + return + + frappe.response["matched_payment_requests"] = matched_payment_requests + + @frappe.whitelist() + def allocate_amount_to_references(self, paid_amount, paid_amount_change, allocate_payment_amount): + """ + Allocate `Allocated Amount` and `Payment Request` against `Reference` based on `Paid Amount` and `Outstanding Amount`.\n + :param paid_amount: Paid Amount / Received Amount. + :param paid_amount_change: Flag to check if `Paid Amount` is changed or not. + :param allocate_payment_amount: Flag to allocate amount or not. (Payment Request is also dependent on this flag) + """ + if not self.references: + return + + if not allocate_payment_amount: + for ref in self.references: + ref.allocated_amount = 0 + return + + # calculating outstanding amounts + precision = self.precision("paid_amount") + total_positive_outstanding_including_order = 0 + total_negative_outstanding = 0 + paid_amount -= sum(flt(d.amount, precision) for d in self.deductions) + + for ref in self.references: + reference_outstanding_amount = ref.outstanding_amount + abs_outstanding_amount = abs(reference_outstanding_amount) + + if reference_outstanding_amount > 0: + total_positive_outstanding_including_order += abs_outstanding_amount + else: + total_negative_outstanding += abs_outstanding_amount + + # calculating allocated outstanding amounts + allocated_negative_outstanding = 0 + allocated_positive_outstanding = 0 + + # checking party type and payment type + if (self.payment_type == "Receive" and self.party_type == "Customer") or ( + self.payment_type == "Pay" and self.party_type in ("Supplier", "Employee") + ): + if total_positive_outstanding_including_order > paid_amount: + remaining_outstanding = flt( + total_positive_outstanding_including_order - paid_amount, precision + ) + allocated_negative_outstanding = min(remaining_outstanding, total_negative_outstanding) + + allocated_positive_outstanding = paid_amount + allocated_negative_outstanding + + elif self.party_type in ("Supplier", "Employee"): + if paid_amount > total_negative_outstanding: + if total_negative_outstanding == 0: + frappe.msgprint( + _("Cannot {0} from {2} without any negative outstanding invoice").format( + self.payment_type, + self.party_type, + ) + ) + else: + frappe.msgprint( + _("Paid Amount cannot be greater than total negative outstanding amount {0}").format( + total_negative_outstanding + ) + ) + + return + + else: + allocated_positive_outstanding = flt(total_negative_outstanding - paid_amount, precision) + allocated_negative_outstanding = paid_amount + min( + total_positive_outstanding_including_order, allocated_positive_outstanding + ) + + # inner function to set `allocated_amount` to those row which have no PR + def _allocation_to_unset_pr_row( + row, outstanding_amount, allocated_positive_outstanding, allocated_negative_outstanding + ): + if outstanding_amount > 0 and allocated_positive_outstanding >= 0: + row.allocated_amount = min(allocated_positive_outstanding, outstanding_amount) + allocated_positive_outstanding = flt( + allocated_positive_outstanding - row.allocated_amount, precision + ) + elif outstanding_amount < 0 and allocated_negative_outstanding: + row.allocated_amount = min(allocated_negative_outstanding, abs(outstanding_amount)) * -1 + allocated_negative_outstanding = flt( + allocated_negative_outstanding - abs(row.allocated_amount), precision + ) + return allocated_positive_outstanding, allocated_negative_outstanding + + # allocate amount based on `paid_amount` is changed or not + if not paid_amount_change: + for ref in self.references: + allocated_positive_outstanding, allocated_negative_outstanding = _allocation_to_unset_pr_row( + ref, + ref.outstanding_amount, + allocated_positive_outstanding, + allocated_negative_outstanding, + ) + + allocate_open_payment_requests_to_references(self.references, self.precision("paid_amount")) + + else: + payment_request_outstanding_amounts = ( + get_payment_request_outstanding_set_in_references(self.references) or {} + ) + references_outstanding_amounts = get_references_outstanding_amount(self.references) or {} + remaining_references_allocated_amounts = references_outstanding_amounts.copy() + + # Re allocate amount to those references which have PR set (Higher priority) + for ref in self.references: + if not ref.payment_request: + continue + + # fetch outstanding_amount of `Reference` (Payment Term) and `Payment Request` to allocate new amount + key = (ref.reference_doctype, ref.reference_name, ref.get("payment_term")) + reference_outstanding_amount = references_outstanding_amounts[key] + pr_outstanding_amount = payment_request_outstanding_amounts[ref.payment_request] + + if reference_outstanding_amount > 0 and allocated_positive_outstanding >= 0: + # allocate amount according to outstanding amounts + outstanding_amounts = ( + allocated_positive_outstanding, + reference_outstanding_amount, + pr_outstanding_amount, + ) + + ref.allocated_amount = min(outstanding_amounts) + + # update amounts to track allocation + allocated_amount = ref.allocated_amount + allocated_positive_outstanding = flt( + allocated_positive_outstanding - allocated_amount, precision + ) + remaining_references_allocated_amounts[key] = flt( + remaining_references_allocated_amounts[key] - allocated_amount, precision + ) + payment_request_outstanding_amounts[ref.payment_request] = flt( + payment_request_outstanding_amounts[ref.payment_request] - allocated_amount, precision + ) + + elif reference_outstanding_amount < 0 and allocated_negative_outstanding: + # allocate amount according to outstanding amounts + outstanding_amounts = ( + allocated_negative_outstanding, + abs(reference_outstanding_amount), + pr_outstanding_amount, + ) + + ref.allocated_amount = min(outstanding_amounts) * -1 + + # update amounts to track allocation + allocated_amount = abs(ref.allocated_amount) + allocated_negative_outstanding = flt( + allocated_negative_outstanding - allocated_amount, precision + ) + remaining_references_allocated_amounts[key] += allocated_amount # negative amount + payment_request_outstanding_amounts[ref.payment_request] = flt( + payment_request_outstanding_amounts[ref.payment_request] - allocated_amount, precision + ) + # Re allocate amount to those references which have no PR (Lower priority) + for ref in self.references: + if ref.payment_request: + continue + + key = (ref.reference_doctype, ref.reference_name, ref.get("payment_term")) + reference_outstanding_amount = remaining_references_allocated_amounts[key] + + allocated_positive_outstanding, allocated_negative_outstanding = _allocation_to_unset_pr_row( + ref, + reference_outstanding_amount, + allocated_positive_outstanding, + allocated_negative_outstanding, + ) + + @frappe.whitelist() + def set_matched_payment_requests(self, matched_payment_requests): + """ + Set `Payment Request` against `Reference` based on `matched_payment_requests`.\n + :param matched_payment_requests: List of tuple of matched Payment Requests. + + --- + Example: [(reference_doctype, reference_name, allocated_amount, payment_request), ...] + """ + if not self.references or not matched_payment_requests: + return + + if isinstance(matched_payment_requests, str): + matched_payment_requests = json.loads(matched_payment_requests) + + # modify matched_payment_requests + # like (reference_doctype, reference_name, allocated_amount): payment_request + payment_requests = {} + + for row in matched_payment_requests: + key = tuple(row[:3]) + payment_requests[key] = row[3] + + for ref in self.references: + if ref.payment_request: + continue + + key = (ref.reference_doctype, ref.reference_name, ref.allocated_amount) + + if key in payment_requests: + ref.payment_request = payment_requests[key] + del payment_requests[key] # to avoid duplicate allocation + + +def get_matched_payment_request_of_references(references=None): + """ + Get those `Payment Requests` which are matched with `References`.\n + - Amount must be same. + - Only single `Payment Request` available for this amount. + + Example: [(reference_doctype, reference_name, allocated_amount, payment_request), ...] + """ + if not references: + return + + # to fetch matched rows + refs = { + (row.reference_doctype, row.reference_name, row.allocated_amount) + for row in references + if row.reference_doctype and row.reference_name and row.allocated_amount + } + + if not refs: + return + + PR = frappe.qb.DocType("Payment Request") + + # query to group by reference_doctype, reference_name, outstanding_amount + subquery = ( + frappe.qb.from_(PR) + .select( + PR.reference_doctype, + PR.reference_name, + PR.outstanding_amount.as_("allocated_amount"), + PR.name.as_("payment_request"), + Count("*").as_("count"), + ) + .where(Tuple(PR.reference_doctype, PR.reference_name, PR.outstanding_amount).isin(refs)) + .where(PR.status != "Paid") + .where(PR.docstatus == 1) + .groupby(PR.reference_doctype, PR.reference_name, PR.outstanding_amount) + ) + + # query to fetch matched rows which are single + matched_prs = ( + frappe.qb.from_(subquery) + .select( + subquery.reference_doctype, + subquery.reference_name, + subquery.allocated_amount, + subquery.payment_request, + ) + .where(subquery.count == 1) + .run() + ) + + return matched_prs if matched_prs else None + + +def get_references_outstanding_amount(references=None): + """ + Fetch accurate outstanding amount of `References`.\n + - If `Payment Term` is set, then fetch outstanding amount from `Payment Schedule`. + - If `Payment Term` is not set, then fetch outstanding amount from `References` it self. + + Example: {(reference_doctype, reference_name, payment_term): outstanding_amount, ...} + """ + if not references: + return + + refs_with_payment_term = get_outstanding_of_references_with_payment_term(references) or {} + refs_without_payment_term = get_outstanding_of_references_with_no_payment_term(references) or {} + + return {**refs_with_payment_term, **refs_without_payment_term} + + +def get_outstanding_of_references_with_payment_term(references=None): + """ + Fetch outstanding amount of `References` which have `Payment Term` set.\n + Example: {(reference_doctype, reference_name, payment_term): outstanding_amount, ...} + """ + if not references: + return + + refs = { + (row.reference_doctype, row.reference_name, row.payment_term) + for row in references + if row.reference_doctype and row.reference_name and row.payment_term + } + + if not refs: + return + + PS = frappe.qb.DocType("Payment Schedule") + + response = ( + frappe.qb.from_(PS) + .select(PS.parenttype, PS.parent, PS.payment_term, PS.outstanding) + .where(Tuple(PS.parenttype, PS.parent, PS.payment_term).isin(refs)) + ).run(as_dict=True) + + if not response: + return + + return {(row.parenttype, row.parent, row.payment_term): row.outstanding for row in response} + + +def get_outstanding_of_references_with_no_payment_term(references): + """ + Fetch outstanding amount of `References` which have no `Payment Term` set.\n + - Fetch outstanding amount from `References` it self. + + Note: `None` is used for allocation of `Payment Request` + Example: {(reference_doctype, reference_name, None): outstanding_amount, ...} + """ + if not references: + return + + outstanding_amounts = {} + + for ref in references: + if ref.payment_term: + continue + + key = (ref.reference_doctype, ref.reference_name, None) + + if key not in outstanding_amounts: + outstanding_amounts[key] = ref.outstanding_amount + + return outstanding_amounts + + +def get_payment_request_outstanding_set_in_references(references=None): + """ + Fetch outstanding amount of `Payment Request` which are set in `References`.\n + Example: {payment_request: outstanding_amount, ...} + """ + if not references: + return + + referenced_payment_requests = {row.payment_request for row in references if row.payment_request} + + if not referenced_payment_requests: + return + + PR = frappe.qb.DocType("Payment Request") + + response = ( + frappe.qb.from_(PR) + .select(PR.name, PR.outstanding_amount) + .where(PR.name.isin(referenced_payment_requests)) + ).run() + + return dict(response) if response else None + def validate_inclusive_tax(tax, doc): def _on_previous_row_error(row_range): @@ -2236,6 +2643,8 @@ def get_payment_entry( party_type=None, payment_type=None, reference_date=None, + ignore_permissions=False, + created_from_payment_request=False, ): doc = frappe.get_doc(dt, dn) over_billing_allowance = frappe.db.get_single_value("Accounts Settings", "over_billing_allowance") @@ -2385,9 +2794,179 @@ def get_payment_entry( pe.set_difference_amount() + # If PE is created from PR directly, then no need to find open PRs for the references + if not created_from_payment_request: + allocate_open_payment_requests_to_references(pe.references, pe.precision("paid_amount")) + return pe +def get_open_payment_requests_for_references(references=None): + """ + Fetch all unpaid Payment Requests for the references. \n + - Each reference can have multiple Payment Requests. \n + + Example: {("Sales Invoice", "SINV-00001"): {"PREQ-00001": 1000, "PREQ-00002": 2000}} + """ + if not references: + return + + refs = { + (row.reference_doctype, row.reference_name) + for row in references + if row.reference_doctype and row.reference_name and row.allocated_amount + } + + if not refs: + return + + PR = frappe.qb.DocType("Payment Request") + + response = ( + frappe.qb.from_(PR) + .select(PR.name, PR.reference_doctype, PR.reference_name, PR.outstanding_amount) + .where(Tuple(PR.reference_doctype, PR.reference_name).isin(list(refs))) + .where(PR.status != "Paid") + .where(PR.docstatus == 1) + .orderby(Coalesce(PR.transaction_date, PR.creation), order=frappe.qb.asc) + ).run(as_dict=True) + + if not response: + return + + reference_payment_requests = {} + + for row in response: + key = (row.reference_doctype, row.reference_name) + + if key not in reference_payment_requests: + reference_payment_requests[key] = {row.name: row.outstanding_amount} + else: + reference_payment_requests[key][row.name] = row.outstanding_amount + + return reference_payment_requests + + +def allocate_open_payment_requests_to_references(references=None, precision=None): + """ + Allocate unpaid Payment Requests to the references. \n + --- + - Allocation based on below factors + - Reference Allocated Amount + - Reference Outstanding Amount (With Payment Terms or without Payment Terms) + - Reference Payment Request's outstanding amount + --- + - Allocation based on below scenarios + - Reference's Allocated Amount == Payment Request's Outstanding Amount + - Allocate the Payment Request to the reference + - This PR will not be allocated further + - Reference's Allocated Amount < Payment Request's Outstanding Amount + - Allocate the Payment Request to the reference + - Reduce the PR's outstanding amount by the allocated amount + - This PR can be allocated further + - Reference's Allocated Amount > Payment Request's Outstanding Amount + - Allocate the Payment Request to the reference + - Reduce Allocated Amount of the reference by the PR's outstanding amount + - Create a new row for the remaining amount until the Allocated Amount is 0 + - Allocate PR if available + --- + - Note: + - Priority is given to the first Payment Request of respective references. + - Single Reference can have multiple rows. + - With Payment Terms or without Payment Terms + - With Payment Request or without Payment Request + """ + if not references: + return + + # get all unpaid payment requests for the references + references_open_payment_requests = get_open_payment_requests_for_references(references) + + if not references_open_payment_requests: + return + + if not precision: + precision = references[0].precision("allocated_amount") + + # to manage new rows + row_number = 1 + MOVE_TO_NEXT_ROW = 1 + TO_SKIP_NEW_ROW = 2 + + while row_number <= len(references): + row = references[row_number - 1] + reference_key = (row.reference_doctype, row.reference_name) + + # update the idx to maintain the order + row.idx = row_number + + # unpaid payment requests for the reference + reference_payment_requests = references_open_payment_requests.get(reference_key) + + if not reference_payment_requests: + row_number += MOVE_TO_NEXT_ROW # to move to next reference row + continue + + # get the first payment request and its outstanding amount + payment_request, pr_outstanding_amount = next(iter(reference_payment_requests.items())) + allocated_amount = row.allocated_amount + + # allocate the payment request to the reference and PR's outstanding amount + row.payment_request = payment_request + + if pr_outstanding_amount == allocated_amount: + del reference_payment_requests[payment_request] + row_number += MOVE_TO_NEXT_ROW + + elif pr_outstanding_amount > allocated_amount: + # reduce the outstanding amount of the payment request + reference_payment_requests[payment_request] -= allocated_amount + row_number += MOVE_TO_NEXT_ROW + + else: + # split the reference row to allocate the remaining amount + del reference_payment_requests[payment_request] + row.allocated_amount = pr_outstanding_amount + allocated_amount = flt(allocated_amount - pr_outstanding_amount, precision) + + # set the remaining amount to the next row + while allocated_amount: + # create a new row for the remaining amount + new_row = frappe.copy_doc(row) + references.insert(row_number, new_row) + + # get the first payment request and its outstanding amount + payment_request, pr_outstanding_amount = next( + iter(reference_payment_requests.items()), (None, None) + ) + + # update new row + new_row.idx = row_number + 1 + new_row.payment_request = payment_request + new_row.allocated_amount = min( + pr_outstanding_amount if pr_outstanding_amount else allocated_amount, allocated_amount + ) + + if not payment_request or not pr_outstanding_amount: + row_number += TO_SKIP_NEW_ROW + break + + elif pr_outstanding_amount == allocated_amount: + del reference_payment_requests[payment_request] + row_number += TO_SKIP_NEW_ROW + break + + elif pr_outstanding_amount > allocated_amount: + reference_payment_requests[payment_request] -= allocated_amount + row_number += TO_SKIP_NEW_ROW + break + + else: + allocated_amount = flt(allocated_amount - pr_outstanding_amount, precision) + del reference_payment_requests[payment_request] + row_number += MOVE_TO_NEXT_ROW + + def update_accounting_dimensions(pe, doc): """ Updates accounting dimensions in Payment Entry based on the accounting dimensions in the reference document diff --git a/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.json b/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.json index 23ed8252333..361f516b830 100644 --- a/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.json +++ b/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.json @@ -10,6 +10,7 @@ "due_date", "bill_no", "payment_term", + "payment_term_outstanding", "account_type", "payment_type", "column_break_4", @@ -18,7 +19,9 @@ "allocated_amount", "exchange_rate", "exchange_gain_loss", - "account" + "account", + "payment_request", + "payment_request_outstanding" ], "fields": [ { @@ -120,12 +123,33 @@ "fieldname": "payment_type", "fieldtype": "Data", "label": "Payment Type" + }, + { + "fieldname": "payment_request", + "fieldtype": "Link", + "label": "Payment Request", + "options": "Payment Request" + }, + { + "depends_on": "eval: doc.payment_term", + "fieldname": "payment_term_outstanding", + "fieldtype": "Float", + "label": "Payment Term Outstanding", + "read_only": 1 + }, + { + "depends_on": "eval: doc.payment_request && doc.payment_request_outstanding", + "fieldname": "payment_request_outstanding", + "fieldtype": "Float", + "is_virtual": 1, + "label": "Payment Request Outstanding", + "read_only": 1 } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2024-04-05 09:44:08.310593", + "modified": "2024-09-16 18:11:50.019343", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Entry Reference", diff --git a/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.py b/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.py index 4a027b4ee32..2ac92ba4a84 100644 --- a/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.py +++ b/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.py @@ -1,7 +1,7 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt - +import frappe from frappe.model.document import Document @@ -25,11 +25,19 @@ class PaymentEntryReference(Document): parent: DF.Data parentfield: DF.Data parenttype: DF.Data + payment_request: DF.Link | None + payment_request_outstanding: DF.Float payment_term: DF.Link | None + payment_term_outstanding: DF.Float payment_type: DF.Data | None reference_doctype: DF.Link reference_name: DF.DynamicLink total_amount: DF.Float # end: auto-generated types - pass + @property + def payment_request_outstanding(self): + if not self.payment_request: + return + + return frappe.db.get_value("Payment Request", self.payment_request, "outstanding_amount") diff --git a/erpnext/accounts/doctype/payment_request/payment_request.js b/erpnext/accounts/doctype/payment_request/payment_request.js index e45aa512fe8..50f96a4e2b6 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.js +++ b/erpnext/accounts/doctype/payment_request/payment_request.js @@ -48,8 +48,8 @@ frappe.ui.form.on("Payment Request", "refresh", function (frm) { } if ( - (!frm.doc.payment_gateway_account || frm.doc.payment_request_type == "Outward") && - frm.doc.status == "Initiated" + frm.doc.payment_request_type == "Outward" && + ["Initiated", "Partially Paid"].includes(frm.doc.status) ) { frm.add_custom_button(__("Create Payment Entry"), function () { frappe.call({ diff --git a/erpnext/accounts/doctype/payment_request/payment_request.json b/erpnext/accounts/doctype/payment_request/payment_request.json index d0651f74bdf..7806bc682b0 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.json +++ b/erpnext/accounts/doctype/payment_request/payment_request.json @@ -19,9 +19,11 @@ "reference_name", "transaction_details", "grand_total", + "currency", "is_a_subscription", "column_break_18", - "currency", + "outstanding_amount", + "party_account_currency", "subscription_section", "subscription_plans", "bank_account_details", @@ -69,6 +71,7 @@ { "fieldname": "transaction_date", "fieldtype": "Date", + "in_preview": 1, "label": "Transaction Date" }, { @@ -133,7 +136,8 @@ "no_copy": 1, "options": "reference_doctype", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "search_index": 1 }, { "fieldname": "transaction_details", @@ -141,12 +145,14 @@ "label": "Transaction Details" }, { - "description": "Amount in customer's currency", + "description": "Amount in transaction currency", "fieldname": "grand_total", "fieldtype": "Currency", + "in_preview": 1, "label": "Amount", "non_negative": 1, - "options": "currency" + "options": "currency", + "reqd": 1 }, { "default": "0", @@ -392,19 +398,50 @@ "print_hide": 1, "read_only": 1 }, + { + "fieldname": "failed_reason", + "fieldtype": "Data", + "hidden": 1, + "label": "Reason for Failure", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 + }, + { + "depends_on": "eval: doc.docstatus === 1", + "description": "Amount in party's bank account currency", + "fieldname": "outstanding_amount", + "fieldtype": "Currency", + "in_preview": 1, + "label": "Outstanding Amount", + "non_negative": 1, + "options": "party_account_currency", + "read_only": 1 + }, { "fieldname": "company", "fieldtype": "Link", "label": "Company", "options": "Company", "read_only": 1 + }, + { + "fieldname": "column_break_pnyv", + "fieldtype": "Column Break" + }, + { + "fieldname": "party_account_currency", + "fieldtype": "Link", + "label": "Party Account Currency", + "options": "Currency", + "read_only": 1 } ], "in_create": 1, "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2024-08-07 16:39:54.288002", + "modified": "2024-09-16 17:50:54.440090", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Request", @@ -439,7 +476,8 @@ "write": 1 } ], + "show_preview_popup": 1, "sort_field": "modified", "sort_order": "DESC", "states": [] -} +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index 83b43a15987..0dab9d905be 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -3,9 +3,11 @@ import json import frappe from frappe import _ from frappe.model.document import Document +from frappe.query_builder.functions import Sum from frappe.utils import flt, nowdate from frappe.utils.background_jobs import enqueue +from erpnext import get_company_currency from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( get_accounting_dimensions, ) @@ -45,9 +47,11 @@ class PaymentRequest(Document): bank_account: DF.Link | None bank_account_no: DF.ReadOnly | None branch_code: DF.ReadOnly | None + company: DF.Link | None cost_center: DF.Link | None currency: DF.Link | None email_to: DF.Data | None + failed_reason: DF.Data | None grand_total: DF.Currency iban: DF.ReadOnly | None is_a_subscription: DF.Check @@ -56,16 +60,18 @@ class PaymentRequest(Document): mode_of_payment: DF.Link | None mute_email: DF.Check naming_series: DF.Literal["ACC-PRQ-.YYYY.-"] + outstanding_amount: DF.Currency party: DF.DynamicLink | None + party_account_currency: DF.Link | None party_type: DF.Link | None payment_account: DF.ReadOnly | None - payment_channel: DF.Literal["", "Email", "Phone"] + payment_channel: DF.Literal["", "Email", "Phone", "Other"] payment_gateway: DF.ReadOnly | None payment_gateway_account: DF.Link | None payment_order: DF.Link | None payment_request_type: DF.Literal["Outward", "Inward"] payment_url: DF.Data | None - print_format: DF.Literal + print_format: DF.Literal[None] project: DF.Link | None reference_doctype: DF.Link | None reference_name: DF.DynamicLink | None @@ -84,7 +90,6 @@ class PaymentRequest(Document): subscription_plans: DF.Table[SubscriptionPlanDetail] swift_number: DF.ReadOnly | None transaction_date: DF.Date | None - company: DF.Link | None # end: auto-generated types def validate(self): @@ -100,6 +105,12 @@ class PaymentRequest(Document): frappe.throw(_("To create a Payment Request reference document is required")) def validate_payment_request_amount(self): + if self.grand_total == 0: + frappe.throw( + _("{0} cannot be zero").format(self.get_label_from_fieldname("grand_total")), + title=_("Invalid Amount"), + ) + existing_payment_request_amount = flt( get_existing_payment_request_amount(self.reference_doctype, self.reference_name) ) @@ -147,28 +158,44 @@ class PaymentRequest(Document): ).format(self.grand_total, amount) ) - def on_submit(self): - if self.payment_request_type == "Outward": - self.db_set("status", "Initiated") - return - elif self.payment_request_type == "Inward": - self.db_set("status", "Requested") - - send_mail = self.payment_gateway_validation() if self.payment_gateway else None - ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name) - + def before_submit(self): if ( - hasattr(ref_doc, "order_type") and ref_doc.order_type == "Shopping Cart" - ) or self.flags.mute_email: - send_mail = False + self.currency != self.party_account_currency + and self.party_account_currency == get_company_currency(self.company) + ): + # set outstanding amount in party account currency + invoice = frappe.get_value( + self.reference_doctype, + self.reference_name, + ["rounded_total", "grand_total", "base_rounded_total", "base_grand_total"], + as_dict=1, + ) + grand_total = invoice.get("rounded_total") or invoice.get("grand_total") + base_grand_total = invoice.get("base_rounded_total") or invoice.get("base_grand_total") + self.outstanding_amount = flt( + self.grand_total / grand_total * base_grand_total, + self.precision("outstanding_amount"), + ) - if send_mail and self.payment_channel != "Phone": - self.set_payment_request_url() - self.send_email() - self.make_communication_entry() + else: + self.outstanding_amount = self.grand_total - elif self.payment_channel == "Phone": - self.request_phone_payment() + if self.payment_request_type == "Outward": + self.status = "Initiated" + elif self.payment_request_type == "Inward": + self.status = "Requested" + + if self.payment_request_type == "Inward": + if self.payment_channel == "Phone": + self.request_phone_payment() + else: + self.set_payment_request_url() + if not (self.mute_email or self.flags.mute_email): + self.send_email() + self.make_communication_entry() + + def on_submit(self): + self.update_reference_advance_payment_status() def request_phone_payment(self): controller = _get_payment_gateway_controller(self.payment_gateway) @@ -207,6 +234,7 @@ class PaymentRequest(Document): def on_cancel(self): self.check_if_payment_entry_exists() self.set_as_cancelled() + self.update_reference_advance_payment_status() def make_invoice(self): ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name) @@ -275,7 +303,7 @@ class PaymentRequest(Document): def set_as_paid(self): if self.payment_channel == "Phone": - self.db_set("status", "Paid") + self.db_set({"status": "Paid", "outstanding_amount": 0}) else: payment_entry = self.create_payment_entry() @@ -296,26 +324,32 @@ class PaymentRequest(Document): else: party_account = get_party_account("Customer", ref_doc.get("customer"), ref_doc.company) - party_account_currency = ref_doc.get("party_account_currency") or get_account_currency(party_account) + party_account_currency = ( + self.get("party_account_currency") + or ref_doc.get("party_account_currency") + or get_account_currency(party_account) + ) + + party_amount = bank_amount = self.outstanding_amount - bank_amount = self.grand_total if party_account_currency == ref_doc.company_currency and party_account_currency != self.currency: - party_amount = ref_doc.get("base_rounded_total") or ref_doc.get("base_grand_total") - else: - party_amount = self.grand_total + exchange_rate = ref_doc.get("conversion_rate") + bank_amount = flt(self.outstanding_amount / exchange_rate, self.precision("grand_total")) + # outstanding amount is already in Part's account currency payment_entry = get_payment_entry( self.reference_doctype, self.reference_name, party_amount=party_amount, bank_account=self.payment_account, bank_amount=bank_amount, + created_from_payment_request=True, ) payment_entry.update( { "mode_of_payment": self.mode_of_payment, - "reference_no": self.name, + "reference_no": self.name, # to prevent validation error "reference_date": nowdate(), "remarks": "Payment Entry against {} {} via Payment Request {}".format( self.reference_doctype, self.reference_name, self.name @@ -323,6 +357,9 @@ class PaymentRequest(Document): } ) + # Allocate payment_request for each reference in payment_entry (Payment Term can splits the row) + self._allocate_payment_request_to_pe_references(references=payment_entry.references) + # Update dimensions payment_entry.update( { @@ -331,14 +368,6 @@ class PaymentRequest(Document): } ) - if party_account_currency == ref_doc.company_currency and party_account_currency != self.currency: - amount = payment_entry.base_paid_amount - else: - amount = self.grand_total - - payment_entry.received_amount = amount - payment_entry.get("references")[0].allocated_amount = amount - # Update 'Paid Amount' on Forex transactions if self.currency != ref_doc.company_currency: if ( @@ -429,6 +458,70 @@ class PaymentRequest(Document): return create_stripe_subscription(gateway_controller, data) + def update_reference_advance_payment_status(self): + advance_payment_doctypes = frappe.get_hooks("advance_payment_receivable_doctypes") + frappe.get_hooks( + "advance_payment_payable_doctypes" + ) + if self.reference_doctype in advance_payment_doctypes: + ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name) + ref_doc.set_advance_payment_status() + + def _allocate_payment_request_to_pe_references(self, references): + """ + Allocate the Payment Request to the Payment Entry references based on\n + - Allocated Amount. + - Outstanding Amount of Payment Request.\n + Payment Request is doc itself and references are the rows of Payment Entry. + """ + if len(references) == 1: + references[0].payment_request = self.name + return + + precision = references[0].precision("allocated_amount") + outstanding_amount = self.outstanding_amount + + # to manage rows + row_number = 1 + MOVE_TO_NEXT_ROW = 1 + TO_SKIP_NEW_ROW = 2 + NEW_ROW_ADDED = False + + while row_number <= len(references): + row = references[row_number - 1] + + # update the idx to maintain the order + row.idx = row_number + + if outstanding_amount == 0: + if not NEW_ROW_ADDED: + break + + row_number += MOVE_TO_NEXT_ROW + continue + + # allocate the payment request to the row + row.payment_request = self.name + + if row.allocated_amount <= outstanding_amount: + outstanding_amount = flt(outstanding_amount - row.allocated_amount, precision) + row_number += MOVE_TO_NEXT_ROW + else: + remaining_allocated_amount = flt(row.allocated_amount - outstanding_amount, precision) + row.allocated_amount = outstanding_amount + outstanding_amount = 0 + + # create a new row without PR for remaining unallocated amount + new_row = frappe.copy_doc(row) + references.insert(row_number, new_row) + + # update new row + new_row.idx = row_number + 1 + new_row.payment_request = None + new_row.allocated_amount = remaining_allocated_amount + + NEW_ROW_ADDED = True + row_number += TO_SKIP_NEW_ROW + @frappe.whitelist(allow_guest=True) def make_payment_request(**args): @@ -459,11 +552,15 @@ def make_payment_request(**args): {"reference_doctype": args.dt, "reference_name": args.dn, "docstatus": 0}, ) - existing_payment_request_amount = get_existing_payment_request_amount(args.dt, args.dn) + # fetches existing payment request `grand_total` amount + existing_payment_request_amount = get_existing_payment_request_amount(ref_doc.doctype, ref_doc.name) if existing_payment_request_amount: grand_total -= existing_payment_request_amount + if not grand_total: + frappe.throw(_("Payment Request is already created")) + if draft_payment_request: frappe.db.set_value( "Payment Request", draft_payment_request, "grand_total", grand_total, update_modified=False @@ -477,6 +574,13 @@ def make_payment_request(**args): "Outward" if args.get("dt") in ["Purchase Order", "Purchase Invoice"] else "Inward" ) + party_type = args.get("party_type") or "Customer" + party_account_currency = ref_doc.party_account_currency + + if not party_account_currency: + party_account = get_party_account(party_type, ref_doc.get(party_type.lower()), ref_doc.company) + party_account_currency = get_account_currency(party_account) + pr.update( { "payment_gateway_account": gateway_account.get("name"), @@ -485,6 +589,7 @@ def make_payment_request(**args): "payment_channel": gateway_account.get("payment_channel"), "payment_request_type": args.get("payment_request_type"), "currency": ref_doc.currency, + "party_account_currency": party_account_currency, "grand_total": grand_total, "mode_of_payment": args.mode_of_payment, "email_to": args.recipient_id or ref_doc.owner, @@ -493,7 +598,7 @@ def make_payment_request(**args): "reference_doctype": args.dt, "reference_name": args.dn, "company": ref_doc.get("company"), - "party_type": args.get("party_type") or "Customer", + "party_type": party_type, "party": args.get("party") or ref_doc.get("customer"), "bank_account": bank_account, } @@ -539,9 +644,11 @@ def get_amount(ref_doc, payment_account=None): elif dt in ["Sales Invoice", "Purchase Invoice"]: if not ref_doc.get("is_pos"): if ref_doc.party_account_currency == ref_doc.currency: - grand_total = flt(ref_doc.grand_total) + grand_total = flt(ref_doc.rounded_total or ref_doc.grand_total) else: - grand_total = flt(ref_doc.base_grand_total) / ref_doc.conversion_rate + grand_total = flt( + flt(ref_doc.base_rounded_total or ref_doc.base_grand_total) / ref_doc.conversion_rate + ) elif dt == "Sales Invoice": for pay in ref_doc.payments: if pay.type == "Phone" and pay.account == payment_account: @@ -563,24 +670,20 @@ def get_amount(ref_doc, payment_account=None): def get_existing_payment_request_amount(ref_dt, ref_dn): """ - Get the existing payment request which are unpaid or partially paid for payment channel other than Phone - and get the summation of existing paid payment request for Phone payment channel. + Return the total amount of Payment Requests against a reference document. """ - existing_payment_request_amount = frappe.db.sql( - """ - select sum(grand_total) - from `tabPayment Request` - where - reference_doctype = %s - and reference_name = %s - and docstatus = 1 - and (status != 'Paid' - or (payment_channel = 'Phone' - and status = 'Paid')) - """, - (ref_dt, ref_dn), + PR = frappe.qb.DocType("Payment Request") + + response = ( + frappe.qb.from_(PR) + .select(Sum(PR.grand_total)) + .where(PR.reference_doctype == ref_dt) + .where(PR.reference_name == ref_dn) + .where(PR.docstatus == 1) + .run() ) - return flt(existing_payment_request_amount[0][0]) if existing_payment_request_amount else 0 + + return response[0][0] if response[0] else 0 def get_gateway_details(args): # nosemgrep @@ -627,41 +730,66 @@ def make_payment_entry(docname): return doc.create_payment_entry(submit=False).as_dict() -def update_payment_req_status(doc, method): - from erpnext.accounts.doctype.payment_entry.payment_entry import get_reference_details +def update_payment_requests_as_per_pe_references(references=None, cancel=False): + """ + Update Payment Request's `Status` and `Outstanding Amount` based on Payment Entry Reference's `Allocated Amount`. + """ + if not references: + return - for ref in doc.references: - payment_request_name = frappe.db.get_value( - "Payment Request", - { - "reference_doctype": ref.reference_doctype, - "reference_name": ref.reference_name, - "docstatus": 1, - }, + precision = references[0].precision("allocated_amount") + + referenced_payment_requests = frappe.get_all( + "Payment Request", + filters={"name": ["in", {row.payment_request for row in references if row.payment_request}]}, + fields=[ + "name", + "grand_total", + "outstanding_amount", + "payment_request_type", + ], + ) + + referenced_payment_requests = {pr.name: pr for pr in referenced_payment_requests} + + for ref in references: + if not ref.payment_request: + continue + + payment_request = referenced_payment_requests[ref.payment_request] + pr_outstanding = payment_request["outstanding_amount"] + + # update outstanding amount + new_outstanding_amount = flt( + pr_outstanding + ref.allocated_amount if cancel else pr_outstanding - ref.allocated_amount, + precision, ) - if payment_request_name: - ref_details = get_reference_details( - ref.reference_doctype, - ref.reference_name, - doc.party_account_currency, - doc.party_type, - doc.party, + # to handle same payment request for the multiple allocations + payment_request["outstanding_amount"] = new_outstanding_amount + + if not cancel and new_outstanding_amount < 0: + frappe.throw( + msg=_( + "The allocated amount is greater than the outstanding amount of Payment Request {0}" + ).format(ref.payment_request), + title=_("Invalid Allocated Amount"), ) - pay_req_doc = frappe.get_doc("Payment Request", payment_request_name) - status = pay_req_doc.status - if status != "Paid" and not ref_details.outstanding_amount: - status = "Paid" - elif status != "Partially Paid" and ref_details.outstanding_amount != ref_details.total_amount: - status = "Partially Paid" - elif ref_details.outstanding_amount == ref_details.total_amount: - if pay_req_doc.payment_request_type == "Outward": - status = "Initiated" - elif pay_req_doc.payment_request_type == "Inward": - status = "Requested" + # update status + if new_outstanding_amount == payment_request["grand_total"]: + status = "Initiated" if payment_request["payment_request_type"] == "Outward" else "Requested" + elif new_outstanding_amount == 0: + status = "Paid" + elif new_outstanding_amount > 0: + status = "Partially Paid" - pay_req_doc.db_set("status", status) + # update database + frappe.db.set_value( + "Payment Request", + ref.payment_request, + {"outstanding_amount": new_outstanding_amount, "status": status}, + ) def get_dummy_message(doc): @@ -745,3 +873,62 @@ def validate_payment(doc, method=None): doc.reference_docname ) ) + + +def get_paid_amount_against_order(dt, dn): + pe_ref = frappe.qb.DocType("Payment Entry Reference") + if dt == "Sales Order": + inv_dt, inv_field = "Sales Invoice Item", "sales_order" + else: + inv_dt, inv_field = "Purchase Invoice Item", "purchase_order" + inv_item = frappe.qb.DocType(inv_dt) + return ( + frappe.qb.from_(pe_ref) + .select( + Sum(pe_ref.allocated_amount), + ) + .where( + (pe_ref.docstatus == 1) + & ( + (pe_ref.reference_name == dn) + | pe_ref.reference_name.isin( + frappe.qb.from_(inv_item) + .select(inv_item.parent) + .where(inv_item[inv_field] == dn) + .distinct() + ) + ) + ) + ).run()[0][0] or 0 + + +@frappe.whitelist() +def get_open_payment_requests_query(doctype, txt, searchfield, start, page_len, filters): + # permission checks in `get_list()` + reference_doctype = filters.get("reference_doctype") + reference_name = filters.get("reference_doctype") + + if not reference_doctype or not reference_name: + return [] + + open_payment_requests = frappe.get_list( + "Payment Request", + filters={ + "reference_doctype": filters["reference_doctype"], + "reference_name": filters["reference_name"], + "status": ["!=", "Paid"], + "outstanding_amount": ["!=", 0], # for compatibility with old data + "docstatus": 1, + }, + fields=["name", "grand_total", "outstanding_amount"], + order_by="transaction_date ASC,creation ASC", + ) + + return [ + ( + pr.name, + _("Grand Total: {0}").format(pr.grand_total), + _("Outstanding Amount: {0}").format(pr.outstanding_amount), + ) + for pr in open_payment_requests + ] diff --git a/erpnext/accounts/doctype/payment_request/test_payment_request.py b/erpnext/accounts/doctype/payment_request/test_payment_request.py index 6d15f84d7cf..4caffdb431a 100644 --- a/erpnext/accounts/doctype/payment_request/test_payment_request.py +++ b/erpnext/accounts/doctype/payment_request/test_payment_request.py @@ -1,11 +1,13 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt +import re import unittest import frappe from frappe.tests.utils import FrappeTestCase +from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_terms_template from erpnext.accounts.doctype.payment_request.payment_request import make_payment_request 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 @@ -278,3 +280,256 @@ class TestPaymentRequest(FrappeTestCase): self.assertEqual(pe.paid_amount, 800) self.assertEqual(pe.base_received_amount, 800) self.assertEqual(pe.received_amount, 10) + + def test_multiple_payment_if_partially_paid_for_same_currency(self): + so = make_sales_order(currency="INR", qty=1, rate=1000) + + self.assertEqual(so.advance_payment_status, "Not Requested") + + pr = make_payment_request( + dt="Sales Order", + dn=so.name, + mute_email=1, + submit_doc=1, + return_doc=1, + ) + + self.assertEqual(pr.grand_total, 1000) + self.assertEqual(pr.outstanding_amount, pr.grand_total) + self.assertEqual(pr.party_account_currency, pr.currency) # INR + self.assertEqual(pr.status, "Requested") + + so.load_from_db() + self.assertEqual(so.advance_payment_status, "Requested") + + # to make partial payment + pe = pr.create_payment_entry(submit=False) + pe.paid_amount = 200 + pe.references[0].allocated_amount = 200 + pe.submit() + + self.assertEqual(pe.references[0].payment_request, pr.name) + + so.load_from_db() + self.assertEqual(so.advance_payment_status, "Partially Paid") + + pr.load_from_db() + self.assertEqual(pr.status, "Partially Paid") + self.assertEqual(pr.outstanding_amount, 800) + self.assertEqual(pr.grand_total, 1000) + + # complete payment + pe = pr.create_payment_entry() + + self.assertEqual(pe.paid_amount, 800) # paid amount set from pr's outstanding amount + self.assertEqual(pe.references[0].allocated_amount, 800) + self.assertEqual(pe.references[0].outstanding_amount, 800) # for Orders it is not zero + self.assertEqual(pe.references[0].payment_request, pr.name) + + so.load_from_db() + self.assertEqual(so.advance_payment_status, "Fully Paid") + + pr.load_from_db() + self.assertEqual(pr.status, "Paid") + self.assertEqual(pr.outstanding_amount, 0) + self.assertEqual(pr.grand_total, 1000) + + # creating a more payment Request must not allowed + self.assertRaisesRegex( + frappe.exceptions.ValidationError, + re.compile(r"Payment Request is already created"), + make_payment_request, + dt="Sales Order", + dn=so.name, + mute_email=1, + submit_doc=1, + return_doc=1, + ) + + def test_multiple_payment_if_partially_paid_for_multi_currency(self): + pi = make_purchase_invoice(currency="USD", conversion_rate=50, qty=1, rate=100) + + pr = make_payment_request( + dt="Purchase Invoice", + dn=pi.name, + mute_email=1, + submit_doc=1, + return_doc=1, + ) + + # 100 USD -> 5000 INR + self.assertEqual(pr.grand_total, 100) + self.assertEqual(pr.outstanding_amount, 5000) + self.assertEqual(pr.currency, "USD") + self.assertEqual(pr.party_account_currency, "INR") + self.assertEqual(pr.status, "Initiated") + + # to make partial payment + pe = pr.create_payment_entry(submit=False) + pe.paid_amount = 2000 + pe.references[0].allocated_amount = 2000 + pe.submit() + + self.assertEqual(pe.references[0].payment_request, pr.name) + + pr.load_from_db() + self.assertEqual(pr.status, "Partially Paid") + self.assertEqual(pr.outstanding_amount, 3000) + self.assertEqual(pr.grand_total, 100) + + # complete payment + pe = pr.create_payment_entry() + self.assertEqual(pe.paid_amount, 3000) # paid amount set from pr's outstanding amount + self.assertEqual(pe.references[0].allocated_amount, 3000) + self.assertEqual(pe.references[0].outstanding_amount, 0) # for Invoices it will zero + self.assertEqual(pe.references[0].payment_request, pr.name) + + pr.load_from_db() + self.assertEqual(pr.status, "Paid") + self.assertEqual(pr.outstanding_amount, 0) + self.assertEqual(pr.grand_total, 100) + + # creating a more payment Request must not allowed + self.assertRaisesRegex( + frappe.exceptions.ValidationError, + re.compile(r"Payment Request is already created"), + make_payment_request, + dt="Purchase Invoice", + dn=pi.name, + mute_email=1, + submit_doc=1, + return_doc=1, + ) + + def test_single_payment_with_payment_term_for_same_currency(self): + create_payment_terms_template() + + po = create_purchase_order(do_not_save=1, currency="INR", qty=1, rate=20000) + po.payment_terms_template = "Test Receivable Template" # 84.746 and 15.254 + po.save() + po.submit() + + self.assertEqual(po.advance_payment_status, "Not Initiated") + + pr = make_payment_request( + dt="Purchase Order", + dn=po.name, + mute_email=1, + submit_doc=1, + return_doc=1, + ) + + self.assertEqual(pr.grand_total, 20000) + self.assertEqual(pr.outstanding_amount, pr.grand_total) + self.assertEqual(pr.party_account_currency, pr.currency) # INR + self.assertEqual(pr.status, "Initiated") + + po.load_from_db() + self.assertEqual(po.advance_payment_status, "Initiated") + + pe = pr.create_payment_entry() + + self.assertEqual(len(pe.references), 2) + self.assertEqual(pe.paid_amount, 20000) + + # check 1st payment term + self.assertEqual(pe.references[0].allocated_amount, 16949.2) + self.assertEqual(pe.references[0].payment_request, pr.name) + + # check 2nd payment term + self.assertEqual(pe.references[1].allocated_amount, 3050.8) + self.assertEqual(pe.references[1].payment_request, pr.name) + + po.load_from_db() + self.assertEqual(po.advance_payment_status, "Fully Paid") + + pr.load_from_db() + self.assertEqual(pr.status, "Paid") + self.assertEqual(pr.outstanding_amount, 0) + self.assertEqual(pr.grand_total, 20000) + + def test_single_payment_with_payment_term_for_multi_currency(self): + create_payment_terms_template() + + si = create_sales_invoice(do_not_save=1, currency="USD", qty=1, rate=200, conversion_rate=50) + si.payment_terms_template = "Test Receivable Template" # 84.746 and 15.254 + si.save() + si.submit() + + pr = make_payment_request( + dt="Sales Invoice", + dn=si.name, + mute_email=1, + submit_doc=1, + return_doc=1, + ) + + # 200 USD -> 10000 INR + self.assertEqual(pr.grand_total, 200) + self.assertEqual(pr.outstanding_amount, 10000) + self.assertEqual(pr.currency, "USD") + self.assertEqual(pr.party_account_currency, "INR") + self.assertEqual(pr.status, "Requested") + + pe = pr.create_payment_entry() + self.assertEqual(len(pe.references), 2) + self.assertEqual(pe.paid_amount, 10000) + + # check 1st payment term + # convert it via dollar and conversion_rate + self.assertEqual(pe.references[0].allocated_amount, 8474.5) # multi currency conversion + self.assertEqual(pe.references[0].payment_request, pr.name) + + # check 2nd payment term + self.assertEqual(pe.references[1].allocated_amount, 1525.5) # multi currency conversion + self.assertEqual(pe.references[1].payment_request, pr.name) + + pr.load_from_db() + self.assertEqual(pr.status, "Paid") + self.assertEqual(pr.outstanding_amount, 0) + self.assertEqual(pr.grand_total, 200) + + def test_payment_cancel_process(self): + so = make_sales_order(currency="INR", qty=1, rate=1000) + self.assertEqual(so.advance_payment_status, "Not Requested") + + pr = make_payment_request( + dt="Sales Order", + dn=so.name, + mute_email=1, + submit_doc=1, + return_doc=1, + ) + + self.assertEqual(pr.status, "Requested") + self.assertEqual(pr.grand_total, 1000) + self.assertEqual(pr.outstanding_amount, pr.grand_total) + + so.load_from_db() + self.assertEqual(so.advance_payment_status, "Requested") + + pe = pr.create_payment_entry(submit=False) + pe.paid_amount = 800 + pe.references[0].allocated_amount = 800 + pe.submit() + + self.assertEqual(pe.references[0].payment_request, pr.name) + + so.load_from_db() + self.assertEqual(so.advance_payment_status, "Partially Paid") + + pr.load_from_db() + self.assertEqual(pr.status, "Partially Paid") + self.assertEqual(pr.outstanding_amount, 200) + self.assertEqual(pr.grand_total, 1000) + + # cancelling PE + pe.cancel() + + pr.load_from_db() + self.assertEqual(pr.status, "Requested") + self.assertEqual(pr.outstanding_amount, 1000) + self.assertEqual(pr.grand_total, 1000) + + so.load_from_db() + self.assertEqual(so.advance_payment_status, "Requested") diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 44a9813c89e..b248d9f5aba 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1966,7 +1966,38 @@ class AccountsController(TransactionBase): ).format(formatted_advance_paid, self.name, formatted_order_total) ) - frappe.db.set_value(self.doctype, self.name, "advance_paid", advance_paid) + self.db_set("advance_paid", advance_paid) + + self.set_advance_payment_status() + + def set_advance_payment_status(self): + new_status = None + + paid_amount = frappe.get_value( + doctype="Payment Request", + filters={ + "reference_doctype": self.doctype, + "reference_name": self.name, + "docstatus": 1, + }, + fieldname="sum(grand_total - outstanding_amount)", + ) + + if not paid_amount: + if self.doctype in frappe.get_hooks("advance_payment_receivable_doctypes"): + new_status = "Not Requested" if paid_amount is None else "Requested" + elif self.doctype in frappe.get_hooks("advance_payment_payable_doctypes"): + new_status = "Not Initiated" if paid_amount is None else "Initiated" + else: + total_amount = self.get("rounded_total") or self.get("grand_total") + new_status = "Fully Paid" if paid_amount == total_amount else "Partially Paid" + + if new_status == self.advance_payment_status: + return + + self.db_set("advance_payment_status", new_status, update_modified=False) + self.set_status(update=True) + self.notify_update() @property def company_abbr(self): From 1d6f97ad945f8d0225533fb03e6866561316481b Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 27 Sep 2024 23:29:39 +0530 Subject: [PATCH 252/734] fix: Ignore transaction deletion check on ledger entry insertion (cherry picked from commit 998f6a92a48b57dbde1d76e848378bd743d0fa03) --- .../transaction_deletion_record.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) 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 c9c3c837ceb..ce3f918f7eb 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py +++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py @@ -485,8 +485,14 @@ def is_deletion_doc_running(company: str | None = None, err_msg: str | None = No def check_for_running_deletion_job(doc, method=None): # Check if DocType has 'company' field - df = qb.DocType("DocField") - if qb.from_(df).select(df.parent).where((df.fieldname == "company") & (df.parent == doc.doctype)).run(): - is_deletion_doc_running( - doc.company, _("Cannot make any transactions until the deletion job is completed") - ) + if doc.doctype not in ("GL Entry", "Payment Ledger Entry", "Stock Ledger Entry"): + df = qb.DocType("DocField") + if ( + qb.from_(df) + .select(df.parent) + .where((df.fieldname == "company") & (df.parent == doc.doctype)) + .run() + ): + is_deletion_doc_running( + doc.company, _("Cannot make any transactions until the deletion job is completed") + ) From 27cd51e267b3a11ab474ca085b5a0dc6cef87fad Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Thu, 26 Sep 2024 16:49:01 +0530 Subject: [PATCH 253/734] feat: added 'cost of new capitalized asset' column (cherry picked from commit 1eb9cc33fc298c90ef5a04e5de2ee4dec67f941a) --- .../asset_depreciations_and_balances.py | 40 ++++++++++++++++--- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py index 64e6a340464..cdd5baf3240 100644 --- a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py +++ b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py @@ -36,8 +36,9 @@ def get_group_by_asset_category_data(filters): + flt(row.cost_of_new_purchase) - flt(row.cost_of_sold_asset) - flt(row.cost_of_scrapped_asset) + - flt(row.cost_of_capitalized_asset) ) - # Update row with corresponding asset data + row.update( next( asset @@ -111,13 +112,24 @@ def get_asset_categories_for_grouped_by_category(filters): end else 0 - end), 0) as cost_of_scrapped_asset + end), 0) as cost_of_scrapped_asset, + ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 + and a.disposal_date >= %(from_date)s + and a.disposal_date <= %(to_date)s then + case when a.status = "Capitalized" then + a.gross_purchase_amount + else + 0 + end + else + 0 + end), 0) as cost_of_capitalized_asset from `tabAsset` a where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s {condition} and not exists( select 1 from `tabAsset Capitalization Asset Item` acai join `tabAsset Capitalization` ac on acai.parent=ac.name where acai.asset = a.name - and ac.posting_date <= %(to_date)s + and ac.posting_date < %(from_date)s and ac.docstatus=1 ) group by a.asset_category @@ -179,13 +191,24 @@ def get_asset_details_for_grouped_by_category(filters): end else 0 - end), 0) as cost_of_scrapped_asset + end), 0) as cost_of_scrapped_asset, + ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 + and a.disposal_date >= %(from_date)s + and a.disposal_date <= %(to_date)s then + case when a.status = "Capitalized" then + a.gross_purchase_amount + else + 0 + end + else + 0 + end), 0) as cost_of_capitalized_asset from `tabAsset` a where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s {condition} and not exists( select 1 from `tabAsset Capitalization Asset Item` acai join `tabAsset Capitalization` ac on acai.parent=ac.name where acai.asset = a.name - and ac.posting_date <= %(to_date)s + and ac.posting_date < %(from_date)s and ac.docstatus=1 ) group by a.name @@ -217,6 +240,7 @@ def get_group_by_asset_data(filters): + flt(row.cost_of_new_purchase) - flt(row.cost_of_sold_asset) - flt(row.cost_of_scrapped_asset) + - flt(row.cost_of_capitalized_asset) ) row.update(next(asset for asset in assets if asset["asset"] == asset_detail.get("name", ""))) @@ -445,6 +469,12 @@ def get_columns(filters): "fieldtype": "Currency", "width": 140, }, + { + "label": _("Cost of New Capitalized Asset"), + "fieldname": "cost_of_capitalized_asset", + "fieldtype": "Currency", + "width": 140, + }, { "label": _("Cost as on") + " " + formatdate(filters.to_date), "fieldname": "cost_as_on_to_date", From c1f14f2991371006c9b8c942b3797fbf728ca54c Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 27 Sep 2024 23:29:39 +0530 Subject: [PATCH 254/734] fix: Ignore transaction deletion check on ledger entry insertion (cherry picked from commit 998f6a92a48b57dbde1d76e848378bd743d0fa03) (cherry picked from commit 1d6f97ad945f8d0225533fb03e6866561316481b) --- .../transaction_deletion_record.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) 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 c9c3c837ceb..ce3f918f7eb 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py +++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py @@ -485,8 +485,14 @@ def is_deletion_doc_running(company: str | None = None, err_msg: str | None = No def check_for_running_deletion_job(doc, method=None): # Check if DocType has 'company' field - df = qb.DocType("DocField") - if qb.from_(df).select(df.parent).where((df.fieldname == "company") & (df.parent == doc.doctype)).run(): - is_deletion_doc_running( - doc.company, _("Cannot make any transactions until the deletion job is completed") - ) + if doc.doctype not in ("GL Entry", "Payment Ledger Entry", "Stock Ledger Entry"): + df = qb.DocType("DocField") + if ( + qb.from_(df) + .select(df.parent) + .where((df.fieldname == "company") & (df.parent == doc.doctype)) + .run() + ): + is_deletion_doc_running( + doc.company, _("Cannot make any transactions until the deletion job is completed") + ) From e706aa692a988a51b6f8791cd2d55951dd2f4c46 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Fri, 27 Sep 2024 18:30:37 +0000 Subject: [PATCH 255/734] chore(release): Bumped to Version 15.36.3 ## [15.36.3](https://github.com/frappe/erpnext/compare/v15.36.2...v15.36.3) (2024-09-27) ### Bug Fixes * Ignore transaction deletion check on ledger entry insertion ([c1f14f2](https://github.com/frappe/erpnext/commit/c1f14f2991371006c9b8c942b3797fbf728ca54c)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 89f9ab1605e..34d07c1e23b 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.36.2" +__version__ = "15.36.3" def get_default_company(user=None): From 907e3af1b0f5a08617f637573f3abc3e3eb56253 Mon Sep 17 00:00:00 2001 From: Abdeali Chharchhoda Date: Sat, 28 Sep 2024 12:09:33 +0530 Subject: [PATCH 256/734] fix: Remove `advance_payment_status` uses --- .../payment_request/payment_request.py | 11 ------- .../payment_request/test_payment_request.py | 13 -------- erpnext/controllers/accounts_controller.py | 31 ------------------- erpnext/hooks.py | 1 - 4 files changed, 56 deletions(-) diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index 0dab9d905be..9fbc73afea7 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -194,9 +194,6 @@ class PaymentRequest(Document): self.send_email() self.make_communication_entry() - def on_submit(self): - self.update_reference_advance_payment_status() - def request_phone_payment(self): controller = _get_payment_gateway_controller(self.payment_gateway) request_amount = self.get_request_amount() @@ -458,14 +455,6 @@ class PaymentRequest(Document): return create_stripe_subscription(gateway_controller, data) - def update_reference_advance_payment_status(self): - advance_payment_doctypes = frappe.get_hooks("advance_payment_receivable_doctypes") + frappe.get_hooks( - "advance_payment_payable_doctypes" - ) - if self.reference_doctype in advance_payment_doctypes: - ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name) - ref_doc.set_advance_payment_status() - def _allocate_payment_request_to_pe_references(self, references): """ Allocate the Payment Request to the Payment Entry references based on\n diff --git a/erpnext/accounts/doctype/payment_request/test_payment_request.py b/erpnext/accounts/doctype/payment_request/test_payment_request.py index 4caffdb431a..053863babdc 100644 --- a/erpnext/accounts/doctype/payment_request/test_payment_request.py +++ b/erpnext/accounts/doctype/payment_request/test_payment_request.py @@ -284,8 +284,6 @@ class TestPaymentRequest(FrappeTestCase): def test_multiple_payment_if_partially_paid_for_same_currency(self): so = make_sales_order(currency="INR", qty=1, rate=1000) - self.assertEqual(so.advance_payment_status, "Not Requested") - pr = make_payment_request( dt="Sales Order", dn=so.name, @@ -300,7 +298,6 @@ class TestPaymentRequest(FrappeTestCase): self.assertEqual(pr.status, "Requested") so.load_from_db() - self.assertEqual(so.advance_payment_status, "Requested") # to make partial payment pe = pr.create_payment_entry(submit=False) @@ -311,7 +308,6 @@ class TestPaymentRequest(FrappeTestCase): self.assertEqual(pe.references[0].payment_request, pr.name) so.load_from_db() - self.assertEqual(so.advance_payment_status, "Partially Paid") pr.load_from_db() self.assertEqual(pr.status, "Partially Paid") @@ -327,7 +323,6 @@ class TestPaymentRequest(FrappeTestCase): self.assertEqual(pe.references[0].payment_request, pr.name) so.load_from_db() - self.assertEqual(so.advance_payment_status, "Fully Paid") pr.load_from_db() self.assertEqual(pr.status, "Paid") @@ -409,8 +404,6 @@ class TestPaymentRequest(FrappeTestCase): po.save() po.submit() - self.assertEqual(po.advance_payment_status, "Not Initiated") - pr = make_payment_request( dt="Purchase Order", dn=po.name, @@ -425,7 +418,6 @@ class TestPaymentRequest(FrappeTestCase): self.assertEqual(pr.status, "Initiated") po.load_from_db() - self.assertEqual(po.advance_payment_status, "Initiated") pe = pr.create_payment_entry() @@ -441,7 +433,6 @@ class TestPaymentRequest(FrappeTestCase): self.assertEqual(pe.references[1].payment_request, pr.name) po.load_from_db() - self.assertEqual(po.advance_payment_status, "Fully Paid") pr.load_from_db() self.assertEqual(pr.status, "Paid") @@ -491,7 +482,6 @@ class TestPaymentRequest(FrappeTestCase): def test_payment_cancel_process(self): so = make_sales_order(currency="INR", qty=1, rate=1000) - self.assertEqual(so.advance_payment_status, "Not Requested") pr = make_payment_request( dt="Sales Order", @@ -506,7 +496,6 @@ class TestPaymentRequest(FrappeTestCase): self.assertEqual(pr.outstanding_amount, pr.grand_total) so.load_from_db() - self.assertEqual(so.advance_payment_status, "Requested") pe = pr.create_payment_entry(submit=False) pe.paid_amount = 800 @@ -516,7 +505,6 @@ class TestPaymentRequest(FrappeTestCase): self.assertEqual(pe.references[0].payment_request, pr.name) so.load_from_db() - self.assertEqual(so.advance_payment_status, "Partially Paid") pr.load_from_db() self.assertEqual(pr.status, "Partially Paid") @@ -532,4 +520,3 @@ class TestPaymentRequest(FrappeTestCase): self.assertEqual(pr.grand_total, 1000) so.load_from_db() - self.assertEqual(so.advance_payment_status, "Requested") diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index b248d9f5aba..1e0745b7861 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1968,37 +1968,6 @@ class AccountsController(TransactionBase): self.db_set("advance_paid", advance_paid) - self.set_advance_payment_status() - - def set_advance_payment_status(self): - new_status = None - - paid_amount = frappe.get_value( - doctype="Payment Request", - filters={ - "reference_doctype": self.doctype, - "reference_name": self.name, - "docstatus": 1, - }, - fieldname="sum(grand_total - outstanding_amount)", - ) - - if not paid_amount: - if self.doctype in frappe.get_hooks("advance_payment_receivable_doctypes"): - new_status = "Not Requested" if paid_amount is None else "Requested" - elif self.doctype in frappe.get_hooks("advance_payment_payable_doctypes"): - new_status = "Not Initiated" if paid_amount is None else "Initiated" - else: - total_amount = self.get("rounded_total") or self.get("grand_total") - new_status = "Fully Paid" if paid_amount == total_amount else "Partially Paid" - - if new_status == self.advance_payment_status: - return - - self.db_set("advance_payment_status", new_status, update_modified=False) - self.set_status(update=True) - self.notify_update() - @property def company_abbr(self): if not hasattr(self, "_abbr"): diff --git a/erpnext/hooks.py b/erpnext/hooks.py index bd367d6d95e..30121e5f2cb 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -365,7 +365,6 @@ doc_events = { "Payment Entry": { "on_submit": [ "erpnext.regional.create_transaction_log", - "erpnext.accounts.doctype.payment_request.payment_request.update_payment_req_status", "erpnext.accounts.doctype.dunning.dunning.resolve_dunning", ], "on_cancel": ["erpnext.accounts.doctype.dunning.dunning.resolve_dunning"], From 770bc1c293b106c1bb3ec5077ce73c068973d179 Mon Sep 17 00:00:00 2001 From: Abdeali Chharchhoda Date: Sat, 28 Sep 2024 12:29:34 +0530 Subject: [PATCH 257/734] fix: Remove unreference method --- erpnext/accounts/doctype/payment_request/payment_request.py | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index 9fbc73afea7..6d1d279c244 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -231,7 +231,6 @@ class PaymentRequest(Document): def on_cancel(self): self.check_if_payment_entry_exists() self.set_as_cancelled() - self.update_reference_advance_payment_status() def make_invoice(self): ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name) From a1b6628c41ef965d855cd6890f0919871b4ca58d Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sun, 29 Sep 2024 09:46:14 +0530 Subject: [PATCH 258/734] fix: quality inspection creation (backport #43416) (#43417) fix: quality inspection creation (#43416) (cherry picked from commit a594c05296cae58ea546933f834dd3f5b651fd09) Co-authored-by: rohitwaghchaure --- erpnext/stock/doctype/quality_inspection/quality_inspection.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/stock/doctype/quality_inspection/quality_inspection.py b/erpnext/stock/doctype/quality_inspection/quality_inspection.py index 5dca440adbe..43efcbc8ca6 100644 --- a/erpnext/stock/doctype/quality_inspection/quality_inspection.py +++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.py @@ -245,6 +245,7 @@ class QualityInspection(Document): for i in range(1, 11): field = "reading_" + str(i) if reading.get(field) is None: + data[field] = 0.0 continue data[field] = parse_float(reading.get(field)) From d495d93840c809bfdf7a7109e6fe883eae0e5193 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sun, 29 Sep 2024 11:09:32 +0530 Subject: [PATCH 259/734] fix: use serial and batch fields (backport #43421) (#43423) fix: use serial and batch fields (#43421) (cherry picked from commit ca16089d9ddb692953ae6e493fff1019e9ab70f2) Co-authored-by: rohitwaghchaure --- erpnext/public/js/controllers/transaction.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 204c09299ad..851bd64f3b4 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -573,6 +573,15 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe callback: function(r) { if(!r.exc) { frappe.run_serially([ + () => { + if (item.docstatus === 0 + && frappe.meta.has_field(item.doctype, "use_serial_batch_fields") + && !item.use_serial_batch_fields + && cint(frappe.user_defaults?.use_serial_batch_fields) === 1 + ) { + item["use_serial_batch_fields"] = 1; + } + }, () => { var d = locals[cdt][cdn]; me.add_taxes_from_item_tax_template(d.item_tax_rate); From 4b3f143f8366881d8cc427d56a14d6c38403d688 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sun, 29 Sep 2024 21:18:25 +0530 Subject: [PATCH 260/734] fix: Data missing in table: None, MandatoryError (backport #43422) (#43429) fix: Data missing in table: None, MandatoryError (#43422) (cherry picked from commit 8e33e0e1d2df18d8401919e518cdf75e469d87a7) Co-authored-by: rohitwaghchaure --- erpnext/stock/doctype/stock_entry/stock_entry.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 4ee4c6b2930..627ad78be58 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -3148,11 +3148,13 @@ def get_available_materials(work_order) -> dict: if row.serial_no: for serial_no in get_serial_nos(row.serial_no): - item_data.serial_nos.remove(serial_no) + if serial_no in item_data.serial_nos: + item_data.serial_nos.remove(serial_no) elif row.serial_nos: for serial_no in get_serial_nos(row.serial_nos): - item_data.serial_nos.remove(serial_no) + if serial_no in item_data.serial_nos: + item_data.serial_nos.remove(serial_no) return available_materials @@ -3270,6 +3272,9 @@ def create_serial_and_batch_bundle(parent_doc, row, child, type_of_transaction=N for batch_no, qty in row.batches_to_be_consume.items(): doc.append("entries", {"batch_no": batch_no, "warehouse": row.warehouse, "qty": qty * -1}) + if not doc.entries: + return None + return doc.insert(ignore_permissions=True).name From 2c4610c021dcbc53af5f7f40a0682ab3d2590711 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sun, 29 Sep 2024 21:18:25 +0530 Subject: [PATCH 261/734] fix: Data missing in table: None, MandatoryError (backport #43422) (#43429) fix: Data missing in table: None, MandatoryError (#43422) (cherry picked from commit 8e33e0e1d2df18d8401919e518cdf75e469d87a7) Co-authored-by: rohitwaghchaure (cherry picked from commit 4b3f143f8366881d8cc427d56a14d6c38403d688) --- erpnext/stock/doctype/stock_entry/stock_entry.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 4ee4c6b2930..627ad78be58 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -3148,11 +3148,13 @@ def get_available_materials(work_order) -> dict: if row.serial_no: for serial_no in get_serial_nos(row.serial_no): - item_data.serial_nos.remove(serial_no) + if serial_no in item_data.serial_nos: + item_data.serial_nos.remove(serial_no) elif row.serial_nos: for serial_no in get_serial_nos(row.serial_nos): - item_data.serial_nos.remove(serial_no) + if serial_no in item_data.serial_nos: + item_data.serial_nos.remove(serial_no) return available_materials @@ -3270,6 +3272,9 @@ def create_serial_and_batch_bundle(parent_doc, row, child, type_of_transaction=N for batch_no, qty in row.batches_to_be_consume.items(): doc.append("entries", {"batch_no": batch_no, "warehouse": row.warehouse, "qty": qty * -1}) + if not doc.entries: + return None + return doc.insert(ignore_permissions=True).name From 86b10ce9bbfbf4279d52df9b17c660e6baeda934 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sun, 29 Sep 2024 22:06:50 +0530 Subject: [PATCH 262/734] fix: tests for work order consumption (backport #41814) (#43430) fix: tests for work order consumption (#41814) * fix: tests for work order automatic SABB creation * fix: qty * chore: show created sabb * chore: fix syntax * fix: check SABB qty * fix: add batched consumable to manufacture * fix: missing fg qty field * fix: improve test debug * chore: linting * chore: removed extra hash icons --------- Co-authored-by: Rohit Waghchaure (cherry picked from commit ca3c6809098e4383db4f6ac2e3243c13da2970cf) Co-authored-by: Richard Case <110036763+casesolved-co-uk@users.noreply.github.com> --- .../doctype/work_order/test_work_order.py | 214 ++++++++++++++++++ 1 file changed, 214 insertions(+) diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py index ebc400979b6..6ee3f73eb08 100644 --- a/erpnext/manufacturing/doctype/work_order/test_work_order.py +++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py @@ -1531,6 +1531,197 @@ class TestWorkOrder(FrappeTestCase): self.assertFalse(serial_nos) + def test_backflushed_batch_raw_materials_based_on_transferred_autosabb(self): + frappe.db.set_single_value( + "Manufacturing Settings", + "backflush_raw_materials_based_on", + "Material Transferred for Manufacture", + ) + + batch_item = "Test Batch MCC Keyboard" + fg_item = "Test FG Item with Batch Raw Materials" + + ste_doc = test_stock_entry.make_stock_entry( + item_code=batch_item, target="Stores - _TC", qty=8, basic_rate=100, do_not_save=True + ) + + # Inward raw materials in Stores warehouse + ste_doc.submit() + ste_doc.reload() + + batch_no = get_batch_from_bundle(ste_doc.items[0].serial_and_batch_bundle) + + wo_doc = make_wo_order_test_record(production_item=fg_item, qty=4) + # action taken upon Start button: + transferred_ste_doc = frappe.get_doc( + make_stock_entry(wo_doc.name, "Material Transfer for Manufacture", 4) + ) + + transferred_ste_doc.submit() + transferred_ste_doc.reload() + + self.assertTrue(transferred_ste_doc.items[0].serial_and_batch_bundle) + self.assertEqual( + get_batch_from_bundle(transferred_ste_doc.items[0].serial_and_batch_bundle), batch_no + ) + self.assertEqual(transferred_ste_doc.items[0].qty, 4.0) + + # Make additional consumption and link to WO + test_stock_entry.make_stock_entry( + item_code="Test Batch Battery Consumable", + target="Stores - _TC", + qty=8, + basic_rate=2.33, + ) + consume_use_doc = test_stock_entry.make_stock_entry( + item_code="Test Batch Battery Consumable", # consumable not linked to BOM + source="Stores - _TC", + qty=4, + purpose="Material Consumption for Manufacture", + do_not_save=True, + ) + consume_use_doc.work_order = wo_doc.name + consume_use_doc.fg_completed_qty = 4 + consume_use_doc.submit() + consume_use_doc.reload() + + manufacture_ste_doc = frappe.get_doc(make_stock_entry(wo_doc.name, "Manufacture", 4)) + mfr_items = [i.as_dict() for i in manufacture_ste_doc.items] + manufacture_ste_doc.submit() + manufacture_ste_doc.reload() + + self.assertTrue(len(mfr_items), 2) + self.assertTrue(manufacture_ste_doc.items[0].serial_and_batch_bundle) + self.assertEqual( + get_batch_from_bundle(manufacture_ste_doc.items[0].serial_and_batch_bundle), batch_no + ) + self.assertEqual(manufacture_ste_doc.items[0].qty, 4.0) + + def test_backflushed_serial_no_raw_materials_based_on_transferred_autosabb(self): + frappe.db.set_single_value( + "Manufacturing Settings", + "backflush_raw_materials_based_on", + "Material Transferred for Manufacture", + ) + + sn_item = "Test Serial No BTT Headphone" + fg_item = "Test FG Item with Serial No Raw Materials" + + ste_doc = test_stock_entry.make_stock_entry( + item_code=sn_item, target="Stores - _TC", qty=4, basic_rate=100, do_not_save=True + ) + + # Inward raw materials in Stores warehouse + ste_doc.submit() + ste_doc.reload() + + serial_nos_list = sorted(get_serial_nos_from_bundle(ste_doc.items[0].serial_and_batch_bundle)) + + wo_doc = make_wo_order_test_record(production_item=fg_item, qty=4) + transferred_ste_doc = frappe.get_doc( + make_stock_entry(wo_doc.name, "Material Transfer for Manufacture", 4) + ) + + transferred_ste_doc.submit() + transferred_ste_doc.reload() + + self.assertTrue(transferred_ste_doc.items[0].serial_and_batch_bundle) + self.assertEqual( + sorted(get_serial_nos_from_bundle(transferred_ste_doc.items[0].serial_and_batch_bundle)), + serial_nos_list, + ) + self.assertEqual(transferred_ste_doc.items[0].qty, 4.0) + + # Make additional consumption and link to WO + test_stock_entry.make_stock_entry( + item_code="Test Serial Battery Consumable", + target="Stores - _TC", + qty=8, + basic_rate=3.33, + ) + consume_use_doc = test_stock_entry.make_stock_entry( + item_code="Test Serial Battery Consumable", # consumable not linked to BOM + source="Stores - _TC", + qty=4, + purpose="Material Consumption for Manufacture", + do_not_save=True, + ) + consume_use_doc.work_order = wo_doc.name + consume_use_doc.fg_completed_qty = 4 + consume_use_doc.submit() + consume_use_doc.reload() + + manufacture_ste_doc = frappe.get_doc(make_stock_entry(wo_doc.name, "Manufacture", 4)) + mfr_items = [i.as_dict() for i in manufacture_ste_doc.items] + manufacture_ste_doc.submit() + manufacture_ste_doc.reload() + + self.assertTrue(len(mfr_items), 2) + self.assertTrue(manufacture_ste_doc.items[0].serial_and_batch_bundle) + self.assertEqual( + sorted(get_serial_nos_from_bundle(manufacture_ste_doc.items[0].serial_and_batch_bundle)), + serial_nos_list, + ) + self.assertEqual(manufacture_ste_doc.items[0].qty, 4.0) + + def test_backflushed_serial_no_batch_raw_materials_based_on_transferred_autosabb(self): + frappe.db.set_single_value( + "Manufacturing Settings", + "backflush_raw_materials_based_on", + "Material Transferred for Manufacture", + ) + + sn_batch_item = "Test Batch Serial No WebCam" + fg_item = "Test FG Item with Serial & Batch No Raw Materials" + + ste_doc = test_stock_entry.make_stock_entry( + item_code=sn_batch_item, target="Stores - _TC", qty=4, basic_rate=100, do_not_save=True + ) + + ste_doc.submit() + ste_doc.reload() + + serial_nos_list = sorted(get_serial_nos_from_bundle(ste_doc.items[0].serial_and_batch_bundle)) + batch_no = get_batch_from_bundle(ste_doc.items[0].serial_and_batch_bundle) + + wo_doc = make_wo_order_test_record(production_item=fg_item, qty=4) + transferred_ste_doc = frappe.get_doc( + make_stock_entry(wo_doc.name, "Material Transfer for Manufacture", 4) + ) + + transferred_ste_doc.submit() + transferred_ste_doc.reload() + + self.assertTrue(transferred_ste_doc.items[0].serial_and_batch_bundle) + self.assertEqual( + sorted(get_serial_nos_from_bundle(transferred_ste_doc.items[0].serial_and_batch_bundle)), + serial_nos_list, + ) + self.assertEqual( + get_batch_from_bundle(transferred_ste_doc.items[0].serial_and_batch_bundle), batch_no + ) + self.assertEqual(transferred_ste_doc.items[0].qty, 4.0) + + manufacture_ste_doc = frappe.get_doc(make_stock_entry(wo_doc.name, "Manufacture", 4)) + manufacture_ste_doc.submit() + manufacture_ste_doc.reload() + + self.assertTrue(manufacture_ste_doc.items[0].serial_and_batch_bundle) + self.assertEqual( + sorted(get_serial_nos_from_bundle(manufacture_ste_doc.items[0].serial_and_batch_bundle)), + serial_nos_list, + ) + self.assertEqual( + get_batch_from_bundle(manufacture_ste_doc.items[0].serial_and_batch_bundle), batch_no + ) + self.assertEqual(manufacture_ste_doc.items[0].qty, 4.0) + + bundle = manufacture_ste_doc.items[0].serial_and_batch_bundle + bundle_doc = frappe.get_doc("Serial and Batch Bundle", bundle) + qty = sum(e.qty for e in bundle_doc.entries) + self.assertEqual(qty, -4.0) + + ### def test_non_consumed_material_return_against_work_order(self): frappe.db.set_single_value( "Manufacturing Settings", @@ -2335,6 +2526,29 @@ def prepare_data_for_backflush_based_on_materials_transferred(): make_bom(item=item.name, source_warehouse="Stores - _TC", raw_materials=[batch_item_doc.name]) + # Make additional items not attached to a BOM + make_item( + "Test Batch Battery Consumable", + { + "is_stock_item": 1, + "has_batch_no": 1, + "create_new_batch": 1, + "batch_number_series": "TBMK.#####", + "valuation_rate": 2.33, + "stock_uom": "Nos", + }, + ) + make_item( + "Test Serial Battery Consumable", + { + "is_stock_item": 1, + "has_serial_no": 1, + "serial_no_series": "TSBH.#####", + "valuation_rate": 3.33, + "stock_uom": "Nos", + }, + ) + sn_item_doc = make_item( "Test Serial No BTT Headphone", { From 2d09ef250936bac9b46dc7b5050da9534a73b103 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Sun, 29 Sep 2024 16:38:13 +0000 Subject: [PATCH 263/734] chore(release): Bumped to Version 15.36.4 ## [15.36.4](https://github.com/frappe/erpnext/compare/v15.36.3...v15.36.4) (2024-09-29) ### Bug Fixes * Data missing in table: None, MandatoryError (backport [#43422](https://github.com/frappe/erpnext/issues/43422)) ([#43429](https://github.com/frappe/erpnext/issues/43429)) ([2c4610c](https://github.com/frappe/erpnext/commit/2c4610c021dcbc53af5f7f40a0682ab3d2590711)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 34d07c1e23b..aab2aa1480a 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.36.3" +__version__ = "15.36.4" def get_default_company(user=None): From 2984bad2c09061c949a55d8d62f0fa39bbbbdfd2 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Sun, 29 Sep 2024 09:18:52 +0530 Subject: [PATCH 264/734] fix: Stock Ledger Invariant Check report (cherry picked from commit d7daedc5b21a952c0bfa46079595f016179bcca0) --- .../stock_ledger_invariant_check.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.py b/erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.py index fb392f7e36a..6fc109f1c6e 100644 --- a/erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.py +++ b/erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.py @@ -52,7 +52,7 @@ def add_invariant_check_fields(sles): balance_qty = 0.0 balance_stock_value = 0.0 for idx, sle in enumerate(sles): - queue = json.loads(sle.stock_queue) + queue = json.loads(sle.stock_queue) if sle.stock_queue else [] fifo_qty = 0.0 fifo_value = 0.0 From 50e47e796dc4a91fb4b3e653fb5cc679c58916cf Mon Sep 17 00:00:00 2001 From: Kitti U Date: Fri, 6 Sep 2024 12:53:47 +0700 Subject: [PATCH 265/734] feat: provide hook point for bulk transaction tasks (cherry picked from commit d4dd01d8d142f10068aa76a3fe2aedb84820a151) --- erpnext/utilities/bulk_transaction.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/erpnext/utilities/bulk_transaction.py b/erpnext/utilities/bulk_transaction.py index 4319fa7d6ea..7ba687941c9 100644 --- a/erpnext/utilities/bulk_transaction.py +++ b/erpnext/utilities/bulk_transaction.py @@ -137,6 +137,11 @@ def task(doc_name, from_doctype, to_doctype): }, "Purchase Receipt": {"Purchase Invoice": purchase_receipt.make_purchase_invoice}, } + + hooks = frappe.get_hooks("bulk_transaction_task_mapper") + for hook in hooks: + mapper.update(frappe.get_attr(hook)()) + frappe.flags.bulk_transaction = True if to_doctype in ["Payment Entry"]: obj = mapper[from_doctype][to_doctype](from_doctype, doc_name) From 21a01575b66aa5f620974f171fbbdd7c1d335fe0 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 30 Sep 2024 12:05:17 +0530 Subject: [PATCH 266/734] fix: 'NoneType' object has no attribute 'has_serial_no' (cherry picked from commit 28f9fd2507861ea72afd0853312ea04a7e42e3f8) --- erpnext/controllers/stock_controller.py | 12 ++++++++++++ .../stock_reconciliation/stock_reconciliation.py | 7 +++++++ 2 files changed, 19 insertions(+) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 1620fa3bfff..9fb0cc88cfb 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -64,6 +64,18 @@ class StockController(AccountsController): self.validate_internal_transfer() self.validate_putaway_capacity() + def validate_items_exist(self): + if not self.get("items"): + return + + items = [d.item_code for d in self.get("items")] + + exists_items = frappe.get_all("Item", filters={"name": ("in", items)}, pluck="name") + non_exists_items = set(items) - set(exists_items) + + if non_exists_items: + frappe.throw(_("Items {0} do not exist in the Item master.").format(", ".join(non_exists_items))) + def validate_duplicate_serial_and_batch_bundle(self, table_name): if not self.get(table_name): return diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index 93e3e69729b..00f5bcf30f1 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -61,6 +61,7 @@ class StockReconciliation(StockController): self.head_row = ["Item Code", "Warehouse", "Quantity", "Valuation Rate"] def validate(self): + self.validate_items_exist() if not self.expense_account: self.expense_account = frappe.get_cached_value( "Company", self.company, "stock_adjustment_account" @@ -162,6 +163,9 @@ class StockReconciliation(StockController): def set_current_serial_and_batch_bundle(self, voucher_detail_no=None, save=False) -> None: """Set Serial and Batch Bundle for each item""" for item in self.items: + if not frappe.db.exists("Item", item.item_code): + frappe.throw(_("Item {0} does not exist").format(item.item_code)) + if not item.reconcile_all_serial_batch and item.serial_and_batch_bundle: bundle = self.get_bundle_for_specific_serial_batch(item) item.current_serial_and_batch_bundle = bundle.name @@ -357,6 +361,9 @@ class StockReconciliation(StockController): def set_new_serial_and_batch_bundle(self): for item in self.items: + if not item.item_code: + continue + if item.use_serial_batch_fields: continue From c551c2714c97da39541d3d47f8a9d0f9ecaa20e3 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 30 Sep 2024 13:38:54 +0530 Subject: [PATCH 267/734] fix: adjustmen entry for stock reco (cherry picked from commit 4e463b7d6deffc33bdd793322cfaf82c0e86fb63) --- .../stock/doctype/stock_reconciliation/stock_reconciliation.py | 2 +- erpnext/stock/stock_ledger.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index 00f5bcf30f1..8ee3d9b3901 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -691,7 +691,7 @@ class StockReconciliation(StockController): 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 + row.item_code, row.warehouse, self.posting_date, self.posting_time, self.name ) if not difference_amount: diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 49a3a3335d1..b167ccf6df8 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -865,7 +865,7 @@ class update_entries_after: sle.stock_value = self.wh_data.stock_value sle.stock_queue = json.dumps(self.wh_data.stock_queue) - if not sle.is_adjustment_entry or not self.args.get("sle_id"): + if not sle.is_adjustment_entry: sle.stock_value_difference = stock_value_difference sle.doctype = "Stock Ledger Entry" From 30fd11f138ce468f5c01f3c5a265eca071293b30 Mon Sep 17 00:00:00 2001 From: Abdeali Chharchhoda Date: Mon, 30 Sep 2024 17:20:42 +0530 Subject: [PATCH 268/734] fix: Add removed test code `https://github.com/frappe/erpnext/commit/b41f10c1b98b01a181a6f9dbdf2531b108dc3bae` --- .../payment_request/test_payment_request.py | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/erpnext/accounts/doctype/payment_request/test_payment_request.py b/erpnext/accounts/doctype/payment_request/test_payment_request.py index 053863babdc..6644254ae7e 100644 --- a/erpnext/accounts/doctype/payment_request/test_payment_request.py +++ b/erpnext/accounts/doctype/payment_request/test_payment_request.py @@ -3,6 +3,7 @@ import re import unittest +from unittest.mock import patch import frappe from frappe.tests.utils import FrappeTestCase @@ -17,6 +18,8 @@ from erpnext.setup.utils import get_exchange_rate test_dependencies = ["Currency Exchange", "Journal Entry", "Contact", "Address"] +PAYMENT_URL = "https://example.com/payment" + payment_gateway = {"doctype": "Payment Gateway", "gateway": "_Test Gateway"} payment_method = [ @@ -49,6 +52,28 @@ class TestPaymentRequest(FrappeTestCase): ): frappe.get_doc(method).insert(ignore_permissions=True) + send_email = patch( + "erpnext.accounts.doctype.payment_request.payment_request.PaymentRequest.send_email", + return_value=None, + ) + self.send_email = send_email.start() + self.addCleanup(send_email.stop) + get_payment_url = patch( + # this also shadows one (1) call to _get_payment_gateway_controller + "erpnext.accounts.doctype.payment_request.payment_request.PaymentRequest.get_payment_url", + return_value=PAYMENT_URL, + ) + self.get_payment_url = get_payment_url.start() + self.addCleanup(get_payment_url.stop) + _get_payment_gateway_controller = patch( + "erpnext.accounts.doctype.payment_request.payment_request._get_payment_gateway_controller", + ) + self._get_payment_gateway_controller = _get_payment_gateway_controller.start() + self.addCleanup(_get_payment_gateway_controller.stop) + + def tearDown(self): + frappe.db.rollback() + def test_payment_request_linkings(self): so_inr = make_sales_order(currency="INR", do_not_save=True) so_inr.disable_rounded_total = 1 From e730b8c6e4e38d241f522108519767e4db4cc048 Mon Sep 17 00:00:00 2001 From: David Date: Mon, 25 Mar 2024 16:11:10 +0100 Subject: [PATCH 269/734] fix(return): set default return warehouse This captures the case of manual modifications to the return and ensures that by default, the correct return warehouse will be set (cherry picked from commit fa65291e9877464aa650371c21c9a355f91483c3) --- erpnext/controllers/sales_and_purchase_return.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py index 3755506b6f5..8e80b15e972 100644 --- a/erpnext/controllers/sales_and_purchase_return.py +++ b/erpnext/controllers/sales_and_purchase_return.py @@ -335,6 +335,9 @@ def make_return_doc(doctype: str, source_name: str, target_doc=None, return_agai doc.select_print_heading = frappe.get_cached_value("Print Heading", _("Debit Note")) if source.tax_withholding_category: doc.set_onload("supplier_tds", source.tax_withholding_category) + elif doctype == "Delivery Note": + # manual additions to the return should hit the return warehous, too + doc.set_warehouse = default_warehouse_for_sales_return for tax in doc.get("taxes") or []: if tax.charge_type == "Actual": From 67bd540135c246a658e0f081db62e01eb4eacc7f Mon Sep 17 00:00:00 2001 From: Abdeali Chharchhoda Date: Mon, 30 Sep 2024 18:21:59 +0530 Subject: [PATCH 270/734] test: Removed initial PR status `assertion` --- .../accounts/doctype/payment_request/test_payment_request.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/erpnext/accounts/doctype/payment_request/test_payment_request.py b/erpnext/accounts/doctype/payment_request/test_payment_request.py index 6644254ae7e..e9f8f25ff2d 100644 --- a/erpnext/accounts/doctype/payment_request/test_payment_request.py +++ b/erpnext/accounts/doctype/payment_request/test_payment_request.py @@ -320,7 +320,6 @@ class TestPaymentRequest(FrappeTestCase): self.assertEqual(pr.grand_total, 1000) self.assertEqual(pr.outstanding_amount, pr.grand_total) self.assertEqual(pr.party_account_currency, pr.currency) # INR - self.assertEqual(pr.status, "Requested") so.load_from_db() @@ -485,7 +484,6 @@ class TestPaymentRequest(FrappeTestCase): self.assertEqual(pr.outstanding_amount, 10000) self.assertEqual(pr.currency, "USD") self.assertEqual(pr.party_account_currency, "INR") - self.assertEqual(pr.status, "Requested") pe = pr.create_payment_entry() self.assertEqual(len(pe.references), 2) @@ -516,7 +514,6 @@ class TestPaymentRequest(FrappeTestCase): return_doc=1, ) - self.assertEqual(pr.status, "Requested") self.assertEqual(pr.grand_total, 1000) self.assertEqual(pr.outstanding_amount, pr.grand_total) From 96d8b5242dd0bfa8ac48450501c32348ab4154c7 Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Thu, 26 Sep 2024 16:49:01 +0530 Subject: [PATCH 271/734] feat: added 'cost of new capitalized asset' column (cherry picked from commit 1eb9cc33fc298c90ef5a04e5de2ee4dec67f941a) (cherry picked from commit 27cd51e267b3a11ab474ca085b5a0dc6cef87fad) --- .../asset_depreciations_and_balances.py | 40 ++++++++++++++++--- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py index 64e6a340464..cdd5baf3240 100644 --- a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py +++ b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py @@ -36,8 +36,9 @@ def get_group_by_asset_category_data(filters): + flt(row.cost_of_new_purchase) - flt(row.cost_of_sold_asset) - flt(row.cost_of_scrapped_asset) + - flt(row.cost_of_capitalized_asset) ) - # Update row with corresponding asset data + row.update( next( asset @@ -111,13 +112,24 @@ def get_asset_categories_for_grouped_by_category(filters): end else 0 - end), 0) as cost_of_scrapped_asset + end), 0) as cost_of_scrapped_asset, + ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 + and a.disposal_date >= %(from_date)s + and a.disposal_date <= %(to_date)s then + case when a.status = "Capitalized" then + a.gross_purchase_amount + else + 0 + end + else + 0 + end), 0) as cost_of_capitalized_asset from `tabAsset` a where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s {condition} and not exists( select 1 from `tabAsset Capitalization Asset Item` acai join `tabAsset Capitalization` ac on acai.parent=ac.name where acai.asset = a.name - and ac.posting_date <= %(to_date)s + and ac.posting_date < %(from_date)s and ac.docstatus=1 ) group by a.asset_category @@ -179,13 +191,24 @@ def get_asset_details_for_grouped_by_category(filters): end else 0 - end), 0) as cost_of_scrapped_asset + end), 0) as cost_of_scrapped_asset, + ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 + and a.disposal_date >= %(from_date)s + and a.disposal_date <= %(to_date)s then + case when a.status = "Capitalized" then + a.gross_purchase_amount + else + 0 + end + else + 0 + end), 0) as cost_of_capitalized_asset from `tabAsset` a where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s {condition} and not exists( select 1 from `tabAsset Capitalization Asset Item` acai join `tabAsset Capitalization` ac on acai.parent=ac.name where acai.asset = a.name - and ac.posting_date <= %(to_date)s + and ac.posting_date < %(from_date)s and ac.docstatus=1 ) group by a.name @@ -217,6 +240,7 @@ def get_group_by_asset_data(filters): + flt(row.cost_of_new_purchase) - flt(row.cost_of_sold_asset) - flt(row.cost_of_scrapped_asset) + - flt(row.cost_of_capitalized_asset) ) row.update(next(asset for asset in assets if asset["asset"] == asset_detail.get("name", ""))) @@ -445,6 +469,12 @@ def get_columns(filters): "fieldtype": "Currency", "width": 140, }, + { + "label": _("Cost of New Capitalized Asset"), + "fieldname": "cost_of_capitalized_asset", + "fieldtype": "Currency", + "width": 140, + }, { "label": _("Cost as on") + " " + formatdate(filters.to_date), "fieldname": "cost_as_on_to_date", From 01f9139ebdfd8c1a88dbfb292ec225ef9524e3b2 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Mon, 30 Sep 2024 17:21:09 +0000 Subject: [PATCH 272/734] chore(release): Bumped to Version 15.37.0 # [15.37.0](https://github.com/frappe/erpnext/compare/v15.36.4...v15.37.0) (2024-09-30) ### Features * added 'cost of new capitalized asset' column ([96d8b52](https://github.com/frappe/erpnext/commit/96d8b5242dd0bfa8ac48450501c32348ab4154c7)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index aab2aa1480a..c240944687b 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.36.4" +__version__ = "15.37.0" def get_default_company(user=None): From 5fc59349421e739cea28aab29b437678808a4224 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 30 Sep 2024 22:18:49 +0200 Subject: [PATCH 273/734] fix(Item): error message on tax rate (backport #42955) (#42956) Co-authored-by: barredterra <14891507+barredterra@users.noreply.github.com> --- erpnext/stock/doctype/item/item.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index 1ceb949d691..751a37e3164 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -345,7 +345,13 @@ class Item(Document): def validate_item_tax_net_rate_range(self): for tax in self.get("taxes"): if flt(tax.maximum_net_rate) < flt(tax.minimum_net_rate): - frappe.throw(_("Row #{0}: Maximum Net Rate cannot be greater than Minimum Net Rate")) + frappe.throw( + _("Taxes row #{0}: {1} cannot be smaller than {2}").format( + tax.idx, + bold(_(tax.meta.get_label("maximum_net_rate"))), + bold(_(tax.meta.get_label("minimum_net_rate"))), + ) + ) def update_template_tables(self): template = frappe.get_cached_doc("Item", self.variant_of) From 7bf6251c21a5249c1c3ade63b29b25d1cb597433 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 1 Oct 2024 10:34:48 +0530 Subject: [PATCH 274/734] fix: negative stock error for batch (backport #43450) (#43454) fix: negative stock error for batch (#43450) (cherry picked from commit 912ba7789c9178c596cc38e253e316f3bd6cd9ce) Co-authored-by: rohitwaghchaure --- .../serial_and_batch_bundle.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py index a1944732fb6..62f95f5b2e8 100644 --- a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py +++ b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py @@ -280,7 +280,7 @@ class SerialandBatchBundle(Document): ) def validate_negative_batch(self, batch_no, available_qty): - if available_qty < 0: + if available_qty < 0 and not self.is_stock_reco_for_valuation_adjustment(available_qty): msg = f"""Batch No {bold(batch_no)} of an Item {bold(self.item_code)} has negative stock of quantity {bold(available_qty)} in the @@ -288,6 +288,18 @@ class SerialandBatchBundle(Document): frappe.throw(_(msg), BatchNegativeStockError) + def is_stock_reco_for_valuation_adjustment(self, available_qty): + if ( + self.voucher_type == "Stock Reconciliation" + and self.type_of_transaction == "Outward" + and self.voucher_detail_no + and abs(frappe.db.get_value("Stock Reconciliation Item", self.voucher_detail_no, "qty")) + == abs(available_qty) + ): + return True + + return False + def get_sle_for_outward_transaction(self): sle = frappe._dict( { From 4fc6d3ef64df33303078b624cba6ca09cea03845 Mon Sep 17 00:00:00 2001 From: Nihantra Patel Date: Tue, 1 Oct 2024 10:50:43 +0530 Subject: [PATCH 275/734] fix: add company filter in Warehouse wise Item Balance Age and Value (cherry picked from commit 75950f86cff9e456693947b530ce257d354648bd) --- .../warehouse_wise_item_balance_age_and_value.js | 15 +++++++++++++++ .../warehouse_wise_item_balance_age_and_value.py | 2 -- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.js b/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.js index 137c786e44b..ac1ecfff530 100644 --- a/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.js +++ b/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.js @@ -3,6 +3,15 @@ frappe.query_reports["Warehouse wise Item Balance Age and Value"] = { filters: [ + { + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + width: "80", + options: "Company", + reqd: 1, + default: frappe.defaults.get_user_default("Company"), + }, { fieldname: "from_date", label: __("From Date"), @@ -39,6 +48,12 @@ frappe.query_reports["Warehouse wise Item Balance Age and Value"] = { fieldtype: "Link", width: "80", options: "Warehouse", + get_query: function () { + const company = frappe.query_report.get_filter_value("company"); + return { + filters: { company: company }, + }; + }, }, { fieldname: "filter_total_zero_qty", diff --git a/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py b/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py index 0df8b6ddb48..68caff40356 100644 --- a/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py +++ b/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py @@ -109,8 +109,6 @@ def validate_filters(filters): sle_count = flt(frappe.qb.from_("Stock Ledger Entry").select(Count("name")).run()[0][0]) if sle_count > 500000: frappe.throw(_("Please set filter based on Item or Warehouse")) - if not filters.get("company"): - filters["company"] = frappe.defaults.get_user_default("Company") def get_warehouse_list(filters): From ee2c8c869ad19a5d5e629b5598f508a9cc4927e9 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 1 Oct 2024 12:54:26 +0530 Subject: [PATCH 276/734] fix: last purchase rate for purchase invoice (backport #43448) (#43452) * fix: last purchase rate for purchase invoice (cherry picked from commit fb9d10663388431644ac1798ba1f9363d9db2775) # Conflicts: # erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py * chore: fix conflicts --------- Co-authored-by: Rohit Waghchaure --- .../purchase_invoice/test_purchase_invoice.py | 18 +++++ erpnext/controllers/buying_controller.py | 8 ++- erpnext/public/js/controllers/transaction.js | 4 ++ erpnext/stock/doctype/item/item.py | 71 ++++++++++++------- 4 files changed, 71 insertions(+), 30 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index 31143fb72b8..cf5bfedaebd 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -2292,6 +2292,24 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin): frappe.db.set_single_value("Buying Settings", "maintain_same_rate", 1) + def test_last_purchase_rate(self): + item = create_item("_Test Item For Last Purchase Rate from PI", is_stock_item=1) + pi1 = make_purchase_invoice(item_code=item.item_code, qty=10, rate=100) + item.reload() + self.assertEqual(item.last_purchase_rate, 100) + + pi2 = make_purchase_invoice(item_code=item.item_code, qty=10, rate=200) + item.reload() + self.assertEqual(item.last_purchase_rate, 200) + + pi2.cancel() + item.reload() + self.assertEqual(item.last_purchase_rate, 100) + + pi1.cancel() + item.reload() + self.assertEqual(item.last_purchase_rate, 0) + def set_advance_flag(company, flag, default_account): frappe.db.set_value( diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index 31b6f391ba4..e9e7ef62670 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -702,9 +702,11 @@ class BuyingController(SubcontractingController): if self.get("is_return"): return - if self.doctype in ["Purchase Order", "Purchase Receipt"] and not frappe.db.get_single_value( - "Buying Settings", "disable_last_purchase_rate" - ): + if self.doctype in [ + "Purchase Order", + "Purchase Receipt", + "Purchase Invoice", + ] and not frappe.db.get_single_value("Buying Settings", "disable_last_purchase_rate"): update_last_purchase_rate(self, is_submit=0) if self.doctype in ["Purchase Receipt", "Purchase Invoice"]: diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 851bd64f3b4..92e9e559ada 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -1277,6 +1277,10 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe .filter(Boolean).length > 0; } else if (this.frm.doc?.items) { let first_row = this.frm.doc.items[0]; + if (!first_row) { + return false + }; + let mapped_rows = mappped_fields.filter(d => first_row[d]) return mapped_rows?.length > 0; diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index 751a37e3164..e40b3822af6 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -22,6 +22,7 @@ from frappe.utils import ( strip_html, ) from frappe.utils.html_utils import clean_html +from pypika import Order import erpnext from erpnext.controllers.item_variant import ( @@ -1139,34 +1140,10 @@ def get_last_purchase_details(item_code, doc_name=None, conversion_rate=1.0): """returns last purchase details in stock uom""" # get last purchase order item details - last_purchase_order = frappe.db.sql( - """\ - select po.name, po.transaction_date, po.conversion_rate, - po_item.conversion_factor, po_item.base_price_list_rate, - po_item.discount_percentage, po_item.base_rate, po_item.base_net_rate - from `tabPurchase Order` po, `tabPurchase Order Item` po_item - where po.docstatus = 1 and po_item.item_code = %s and po.name != %s and - po.name = po_item.parent - order by po.transaction_date desc, po.name desc - limit 1""", - (item_code, cstr(doc_name)), - as_dict=1, - ) + last_purchase_order = get_purchase_voucher_details("Purchase Order", item_code, doc_name) # get last purchase receipt item details - last_purchase_receipt = frappe.db.sql( - """\ - select pr.name, pr.posting_date, pr.posting_time, pr.conversion_rate, - pr_item.conversion_factor, pr_item.base_price_list_rate, pr_item.discount_percentage, - pr_item.base_rate, pr_item.base_net_rate - from `tabPurchase Receipt` pr, `tabPurchase Receipt Item` pr_item - where pr.docstatus = 1 and pr_item.item_code = %s and pr.name != %s and - pr.name = pr_item.parent - order by pr.posting_date desc, pr.posting_time desc, pr.name desc - limit 1""", - (item_code, cstr(doc_name)), - as_dict=1, - ) + last_purchase_receipt = get_purchase_voucher_details("Purchase Receipt", item_code, doc_name) purchase_order_date = getdate( last_purchase_order and last_purchase_order[0].transaction_date or "1900-01-01" @@ -1187,7 +1164,13 @@ def get_last_purchase_details(item_code, doc_name=None, conversion_rate=1.0): purchase_date = purchase_receipt_date else: - return frappe._dict() + last_purchase_invoice = get_purchase_voucher_details("Purchase Invoice", item_code, doc_name) + + if last_purchase_invoice: + last_purchase = last_purchase_invoice[0] + purchase_date = getdate(last_purchase.posting_date) + else: + return frappe._dict() conversion_factor = flt(last_purchase.conversion_factor) out = frappe._dict( @@ -1213,6 +1196,40 @@ def get_last_purchase_details(item_code, doc_name=None, conversion_rate=1.0): return out +def get_purchase_voucher_details(doctype, item_code, document_name): + parent_doc = frappe.qb.DocType(doctype) + child_doc = frappe.qb.DocType(doctype + " Item") + + query = ( + frappe.qb.from_(parent_doc) + .inner_join(child_doc) + .on(parent_doc.name == child_doc.parent) + .select( + parent_doc.name, + parent_doc.conversion_rate, + child_doc.conversion_factor, + child_doc.base_price_list_rate, + child_doc.discount_percentage, + child_doc.base_rate, + child_doc.base_net_rate, + ) + .where(parent_doc.docstatus == 1) + .where(child_doc.item_code == item_code) + .where(parent_doc.name != document_name) + ) + + if doctype in ("Purchase Receipt", "Purchase Invoice"): + query = query.select(parent_doc.posting_date, parent_doc.posting_time) + query = query.orderby( + parent_doc.posting_date, parent_doc.posting_time, parent_doc.name, order=Order.desc + ) + else: + query = query.select(parent_doc.transaction_date) + query = query.orderby(parent_doc.transaction_date, parent_doc.name, order=Order.desc) + + return query.run(as_dict=1) + + def check_stock_uom_with_bin(item, stock_uom): if stock_uom == frappe.db.get_value("Item", item, "stock_uom"): return From 9c0a17e4d5dceff6e4ef0a90e1d36de762f8d3af Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 1 Oct 2024 14:26:38 +0530 Subject: [PATCH 277/734] fix: removed validation for materials return (backport #43461) (#43463) fix: removed validation for materials return (#43461) (cherry picked from commit 1c7154c7ca3df8b53be00e60b977e48ec9dd2ae3) Co-authored-by: rohitwaghchaure --- erpnext/stock/doctype/stock_entry/stock_entry.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 627ad78be58..ae6ca4e25d0 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -1594,10 +1594,6 @@ class StockEntry(StockController): if pro_doc.status == "Stopped": msg = f"Transaction not allowed against stopped Work Order {self.work_order}" - if self.is_return and pro_doc.status not in ["Completed", "Closed"]: - title = _("Stock Return") - msg = f"Work Order {self.work_order} must be completed or closed" - if msg: frappe.throw(_(msg), title=title) From 96c4d1af63e6f47a6fe4f27b085b7e09a889acbe Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 1 Oct 2024 14:27:27 +0530 Subject: [PATCH 278/734] Serial no report (backport #43444) (#43464) Serial no report (#43444) * chore: remove the field that which is not exiting in serial no * chore: remove the field that which is not exiting in serial no * chore: remove the field that which is not exiting in serial no (cherry picked from commit 661efadf410daa15eb4cdea7652aa6018b30633a) Co-authored-by: Vishv-silveroak <108357657+Vishv-024@users.noreply.github.com> --- .../serial_no_service_contract_expiry.json | 43 ++++++++++--------- .../serial_no_status/serial_no_status.json | 43 ++++++++++--------- .../serial_no_warranty_expiry.json | 43 ++++++++++--------- 3 files changed, 69 insertions(+), 60 deletions(-) diff --git a/erpnext/stock/report/serial_no_service_contract_expiry/serial_no_service_contract_expiry.json b/erpnext/stock/report/serial_no_service_contract_expiry/serial_no_service_contract_expiry.json index 6d20719f8eb..75e2fac98fd 100644 --- a/erpnext/stock/report/serial_no_service_contract_expiry/serial_no_service_contract_expiry.json +++ b/erpnext/stock/report/serial_no_service_contract_expiry/serial_no_service_contract_expiry.json @@ -1,30 +1,33 @@ { - "add_total_row": 0, - "apply_user_permissions": 1, - "creation": "2013-01-14 10:52:58", - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 3, - "is_standard": "Yes", - "json": "{\"add_total_row\": 0, \"sort_by\": \"Serial No.modified\", \"sort_order\": \"desc\", \"sort_by_next\": null, \"filters\": [[\"Serial No\", \"delivery_document_type\", \"in\", [\"Delivery Note\", \"Sales Invoice\"]], [\"Serial No\", \"warehouse\", \"=\", \"\"]], \"sort_order_next\": \"desc\", \"columns\": [[\"name\", \"Serial No\"], [\"item_code\", \"Serial No\"], [\"amc_expiry_date\", \"Serial No\"], [\"maintenance_status\", \"Serial No\"], [\"delivery_document_no\", \"Serial No\"], [\"customer\", \"Serial No\"], [\"customer_name\", \"Serial No\"], [\"item_name\", \"Serial No\"], [\"description\", \"Serial No\"], [\"item_group\", \"Serial No\"], [\"brand\", \"Serial No\"]]}", - "modified": "2017-02-24 20:02:00.706889", - "modified_by": "Administrator", - "module": "Stock", - "name": "Serial No Service Contract Expiry", - "owner": "Administrator", - "ref_doctype": "Serial No", - "report_name": "Serial No Service Contract Expiry", - "report_type": "Report Builder", + "add_total_row": 0, + "columns": [], + "creation": "2013-01-14 10:52:58", + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [], + "idx": 3, + "is_standard": "Yes", + "json": "{\"add_total_row\": 0, \"sort_by\": \"Serial No.modified\", \"sort_order\": \"desc\", \"sort_by_next\": null, \"filters\": [[\"Serial No\", \"warehouse\", \"=\", \"\"]], \"sort_order_next\": \"desc\", \"columns\": [[\"name\", \"Serial No\"], [\"item_code\", \"Serial No\"], [\"amc_expiry_date\", \"Serial No\"], [\"maintenance_status\", \"Serial No\"],[\"item_name\", \"Serial No\"], [\"description\", \"Serial No\"], [\"item_group\", \"Serial No\"], [\"brand\", \"Serial No\"]]}", + "letterhead": null, + "modified": "2024-09-26 13:07:23.451182", + "modified_by": "Administrator", + "module": "Stock", + "name": "Serial No Service Contract Expiry", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Serial No", + "report_name": "Serial No Service Contract Expiry", + "report_type": "Report Builder", "roles": [ { "role": "Item Manager" - }, + }, { "role": "Stock Manager" - }, + }, { "role": "Stock User" } ] -} \ No newline at end of file +} diff --git a/erpnext/stock/report/serial_no_status/serial_no_status.json b/erpnext/stock/report/serial_no_status/serial_no_status.json index 8f03567c852..d74c2087f72 100644 --- a/erpnext/stock/report/serial_no_status/serial_no_status.json +++ b/erpnext/stock/report/serial_no_status/serial_no_status.json @@ -1,30 +1,33 @@ { - "add_total_row": 0, - "apply_user_permissions": 1, - "creation": "2013-01-14 10:52:58", - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 4, - "is_standard": "Yes", - "json": "{\"add_total_row\": 0, \"sort_by\": \"Serial No.name\", \"sort_order\": \"desc\", \"sort_by_next\": null, \"filters\": [], \"sort_order_next\": \"desc\", \"columns\": [[\"name\", \"Serial No\"], [\"item_code\", \"Serial No\"], [\"warehouse\", \"Serial No\"], [\"item_name\", \"Serial No\"], [\"description\", \"Serial No\"], [\"item_group\", \"Serial No\"], [\"brand\", \"Serial No\"], [\"purchase_document_type\", \"Serial No\"], [\"purchase_document_no\", \"Serial No\"], [\"purchase_date\", \"Serial No\"], [\"customer\", \"Serial No\"], [\"customer_name\", \"Serial No\"], [\"purchase_rate\", \"Serial No\"], [\"delivery_document_type\", \"Serial No\"], [\"delivery_document_no\", \"Serial No\"], [\"delivery_date\", \"Serial No\"], [\"supplier\", \"Serial No\"], [\"supplier_name\", \"Serial No\"]]}", - "modified": "2017-02-24 19:54:21.392265", - "modified_by": "Administrator", - "module": "Stock", - "name": "Serial No Status", - "owner": "Administrator", - "ref_doctype": "Serial No", - "report_name": "Serial No Status", - "report_type": "Report Builder", + "add_total_row": 0, + "columns": [], + "creation": "2013-01-14 10:52:58", + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [], + "idx": 4, + "is_standard": "Yes", + "json": "{\"add_total_row\": 0, \"sort_by\": \"Serial No.name\", \"sort_order\": \"desc\", \"sort_by_next\": null, \"filters\": [], \"sort_order_next\": \"desc\", \"columns\": [[\"name\", \"Serial No\"], [\"item_code\", \"Serial No\"], [\"warehouse\", \"Serial No\"], [\"item_name\", \"Serial No\"], [\"description\", \"Serial No\"], [\"item_group\", \"Serial No\"], [\"brand\", \"Serial No\"],[\"purchase_document_no\", \"Serial No\"]]}", + "letterhead": null, + "modified": "2024-09-26 13:10:52.693648", + "modified_by": "Administrator", + "module": "Stock", + "name": "Serial No Status", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Serial No", + "report_name": "Serial No Status", + "report_type": "Report Builder", "roles": [ { "role": "Item Manager" - }, + }, { "role": "Stock Manager" - }, + }, { "role": "Stock User" } ] -} \ No newline at end of file +} diff --git a/erpnext/stock/report/serial_no_warranty_expiry/serial_no_warranty_expiry.json b/erpnext/stock/report/serial_no_warranty_expiry/serial_no_warranty_expiry.json index ef345f45216..75e2fac98fd 100644 --- a/erpnext/stock/report/serial_no_warranty_expiry/serial_no_warranty_expiry.json +++ b/erpnext/stock/report/serial_no_warranty_expiry/serial_no_warranty_expiry.json @@ -1,30 +1,33 @@ { - "add_total_row": 0, - "apply_user_permissions": 1, - "creation": "2013-01-14 10:52:58", - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 3, - "is_standard": "Yes", - "json": "{\"add_total_row\": 0, \"sort_by\": \"Serial No.modified\", \"sort_order\": \"desc\", \"sort_by_next\": null, \"filters\": [[\"Serial No\", \"delivery_document_type\", \"in\", [\"Delivery Note\", \"Sales Invoice\"]], [\"Serial No\", \"warehouse\", \"=\", \"\"]], \"sort_order_next\": \"desc\", \"columns\": [[\"name\", \"Serial No\"], [\"item_code\", \"Serial No\"], [\"warranty_expiry_date\", \"Serial No\"], [\"warranty_period\", \"Serial No\"], [\"maintenance_status\", \"Serial No\"], [\"purchase_document_no\", \"Serial No\"], [\"purchase_date\", \"Serial No\"], [\"supplier\", \"Serial No\"], [\"supplier_name\", \"Serial No\"], [\"delivery_document_no\", \"Serial No\"], [\"delivery_date\", \"Serial No\"], [\"customer\", \"Serial No\"], [\"customer_name\", \"Serial No\"], [\"item_name\", \"Serial No\"], [\"description\", \"Serial No\"], [\"item_group\", \"Serial No\"], [\"brand\", \"Serial No\"]]}", - "modified": "2017-02-24 20:01:53.097456", - "modified_by": "Administrator", - "module": "Stock", - "name": "Serial No Warranty Expiry", - "owner": "Administrator", - "ref_doctype": "Serial No", - "report_name": "Serial No Warranty Expiry", - "report_type": "Report Builder", + "add_total_row": 0, + "columns": [], + "creation": "2013-01-14 10:52:58", + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [], + "idx": 3, + "is_standard": "Yes", + "json": "{\"add_total_row\": 0, \"sort_by\": \"Serial No.modified\", \"sort_order\": \"desc\", \"sort_by_next\": null, \"filters\": [[\"Serial No\", \"warehouse\", \"=\", \"\"]], \"sort_order_next\": \"desc\", \"columns\": [[\"name\", \"Serial No\"], [\"item_code\", \"Serial No\"], [\"amc_expiry_date\", \"Serial No\"], [\"maintenance_status\", \"Serial No\"],[\"item_name\", \"Serial No\"], [\"description\", \"Serial No\"], [\"item_group\", \"Serial No\"], [\"brand\", \"Serial No\"]]}", + "letterhead": null, + "modified": "2024-09-26 13:07:23.451182", + "modified_by": "Administrator", + "module": "Stock", + "name": "Serial No Service Contract Expiry", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Serial No", + "report_name": "Serial No Service Contract Expiry", + "report_type": "Report Builder", "roles": [ { "role": "Item Manager" - }, + }, { "role": "Stock Manager" - }, + }, { "role": "Stock User" } ] -} \ No newline at end of file +} From d96cee8779a8143b54231968888a1016dd00b42a Mon Sep 17 00:00:00 2001 From: Corentin Forler Date: Wed, 2 Oct 2024 10:06:44 +0200 Subject: [PATCH 279/734] fix: Fix API endpoint for Frankfurter (cherry picked from commit 33e72111c7d7f60dbe041282026f607230624f52) --- .../currency_exchange_settings/currency_exchange_settings.py | 2 +- erpnext/setup/install.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.py b/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.py index 8cbb99e9252..160e791978e 100644 --- a/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.py +++ b/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.py @@ -109,7 +109,7 @@ def get_api_endpoint(service_provider: str | None = None, use_http: bool = False if service_provider == "exchangerate.host": api = "api.exchangerate.host/convert" elif service_provider == "frankfurter.app": - api = "frankfurter.app/{transaction_date}" + api = "api.frankfurter.app/{transaction_date}" protocol = "https://" if use_http: diff --git a/erpnext/setup/install.py b/erpnext/setup/install.py index bba9e79a348..97ec418d955 100644 --- a/erpnext/setup/install.py +++ b/erpnext/setup/install.py @@ -98,7 +98,7 @@ def setup_currency_exchange(): ces.set("result_key", []) ces.set("req_params", []) - ces.api_endpoint = "https://frankfurter.app/{transaction_date}" + ces.api_endpoint = "https://api.frankfurter.app/{transaction_date}" ces.append("result_key", {"key": "rates"}) ces.append("result_key", {"key": "{to_currency}"}) ces.append("req_params", {"key": "base", "value": "{from_currency}"}) From 8e7d8936692c28ac9830a3d64697a15b9e4d3ea8 Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Wed, 2 Oct 2024 14:28:54 +0530 Subject: [PATCH 280/734] test: update test for API change (cherry picked from commit c444de017aee1cfa6e4e214dbddbc2543dccd966) --- .../setup/doctype/currency_exchange/test_currency_exchange.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/setup/doctype/currency_exchange/test_currency_exchange.py b/erpnext/setup/doctype/currency_exchange/test_currency_exchange.py index 3249c93ef87..d28b1e65b7b 100644 --- a/erpnext/setup/doctype/currency_exchange/test_currency_exchange.py +++ b/erpnext/setup/doctype/currency_exchange/test_currency_exchange.py @@ -68,9 +68,9 @@ def patched_requests_get(*args, **kwargs): if kwargs["params"].get("date") and kwargs["params"].get("from") and kwargs["params"].get("to"): if test_exchange_values.get(kwargs["params"]["date"]): return PatchResponse({"result": test_exchange_values[kwargs["params"]["date"]]}, 200) - elif args[0].startswith("https://frankfurter.app") and kwargs.get("params"): + elif args[0].startswith("https://api.frankfurter.app") and kwargs.get("params"): if kwargs["params"].get("base") and kwargs["params"].get("symbols"): - date = args[0].replace("https://frankfurter.app/", "") + date = args[0].replace("https://api.frankfurter.app/", "") if test_exchange_values.get(date): return PatchResponse( {"rates": {kwargs["params"].get("symbols"): test_exchange_values.get(date)}}, 200 From 35a08f88301b8c80e752a5bd236de97a3546245e Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 2 Oct 2024 15:15:48 +0530 Subject: [PATCH 281/734] fix: patch to update Currency Exchange Settings for `frankfurter.app` (backport #43481) (#43483) Co-authored-by: Sagar Vora --- erpnext/patches.txt | 1 + ...date_currency_exchange_settings_for_frankfurter.py | 11 +++++++++++ 2 files changed, 12 insertions(+) create mode 100644 erpnext/patches/v14_0/update_currency_exchange_settings_for_frankfurter.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 5b1455ef723..333fca1d1da 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -377,3 +377,4 @@ erpnext.patches.v15_0.drop_index_posting_datetime_from_sle erpnext.patches.v15_0.add_disassembly_order_stock_entry_type #1 erpnext.patches.v15_0.set_standard_stock_entry_type erpnext.patches.v15_0.link_purchase_item_to_asset_doc +erpnext.patches.v14_0.update_currency_exchange_settings_for_frankfurter diff --git a/erpnext/patches/v14_0/update_currency_exchange_settings_for_frankfurter.py b/erpnext/patches/v14_0/update_currency_exchange_settings_for_frankfurter.py new file mode 100644 index 00000000000..a67c5a26237 --- /dev/null +++ b/erpnext/patches/v14_0/update_currency_exchange_settings_for_frankfurter.py @@ -0,0 +1,11 @@ +import frappe + + +def execute(): + settings = frappe.get_doc("Currency Exchange Settings") + if settings.service_provider != "frankfurter.app": + return + + settings.set_parameters_and_result() + settings.flags.ignore_validate = True + settings.save() From 6516e68fa0c296507e12ff6944d797e09da1d0cc Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Wed, 2 Oct 2024 13:11:13 +0530 Subject: [PATCH 282/734] fix: set margin fields for purchase documents when updating items (cherry picked from commit 7be4d56be2714446525b93acce961f04ca1bd2d6) --- erpnext/controllers/accounts_controller.py | 23 +++++++++------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 44a9813c89e..77f54818040 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -3309,7 +3309,6 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil items_added_or_removed = False # updated to true if any new item is added or removed any_conversion_factor_changed = False - sales_doctypes = ["Sales Order", "Sales Invoice", "Delivery Note", "Quotation"] parent = frappe.get_doc(parent_doctype, parent_doctype_name) check_doc_permissions(parent, "write") @@ -3425,25 +3424,21 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil # if rate is greater than price_list_rate, set margin # or set discount child_item.discount_percentage = 0 - - if parent_doctype in sales_doctypes: - child_item.margin_type = "Amount" - child_item.margin_rate_or_amount = flt( - child_item.rate - child_item.price_list_rate, - child_item.precision("margin_rate_or_amount"), - ) - child_item.rate_with_margin = child_item.rate + child_item.margin_type = "Amount" + child_item.margin_rate_or_amount = flt( + child_item.rate - child_item.price_list_rate, + child_item.precision("margin_rate_or_amount"), + ) + child_item.rate_with_margin = child_item.rate else: child_item.discount_percentage = flt( (1 - flt(child_item.rate) / flt(child_item.price_list_rate)) * 100.0, child_item.precision("discount_percentage"), ) child_item.discount_amount = flt(child_item.price_list_rate) - flt(child_item.rate) - - if parent_doctype in sales_doctypes: - child_item.margin_type = "" - child_item.margin_rate_or_amount = 0 - child_item.rate_with_margin = 0 + child_item.margin_type = "" + child_item.margin_rate_or_amount = 0 + child_item.rate_with_margin = 0 child_item.flags.ignore_validate_update_after_submit = True if new_child_flag: From f2a72e5f82feec1683b858d72a2ca315892a97e2 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 2 Oct 2024 18:46:01 +0530 Subject: [PATCH 283/734] fix: Stock UOM not fetched when Stock Entry create from Item Dashboard (backport #43457) (#43465) fix: Stock UOM not fetched when Stock Entry create from Item Dashboard (#43457) (cherry picked from commit 895b072bad6caaca384609a842719349eaa371f4) Co-authored-by: rohitwaghchaure --- erpnext/stock/dashboard/item_dashboard.js | 9 ++++++--- erpnext/stock/dashboard/item_dashboard.py | 1 + erpnext/stock/dashboard/item_dashboard_list.html | 2 ++ 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/dashboard/item_dashboard.js b/erpnext/stock/dashboard/item_dashboard.js index 36a51056bb4..6fc9e6666a2 100644 --- a/erpnext/stock/dashboard/item_dashboard.js +++ b/erpnext/stock/dashboard/item_dashboard.js @@ -48,17 +48,18 @@ erpnext.stock.ItemDashboard = class ItemDashboard { let actual_qty = unescape(element.attr("data-actual_qty")); let disable_quick_entry = Number(unescape(element.attr("data-disable_quick_entry"))); let entry_type = action === "Move" ? "Material Transfer" : "Material Receipt"; + let stock_uom = unescape(element.attr("data-stock-uom")); if (disable_quick_entry) { open_stock_entry(item, warehouse, entry_type); } else { if (action === "Add") { let rate = unescape($(this).attr("data-rate")); - erpnext.stock.move_item(item, null, warehouse, actual_qty, rate, function () { + erpnext.stock.move_item(item, null, warehouse, actual_qty, rate, stock_uom, function () { me.refresh(); }); } else { - erpnext.stock.move_item(item, warehouse, null, actual_qty, null, function () { + erpnext.stock.move_item(item, warehouse, null, actual_qty, null, stock_uom, function () { me.refresh(); }); } @@ -207,7 +208,7 @@ erpnext.stock.ItemDashboard = class ItemDashboard { } }; -erpnext.stock.move_item = function (item, source, target, actual_qty, rate, callback) { +erpnext.stock.move_item = function (item, source, target, actual_qty, rate, stock_uom, callback) { var dialog = new frappe.ui.Dialog({ title: target ? __("Add Item") : __("Move Item"), fields: [ @@ -295,6 +296,8 @@ erpnext.stock.move_item = function (item, source, target, actual_qty, rate, call let row = frappe.model.add_child(doc, "items"); row.item_code = dialog.get_value("item_code"); row.s_warehouse = dialog.get_value("source"); + row.stock_uom = stock_uom; + row.uom = stock_uom; row.t_warehouse = dialog.get_value("target"); row.qty = dialog.get_value("qty"); row.conversion_factor = 1; diff --git a/erpnext/stock/dashboard/item_dashboard.py b/erpnext/stock/dashboard/item_dashboard.py index e6382688669..3d7c21639e0 100644 --- a/erpnext/stock/dashboard/item_dashboard.py +++ b/erpnext/stock/dashboard/item_dashboard.py @@ -71,6 +71,7 @@ def get_data( item.update( { "item_name": frappe.get_cached_value("Item", item.item_code, "item_name"), + "stock_uom": frappe.get_cached_value("Item", item.item_code, "stock_uom"), "disable_quick_entry": frappe.get_cached_value("Item", item.item_code, "has_batch_no") or frappe.get_cached_value("Item", item.item_code, "has_serial_no"), "projected_qty": flt(item.projected_qty, precision), diff --git a/erpnext/stock/dashboard/item_dashboard_list.html b/erpnext/stock/dashboard/item_dashboard_list.html index 3b2619133bf..ae90ff80686 100644 --- a/erpnext/stock/dashboard/item_dashboard_list.html +++ b/erpnext/stock/dashboard/item_dashboard_list.html @@ -49,12 +49,14 @@ data-disable_quick_entry="{{ d.disable_quick_entry }}" data-warehouse="{{ d.warehouse }}" data-actual_qty="{{ d.actual_qty }}" + data-stock-uom="{{ d.stock_uom }}" data-item="{{ escape(d.item_code) }}">{{ __("Move") }} {% endif %}
From 1b28a4e9285f5131504f20b36172b9716404dccb Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 3 Oct 2024 20:28:53 +0100 Subject: [PATCH 284/734] fix(Dunning): logic for fetching text (backport #43160) (#43490) Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com> fix(Dunning): logic for fetching text (#43160) --- erpnext/accounts/doctype/dunning/dunning.py | 38 ++++++++++++++------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/erpnext/accounts/doctype/dunning/dunning.py b/erpnext/accounts/doctype/dunning/dunning.py index ad58761e1bf..d63db3a09a1 100644 --- a/erpnext/accounts/doctype/dunning/dunning.py +++ b/erpnext/accounts/doctype/dunning/dunning.py @@ -210,19 +210,31 @@ def get_linked_dunnings_as_per_state(sales_invoice, state): @frappe.whitelist() -def get_dunning_letter_text(dunning_type, doc, language=None): +def get_dunning_letter_text(dunning_type: str, doc: str | dict, language: str | None = None) -> dict: + DOCTYPE = "Dunning Letter Text" + FIELDS = ["body_text", "closing_text", "language"] + if isinstance(doc, str): doc = json.loads(doc) + + if not language: + language = doc.get("language") + if language: - filters = {"parent": dunning_type, "language": language} - else: - filters = {"parent": dunning_type, "is_default_language": 1} - letter_text = frappe.db.get_value( - "Dunning Letter Text", filters, ["body_text", "closing_text", "language"], as_dict=1 - ) - if letter_text: - return { - "body_text": frappe.render_template(letter_text.body_text, doc), - "closing_text": frappe.render_template(letter_text.closing_text, doc), - "language": letter_text.language, - } + letter_text = frappe.db.get_value( + DOCTYPE, {"parent": dunning_type, "language": language}, FIELDS, as_dict=1 + ) + + if not letter_text: + letter_text = frappe.db.get_value( + DOCTYPE, {"parent": dunning_type, "is_default_language": 1}, FIELDS, as_dict=1 + ) + + if not letter_text: + return {} + + return { + "body_text": frappe.render_template(letter_text.body_text, doc), + "closing_text": frappe.render_template(letter_text.closing_text, doc), + "language": letter_text.language, + } From 4fa513159088aeb393d1365cac55af7628d02355 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 4 Oct 2024 01:23:59 +0100 Subject: [PATCH 285/734] fix(Quotation): calculate row values for alternative items (backport #43054) (#43495) Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com> fix(Quotation): calculate row values for alternative items (#43054) --- erpnext/controllers/taxes_and_totals.py | 8 ++-- .../public/js/controllers/taxes_and_totals.js | 6 +-- .../doctype/quotation/test_quotation.py | 42 ++++++++++++++++++- 3 files changed, 47 insertions(+), 9 deletions(-) diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index 2d3b224b76f..fc0ba56d1f4 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -40,7 +40,7 @@ class calculate_taxes_and_totals: return items def calculate(self): - if not len(self._items): + if not len(self.doc.items): return self.discount_amount_applied = False @@ -95,7 +95,7 @@ class calculate_taxes_and_totals: if self.doc.get("is_return") and self.doc.get("return_against"): return - for item in self._items: + for item in self.doc.items: if item.item_code and item.get("item_tax_template"): item_doc = frappe.get_cached_doc("Item", item.item_code) args = { @@ -154,7 +154,7 @@ class calculate_taxes_and_totals: return if not self.discount_amount_applied: - for item in self._items: + for item in self.doc.items: self.doc.round_floats_in(item) if item.discount_percentage == 100: @@ -258,7 +258,7 @@ class calculate_taxes_and_totals: if not any(cint(tax.included_in_print_rate) for tax in self.doc.get("taxes")): return - for item in self._items: + for item in self.doc.items: item_tax_map = self._load_item_tax_rate(item.item_tax_rate) cumulated_tax_fraction = 0 total_inclusive_tax_amount_per_qty = 0 diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index adf05ffb154..dc7a5a8eef1 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -128,7 +128,7 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { calculate_item_values() { var me = this; if (!this.discount_amount_applied) { - for (const item of this.frm._items || []) { + for (const item of this.frm.doc.items || []) { frappe.model.round_floats_in(item); item.net_rate = item.rate; item.qty = item.qty === undefined ? (me.frm.doc.is_return ? -1 : 1) : item.qty; @@ -227,7 +227,7 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { }); if(has_inclusive_tax==false) return; - $.each(me.frm._items || [], function(n, item) { + $.each(this.frm.doc.items || [], function(n, item) { var item_tax_map = me._load_item_tax_rate(item.item_tax_rate); var cumulated_tax_fraction = 0.0; var total_inclusive_tax_amount_per_qty = 0; @@ -630,7 +630,7 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { _cleanup() { this.frm.doc.base_in_words = this.frm.doc.in_words = ""; - let items = this.frm._items; + let items = this.frm.doc.items; if(items && items.length) { if(!frappe.meta.get_docfield(items[0].doctype, "item_tax_amount", this.frm.doctype)) { diff --git a/erpnext/selling/doctype/quotation/test_quotation.py b/erpnext/selling/doctype/quotation/test_quotation.py index 7b66b7a251a..9a31e335a05 100644 --- a/erpnext/selling/doctype/quotation/test_quotation.py +++ b/erpnext/selling/doctype/quotation/test_quotation.py @@ -561,12 +561,50 @@ class TestQuotation(FrappeTestCase): "description": "VAT", "doctype": "Sales Taxes and Charges", "rate": 10, + "included_in_print_rate": 1, }, ) quotation.submit() - self.assertEqual(quotation.net_total, 290) - self.assertEqual(quotation.grand_total, 319) + self.assertEqual(round(quotation.items[1].net_rate, 2), 136.36) + self.assertEqual(round(quotation.items[1].amount, 2), 150) + + self.assertEqual(round(quotation.items[2].net_rate, 2), 163.64) + self.assertEqual(round(quotation.items[2].amount, 2), 180) + + self.assertEqual(round(quotation.net_total, 2), 263.64) + self.assertEqual(round(quotation.total_taxes_and_charges, 2), 26.36) + self.assertEqual(quotation.grand_total, 290) + + def test_amount_calculation_for_alternative_items(self): + """Make sure that the amount is calculated correctly for alternative items when the qty is changed.""" + from erpnext.stock.doctype.item.test_item import make_item + + item_list = [] + stock_items = { + "_Test Simple Item 1": 100, + "_Test Alt 1": 120, + } + + for item, rate in stock_items.items(): + make_item(item, {"is_stock_item": 0}) + item_list.append( + { + "item_code": item, + "qty": 1, + "rate": rate, + "is_alternative": "Alt" in item, + } + ) + + quotation = make_quotation(item_list=item_list, do_not_submit=1) + + self.assertEqual(quotation.items[1].amount, 120) + + quotation.items[1].qty = 2 + quotation.save() + + self.assertEqual(quotation.items[1].amount, 240) def test_alternative_items_sales_order_mapping_with_stock_items(self): from erpnext.selling.doctype.quotation.quotation import make_sales_order From edfa6e41e1fe296332997c92c610f536ca76cda3 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Fri, 4 Oct 2024 03:07:05 +0000 Subject: [PATCH 286/734] chore(release): Bumped to Version 15.38.0 # [15.38.0](https://github.com/frappe/erpnext/compare/v15.37.0...v15.38.0) (2024-10-04) ### Bug Fixes * 'NoneType' object has no attribute 'has_serial_no' ([21a0157](https://github.com/frappe/erpnext/commit/21a01575b66aa5f620974f171fbbdd7c1d335fe0)) * add company filter in Warehouse wise Item Balance Age and Value ([4fc6d3e](https://github.com/frappe/erpnext/commit/4fc6d3ef64df33303078b624cba6ca09cea03845)) * adjustmen entry for stock reco ([c551c27](https://github.com/frappe/erpnext/commit/c551c2714c97da39541d3d47f8a9d0f9ecaa20e3)) * Cannot read properties of undefined (reading 'price_list_rate') (backport [#43376](https://github.com/frappe/erpnext/issues/43376)) ([#43377](https://github.com/frappe/erpnext/issues/43377)) ([47f06dc](https://github.com/frappe/erpnext/commit/47f06dc180ee434ce21eea9587fa43bb20079215)) * Data missing in table: None, MandatoryError (backport [#43422](https://github.com/frappe/erpnext/issues/43422)) ([#43429](https://github.com/frappe/erpnext/issues/43429)) ([4b3f143](https://github.com/frappe/erpnext/commit/4b3f143f8366881d8cc427d56a14d6c38403d688)) * **Dunning:** logic for fetching text (backport [#43160](https://github.com/frappe/erpnext/issues/43160)) ([#43490](https://github.com/frappe/erpnext/issues/43490)) ([1b28a4e](https://github.com/frappe/erpnext/commit/1b28a4e9285f5131504f20b36172b9716404dccb)) * Fix API endpoint for Frankfurter ([d96cee8](https://github.com/frappe/erpnext/commit/d96cee8779a8143b54231968888a1016dd00b42a)) * Ignore transaction deletion check on ledger entry insertion ([1d6f97a](https://github.com/frappe/erpnext/commit/1d6f97ad945f8d0225533fb03e6866561316481b)) * **Item:** error message on tax rate (backport [#42955](https://github.com/frappe/erpnext/issues/42955)) ([#42956](https://github.com/frappe/erpnext/issues/42956)) ([5fc5934](https://github.com/frappe/erpnext/commit/5fc59349421e739cea28aab29b437678808a4224)) * last purchase rate for purchase invoice (backport [#43448](https://github.com/frappe/erpnext/issues/43448)) ([#43452](https://github.com/frappe/erpnext/issues/43452)) ([ee2c8c8](https://github.com/frappe/erpnext/commit/ee2c8c869ad19a5d5e629b5598f508a9cc4927e9)) * negative stock error for batch (backport [#43450](https://github.com/frappe/erpnext/issues/43450)) ([#43454](https://github.com/frappe/erpnext/issues/43454)) ([7bf6251](https://github.com/frappe/erpnext/commit/7bf6251c21a5249c1c3ade63b29b25d1cb597433)) * patch to update Currency Exchange Settings for `frankfurter.app` (backport [#43481](https://github.com/frappe/erpnext/issues/43481)) ([#43483](https://github.com/frappe/erpnext/issues/43483)) ([35a08f8](https://github.com/frappe/erpnext/commit/35a08f88301b8c80e752a5bd236de97a3546245e)) * quality inspection creation (backport [#43416](https://github.com/frappe/erpnext/issues/43416)) ([#43417](https://github.com/frappe/erpnext/issues/43417)) ([a1b6628](https://github.com/frappe/erpnext/commit/a1b6628c41ef965d855cd6890f0919871b4ca58d)) * **Quotation:** calculate row values for alternative items (backport [#43054](https://github.com/frappe/erpnext/issues/43054)) ([#43495](https://github.com/frappe/erpnext/issues/43495)) ([4fa5131](https://github.com/frappe/erpnext/commit/4fa513159088aeb393d1365cac55af7628d02355)) * removed validation for materials return (backport [#43461](https://github.com/frappe/erpnext/issues/43461)) ([#43463](https://github.com/frappe/erpnext/issues/43463)) ([9c0a17e](https://github.com/frappe/erpnext/commit/9c0a17e4d5dceff6e4ef0a90e1d36de762f8d3af)) * serial and batch no selector (backport [#43387](https://github.com/frappe/erpnext/issues/43387)) ([#43390](https://github.com/frappe/erpnext/issues/43390)) ([74c880c](https://github.com/frappe/erpnext/commit/74c880c232d6fa1cf4f0d53c5faad16f7e809c16)) * set margin fields for purchase documents when updating items ([6516e68](https://github.com/frappe/erpnext/commit/6516e68fa0c296507e12ff6944d797e09da1d0cc)) * Stock Ledger Invariant Check report ([2984bad](https://github.com/frappe/erpnext/commit/2984bad2c09061c949a55d8d62f0fa39bbbbdfd2)) * Stock UOM not fetched when Stock Entry create from Item Dashboard (backport [#43457](https://github.com/frappe/erpnext/issues/43457)) ([#43465](https://github.com/frappe/erpnext/issues/43465)) ([f2a72e5](https://github.com/frappe/erpnext/commit/f2a72e5f82feec1683b858d72a2ca315892a97e2)) * tests for work order consumption (backport [#41814](https://github.com/frappe/erpnext/issues/41814)) ([#43430](https://github.com/frappe/erpnext/issues/43430)) ([86b10ce](https://github.com/frappe/erpnext/commit/86b10ce9bbfbf4279d52df9b17c660e6baeda934)) * use serial and batch fields (backport [#43421](https://github.com/frappe/erpnext/issues/43421)) ([#43423](https://github.com/frappe/erpnext/issues/43423)) ([d495d93](https://github.com/frappe/erpnext/commit/d495d93840c809bfdf7a7109e6fe883eae0e5193)) ### Features * added 'cost of new capitalized asset' column ([27cd51e](https://github.com/frappe/erpnext/commit/27cd51e267b3a11ab474ca085b5a0dc6cef87fad)) * provide hook point for bulk transaction tasks ([50e47e7](https://github.com/frappe/erpnext/commit/50e47e796dc4a91fb4b3e653fb5cc679c58916cf)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index c240944687b..bbb02e2ed0d 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.37.0" +__version__ = "15.38.0" def get_default_company(user=None): From e785928c0fcb5e2549302ea77217fe88f9797717 Mon Sep 17 00:00:00 2001 From: Abdeali Chharchhoda Date: Mon, 7 Oct 2024 12:08:02 +0530 Subject: [PATCH 287/734] fix: Remove unused field --- .../doctype/payment_request/payment_request.json | 11 +---------- .../doctype/payment_request/payment_request.py | 1 - 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/erpnext/accounts/doctype/payment_request/payment_request.json b/erpnext/accounts/doctype/payment_request/payment_request.json index 7806bc682b0..67d540701c1 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.json +++ b/erpnext/accounts/doctype/payment_request/payment_request.json @@ -398,15 +398,6 @@ "print_hide": 1, "read_only": 1 }, - { - "fieldname": "failed_reason", - "fieldtype": "Data", - "hidden": 1, - "label": "Reason for Failure", - "no_copy": 1, - "print_hide": 1, - "read_only": 1 - }, { "depends_on": "eval: doc.docstatus === 1", "description": "Amount in party's bank account currency", @@ -480,4 +471,4 @@ "sort_field": "modified", "sort_order": "DESC", "states": [] -} \ No newline at end of file +} diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index 6d1d279c244..a12cfb4eb92 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -51,7 +51,6 @@ class PaymentRequest(Document): cost_center: DF.Link | None currency: DF.Link | None email_to: DF.Data | None - failed_reason: DF.Data | None grand_total: DF.Currency iban: DF.ReadOnly | None is_a_subscription: DF.Check From 75916629c857465a1a6efadd2fd0c77624c93cf5 Mon Sep 17 00:00:00 2001 From: Abdeali Chharchhoda Date: Mon, 7 Oct 2024 12:10:51 +0530 Subject: [PATCH 288/734] fix: Remove unused function `get_paid_amount_against_order` --- .../payment_request/payment_request.py | 27 ------------------- 1 file changed, 27 deletions(-) diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index a12cfb4eb92..abed1610119 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -862,33 +862,6 @@ def validate_payment(doc, method=None): ) -def get_paid_amount_against_order(dt, dn): - pe_ref = frappe.qb.DocType("Payment Entry Reference") - if dt == "Sales Order": - inv_dt, inv_field = "Sales Invoice Item", "sales_order" - else: - inv_dt, inv_field = "Purchase Invoice Item", "purchase_order" - inv_item = frappe.qb.DocType(inv_dt) - return ( - frappe.qb.from_(pe_ref) - .select( - Sum(pe_ref.allocated_amount), - ) - .where( - (pe_ref.docstatus == 1) - & ( - (pe_ref.reference_name == dn) - | pe_ref.reference_name.isin( - frappe.qb.from_(inv_item) - .select(inv_item.parent) - .where(inv_item[inv_field] == dn) - .distinct() - ) - ) - ) - ).run()[0][0] or 0 - - @frappe.whitelist() def get_open_payment_requests_query(doctype, txt, searchfield, start, page_len, filters): # permission checks in `get_list()` From 6770610c6d62a2dcddf95221ed85ee37c5a56625 Mon Sep 17 00:00:00 2001 From: Ninad Parikh <109862100+Ninad1306@users.noreply.github.com> Date: Sun, 6 Oct 2024 08:52:58 +0530 Subject: [PATCH 289/734] fix: Update Values before `after_mapping` hook is called (#42682) * fix: update values before after_mapping hook is called * fix: appropriate function name --- .../doctype/purchase_order/purchase_order.py | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index 14424dfdf4a..64c9ba36794 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -889,6 +889,20 @@ def make_subcontracting_order(source_name, target_doc=None, save=False, submit=F def get_mapped_subcontracting_order(source_name, target_doc=None): + def post_process(source_doc, target_doc): + target_doc.populate_items_table() + + if target_doc.set_warehouse: + for item in target_doc.items: + item.warehouse = target_doc.set_warehouse + else: + if source_doc.set_warehouse: + for item in target_doc.items: + item.warehouse = source_doc.set_warehouse + else: + for idx, item in enumerate(target_doc.items): + item.warehouse = source_doc.items[idx].warehouse + if target_doc and isinstance(target_doc, str): target_doc = json.loads(target_doc) for key in ["service_items", "items", "supplied_items"]: @@ -919,22 +933,9 @@ def get_mapped_subcontracting_order(source_name, target_doc=None): }, }, target_doc, + post_process, ) - target_doc.populate_items_table() - source_doc = frappe.get_doc("Purchase Order", source_name) - - if target_doc.set_warehouse: - for item in target_doc.items: - item.warehouse = target_doc.set_warehouse - else: - if source_doc.set_warehouse: - for item in target_doc.items: - item.warehouse = source_doc.set_warehouse - else: - for idx, item in enumerate(target_doc.items): - item.warehouse = source_doc.items[idx].warehouse - return target_doc From dbd7b83204d1e36790d838a5c96c55d4d93464bd Mon Sep 17 00:00:00 2001 From: Abdeali Chharchhoda Date: Mon, 7 Oct 2024 12:33:42 +0530 Subject: [PATCH 290/734] fix: Separate `on_submit` and `before_submit` of PR --- .../payment_request/payment_request.py | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index abed1610119..8c370ba9d8c 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -179,19 +179,28 @@ class PaymentRequest(Document): else: self.outstanding_amount = self.grand_total + def on_submit(self): if self.payment_request_type == "Outward": - self.status = "Initiated" + self.db_set("status", "Initiated") + return elif self.payment_request_type == "Inward": - self.status = "Requested" + self.db_set("status", "Requested") - if self.payment_request_type == "Inward": - if self.payment_channel == "Phone": - self.request_phone_payment() - else: - self.set_payment_request_url() - if not (self.mute_email or self.flags.mute_email): - self.send_email() - self.make_communication_entry() + send_mail = self.payment_gateway_validation() if self.payment_gateway else None + ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name) + + if ( + hasattr(ref_doc, "order_type") and ref_doc.order_type == "Shopping Cart" + ) or self.flags.mute_email: + send_mail = False + + if send_mail and self.payment_channel != "Phone": + self.set_payment_request_url() + self.send_email() + self.make_communication_entry() + + elif self.payment_channel == "Phone": + self.request_phone_payment() def request_phone_payment(self): controller = _get_payment_gateway_controller(self.payment_gateway) From 3d9d56ab504ef2fd321fe4cbdd38414a1700ccfa Mon Sep 17 00:00:00 2001 From: Abdeali Chharchhoda Date: Mon, 7 Oct 2024 12:42:59 +0530 Subject: [PATCH 291/734] test: Remove `Payment Gateway` settings from test --- .../payment_request/test_payment_request.py | 24 ------------------- 1 file changed, 24 deletions(-) diff --git a/erpnext/accounts/doctype/payment_request/test_payment_request.py b/erpnext/accounts/doctype/payment_request/test_payment_request.py index e9f8f25ff2d..34b3b7bb5fa 100644 --- a/erpnext/accounts/doctype/payment_request/test_payment_request.py +++ b/erpnext/accounts/doctype/payment_request/test_payment_request.py @@ -3,7 +3,6 @@ import re import unittest -from unittest.mock import patch import frappe from frappe.tests.utils import FrappeTestCase @@ -18,7 +17,6 @@ from erpnext.setup.utils import get_exchange_rate test_dependencies = ["Currency Exchange", "Journal Entry", "Contact", "Address"] -PAYMENT_URL = "https://example.com/payment" payment_gateway = {"doctype": "Payment Gateway", "gateway": "_Test Gateway"} @@ -52,28 +50,6 @@ class TestPaymentRequest(FrappeTestCase): ): frappe.get_doc(method).insert(ignore_permissions=True) - send_email = patch( - "erpnext.accounts.doctype.payment_request.payment_request.PaymentRequest.send_email", - return_value=None, - ) - self.send_email = send_email.start() - self.addCleanup(send_email.stop) - get_payment_url = patch( - # this also shadows one (1) call to _get_payment_gateway_controller - "erpnext.accounts.doctype.payment_request.payment_request.PaymentRequest.get_payment_url", - return_value=PAYMENT_URL, - ) - self.get_payment_url = get_payment_url.start() - self.addCleanup(get_payment_url.stop) - _get_payment_gateway_controller = patch( - "erpnext.accounts.doctype.payment_request.payment_request._get_payment_gateway_controller", - ) - self._get_payment_gateway_controller = _get_payment_gateway_controller.start() - self.addCleanup(_get_payment_gateway_controller.stop) - - def tearDown(self): - frappe.db.rollback() - def test_payment_request_linkings(self): so_inr = make_sales_order(currency="INR", do_not_save=True) so_inr.disable_rounded_total = 1 From d69a974a4de50e0208127ea3b078acbd0fa0364d Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 7 Oct 2024 13:57:56 +0530 Subject: [PATCH 292/734] fix: read only filters in multidialog fields (backport #43503) (#43513) fix: read only filters in multidialog fields (#43503) (cherry picked from commit 13eb3c5c147cc2978f673dd6c7027e690e8767ac) Co-authored-by: rohitwaghchaure --- erpnext/public/js/utils.js | 1 + erpnext/stock/doctype/stock_entry/stock_entry.js | 2 ++ 2 files changed, 3 insertions(+) diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index a7d88edcafa..7c02fefc0f9 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -920,6 +920,7 @@ erpnext.utils.map_current_doc = function (opts) { target: opts.target, date_field: opts.date_field || undefined, setters: opts.setters, + read_only_setters: opts.read_only_setters, data_fields: data_fields, get_query: opts.get_query, add_filters_group: 1, diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js index c54876713c3..cb442f6666d 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry.js @@ -447,9 +447,11 @@ frappe.ui.form.on("Stock Entry", { source_doctype: "Stock Entry", target: frm, date_field: "posting_date", + read_only_setters: ["stock_entry_type", "purpose", "add_to_transit"], setters: { stock_entry_type: "Material Transfer", purpose: "Material Transfer", + add_to_transit: 1, }, get_query_filters: { docstatus: 1, From 85d74050e1b35126e4c9b2d4a7cb1bc3c27ae835 Mon Sep 17 00:00:00 2001 From: "Nihantra C. Patel" <141945075+Nihantra-Patel@users.noreply.github.com> Date: Mon, 7 Oct 2024 14:51:04 +0530 Subject: [PATCH 293/734] fix: #42014 --resolve conflicts --- erpnext/crm/doctype/lead/lead.js | 160 +++++-------------------------- 1 file changed, 23 insertions(+), 137 deletions(-) diff --git a/erpnext/crm/doctype/lead/lead.js b/erpnext/crm/doctype/lead/lead.js index f27187924af..e50cf9e4dd0 100644 --- a/erpnext/crm/doctype/lead/lead.js +++ b/erpnext/crm/doctype/lead/lead.js @@ -28,42 +28,18 @@ erpnext.LeadController = class LeadController extends frappe.ui.form.Controller erpnext.toggle_naming_series(); if (!this.frm.is_new() && doc.__onload && !doc.__onload.is_customer) { -<<<<<<< HEAD -<<<<<<< HEAD - this.frm.add_custom_button(__("Customer"), this.make_customer, __("Create")); - this.frm.add_custom_button( - __("Opportunity"), - function () { - me.frm.trigger("make_opportunity"); - }, - __("Create") - ); - this.frm.add_custom_button(__("Quotation"), this.make_quotation, __("Create")); -======= - this.frm.add_custom_button( - __("Customer"), - this.make_customer.bind(this), - __("Create") - ); - this.frm.add_custom_button( - __("Opportunity"), - this.make_opportunity.bind(this), - __("Create") - ); - this.frm.add_custom_button( - __("Quotation"), - this.make_quotation.bind(this), - __("Create") - ); ->>>>>>> 8304d19e8b (fix: creation of contact, customer, opportunity, quotation and prospect from lead) -======= this.frm.add_custom_button(__("Customer"), this.make_customer.bind(this), __("Create")); this.frm.add_custom_button(__("Opportunity"), this.make_opportunity.bind(this), __("Create")); this.frm.add_custom_button(__("Quotation"), this.make_quotation.bind(this), __("Create")); ->>>>>>> 5844897c34 (fix: creation of contact, customer, opportunity, quotation and prospect from lead --prettier) if (!doc.__onload.linked_prospects.length) { this.frm.add_custom_button(__("Prospect"), this.make_prospect.bind(this), __("Create")); - this.frm.add_custom_button(__("Add to Prospect"), this.add_lead_to_prospect, __("Action")); + this.frm.add_custom_button( + __("Add to Prospect"), + () => { + this.add_lead_to_prospect(this.frm); + }, + __("Action") + ); } } @@ -77,8 +53,7 @@ erpnext.LeadController = class LeadController extends frappe.ui.form.Controller this.show_activities(); } - add_lead_to_prospect() { - let me = this; + add_lead_to_prospect(frm) { frappe.prompt( [ { @@ -93,12 +68,12 @@ erpnext.LeadController = class LeadController extends frappe.ui.form.Controller frappe.call({ method: "erpnext.crm.doctype.lead.lead.add_lead_to_prospect", args: { - lead: me.frm.doc.name, + lead: frm.doc.name, prospect: data.prospect, }, callback: function (r) { if (!r.exc) { - me.frm.reload_doc(); + frm.reload_doc(); } }, freeze: true, @@ -113,19 +88,17 @@ erpnext.LeadController = class LeadController extends frappe.ui.form.Controller make_customer() { frappe.model.open_mapped_doc({ method: "erpnext.crm.doctype.lead.lead.make_customer", - frm: cur_frm, + frm: this.frm, }); } make_quotation() { frappe.model.open_mapped_doc({ method: "erpnext.crm.doctype.lead.lead.make_quotation", - frm: cur_frm, + frm: this.frm, }); } -<<<<<<< HEAD -======= async make_opportunity() { const frm = this.frm; let existing_prospect = ( @@ -216,22 +189,22 @@ erpnext.LeadController = class LeadController extends frappe.ui.form.Controller } } ->>>>>>> 8304d19e8b (fix: creation of contact, customer, opportunity, quotation and prospect from lead) make_prospect() { + const me = this; frappe.model.with_doctype("Prospect", function () { let prospect = frappe.model.get_new_doc("Prospect"); - prospect.company_name = cur_frm.doc.company_name; - prospect.no_of_employees = cur_frm.doc.no_of_employees; - prospect.industry = cur_frm.doc.industry; - prospect.market_segment = cur_frm.doc.market_segment; - prospect.territory = cur_frm.doc.territory; - prospect.fax = cur_frm.doc.fax; - prospect.website = cur_frm.doc.website; - prospect.prospect_owner = cur_frm.doc.lead_owner; - prospect.notes = cur_frm.doc.notes; + prospect.company_name = me.frm.doc.company_name; + prospect.no_of_employees = me.frm.doc.no_of_employees; + prospect.industry = me.frm.doc.industry; + prospect.market_segment = me.frm.doc.market_segment; + prospect.territory = me.frm.doc.territory; + prospect.fax = me.frm.doc.fax; + prospect.website = me.frm.doc.website; + prospect.prospect_owner = me.frm.doc.lead_owner; + prospect.notes = me.frm.doc.notes; let leads_row = frappe.model.add_child(prospect, "leads"); - leads_row.lead = cur_frm.doc.name; + leads_row.lead = me.frm.doc.name; frappe.set_route("Form", "Prospect", prospect.name); }); @@ -267,90 +240,3 @@ erpnext.LeadController = class LeadController extends frappe.ui.form.Controller }; extend_cscript(cur_frm.cscript, new erpnext.LeadController({ frm: cur_frm })); - -frappe.ui.form.on("Lead", { - make_opportunity: async function (frm) { - let existing_prospect = ( - await frappe.db.get_value( - "Prospect Lead", - { - lead: frm.doc.name, - }, - "name", - null, - "Prospect" - ) - ).message.name; - - if (!existing_prospect) { - var fields = [ - { - label: "Create Prospect", - fieldname: "create_prospect", - fieldtype: "Check", - default: 1, - }, - { - label: "Prospect Name", - fieldname: "prospect_name", - fieldtype: "Data", - default: frm.doc.company_name, - depends_on: "create_prospect", - }, - ]; - } - let existing_contact = ( - await frappe.db.get_value( - "Contact", - { - first_name: frm.doc.first_name || frm.doc.lead_name, - last_name: frm.doc.last_name, - }, - "name" - ) - ).message.name; - - if (!existing_contact) { - fields.push({ - label: "Create Contact", - fieldname: "create_contact", - fieldtype: "Check", - default: "1", - }); - } - - if (fields) { - var d = new frappe.ui.Dialog({ - title: __("Create Opportunity"), - fields: fields, - primary_action: function () { - var data = d.get_values(); - frappe.call({ - method: "create_prospect_and_contact", - doc: frm.doc, - args: { - data: data, - }, - freeze: true, - callback: function (r) { - if (!r.exc) { - frappe.model.open_mapped_doc({ - method: "erpnext.crm.doctype.lead.lead.make_opportunity", - frm: frm, - }); - } - d.hide(); - }, - }); - }, - primary_action_label: __("Create"), - }); - d.show(); - } else { - frappe.model.open_mapped_doc({ - method: "erpnext.crm.doctype.lead.lead.make_opportunity", - frm: frm, - }); - } - }, -}); From 62cc86114b3f2bca31d66975fb4182e165ce2c90 Mon Sep 17 00:00:00 2001 From: Abdeali Chharchhoda Date: Mon, 7 Oct 2024 16:00:06 +0530 Subject: [PATCH 294/734] test: Change `Accounts Settings` for multi currency (https://github.com/frappe/erpnext/pull/42427#discussion_r1789859737) --- .../doctype/payment_request/test_payment_request.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/payment_request/test_payment_request.py b/erpnext/accounts/doctype/payment_request/test_payment_request.py index 34b3b7bb5fa..b0c3dbf4d5b 100644 --- a/erpnext/accounts/doctype/payment_request/test_payment_request.py +++ b/erpnext/accounts/doctype/payment_request/test_payment_request.py @@ -5,7 +5,7 @@ import re import unittest import frappe -from frappe.tests.utils import FrappeTestCase +from frappe.tests.utils import FrappeTestCase, change_settings from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_terms_template from erpnext.accounts.doctype.payment_request.payment_request import make_payment_request @@ -341,8 +341,11 @@ class TestPaymentRequest(FrappeTestCase): return_doc=1, ) + @change_settings("Accounts Settings", {"allow_multi_currency_invoices_against_single_party_account": 1}) def test_multiple_payment_if_partially_paid_for_multi_currency(self): - pi = make_purchase_invoice(currency="USD", conversion_rate=50, qty=1, rate=100) + pi = make_purchase_invoice(currency="USD", conversion_rate=50, qty=1, rate=100, do_not_save=1) + pi.credit_to = "Creditors - _TC" + pi.submit() pr = make_payment_request( dt="Purchase Invoice", @@ -439,10 +442,13 @@ class TestPaymentRequest(FrappeTestCase): self.assertEqual(pr.outstanding_amount, 0) self.assertEqual(pr.grand_total, 20000) + @change_settings("Accounts Settings", {"allow_multi_currency_invoices_against_single_party_account": 1}) def test_single_payment_with_payment_term_for_multi_currency(self): create_payment_terms_template() - si = create_sales_invoice(do_not_save=1, currency="USD", qty=1, rate=200, conversion_rate=50) + si = create_sales_invoice( + do_not_save=1, currency="USD", debit_to="Debtors - _TC", qty=1, rate=200, conversion_rate=50 + ) si.payment_terms_template = "Test Receivable Template" # 84.746 and 15.254 si.save() si.submit() From eb1f1255ebb7f1424e982864bd2b08ceb54e86a2 Mon Sep 17 00:00:00 2001 From: Syed Mujeer Hashmi Date: Mon, 7 Oct 2024 14:12:43 +0000 Subject: [PATCH 295/734] fix: Link opportunity from RFQ to supplier quotation --- .../doctype/request_for_quotation/request_for_quotation.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py index c8ef833e0e9..e123822f386 100644 --- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py +++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py @@ -390,6 +390,7 @@ def make_supplier_quotation_from_rfq(source_name, target_doc=None, for_supplier= "Request for Quotation": { "doctype": "Supplier Quotation", "validation": {"docstatus": ["=", 1]}, + "field_map": {"opportunity": "opportunity"}, }, "Request for Quotation Item": { "doctype": "Supplier Quotation Item", From 0c599c2b6d171161de4c697ae7e6c691e70cc2f4 Mon Sep 17 00:00:00 2001 From: Smit Vora Date: Mon, 7 Oct 2024 20:36:53 +0530 Subject: [PATCH 296/734] chore: remove unused filed --- erpnext/accounts/doctype/payment_request/payment_request.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/erpnext/accounts/doctype/payment_request/payment_request.json b/erpnext/accounts/doctype/payment_request/payment_request.json index 67d540701c1..b7af8412810 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.json +++ b/erpnext/accounts/doctype/payment_request/payment_request.json @@ -416,10 +416,6 @@ "options": "Company", "read_only": 1 }, - { - "fieldname": "column_break_pnyv", - "fieldtype": "Column Break" - }, { "fieldname": "party_account_currency", "fieldtype": "Link", From 55464c79c4a068146b7254f57c9575b44c3966ac Mon Sep 17 00:00:00 2001 From: ljain112 Date: Wed, 25 Sep 2024 18:24:42 +0530 Subject: [PATCH 297/734] fix: include parent item group in query (cherry picked from commit ad0090068dc9dc0325dea180ff38e2c2cc86a4a6) --- erpnext/accounts/doctype/pos_invoice/pos_invoice.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py index 742af7dd0fe..20316e3394b 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py @@ -862,6 +862,7 @@ def get_item_group(pos_profile): if pos_profile.get("item_groups"): # Get items based on the item groups defined in the POS profile for row in pos_profile.get("item_groups"): + item_groups.append(row.item_group) item_groups.extend(get_descendants_of("Item Group", row.item_group)) return list(set(item_groups)) From 8ce81a058a074ecf11c95137e3c14e30480f241e Mon Sep 17 00:00:00 2001 From: ljain112 Date: Wed, 25 Sep 2024 18:26:10 +0530 Subject: [PATCH 298/734] fix: add parenttype condition for item table in Purchase Register Report (cherry picked from commit 28abf191fcc0b74e60610eb7af29160d8fd2c6ad) --- .../accounts/report/purchase_register/purchase_register.py | 5 +++-- erpnext/accounts/report/utils.py | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/purchase_register/purchase_register.py b/erpnext/accounts/report/purchase_register/purchase_register.py index 504c74babcb..48364cc2c91 100644 --- a/erpnext/accounts/report/purchase_register/purchase_register.py +++ b/erpnext/accounts/report/purchase_register/purchase_register.py @@ -311,6 +311,7 @@ def get_account_columns(invoice_list, include_payments): """select distinct expense_account from `tabPurchase Invoice Item` where docstatus = 1 and (expense_account is not null and expense_account != '') + and parenttype='Purchase Invoice' and parent in (%s) order by expense_account""" % ", ".join(["%s"] * len(invoice_list)), tuple([inv.name for inv in invoice_list]), @@ -451,7 +452,7 @@ def get_invoice_expense_map(invoice_list): """ select parent, expense_account, sum(base_net_amount) as amount from `tabPurchase Invoice Item` - where parent in (%s) + where parent in (%s) and parenttype='Purchase Invoice' group by parent, expense_account """ % ", ".join(["%s"] * len(invoice_list)), @@ -522,7 +523,7 @@ def get_invoice_po_pr_map(invoice_list): """ select parent, purchase_order, purchase_receipt, po_detail, project from `tabPurchase Invoice Item` - where parent in (%s) + where parent in (%s) and parenttype='Purchase Invoice' """ % ", ".join(["%s"] * len(invoice_list)), tuple(inv.name for inv in invoice_list), diff --git a/erpnext/accounts/report/utils.py b/erpnext/accounts/report/utils.py index bd1b35559ea..d6c1b95cf7c 100644 --- a/erpnext/accounts/report/utils.py +++ b/erpnext/accounts/report/utils.py @@ -326,6 +326,7 @@ def apply_common_conditions(filters, query, doctype, child_doctype=None, payment if join_required: query = query.inner_join(child_doc).on(parent_doc.name == child_doc.parent) + query = query.where(child_doc.parenttype == doctype) query = query.distinct() if parent_doc.get_table_name() != "tabJournal Entry": From d6f70f533acd24ddb1440cbced74336f59e109e5 Mon Sep 17 00:00:00 2001 From: ljain112 Date: Thu, 26 Sep 2024 13:41:16 +0530 Subject: [PATCH 299/734] fix: create Account Closing Balance even though there are no transaction in period (cherry picked from commit 43deaea96b178592a4e18d9e01b33823a9cab8bd) --- .../doctype/period_closing_voucher/period_closing_voucher.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py index 9bc110d243e..c68cd292523 100644 --- a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py +++ b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py @@ -392,8 +392,7 @@ def process_closing_entries(gl_entries, closing_entries, voucher_name, company, ) try: - if gl_entries + closing_entries: - make_closing_entries(gl_entries + closing_entries, voucher_name, company, closing_date) + make_closing_entries(gl_entries + closing_entries, voucher_name, company, closing_date) except Exception as e: frappe.db.rollback() frappe.log_error(e) From bcd0105915ddd7fc433f8de5f743a16023d99748 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 8 Oct 2024 09:01:25 +0530 Subject: [PATCH 300/734] chore: Allow apps to extend voucher subtypes (#43528) * chore: Allow apps to extend voucher subtypes (cherry picked from commit a1525d9b8e45c6b71f1a9e490af82e2e84b09c25) * chore: Allow apps to extend voucher subtypes (cherry picked from commit 8a1e38a43b8f47ef388a5d7f57b8e84212bbe2b3) * chore: Allow apps to extend voucher subtypes (cherry picked from commit ca8820b5660f371ec9bd05ac6afe104c55168b18) --------- Co-authored-by: Deepesh Garg --- erpnext/controllers/accounts_controller.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 9a590ef2e40..73923464ed9 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1063,6 +1063,13 @@ class AccountsController(TransactionBase): "Stock Entry": "stock_entry_type", "Asset Capitalization": "entry_type", } + + for method_name in frappe.get_hooks("voucher_subtypes"): + voucher_subtype = frappe.get_attr(method_name)(self) + + if voucher_subtype: + return voucher_subtype + if self.doctype in voucher_subtypes: return self.get(voucher_subtypes[self.doctype]) elif self.doctype == "Purchase Receipt" and self.is_return: @@ -1073,6 +1080,7 @@ class AccountsController(TransactionBase): return "Credit Note" elif (self.doctype == "Purchase Invoice" and self.is_return) or self.doctype == "Sales Invoice": return "Debit Note" + return self.doctype def get_value_in_transaction_currency(self, account_currency, gl_dict, field): From 05db28c64f74bf95a1cd8a9d6a3e6ee782034d49 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 8 Oct 2024 09:20:34 +0530 Subject: [PATCH 301/734] chore: Allow apps to extend voucher subtypes (backport #43528) (backport #43550) (#43551) chore: Allow apps to extend voucher subtypes (#43528) * chore: Allow apps to extend voucher subtypes (cherry picked from commit a1525d9b8e45c6b71f1a9e490af82e2e84b09c25) * chore: Allow apps to extend voucher subtypes (cherry picked from commit 8a1e38a43b8f47ef388a5d7f57b8e84212bbe2b3) * chore: Allow apps to extend voucher subtypes (cherry picked from commit ca8820b5660f371ec9bd05ac6afe104c55168b18) --------- Co-authored-by: Deepesh Garg (cherry picked from commit bcd0105915ddd7fc433f8de5f743a16023d99748) Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- erpnext/controllers/accounts_controller.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 77f54818040..3c1fb8bae44 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1063,6 +1063,13 @@ class AccountsController(TransactionBase): "Stock Entry": "stock_entry_type", "Asset Capitalization": "entry_type", } + + for method_name in frappe.get_hooks("voucher_subtypes"): + voucher_subtype = frappe.get_attr(method_name)(self) + + if voucher_subtype: + return voucher_subtype + if self.doctype in voucher_subtypes: return self.get(voucher_subtypes[self.doctype]) elif self.doctype == "Purchase Receipt" and self.is_return: @@ -1073,6 +1080,7 @@ class AccountsController(TransactionBase): return "Credit Note" elif (self.doctype == "Purchase Invoice" and self.is_return) or self.doctype == "Sales Invoice": return "Debit Note" + return self.doctype def get_value_in_transaction_currency(self, account_currency, gl_dict, field): From ee8485a54ac0439e9cf3a6c8b9b7e0a02f3ae53f Mon Sep 17 00:00:00 2001 From: ljain112 Date: Thu, 26 Sep 2024 19:52:57 +0530 Subject: [PATCH 302/734] fix: do not include advances for tds vouchers (cherry picked from commit 7ef918421e18187e9662d57a828923a74934b190) --- .../tax_withholding_category/tax_withholding_category.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py index 9fd78da2d07..d9a569e2269 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -414,6 +414,9 @@ def get_advance_vouchers(parties, company=None, from_date=None, to_date=None, pa Use Payment Ledger to fetch unallocated Advance Payments """ + if party_type == "Supplier": + return [] + ple = qb.DocType("Payment Ledger Entry") conditions = [] From 6decb7cc34a02c1bf8d33e019ead5cafc3f65363 Mon Sep 17 00:00:00 2001 From: ljain112 Date: Wed, 18 Sep 2024 13:08:38 +0530 Subject: [PATCH 303/734] fix: deduct advances adjusted for threshold check for tcs (cherry picked from commit 767c8f92bef52aa8f1855b6ac3388f0922eb7249) --- .../tax_withholding_category.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py index d9a569e2269..67a33faf9bf 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -327,7 +327,7 @@ def get_tax_amount(party_type, parties, inv, tax_details, posting_date, pan_no=N tax_amount = 0 else: # if no TCS has been charged in FY, - # then chargeable value is "prev invoices + advances" value which cross the threshold + # then chargeable value is "prev invoices + advances - advance_adjusted" value which cross the threshold tax_amount = get_tcs_amount(parties, inv, tax_details, vouchers, advance_vouchers) if cint(tax_details.round_off_tax_amount): @@ -634,9 +634,12 @@ def get_tcs_amount(parties, inv, tax_details, vouchers, adv_vouchers): ) cumulative_threshold = tax_details.get("cumulative_threshold", 0) + advance_adjusted = get_advance_adjusted_in_invoice(inv) current_invoice_total = get_invoice_total_without_tcs(inv, tax_details) - total_invoiced_amt = current_invoice_total + invoiced_amt + advance_amt - credit_note_amt + total_invoiced_amt = ( + current_invoice_total + invoiced_amt + advance_amt - credit_note_amt - advance_adjusted + ) if cumulative_threshold and total_invoiced_amt >= cumulative_threshold: chargeable_amt = total_invoiced_amt - cumulative_threshold @@ -645,6 +648,14 @@ def get_tcs_amount(parties, inv, tax_details, vouchers, adv_vouchers): return tcs_amount +def get_advance_adjusted_in_invoice(inv): + advances_adjusted = 0 + for row in inv.get("advances", []): + advances_adjusted += row.allocated_amount + + return advances_adjusted + + def get_invoice_total_without_tcs(inv, tax_details): tcs_tax_row = [d for d in inv.taxes if d.account_head == tax_details.account_head] tcs_tax_row_amount = tcs_tax_row[0].base_tax_amount if tcs_tax_row else 0 From 2b4cd0a9bb7696234c4c40425cb9ee5669f61297 Mon Sep 17 00:00:00 2001 From: ljain112 Date: Tue, 8 Oct 2024 11:24:44 +0530 Subject: [PATCH 304/734] test: added test cases for the tcs deduction for advances adjusted. (cherry picked from commit efe238cefd627125d1c293297024e9ad5290d6dd) --- .../test_tax_withholding_category.py | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py index 1e3939d98a4..24c9265eecd 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py @@ -210,6 +210,46 @@ class TestTaxWithholdingCategory(FrappeTestCase): d.reload() d.cancel() + def test_tcs_on_allocated_advance_payments(self): + frappe.db.set_value( + "Customer", "Test TCS Customer", "tax_withholding_category", "Cumulative Threshold TCS" + ) + + vouchers = [] + + # create advance payment + pe = create_payment_entry( + payment_type="Receive", party_type="Customer", party="Test TCS Customer", paid_amount=30000 + ) + pe.paid_from = "Debtors - _TC" + pe.paid_to = "Cash - _TC" + pe.submit() + vouchers.append(pe) + + si = create_sales_invoice(customer="Test TCS Customer", rate=50000) + advances = si.get_advance_entries() + si.append( + "advances", + { + "reference_type": advances[0].reference_type, + "reference_name": advances[0].reference_name, + "advance_amount": advances[0].amount, + "allocated_amount": 30000, + }, + ) + si.submit() + vouchers.append(si) + + # assert tax collection on total invoice ,advance payment adjusted should be excluded. + tcs_charged = sum([d.base_tax_amount for d in si.taxes if d.account_head == "TCS - _TC"]) + # tcs = (inv amt)50000+(adv amt)30000-(adv adj) 30000 - threshold(30000) * rate 10% + self.assertEqual(tcs_charged, 2000) + + # cancel invoice and payments to avoid clashing + for d in reversed(vouchers): + d.reload() + d.cancel() + def test_tds_calculation_on_net_total(self): frappe.db.set_value( "Supplier", "Test TDS Supplier4", "tax_withholding_category", "Cumulative Threshold TDS" From fc9a3c0c92cf2537e0b871c8a59a927517150d7f Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 8 Oct 2024 17:40:22 +0530 Subject: [PATCH 305/734] fix: Unknown column 'serial_no' in 'field list' (backport #43515) (#43569) fix: Unknown column 'serial_no' in 'field list' (#43515) (cherry picked from commit 69127e8609c701d1d733f113af59f12076368905) Co-authored-by: rohitwaghchaure --- erpnext/manufacturing/doctype/work_order/work_order.py | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index 057c49949ec..218ab2f2bf8 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -520,7 +520,6 @@ class WorkOrder(Document): def delete_auto_created_batch_and_serial_no(self): for row in frappe.get_all("Serial No", filters={"work_order": self.name}): frappe.delete_doc("Serial No", row.name) - self.db_set("serial_no", "") for row in frappe.get_all("Batch", filters={"reference_name": self.name}): frappe.delete_doc("Batch", row.name) From cf0fa0db7b7c2a9a5dbd3802ddec1db757afeb24 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 8 Oct 2024 17:40:52 +0530 Subject: [PATCH 306/734] fix: validation for corrective job card (backport #43555) (#43558) * fix: validation for corrective job card (#43555) (cherry picked from commit 7a0a893d08217b3131f2a1d38db7c0be51aed82b) # Conflicts: # erpnext/manufacturing/doctype/job_card/job_card.py * chore: fix conflicts * chore: fix linters issue --------- Co-authored-by: rohitwaghchaure --- erpnext/manufacturing/doctype/job_card/job_card.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py index abea4c86279..de8116b296c 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.py +++ b/erpnext/manufacturing/doctype/job_card/job_card.py @@ -669,7 +669,7 @@ class JobCard(Document): self.set_transferred_qty() def validate_transfer_qty(self): - if self.items and self.transferred_qty < self.for_quantity: + if not self.is_corrective_job_card and self.items and self.transferred_qty < self.for_quantity: frappe.throw( _( "Materials needs to be transferred to the work in progress warehouse for the job card {0}" From 5660e8b26dc5cba40b18bbd532638ac52f84451a Mon Sep 17 00:00:00 2001 From: Nihantra Patel Date: Tue, 8 Oct 2024 18:48:11 +0530 Subject: [PATCH 307/734] fix: add include closed orders option in so/po trends report v15 --- .../report/purchase_order_trends/purchase_order_trends.js | 7 +++++++ erpnext/controllers/trends.py | 6 ++++-- .../report/sales_order_trends/sales_order_trends.js | 7 +++++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/erpnext/buying/report/purchase_order_trends/purchase_order_trends.js b/erpnext/buying/report/purchase_order_trends/purchase_order_trends.js index 56684a8659b..9b193a34d83 100644 --- a/erpnext/buying/report/purchase_order_trends/purchase_order_trends.js +++ b/erpnext/buying/report/purchase_order_trends/purchase_order_trends.js @@ -2,3 +2,10 @@ // License: GNU General Public License v3. See license.txt frappe.query_reports["Purchase Order Trends"] = $.extend({}, erpnext.purchase_trends_filters); + +frappe.query_reports["Purchase Order Trends"]["filters"].push({ + fieldname: "include_closed_orders", + label: __("Include Closed Orders"), + fieldtype: "Check", + default: 0, +}); diff --git a/erpnext/controllers/trends.py b/erpnext/controllers/trends.py index 18fe7767c5d..7f07466b3bc 100644 --- a/erpnext/controllers/trends.py +++ b/erpnext/controllers/trends.py @@ -74,8 +74,10 @@ def get_data(filters, conditions): if conditions["based_on_select"] in ["t1.project,", "t2.project,"]: cond = " and " + conditions["based_on_select"][:-1] + " IS Not NULL" - if conditions.get("trans") in ["Sales Order", "Purchase Order"]: - cond += " and t1.status != 'Closed'" + + if not filters.get("include_closed_orders"): + if conditions.get("trans") in ["Sales Order", "Purchase Order"]: + cond += " and t1.status != 'Closed'" if conditions.get("trans") == "Quotation" and filters.get("group_by") == "Customer": cond += " and t1.quotation_to = 'Customer'" diff --git a/erpnext/selling/report/sales_order_trends/sales_order_trends.js b/erpnext/selling/report/sales_order_trends/sales_order_trends.js index 28bd5504930..a44353cf54b 100644 --- a/erpnext/selling/report/sales_order_trends/sales_order_trends.js +++ b/erpnext/selling/report/sales_order_trends/sales_order_trends.js @@ -2,3 +2,10 @@ // License: GNU General Public License v3. See license.txt frappe.query_reports["Sales Order Trends"] = $.extend({}, erpnext.sales_trends_filters); + +frappe.query_reports["Sales Order Trends"]["filters"].push({ + fieldname: "include_closed_orders", + label: __("Include Closed Orders"), + fieldtype: "Check", + default: 0, +}); From 60508a97061ad910d7c9324ee1da7be64d326884 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 9 Oct 2024 09:06:20 +0530 Subject: [PATCH 308/734] fix: 'NoneType' object has no attribute 'has_serial_no' (backport #43514) (#43574) fix: 'NoneType' object has no attribute 'has_serial_no' (#43514) (cherry picked from commit 6ddda6c949c582cd6f991637620f7082878f0495) Co-authored-by: rohitwaghchaure --- erpnext/controllers/stock_controller.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 9fb0cc88cfb..537b37facf4 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -357,6 +357,9 @@ class StockController(AccountsController): @frappe.request_cache def is_serial_batch_item(self, item_code) -> bool: + if not frappe.db.exists("Item", item_code): + frappe.throw(_("Item {0} does not exist.").format(bold(item_code))) + item_details = frappe.db.get_value("Item", item_code, ["has_serial_no", "has_batch_no"], as_dict=1) if item_details.has_serial_no or item_details.has_batch_no: From 83ce3dd91598b8c2aad65e7f934c16131a9dbdc7 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 9 Oct 2024 09:06:34 +0530 Subject: [PATCH 309/734] fix: Accepted and Rejected warehouse cannot be same (backport #43568) (#43573) fix: Accepted and Rejected warehouse cannot be same (#43568) (cherry picked from commit 5130f7d411a8f27c1b51064611b739aaf8e09869) Co-authored-by: rohitwaghchaure --- erpnext/public/js/controllers/buying.js | 4 +-- .../js/utils/serial_no_batch_selector.js | 25 ++++++++++++++----- .../serial_and_batch_bundle.py | 12 ++++++--- 3 files changed, 29 insertions(+), 12 deletions(-) diff --git a/erpnext/public/js/controllers/buying.js b/erpnext/public/js/controllers/buying.js index 1d0680de861..202efe157f0 100644 --- a/erpnext/public/js/controllers/buying.js +++ b/erpnext/public/js/controllers/buying.js @@ -392,7 +392,7 @@ erpnext.buying = { item[field] = r.message[field]; }); - item.type_of_transaction = item.rejected_qty > 0 ? "Inward" : "Outward"; + item.type_of_transaction = !doc.is_return > 0 ? "Inward" : "Outward"; item.is_rejected = true; new erpnext.SerialBatchPackageSelector( @@ -404,7 +404,7 @@ erpnext.buying = { } let update_values = { - "serial_and_batch_bundle": r.name, + "rejected_serial_and_batch_bundle": r.name, "use_serial_batch_fields": 0, "rejected_qty": qty / flt(item.conversion_factor || 1, precision("conversion_factor", item)) } diff --git a/erpnext/public/js/utils/serial_no_batch_selector.js b/erpnext/public/js/utils/serial_no_batch_selector.js index 46ff6366de8..1045965e43d 100644 --- a/erpnext/public/js/utils/serial_no_batch_selector.js +++ b/erpnext/public/js/utils/serial_no_batch_selector.js @@ -96,7 +96,12 @@ erpnext.SerialBatchPackageSelector = class SerialNoBatchBundleUpdate { options: "Warehouse", default: this.get_warehouse(), onchange: () => { - this.item.warehouse = this.dialog.get_value("warehouse"); + if (this.item?.is_rejected) { + this.item.rejected_warehouse = this.dialog.get_value("warehouse"); + } else { + this.item.warehouse = this.dialog.get_value("warehouse"); + } + this.get_auto_data(); }, get_query: () => { @@ -282,10 +287,6 @@ erpnext.SerialBatchPackageSelector = class SerialNoBatchBundleUpdate { return fields; } - set_serial_nos_from_series() {} - - set_batch_nos_from_series() {} - set_serial_nos_from_range() { const serial_no_range = this.dialog.get_value("serial_no_range"); @@ -508,12 +509,17 @@ erpnext.SerialBatchPackageSelector = class SerialNoBatchBundleUpdate { based_on = "FIFO"; } + let warehouse = this.item.warehouse || this.item.s_warehouse; + if (this.item?.is_rejected) { + warehouse = this.item.rejected_warehouse; + } + if (qty) { frappe.call({ method: "erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle.get_auto_data", args: { item_code: this.item.item_code, - warehouse: this.item.warehouse || this.item.s_warehouse, + warehouse: warehouse, has_serial_no: this.item.has_serial_no, has_batch_no: this.item.has_batch_no, qty: qty, @@ -627,6 +633,10 @@ erpnext.SerialBatchPackageSelector = class SerialNoBatchBundleUpdate { frappe.throw(__("Please select a Warehouse")); } + if (this.item?.is_rejected && this.item.rejected_warehouse === this.item.warehouse) { + frappe.throw(__("Rejected Warehouse and Accepted Warehouse cannot be same.")); + } + frappe .call({ method: "erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle.add_serial_batch_ledgers", @@ -701,5 +711,8 @@ erpnext.SerialBatchPackageSelector = class SerialNoBatchBundleUpdate { }); this.dialog.fields_dict.entries.grid.refresh(); + if (this.dialog.fields_dict.entries.df.data?.length) { + this.dialog.set_value("enter_manually", 0); + } } }; diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py index 62f95f5b2e8..94ec8675db8 100644 --- a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py +++ b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py @@ -1308,8 +1308,12 @@ def add_serial_batch_ledgers(entries, child_row, doc, warehouse, do_not_save=Fal if parent_doc and isinstance(parent_doc, str): parent_doc = parse_json(parent_doc) - if frappe.db.exists("Serial and Batch Bundle", child_row.serial_and_batch_bundle): - sb_doc = update_serial_batch_no_ledgers(entries, child_row, parent_doc, warehouse) + bundle = child_row.serial_and_batch_bundle + if child_row.get("is_rejected"): + bundle = child_row.rejected_serial_and_batch_bundle + + if frappe.db.exists("Serial and Batch Bundle", bundle): + sb_doc = update_serial_batch_no_ledgers(bundle, entries, child_row, parent_doc, warehouse) else: sb_doc = create_serial_batch_no_ledgers( entries, child_row, parent_doc, warehouse, do_not_save=do_not_save @@ -1412,8 +1416,8 @@ def get_type_of_transaction(parent_doc, child_row): return type_of_transaction -def update_serial_batch_no_ledgers(entries, child_row, parent_doc, warehouse=None) -> object: - doc = frappe.get_doc("Serial and Batch Bundle", child_row.serial_and_batch_bundle) +def update_serial_batch_no_ledgers(bundle, entries, child_row, parent_doc, warehouse=None) -> object: + doc = frappe.get_doc("Serial and Batch Bundle", bundle) doc.voucher_detail_no = child_row.name doc.posting_date = parent_doc.posting_date doc.posting_time = parent_doc.posting_time From 355ba2f6324c14de3adfdae398089d7c11903347 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 9 Oct 2024 13:38:43 +0530 Subject: [PATCH 310/734] fix: the purchase receipt trends and delivery note trends report (backport #43585) (#43587) * fix: fix the purchase receipt trends and delivery note trends report (cherry picked from commit 2e9dda1588533d425ec3b22b089fcafebb17856d) * fix: trends date filter issue --formatter (cherry picked from commit b3e4463a4f53c8949b165374d2d70e135c07b1e5) --------- Co-authored-by: Vishv-silveroak <108357657+Vishv-024@users.noreply.github.com> Co-authored-by: Nihantra C. Patel <141945075+Nihantra-Patel@users.noreply.github.com> --- erpnext/controllers/trends.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/controllers/trends.py b/erpnext/controllers/trends.py index 7f07466b3bc..24d11e6050a 100644 --- a/erpnext/controllers/trends.py +++ b/erpnext/controllers/trends.py @@ -69,7 +69,7 @@ def get_data(filters, conditions): "Delivery Note", ]: posting_date = "t1.posting_date" - if filters.period_based_on: + if filters.period_based_on and conditions.get("trans") in ["Sales Invoice", "Purchase Invoice"]: posting_date = "t1." + filters.period_based_on if conditions["based_on_select"] in ["t1.project,", "t2.project,"]: @@ -224,7 +224,7 @@ def period_wise_columns_query(filters, trans): if trans in ["Purchase Receipt", "Delivery Note", "Purchase Invoice", "Sales Invoice"]: trans_date = "posting_date" - if filters.period_based_on: + if filters.period_based_on and trans in ["Purchase Invoice", "Sales Invoice"]: trans_date = filters.period_based_on else: trans_date = "transaction_date" From 9e109acec79823fa9bda46b4aeaf2909b941883c Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 9 Oct 2024 13:43:02 +0530 Subject: [PATCH 311/734] fix: allow to change the batch in the subcontracting receipt (backport #43584) (#43588) fix: allow to change the batch in the subcontracting receipt (#43584) (cherry picked from commit fc67867a60db9d89752f395301b55322c25d8eaa) Co-authored-by: rohitwaghchaure --- .../controllers/subcontracting_controller.py | 64 +++++++++++++------ .../subcontracting_receipt.js | 12 ++-- .../subcontracting_receipt.py | 9 ++- .../test_subcontracting_receipt.py | 60 +++++++++++++++++ 4 files changed, 120 insertions(+), 25 deletions(-) diff --git a/erpnext/controllers/subcontracting_controller.py b/erpnext/controllers/subcontracting_controller.py index a6727ef8826..f6f6742cc87 100644 --- a/erpnext/controllers/subcontracting_controller.py +++ b/erpnext/controllers/subcontracting_controller.py @@ -576,30 +576,56 @@ class SubcontractingController(StockController): self.__set_batch_nos(bom_item, item_row, rm_obj, qty) if self.doctype == "Subcontracting Receipt" and not use_serial_batch_fields: - args = frappe._dict( - { - "item_code": rm_obj.rm_item_code, - "warehouse": self.supplier_warehouse, - "posting_date": self.posting_date, - "posting_time": self.posting_time, - "qty": -1 * flt(rm_obj.consumed_qty), - "actual_qty": -1 * flt(rm_obj.consumed_qty), - "voucher_type": self.doctype, - "voucher_no": self.name, - "voucher_detail_no": item_row.name, - "company": self.company, - "allow_zero_valuation": 1, - } - ) - rm_obj.serial_and_batch_bundle = self.__set_serial_and_batch_bundle( item_row, rm_obj, rm_obj.consumed_qty ) - if rm_obj.serial_and_batch_bundle: - args["serial_and_batch_bundle"] = rm_obj.serial_and_batch_bundle + self.set_rate_for_supplied_items(rm_obj, item_row) - rm_obj.rate = get_incoming_rate(args) + def update_rate_for_supplied_items(self): + if self.doctype != "Subcontracting Receipt": + return + + for row in self.supplied_items: + item_row = None + if row.reference_name: + item_row = self.get_item_row(row.reference_name) + + if not item_row: + continue + + self.set_rate_for_supplied_items(row, item_row) + + def get_item_row(self, reference_name): + for item in self.items: + if item.name == reference_name: + return item + + def set_rate_for_supplied_items(self, rm_obj, item_row): + args = frappe._dict( + { + "item_code": rm_obj.rm_item_code, + "warehouse": self.supplier_warehouse, + "posting_date": self.posting_date, + "posting_time": self.posting_time, + "qty": -1 * flt(rm_obj.consumed_qty), + "actual_qty": -1 * flt(rm_obj.consumed_qty), + "voucher_type": self.doctype, + "voucher_no": self.name, + "voucher_detail_no": item_row.name, + "company": self.company, + "allow_zero_valuation": 1, + } + ) + + if rm_obj.serial_and_batch_bundle: + args["serial_and_batch_bundle"] = rm_obj.serial_and_batch_bundle + + if rm_obj.use_serial_batch_fields: + args["batch_no"] = rm_obj.batch_no + args["serial_no"] = rm_obj.serial_no + + rm_obj.rate = get_incoming_rate(args) def __set_batch_nos(self, bom_item, item_row, rm_obj, qty): key = (rm_obj.rm_item_code, item_row.item_code, item_row.get(self.subcontract_data.order_field)) diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js index 83113a223c2..2aaf8a8adcd 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js @@ -249,11 +249,15 @@ frappe.ui.form.on("Subcontracting Receipt", { }); frm.set_query("batch_no", "supplied_items", (doc, cdt, cdn) => { - var row = locals[cdt][cdn]; + let row = locals[cdt][cdn]; + let filters = { + item_code: row.rm_item_code, + warehouse: doc.supplier_warehouse, + }; + return { - filters: { - item: row.rm_item_code, - }, + query: "erpnext.controllers.queries.get_batch_no", + filters: filters, }; }); diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py index 48203167187..db912514988 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py @@ -237,9 +237,14 @@ class SubcontractingReceipt(SubcontractingController): frappe.db.get_single_value("Buying Settings", "backflush_raw_materials_of_subcontract_based_on") == "BOM" and self.supplied_items - and not any(item.serial_and_batch_bundle for item in self.supplied_items) ): - self.supplied_items = [] + if not any( + item.serial_and_batch_bundle or item.batch_no or item.serial_no + for item in self.supplied_items + ): + self.supplied_items = [] + else: + self.update_rate_for_supplied_items() @frappe.whitelist() def get_scrap_items(self, recalculate_rate=False): diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py index 8ff5c8f27b0..27ad7dbebdf 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py @@ -1361,6 +1361,66 @@ class TestSubcontractingReceipt(FrappeTestCase): frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 1) + def test_change_batch_for_raw_materials(self): + set_backflush_based_on("BOM") + + fg_item = make_item(properties={"is_stock_item": 1, "is_sub_contracted_item": 1}).name + rm_item1 = make_item( + properties={ + "is_stock_item": 1, + "has_batch_no": 1, + "create_new_batch": 1, + "batch_number_series": "BNGS-.####", + } + ).name + + bom = make_bom(item=fg_item, raw_materials=[rm_item1]) + second_batch_no = None + for row in bom.items: + se = make_stock_entry( + item_code=row.item_code, + qty=1, + target="_Test Warehouse 1 - _TC", + rate=300, + ) + + se.reload() + se1 = make_stock_entry( + item_code=row.item_code, + qty=1, + target="_Test Warehouse 1 - _TC", + rate=300, + ) + + se1.reload() + + second_batch_no = get_batch_from_bundle(se1.items[0].serial_and_batch_bundle) + + service_items = [ + { + "warehouse": "_Test Warehouse - _TC", + "item_code": "Subcontracted Service Item 1", + "qty": 1, + "rate": 100, + "fg_item": fg_item, + "fg_item_qty": 1, + }, + ] + sco = get_subcontracting_order(service_items=service_items) + scr = make_subcontracting_receipt(sco.name) + scr.save() + scr.reload() + + scr.supplied_items[0].batch_no = second_batch_no + scr.supplied_items[0].use_serial_batch_fields = 1 + scr.submit() + scr.reload() + + batch_no = get_batch_from_bundle(scr.supplied_items[0].serial_and_batch_bundle) + self.assertEqual(batch_no, second_batch_no) + self.assertEqual(scr.items[0].rm_cost_per_qty, 300) + self.assertEqual(scr.items[0].service_cost_per_qty, 100) + def make_return_subcontracting_receipt(**args): args = frappe._dict(args) From 029021f035b31768acfb94386219e6602cbcbc46 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 9 Oct 2024 15:41:21 +0530 Subject: [PATCH 312/734] fix: production plan bom error (backport #43591) (#43594) fix: production plan bom error (#43591) (cherry picked from commit ab171326f337887ce323ad073e0a1882ed19c32d) Co-authored-by: rohitwaghchaure --- .../production_plan/production_plan.js | 2 +- .../production_plan/production_plan.py | 41 ++++++++++++++++--- 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.js b/erpnext/manufacturing/doctype/production_plan/production_plan.js index 5b4ef233926..aba213ebca4 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.js +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.js @@ -277,7 +277,7 @@ frappe.ui.form.on("Production Plan", { frm.clear_table("prod_plan_references"); frappe.call({ - method: "get_items", + method: "combine_so_items", freeze: true, doc: frm.doc, callback: function () { diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index 7d3aa000c87..3f82a75d302 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -269,6 +269,31 @@ class ProductionPlan(Document): {"material_request": data.name, "material_request_date": data.transaction_date}, ) + @frappe.whitelist() + def combine_so_items(self): + if self.combine_items and self.po_items and len(self.po_items) > 0: + items = [] + for row in self.po_items: + items.append( + frappe._dict( + { + "parent": row.sales_order, + "item_code": row.item_code, + "warehouse": row.warehouse, + "qty": row.pending_qty, + "pending_qty": row.pending_qty, + "conversion_factor": 1.0, + "description": row.description, + "bom_no": row.bom_no, + } + ) + ) + + self.set("po_items", []) + self.add_items(items) + else: + self.get_items() + @frappe.whitelist() def get_items(self): self.set("po_items", []) @@ -435,24 +460,28 @@ class ProductionPlan(Document): item_details = get_item_details(data.item_code, throw=False) if self.combine_items: - if item_details.bom_no in refs: - refs[item_details.bom_no]["so_details"].append( + bom_no = item_details.bom_no + if data.get("bom_no"): + bom_no = data.get("bom_no") + + if bom_no in refs: + refs[bom_no]["so_details"].append( {"sales_order": data.parent, "sales_order_item": data.name, "qty": data.pending_qty} ) - refs[item_details.bom_no]["qty"] += data.pending_qty + refs[bom_no]["qty"] += data.pending_qty continue else: - refs[item_details.bom_no] = { + refs[bom_no] = { "qty": data.pending_qty, "po_item_ref": data.name, "so_details": [], } - refs[item_details.bom_no]["so_details"].append( + refs[bom_no]["so_details"].append( {"sales_order": data.parent, "sales_order_item": data.name, "qty": data.pending_qty} ) - bom_no = data.bom_no or item_details and item_details.bom_no or "" + bom_no = data.bom_no or item_details and item_details.get("bom_no") or "" if not bom_no: continue From 120b481c4a9038c832d090f3a03a1fd7d5e2a1c3 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 9 Oct 2024 15:41:38 +0530 Subject: [PATCH 313/734] fix: make LCV button not working for PI and PR (backport #43592) (#43593) fix: make LCV button not working for PI and PR (#43592) (cherry picked from commit 48a12e72139e12af4bf80c3c8dce0fef8069fda0) Co-authored-by: rohitwaghchaure --- .../purchase_invoice/purchase_invoice.js | 47 ++++++++++++------- .../purchase_receipt/purchase_receipt.js | 43 ++++++++++------- .../purchase_receipt/purchase_receipt.py | 23 +++++++++ 3 files changed, 80 insertions(+), 33 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index 2d5cbb9e6c3..c78db5b86d6 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -561,11 +561,12 @@ frappe.ui.form.on("Purchase Invoice", { frm.custom_make_buttons = { "Purchase Invoice": "Return / Debit Note", "Payment Entry": "Payment", - "Landed Cost Voucher": function () { - frm.trigger("create_landed_cost_voucher"); - }, }; + if (frm.doc.update_stock) { + frm.custom_make_buttons["Landed Cost Voucher"] = "Landed Cost Voucher"; + } + frm.set_query("additional_discount_account", function () { return { filters: { @@ -607,20 +608,6 @@ frappe.ui.form.on("Purchase Invoice", { }); }, - create_landed_cost_voucher: function (frm) { - let lcv = frappe.model.get_new_doc("Landed Cost Voucher"); - lcv.company = frm.doc.company; - - let lcv_receipt = frappe.model.get_new_doc("Landed Cost Purchase Invoice"); - lcv_receipt.receipt_document_type = "Purchase Invoice"; - lcv_receipt.receipt_document = frm.doc.name; - lcv_receipt.supplier = frm.doc.supplier; - lcv_receipt.grand_total = frm.doc.grand_total; - lcv.purchase_receipts = [lcv_receipt]; - - frappe.set_route("Form", lcv.doctype, lcv.name); - }, - add_custom_buttons: function (frm) { if (frm.doc.docstatus == 1 && frm.doc.per_received < 100) { frm.add_custom_button( @@ -645,6 +632,32 @@ frappe.ui.form.on("Purchase Invoice", { __("View") ); } + + if (frm.doc.docstatus === 1 && frm.doc.update_stock) { + frm.add_custom_button( + __("Landed Cost Voucher"), + () => { + frm.events.make_lcv(frm); + }, + __("Create") + ); + } + }, + + make_lcv(frm) { + frappe.call({ + method: "erpnext.stock.doctype.purchase_receipt.purchase_receipt.make_lcv", + args: { + doctype: frm.doc.doctype, + docname: frm.doc.name, + }, + callback: (r) => { + if (r.message) { + var doc = frappe.model.sync(r.message); + frappe.set_route("Form", doc[0].doctype, doc[0].name); + } + }, + }); }, onload: function (frm) { diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js index bfac4381a06..bcecf8be14d 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js @@ -11,25 +11,10 @@ erpnext.buying.setup_buying_controller(); frappe.ui.form.on("Purchase Receipt", { setup: (frm) => { - frm.make_methods = { - "Landed Cost Voucher": () => { - let lcv = frappe.model.get_new_doc("Landed Cost Voucher"); - lcv.company = frm.doc.company; - - let lcv_receipt = frappe.model.get_new_doc("Landed Cost Purchase Receipt"); - lcv_receipt.receipt_document_type = "Purchase Receipt"; - lcv_receipt.receipt_document = frm.doc.name; - lcv_receipt.supplier = frm.doc.supplier; - lcv_receipt.grand_total = frm.doc.grand_total; - lcv.purchase_receipts = [lcv_receipt]; - - frappe.set_route("Form", lcv.doctype, lcv.name); - }, - }; - frm.custom_make_buttons = { "Stock Entry": "Return", "Purchase Invoice": "Purchase Invoice", + "Landed Cost Voucher": "Landed Cost Voucher", }; frm.set_query("expense_account", "items", function () { @@ -114,9 +99,35 @@ frappe.ui.form.on("Purchase Receipt", { } } + if (frm.doc.docstatus === 1) { + frm.add_custom_button( + __("Landed Cost Voucher"), + () => { + frm.events.make_lcv(frm); + }, + __("Create") + ); + } + frm.events.add_custom_buttons(frm); }, + make_lcv(frm) { + frappe.call({ + method: "erpnext.stock.doctype.purchase_receipt.purchase_receipt.make_lcv", + args: { + doctype: frm.doc.doctype, + docname: frm.doc.name, + }, + callback: (r) => { + if (r.message) { + var doc = frappe.model.sync(r.message); + frappe.set_route("Form", doc[0].doctype, doc[0].name); + } + }, + }); + }, + add_custom_buttons: function (frm) { if (frm.doc.docstatus == 0) { frm.add_custom_button( diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 8b046203eee..228bc35693b 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -1370,3 +1370,26 @@ def get_item_account_wise_additional_cost(purchase_document): @erpnext.allow_regional def update_regional_gl_entries(gl_list, doc): return + + +@frappe.whitelist() +def make_lcv(doctype, docname): + landed_cost_voucher = frappe.new_doc("Landed Cost Voucher") + + details = frappe.db.get_value(doctype, docname, ["supplier", "company", "base_grand_total"], as_dict=1) + + landed_cost_voucher.company = details.company + + landed_cost_voucher.append( + "purchase_receipts", + { + "receipt_document_type": doctype, + "receipt_document": docname, + "grand_total": details.base_grand_total, + "supplier": details.supplier, + }, + ) + + landed_cost_voucher.get_items_from_purchase_receipts() + + return landed_cost_voucher.as_dict() From d46cf46375e48d41cc10284305b9ca1b43a6c828 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 9 Oct 2024 12:02:42 +0000 Subject: [PATCH 314/734] chore(release): Bumped to Version 15.38.1 ## [15.38.1](https://github.com/frappe/erpnext/compare/v15.38.0...v15.38.1) (2024-10-09) ### Bug Fixes * 'NoneType' object has no attribute 'has_serial_no' (backport [#43514](https://github.com/frappe/erpnext/issues/43514)) ([#43574](https://github.com/frappe/erpnext/issues/43574)) ([60508a9](https://github.com/frappe/erpnext/commit/60508a97061ad910d7c9324ee1da7be64d326884)) * [#42014](https://github.com/frappe/erpnext/issues/42014) --resolve conflicts ([85d7405](https://github.com/frappe/erpnext/commit/85d74050e1b35126e4c9b2d4a7cb1bc3c27ae835)) * Accepted and Rejected warehouse cannot be same (backport [#43568](https://github.com/frappe/erpnext/issues/43568)) ([#43573](https://github.com/frappe/erpnext/issues/43573)) ([83ce3dd](https://github.com/frappe/erpnext/commit/83ce3dd91598b8c2aad65e7f934c16131a9dbdc7)) * add include closed orders option in so/po trends report v15 ([5660e8b](https://github.com/frappe/erpnext/commit/5660e8b26dc5cba40b18bbd532638ac52f84451a)) * add parenttype condition for item table in Purchase Register Report ([8ce81a0](https://github.com/frappe/erpnext/commit/8ce81a058a074ecf11c95137e3c14e30480f241e)) * Add removed test code `https://github.com/frappe/erpnext/commit/b41f10c1b98b01a181a6f9dbdf2531b108dc3bae` ([30fd11f](https://github.com/frappe/erpnext/commit/30fd11f138ce468f5c01f3c5a265eca071293b30)) * allow to change the batch in the subcontracting receipt (backport [#43584](https://github.com/frappe/erpnext/issues/43584)) ([#43588](https://github.com/frappe/erpnext/issues/43588)) ([9e109ac](https://github.com/frappe/erpnext/commit/9e109acec79823fa9bda46b4aeaf2909b941883c)) * create Account Closing Balance even though there are no transaction in period ([d6f70f5](https://github.com/frappe/erpnext/commit/d6f70f533acd24ddb1440cbced74336f59e109e5)) * creation of contact, customer, opportunity, quotation and prospect from lead ([ef10c4e](https://github.com/frappe/erpnext/commit/ef10c4ea4f969a0c6ef12f3733324ae43e689675)) * creation of contact, customer, opportunity, quotation and prospect from lead --prettier ([5a2a404](https://github.com/frappe/erpnext/commit/5a2a404a50ebb40507abbd34dc6a1975db4940ca)) * deduct advances adjusted for threshold check for tcs ([6decb7c](https://github.com/frappe/erpnext/commit/6decb7cc34a02c1bf8d33e019ead5cafc3f65363)) * do not include advances for tds vouchers ([ee8485a](https://github.com/frappe/erpnext/commit/ee8485a54ac0439e9cf3a6c8b9b7e0a02f3ae53f)) * frappe dependency update ([0a70b3f](https://github.com/frappe/erpnext/commit/0a70b3ffccf9367eb0a650b18e78fbc3cf793f90)) * include parent item group in query ([55464c7](https://github.com/frappe/erpnext/commit/55464c79c4a068146b7254f57c9575b44c3966ac)) * make LCV button not working for PI and PR (backport [#43592](https://github.com/frappe/erpnext/issues/43592)) ([#43593](https://github.com/frappe/erpnext/issues/43593)) ([120b481](https://github.com/frappe/erpnext/commit/120b481c4a9038c832d090f3a03a1fd7d5e2a1c3)) * multiple issues in Payment Request ([#42427](https://github.com/frappe/erpnext/issues/42427)) ([ea69ba7](https://github.com/frappe/erpnext/commit/ea69ba7cd8967af626d8efee1c4575d980533923)) * production plan bom error (backport [#43591](https://github.com/frappe/erpnext/issues/43591)) ([#43594](https://github.com/frappe/erpnext/issues/43594)) ([029021f](https://github.com/frappe/erpnext/commit/029021f035b31768acfb94386219e6602cbcbc46)) * read only filters in multidialog fields (backport [#43503](https://github.com/frappe/erpnext/issues/43503)) ([#43513](https://github.com/frappe/erpnext/issues/43513)) ([d69a974](https://github.com/frappe/erpnext/commit/d69a974a4de50e0208127ea3b078acbd0fa0364d)) * Remove `advance_payment_status` uses ([907e3af](https://github.com/frappe/erpnext/commit/907e3af1b0f5a08617f637573f3abc3e3eb56253)) * Remove unreference method ([770bc1c](https://github.com/frappe/erpnext/commit/770bc1c293b106c1bb3ec5077ce73c068973d179)) * Remove unused field ([e785928](https://github.com/frappe/erpnext/commit/e785928c0fcb5e2549302ea77217fe88f9797717)) * Remove unused function `get_paid_amount_against_order` ([7591662](https://github.com/frappe/erpnext/commit/75916629c857465a1a6efadd2fd0c77624c93cf5)) * Separate `on_submit` and `before_submit` of PR ([dbd7b83](https://github.com/frappe/erpnext/commit/dbd7b83204d1e36790d838a5c96c55d4d93464bd)) * the purchase receipt trends and delivery note trends report (backport [#43585](https://github.com/frappe/erpnext/issues/43585)) ([#43587](https://github.com/frappe/erpnext/issues/43587)) ([355ba2f](https://github.com/frappe/erpnext/commit/355ba2f6324c14de3adfdae398089d7c11903347)) * Unknown column 'serial_no' in 'field list' (backport [#43515](https://github.com/frappe/erpnext/issues/43515)) ([#43569](https://github.com/frappe/erpnext/issues/43569)) ([fc9a3c0](https://github.com/frappe/erpnext/commit/fc9a3c0c92cf2537e0b871c8a59a927517150d7f)) * Update Values before `after_mapping` hook is called ([#42682](https://github.com/frappe/erpnext/issues/42682)) ([6770610](https://github.com/frappe/erpnext/commit/6770610c6d62a2dcddf95221ed85ee37c5a56625)) * validation for corrective job card (backport [#43555](https://github.com/frappe/erpnext/issues/43555)) ([#43558](https://github.com/frappe/erpnext/issues/43558)) ([cf0fa0d](https://github.com/frappe/erpnext/commit/cf0fa0db7b7c2a9a5dbd3802ddec1db757afeb24)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index bbb02e2ed0d..784ac134a56 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.38.0" +__version__ = "15.38.1" def get_default_company(user=None): From d604b12d5111e9dd5e0fcd3bc35f06c87284c349 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 11 Oct 2024 16:22:22 +0530 Subject: [PATCH 315/734] fix: show incorrect entries filter in Stock Ledger Invariant Check report (backport #43619) (#43622) fix: show incorrect entries filter in Stock Ledger Invariant Check report (#43619) fix: show incorrect entry filter in Stock Ledger Invariant Check report (cherry picked from commit 8beee1982f7e04cba7a4887d74ac77469a9648cb) Co-authored-by: rohitwaghchaure --- .../stock_ledger_invariant_check.js | 6 ++++ .../stock_ledger_invariant_check.py | 29 +++++++++++++++++-- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.js b/erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.js index 1b05d3a65e4..df65654e36b 100644 --- a/erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.js +++ b/erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.js @@ -32,6 +32,12 @@ frappe.query_reports["Stock Ledger Invariant Check"] = { mandatory: 1, options: "Warehouse", }, + { + fieldname: "show_incorrect_entries", + fieldtype: "Check", + label: "Show Incorrect Entries", + default: 0, + }, ], formatter(value, row, column, data, default_formatter) { diff --git a/erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.py b/erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.py index 6fc109f1c6e..5cf653e3851 100644 --- a/erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.py +++ b/erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.py @@ -5,7 +5,7 @@ import json import frappe from frappe import _ -from frappe.utils import get_link_to_form, parse_json +from frappe.utils import cint, flt, get_link_to_form, parse_json SLE_FIELDS = ( "name", @@ -36,7 +36,7 @@ def execute(filters=None): def get_data(filters): sles = get_stock_ledger_entries(filters) - return add_invariant_check_fields(sles) + return add_invariant_check_fields(sles, filters) def get_stock_ledger_entries(filters): @@ -48,9 +48,12 @@ def get_stock_ledger_entries(filters): ) -def add_invariant_check_fields(sles): +def add_invariant_check_fields(sles, filters): balance_qty = 0.0 balance_stock_value = 0.0 + + incorrect_idx = 0 + precision = frappe.get_precision("Stock Ledger Entry", "actual_qty") for idx, sle in enumerate(sles): queue = json.loads(sle.stock_queue) if sle.stock_queue else [] @@ -95,6 +98,12 @@ def add_invariant_check_fields(sles): ) sle.diff_value_diff = sle.stock_value_from_diff - sle.stock_value + if not incorrect_idx and filters.get("show_incorrect_entries"): + if is_sle_has_correct_data(sle, precision): + continue + else: + incorrect_idx = idx + if idx > 0: sle.fifo_stock_diff = sle.fifo_stock_value - sles[idx - 1].fifo_stock_value sle.fifo_difference_diff = sle.fifo_stock_diff - sle.stock_value_difference @@ -104,9 +113,23 @@ def add_invariant_check_fields(sles): "Batch", sle.batch_no, "use_batchwise_valuation", cache=True ) + if filters.get("show_incorrect_entries"): + if incorrect_idx > 0: + sles = sles[cint(incorrect_idx) - 1 :] + + return [] + return sles +def is_sle_has_correct_data(sle, precision): + if flt(sle.difference_in_qty, precision) != 0.0 or flt(sle.diff_value_diff, precision) != 0: + print(flt(sle.difference_in_qty, precision), flt(sle.diff_value_diff, precision)) + return False + + return True + + def get_columns(): return [ { From b72906a7a1de57d9344384226cf8e9061ab47cfa Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sat, 12 Oct 2024 14:52:35 +0530 Subject: [PATCH 316/734] fix: don't update reference to SI / PI on advances (cherry picked from commit b409f7462070f301469e958be1764b90352298b6) --- .../doctype/payment_entry/payment_entry.py | 16 ++++++++++++++-- erpnext/accounts/utils.py | 11 +++++++++-- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 9424d722cf5..34179b0b104 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -1211,11 +1211,23 @@ class PaymentEntry(AccountsController): { dr_or_cr: allocated_amount_in_company_currency, dr_or_cr + "_in_account_currency": d.allocated_amount, - "against_voucher_type": d.reference_doctype, - "against_voucher": d.reference_name, "cost_center": cost_center, } ) + + if self.book_advance_payments_in_separate_party_account and d.reference_doctype in [ + "Sales Order", + "Purchase Order", + ]: + gle.update( + { + "against_voucher_type": d.reference_doctype, + "against_voucher": d.reference_name, + } + ) + else: + gle.update({"against_voucher_type": self.doctype, "against_voucher": self.name}) + gl_entries.append(gle) if self.unallocated_amount: diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 8e47ddc3652..cf25b201129 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -476,8 +476,12 @@ def reconcile_against_document( # For payments with `Advance` in separate account feature enabled, only new ledger entries are posted for each reference. # No need to cancel/delete payment ledger entries + repost_whole_ledger = any([x.voucher_detail_no for x in entries]) if voucher_type == "Payment Entry" and doc.book_advance_payments_in_separate_party_account: - doc.make_advance_gl_entries(cancel=1) + if repost_whole_ledger: + doc.make_gl_entries(cancel=1) + else: + doc.make_advance_gl_entries(cancel=1) else: _delete_pl_entries(voucher_type, voucher_no) @@ -513,7 +517,10 @@ def reconcile_against_document( if voucher_type == "Payment Entry" and doc.book_advance_payments_in_separate_party_account: # both ledgers must be posted to for `Advance` in separate account feature # TODO: find a more efficient way post only for the new linked vouchers - doc.make_advance_gl_entries() + if repost_whole_ledger: + doc.make_gl_entries() + else: + doc.make_advance_gl_entries() else: gl_map = doc.build_gl_map() # Make sure there is no overallocation From ca0a962870c2be7007d6453f851b5eee571f4e73 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sat, 12 Oct 2024 15:29:48 +0530 Subject: [PATCH 317/734] refactor: reference update logic in advance (cherry picked from commit a112581acd77b41f9140b0cee082db3b00cd1e22) --- .../doctype/payment_entry/payment_entry.py | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 34179b0b104..f031bda045e 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -1215,18 +1215,26 @@ class PaymentEntry(AccountsController): } ) - if self.book_advance_payments_in_separate_party_account and d.reference_doctype in [ - "Sales Order", - "Purchase Order", - ]: + if self.book_advance_payments_in_separate_party_account: + if d.reference_doctype in [ + "Sales Order", + "Purchase Order", + ]: + gle.update( + { + "against_voucher_type": d.reference_doctype, + "against_voucher": d.reference_name, + } + ) + else: + gle.update({"against_voucher_type": self.doctype, "against_voucher": self.name}) + else: gle.update( { "against_voucher_type": d.reference_doctype, "against_voucher": d.reference_name, } ) - else: - gle.update({"against_voucher_type": self.doctype, "against_voucher": self.name}) gl_entries.append(gle) From 5ce2d73692f09b70784da47db52e02c7eb773c79 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sat, 12 Oct 2024 15:54:21 +0530 Subject: [PATCH 318/734] refactor: use hooks to identify advance doctypes (cherry picked from commit e7bb960bb557a0cc05570b248962ba519a3278db) --- erpnext/accounts/doctype/payment_entry/payment_entry.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index f031bda045e..eddcdf56fb2 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -1166,6 +1166,10 @@ class PaymentEntry(AccountsController): if not self.party_account: return + advance_payment_doctypes = frappe.get_hooks("advance_payment_receivable_doctypes") + frappe.get_hooks( + "advance_payment_payable_doctypes" + ) + if self.payment_type == "Receive": against_account = self.paid_to else: @@ -1216,10 +1220,7 @@ class PaymentEntry(AccountsController): ) if self.book_advance_payments_in_separate_party_account: - if d.reference_doctype in [ - "Sales Order", - "Purchase Order", - ]: + if d.reference_doctype in advance_payment_doctypes: gle.update( { "against_voucher_type": d.reference_doctype, From 9c26093a51effc459a7f5f7520ec176423b028c5 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sat, 12 Oct 2024 15:55:51 +0530 Subject: [PATCH 319/734] refactor(test): utility methods for enabling advance in separate acc (cherry picked from commit a21a406d0409c28c0880b91577343f24db6e510a) # Conflicts: # erpnext/accounts/test/accounts_mixin.py --- erpnext/accounts/test/accounts_mixin.py | 41 +++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/erpnext/accounts/test/accounts_mixin.py b/erpnext/accounts/test/accounts_mixin.py index d503f7bc4af..b67e190f431 100644 --- a/erpnext/accounts/test/accounts_mixin.py +++ b/erpnext/accounts/test/accounts_mixin.py @@ -87,6 +87,22 @@ class AccountsTestMixin: "parent_account": "Bank Accounts - " + abbr, } ), + frappe._dict( + { + "attribute_name": "advance_received", + "account_name": "Advance Received", + "parent_account": "Current Liabilities - " + abbr, + "account_type": "Receivable", + } + ), + frappe._dict( + { + "attribute_name": "advance_paid", + "account_name": "Advance Paid", + "parent_account": "Current Assets - " + abbr, + "account_type": "Payable", + } + ), ] for acc in other_accounts: acc_name = acc.account_name + " - " + abbr @@ -101,9 +117,34 @@ class AccountsTestMixin: "company": self.company, } ) + new_acc.account_type = acc.get("account_type", None) new_acc.save() setattr(self, acc.attribute_name, new_acc.name) +<<<<<<< HEAD +======= + self.identify_default_warehouses() + + def enable_advance_as_liability(self): + company = frappe.get_doc("Company", self.company) + company.book_advance_payments_in_separate_party_account = True + company.default_advance_received_account = self.advance_received + company.default_advance_paid_account = self.advance_paid + company.save() + + def disable_advance_as_liability(self): + company = frappe.get_doc("Company", self.company) + company.book_advance_payments_in_separate_party_account = False + company.default_advance_paid_account = company.default_advance_received_account = None + company.save() + + def identify_default_warehouses(self): + for w in frappe.db.get_all( + "Warehouse", filters={"company": self.company}, fields=["name", "warehouse_name"] + ): + setattr(self, "warehouse_" + w.warehouse_name.lower().strip().replace(" ", "_"), w.name) + +>>>>>>> a21a406d04 (refactor(test): utility methods for enabling advance in separate acc) def create_usd_receivable_account(self): account_name = "Debtors USD" if not frappe.db.get_value( From e37a88fdb647afc11e54bd00692ab87027a84c3e Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sat, 12 Oct 2024 15:56:53 +0530 Subject: [PATCH 320/734] test: unreconciliation of individual SO from Advance Payment (cherry picked from commit 8a6978e55000efda874931dcf4a11403bf795e84) --- .../test_unreconcile_payment.py | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/erpnext/accounts/doctype/unreconcile_payment/test_unreconcile_payment.py b/erpnext/accounts/doctype/unreconcile_payment/test_unreconcile_payment.py index 43dfbfaef60..eb5530706b6 100644 --- a/erpnext/accounts/doctype/unreconcile_payment/test_unreconcile_payment.py +++ b/erpnext/accounts/doctype/unreconcile_payment/test_unreconcile_payment.py @@ -363,3 +363,54 @@ class TestUnreconcilePayment(AccountsTestMixin, FrappeTestCase): self.assertEqual(so.advance_paid, 0) self.assertEqual(len(pe.references), 0) self.assertEqual(pe.unallocated_amount, 100) + + def test_06_unreconcile_advance_from_payment_entry(self): + self.enable_advance_as_liability() + so1 = self.create_sales_order() + so2 = self.create_sales_order() + + pe = self.create_payment_entry() + # Allocation payment against Sales Order + pe.paid_amount = 260 + pe.append( + "references", + {"reference_doctype": so1.doctype, "reference_name": so1.name, "allocated_amount": 150}, + ) + pe.append( + "references", + {"reference_doctype": so2.doctype, "reference_name": so2.name, "allocated_amount": 110}, + ) + pe.save().submit() + + # Assert 'Advance Paid' + so1.reload() + self.assertEqual(so1.advance_paid, 150) + so2.reload() + self.assertEqual(so2.advance_paid, 110) + + unreconcile = frappe.get_doc( + { + "doctype": "Unreconcile Payment", + "company": self.company, + "voucher_type": pe.doctype, + "voucher_no": pe.name, + } + ) + unreconcile.add_references() + self.assertEqual(len(unreconcile.allocations), 2) + allocations = [(x.reference_name, x.allocated_amount) for x in unreconcile.allocations] + self.assertListEqual(allocations, [(so1.name, 150), (so2.name, 110)]) + # unreconcile so2 + unreconcile.remove(unreconcile.allocations[0]) + unreconcile.save().submit() + + # Assert 'Advance Paid' + so1.reload() + so2.reload() + pe.reload() + self.assertEqual(so1.advance_paid, 150) + self.assertEqual(so2.advance_paid, 0) + self.assertEqual(len(pe.references), 1) + self.assertEqual(pe.unallocated_amount, 110) + + self.disable_advance_as_liability() From 2c2ca22d1288d2a771e292d5d1fa84dd99194b93 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sat, 12 Oct 2024 18:12:03 +0530 Subject: [PATCH 321/734] test: reconciled Invoice should not showup in tool Scenario should be tested on 'Advance in separate party account' (cherry picked from commit f1ec61c19ec8275804958e4eaf131906a2cffc90) --- .../test_unreconcile_payment.py | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/erpnext/accounts/doctype/unreconcile_payment/test_unreconcile_payment.py b/erpnext/accounts/doctype/unreconcile_payment/test_unreconcile_payment.py index eb5530706b6..13e5294aa78 100644 --- a/erpnext/accounts/doctype/unreconcile_payment/test_unreconcile_payment.py +++ b/erpnext/accounts/doctype/unreconcile_payment/test_unreconcile_payment.py @@ -7,7 +7,9 @@ from frappe.utils import today from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_entry from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice +from erpnext.accounts.party import get_party_account from erpnext.accounts.test.accounts_mixin import AccountsTestMixin +from erpnext.selling.doctype.sales_order.sales_order import make_sales_invoice from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order @@ -414,3 +416,49 @@ class TestUnreconcilePayment(AccountsTestMixin, FrappeTestCase): self.assertEqual(pe.unallocated_amount, 110) self.disable_advance_as_liability() + + def test_07_adv_from_so_to_invoice(self): + self.enable_advance_as_liability() + so = self.create_sales_order() + pe = self.create_payment_entry() + pe.paid_amount = 1000 + pe.append( + "references", + {"reference_doctype": so.doctype, "reference_name": so.name, "allocated_amount": 1000}, + ) + pe.save().submit() + + # Assert 'Advance Paid' + so.reload() + self.assertEqual(so.advance_paid, 1000) + + si = make_sales_invoice(so.name) + si.insert().submit() + + pr = frappe.get_doc( + { + "doctype": "Payment Reconciliation", + "company": self.company, + "party_type": "Customer", + "party": so.customer, + } + ) + accounts = get_party_account("Customer", so.customer, so.company, True) + pr.receivable_payable_account = accounts[0] + pr.default_advance_account = accounts[1] + pr.get_unreconciled_entries() + self.assertEqual(len(pr.get("invoices")), 1) + self.assertEqual(len(pr.get("payments")), 1) + invoices = [x.as_dict() for x in pr.get("invoices")] + payments = [x.as_dict() for x in pr.get("payments")] + pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments})) + pr.reconcile() + + self.assertEqual(len(pr.get("invoices")), 0) + self.assertEqual(len(pr.get("payments")), 0) + + # Assert 'Advance Paid' + so.reload() + self.assertEqual(so.advance_paid, 0) + + self.disable_advance_as_liability() From ae73d9c6216c19db829f32d45cee73ce689813d7 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sun, 13 Oct 2024 08:08:54 +0530 Subject: [PATCH 322/734] chore: better comments for context (cherry picked from commit e7505e92c9b4200d52214d2dc97f7052b2333095) --- .../accounts/doctype/payment_entry/payment_entry.py | 2 ++ erpnext/accounts/utils.py | 10 ++++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index eddcdf56fb2..7e3aa25f358 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -1221,6 +1221,7 @@ class PaymentEntry(AccountsController): if self.book_advance_payments_in_separate_party_account: if d.reference_doctype in advance_payment_doctypes: + # Upon reconciliation, whole ledger will be reposted. So, reference to SO/PO is fine gle.update( { "against_voucher_type": d.reference_doctype, @@ -1228,6 +1229,7 @@ class PaymentEntry(AccountsController): } ) else: + # Do not reference Invoices while Advance is in separate party account gle.update({"against_voucher_type": self.doctype, "against_voucher": self.name}) else: gle.update( diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index cf25b201129..1d75fafc1a9 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -474,8 +474,8 @@ def reconcile_against_document( doc = frappe.get_doc(voucher_type, voucher_no) frappe.flags.ignore_party_validation = True - # For payments with `Advance` in separate account feature enabled, only new ledger entries are posted for each reference. - # No need to cancel/delete payment ledger entries + # When Advance is allocated from an Order to an Invoice + # whole ledger must be reposted repost_whole_ledger = any([x.voucher_detail_no for x in entries]) if voucher_type == "Payment Entry" and doc.book_advance_payments_in_separate_party_account: if repost_whole_ledger: @@ -515,11 +515,13 @@ def reconcile_against_document( doc = frappe.get_doc(entry.voucher_type, entry.voucher_no) if voucher_type == "Payment Entry" and doc.book_advance_payments_in_separate_party_account: - # both ledgers must be posted to for `Advance` in separate account feature - # TODO: find a more efficient way post only for the new linked vouchers + # When Advance is allocated from an Order to an Invoice + # whole ledger must be reposted if repost_whole_ledger: doc.make_gl_entries() else: + # both ledgers must be posted to for `Advance` in separate account feature + # TODO: find a more efficient way post only for the new linked vouchers doc.make_advance_gl_entries() else: gl_map = doc.build_gl_map() From 361836e7350ec5210979f6cbc9434f69aa0583bc Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sun, 13 Oct 2024 08:45:15 +0530 Subject: [PATCH 323/734] chore: resolve conflict --- erpnext/accounts/test/accounts_mixin.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/erpnext/accounts/test/accounts_mixin.py b/erpnext/accounts/test/accounts_mixin.py index b67e190f431..e526e07c734 100644 --- a/erpnext/accounts/test/accounts_mixin.py +++ b/erpnext/accounts/test/accounts_mixin.py @@ -121,8 +121,6 @@ class AccountsTestMixin: new_acc.save() setattr(self, acc.attribute_name, new_acc.name) -<<<<<<< HEAD -======= self.identify_default_warehouses() def enable_advance_as_liability(self): @@ -144,7 +142,6 @@ class AccountsTestMixin: ): setattr(self, "warehouse_" + w.warehouse_name.lower().strip().replace(" ", "_"), w.name) ->>>>>>> a21a406d04 (refactor(test): utility methods for enabling advance in separate acc) def create_usd_receivable_account(self): account_name = "Debtors USD" if not frappe.db.get_value( From 6265582e53d1cec3b22a6fbb5444ac596d03c88e Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sun, 13 Oct 2024 09:50:41 +0530 Subject: [PATCH 324/734] refactor: use correct hook for identifying advance doctypes --- erpnext/accounts/doctype/payment_entry/payment_entry.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 7e3aa25f358..7885d825129 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -1166,9 +1166,7 @@ class PaymentEntry(AccountsController): if not self.party_account: return - advance_payment_doctypes = frappe.get_hooks("advance_payment_receivable_doctypes") + frappe.get_hooks( - "advance_payment_payable_doctypes" - ) + advance_payment_doctypes = frappe.get_hooks("advance_payment_doctypes") if self.payment_type == "Receive": against_account = self.paid_to From 8bf8bcf7390296f79432d7a257fa85e095cc6277 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sat, 12 Oct 2024 14:52:35 +0530 Subject: [PATCH 325/734] fix: don't update reference to SI / PI on advances (cherry picked from commit b409f7462070f301469e958be1764b90352298b6) --- .../doctype/payment_entry/payment_entry.py | 16 ++++++++++++++-- erpnext/accounts/utils.py | 11 +++++++++-- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 9424d722cf5..34179b0b104 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -1211,11 +1211,23 @@ class PaymentEntry(AccountsController): { dr_or_cr: allocated_amount_in_company_currency, dr_or_cr + "_in_account_currency": d.allocated_amount, - "against_voucher_type": d.reference_doctype, - "against_voucher": d.reference_name, "cost_center": cost_center, } ) + + if self.book_advance_payments_in_separate_party_account and d.reference_doctype in [ + "Sales Order", + "Purchase Order", + ]: + gle.update( + { + "against_voucher_type": d.reference_doctype, + "against_voucher": d.reference_name, + } + ) + else: + gle.update({"against_voucher_type": self.doctype, "against_voucher": self.name}) + gl_entries.append(gle) if self.unallocated_amount: diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 8e47ddc3652..cf25b201129 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -476,8 +476,12 @@ def reconcile_against_document( # For payments with `Advance` in separate account feature enabled, only new ledger entries are posted for each reference. # No need to cancel/delete payment ledger entries + repost_whole_ledger = any([x.voucher_detail_no for x in entries]) if voucher_type == "Payment Entry" and doc.book_advance_payments_in_separate_party_account: - doc.make_advance_gl_entries(cancel=1) + if repost_whole_ledger: + doc.make_gl_entries(cancel=1) + else: + doc.make_advance_gl_entries(cancel=1) else: _delete_pl_entries(voucher_type, voucher_no) @@ -513,7 +517,10 @@ def reconcile_against_document( if voucher_type == "Payment Entry" and doc.book_advance_payments_in_separate_party_account: # both ledgers must be posted to for `Advance` in separate account feature # TODO: find a more efficient way post only for the new linked vouchers - doc.make_advance_gl_entries() + if repost_whole_ledger: + doc.make_gl_entries() + else: + doc.make_advance_gl_entries() else: gl_map = doc.build_gl_map() # Make sure there is no overallocation From 6267ab994c79b3ab8828069c760e81538ff73887 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sat, 12 Oct 2024 15:29:48 +0530 Subject: [PATCH 326/734] refactor: reference update logic in advance (cherry picked from commit a112581acd77b41f9140b0cee082db3b00cd1e22) --- .../doctype/payment_entry/payment_entry.py | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 34179b0b104..f031bda045e 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -1215,18 +1215,26 @@ class PaymentEntry(AccountsController): } ) - if self.book_advance_payments_in_separate_party_account and d.reference_doctype in [ - "Sales Order", - "Purchase Order", - ]: + if self.book_advance_payments_in_separate_party_account: + if d.reference_doctype in [ + "Sales Order", + "Purchase Order", + ]: + gle.update( + { + "against_voucher_type": d.reference_doctype, + "against_voucher": d.reference_name, + } + ) + else: + gle.update({"against_voucher_type": self.doctype, "against_voucher": self.name}) + else: gle.update( { "against_voucher_type": d.reference_doctype, "against_voucher": d.reference_name, } ) - else: - gle.update({"against_voucher_type": self.doctype, "against_voucher": self.name}) gl_entries.append(gle) From 8c115e146b65cebbbf09b3c34090ef37a93ce530 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sat, 12 Oct 2024 15:54:21 +0530 Subject: [PATCH 327/734] refactor: use hooks to identify advance doctypes (cherry picked from commit e7bb960bb557a0cc05570b248962ba519a3278db) --- erpnext/accounts/doctype/payment_entry/payment_entry.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index f031bda045e..eddcdf56fb2 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -1166,6 +1166,10 @@ class PaymentEntry(AccountsController): if not self.party_account: return + advance_payment_doctypes = frappe.get_hooks("advance_payment_receivable_doctypes") + frappe.get_hooks( + "advance_payment_payable_doctypes" + ) + if self.payment_type == "Receive": against_account = self.paid_to else: @@ -1216,10 +1220,7 @@ class PaymentEntry(AccountsController): ) if self.book_advance_payments_in_separate_party_account: - if d.reference_doctype in [ - "Sales Order", - "Purchase Order", - ]: + if d.reference_doctype in advance_payment_doctypes: gle.update( { "against_voucher_type": d.reference_doctype, From e0477cf59f7612276e3d9549522c8fafa53557a1 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sat, 12 Oct 2024 15:55:51 +0530 Subject: [PATCH 328/734] refactor(test): utility methods for enabling advance in separate acc (cherry picked from commit a21a406d0409c28c0880b91577343f24db6e510a) # Conflicts: # erpnext/accounts/test/accounts_mixin.py --- erpnext/accounts/test/accounts_mixin.py | 41 +++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/erpnext/accounts/test/accounts_mixin.py b/erpnext/accounts/test/accounts_mixin.py index d503f7bc4af..b67e190f431 100644 --- a/erpnext/accounts/test/accounts_mixin.py +++ b/erpnext/accounts/test/accounts_mixin.py @@ -87,6 +87,22 @@ class AccountsTestMixin: "parent_account": "Bank Accounts - " + abbr, } ), + frappe._dict( + { + "attribute_name": "advance_received", + "account_name": "Advance Received", + "parent_account": "Current Liabilities - " + abbr, + "account_type": "Receivable", + } + ), + frappe._dict( + { + "attribute_name": "advance_paid", + "account_name": "Advance Paid", + "parent_account": "Current Assets - " + abbr, + "account_type": "Payable", + } + ), ] for acc in other_accounts: acc_name = acc.account_name + " - " + abbr @@ -101,9 +117,34 @@ class AccountsTestMixin: "company": self.company, } ) + new_acc.account_type = acc.get("account_type", None) new_acc.save() setattr(self, acc.attribute_name, new_acc.name) +<<<<<<< HEAD +======= + self.identify_default_warehouses() + + def enable_advance_as_liability(self): + company = frappe.get_doc("Company", self.company) + company.book_advance_payments_in_separate_party_account = True + company.default_advance_received_account = self.advance_received + company.default_advance_paid_account = self.advance_paid + company.save() + + def disable_advance_as_liability(self): + company = frappe.get_doc("Company", self.company) + company.book_advance_payments_in_separate_party_account = False + company.default_advance_paid_account = company.default_advance_received_account = None + company.save() + + def identify_default_warehouses(self): + for w in frappe.db.get_all( + "Warehouse", filters={"company": self.company}, fields=["name", "warehouse_name"] + ): + setattr(self, "warehouse_" + w.warehouse_name.lower().strip().replace(" ", "_"), w.name) + +>>>>>>> a21a406d04 (refactor(test): utility methods for enabling advance in separate acc) def create_usd_receivable_account(self): account_name = "Debtors USD" if not frappe.db.get_value( From e56dd8268b6aa05fc9c6d52924baa0be1747adab Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sat, 12 Oct 2024 15:56:53 +0530 Subject: [PATCH 329/734] test: unreconciliation of individual SO from Advance Payment (cherry picked from commit 8a6978e55000efda874931dcf4a11403bf795e84) --- .../test_unreconcile_payment.py | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/erpnext/accounts/doctype/unreconcile_payment/test_unreconcile_payment.py b/erpnext/accounts/doctype/unreconcile_payment/test_unreconcile_payment.py index 43dfbfaef60..eb5530706b6 100644 --- a/erpnext/accounts/doctype/unreconcile_payment/test_unreconcile_payment.py +++ b/erpnext/accounts/doctype/unreconcile_payment/test_unreconcile_payment.py @@ -363,3 +363,54 @@ class TestUnreconcilePayment(AccountsTestMixin, FrappeTestCase): self.assertEqual(so.advance_paid, 0) self.assertEqual(len(pe.references), 0) self.assertEqual(pe.unallocated_amount, 100) + + def test_06_unreconcile_advance_from_payment_entry(self): + self.enable_advance_as_liability() + so1 = self.create_sales_order() + so2 = self.create_sales_order() + + pe = self.create_payment_entry() + # Allocation payment against Sales Order + pe.paid_amount = 260 + pe.append( + "references", + {"reference_doctype": so1.doctype, "reference_name": so1.name, "allocated_amount": 150}, + ) + pe.append( + "references", + {"reference_doctype": so2.doctype, "reference_name": so2.name, "allocated_amount": 110}, + ) + pe.save().submit() + + # Assert 'Advance Paid' + so1.reload() + self.assertEqual(so1.advance_paid, 150) + so2.reload() + self.assertEqual(so2.advance_paid, 110) + + unreconcile = frappe.get_doc( + { + "doctype": "Unreconcile Payment", + "company": self.company, + "voucher_type": pe.doctype, + "voucher_no": pe.name, + } + ) + unreconcile.add_references() + self.assertEqual(len(unreconcile.allocations), 2) + allocations = [(x.reference_name, x.allocated_amount) for x in unreconcile.allocations] + self.assertListEqual(allocations, [(so1.name, 150), (so2.name, 110)]) + # unreconcile so2 + unreconcile.remove(unreconcile.allocations[0]) + unreconcile.save().submit() + + # Assert 'Advance Paid' + so1.reload() + so2.reload() + pe.reload() + self.assertEqual(so1.advance_paid, 150) + self.assertEqual(so2.advance_paid, 0) + self.assertEqual(len(pe.references), 1) + self.assertEqual(pe.unallocated_amount, 110) + + self.disable_advance_as_liability() From 4752ed2483d7697129c2af245222977bdcb1b750 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sat, 12 Oct 2024 18:12:03 +0530 Subject: [PATCH 330/734] test: reconciled Invoice should not showup in tool Scenario should be tested on 'Advance in separate party account' (cherry picked from commit f1ec61c19ec8275804958e4eaf131906a2cffc90) --- .../test_unreconcile_payment.py | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/erpnext/accounts/doctype/unreconcile_payment/test_unreconcile_payment.py b/erpnext/accounts/doctype/unreconcile_payment/test_unreconcile_payment.py index eb5530706b6..13e5294aa78 100644 --- a/erpnext/accounts/doctype/unreconcile_payment/test_unreconcile_payment.py +++ b/erpnext/accounts/doctype/unreconcile_payment/test_unreconcile_payment.py @@ -7,7 +7,9 @@ from frappe.utils import today from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_entry from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice +from erpnext.accounts.party import get_party_account from erpnext.accounts.test.accounts_mixin import AccountsTestMixin +from erpnext.selling.doctype.sales_order.sales_order import make_sales_invoice from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order @@ -414,3 +416,49 @@ class TestUnreconcilePayment(AccountsTestMixin, FrappeTestCase): self.assertEqual(pe.unallocated_amount, 110) self.disable_advance_as_liability() + + def test_07_adv_from_so_to_invoice(self): + self.enable_advance_as_liability() + so = self.create_sales_order() + pe = self.create_payment_entry() + pe.paid_amount = 1000 + pe.append( + "references", + {"reference_doctype": so.doctype, "reference_name": so.name, "allocated_amount": 1000}, + ) + pe.save().submit() + + # Assert 'Advance Paid' + so.reload() + self.assertEqual(so.advance_paid, 1000) + + si = make_sales_invoice(so.name) + si.insert().submit() + + pr = frappe.get_doc( + { + "doctype": "Payment Reconciliation", + "company": self.company, + "party_type": "Customer", + "party": so.customer, + } + ) + accounts = get_party_account("Customer", so.customer, so.company, True) + pr.receivable_payable_account = accounts[0] + pr.default_advance_account = accounts[1] + pr.get_unreconciled_entries() + self.assertEqual(len(pr.get("invoices")), 1) + self.assertEqual(len(pr.get("payments")), 1) + invoices = [x.as_dict() for x in pr.get("invoices")] + payments = [x.as_dict() for x in pr.get("payments")] + pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments})) + pr.reconcile() + + self.assertEqual(len(pr.get("invoices")), 0) + self.assertEqual(len(pr.get("payments")), 0) + + # Assert 'Advance Paid' + so.reload() + self.assertEqual(so.advance_paid, 0) + + self.disable_advance_as_liability() From 4c78a682ad38121bd3b9135db9acca68f30028a9 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sun, 13 Oct 2024 08:08:54 +0530 Subject: [PATCH 331/734] chore: better comments for context (cherry picked from commit e7505e92c9b4200d52214d2dc97f7052b2333095) --- .../accounts/doctype/payment_entry/payment_entry.py | 2 ++ erpnext/accounts/utils.py | 10 ++++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index eddcdf56fb2..7e3aa25f358 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -1221,6 +1221,7 @@ class PaymentEntry(AccountsController): if self.book_advance_payments_in_separate_party_account: if d.reference_doctype in advance_payment_doctypes: + # Upon reconciliation, whole ledger will be reposted. So, reference to SO/PO is fine gle.update( { "against_voucher_type": d.reference_doctype, @@ -1228,6 +1229,7 @@ class PaymentEntry(AccountsController): } ) else: + # Do not reference Invoices while Advance is in separate party account gle.update({"against_voucher_type": self.doctype, "against_voucher": self.name}) else: gle.update( diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index cf25b201129..1d75fafc1a9 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -474,8 +474,8 @@ def reconcile_against_document( doc = frappe.get_doc(voucher_type, voucher_no) frappe.flags.ignore_party_validation = True - # For payments with `Advance` in separate account feature enabled, only new ledger entries are posted for each reference. - # No need to cancel/delete payment ledger entries + # When Advance is allocated from an Order to an Invoice + # whole ledger must be reposted repost_whole_ledger = any([x.voucher_detail_no for x in entries]) if voucher_type == "Payment Entry" and doc.book_advance_payments_in_separate_party_account: if repost_whole_ledger: @@ -515,11 +515,13 @@ def reconcile_against_document( doc = frappe.get_doc(entry.voucher_type, entry.voucher_no) if voucher_type == "Payment Entry" and doc.book_advance_payments_in_separate_party_account: - # both ledgers must be posted to for `Advance` in separate account feature - # TODO: find a more efficient way post only for the new linked vouchers + # When Advance is allocated from an Order to an Invoice + # whole ledger must be reposted if repost_whole_ledger: doc.make_gl_entries() else: + # both ledgers must be posted to for `Advance` in separate account feature + # TODO: find a more efficient way post only for the new linked vouchers doc.make_advance_gl_entries() else: gl_map = doc.build_gl_map() From cf1eabe049c4ee3b7c8f6e978fd7788b4bc7e566 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sun, 13 Oct 2024 10:35:14 +0530 Subject: [PATCH 332/734] chore: resolve conflict --- erpnext/accounts/test/accounts_mixin.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/erpnext/accounts/test/accounts_mixin.py b/erpnext/accounts/test/accounts_mixin.py index b67e190f431..e526e07c734 100644 --- a/erpnext/accounts/test/accounts_mixin.py +++ b/erpnext/accounts/test/accounts_mixin.py @@ -121,8 +121,6 @@ class AccountsTestMixin: new_acc.save() setattr(self, acc.attribute_name, new_acc.name) -<<<<<<< HEAD -======= self.identify_default_warehouses() def enable_advance_as_liability(self): @@ -144,7 +142,6 @@ class AccountsTestMixin: ): setattr(self, "warehouse_" + w.warehouse_name.lower().strip().replace(" ", "_"), w.name) ->>>>>>> a21a406d04 (refactor(test): utility methods for enabling advance in separate acc) def create_usd_receivable_account(self): account_name = "Debtors USD" if not frappe.db.get_value( From a329003f7fc49ad1e534bcff6ce0bfa0fc76bbfe Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sun, 13 Oct 2024 10:35:48 +0530 Subject: [PATCH 333/734] chore: use correct hook for advance payment doctypes --- erpnext/accounts/doctype/payment_entry/payment_entry.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 7e3aa25f358..7885d825129 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -1166,9 +1166,7 @@ class PaymentEntry(AccountsController): if not self.party_account: return - advance_payment_doctypes = frappe.get_hooks("advance_payment_receivable_doctypes") + frappe.get_hooks( - "advance_payment_payable_doctypes" - ) + advance_payment_doctypes = frappe.get_hooks("advance_payment_doctypes") if self.payment_type == "Receive": against_account = self.paid_to From ef1e121bd409462da781360fca7eda5ae7495f9c Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Sun, 13 Oct 2024 05:23:41 +0000 Subject: [PATCH 334/734] chore(release): Bumped to Version 15.38.2 ## [15.38.2](https://github.com/frappe/erpnext/compare/v15.38.1...v15.38.2) (2024-10-13) ### Bug Fixes * don't update reference to SI / PI on advances ([8bf8bcf](https://github.com/frappe/erpnext/commit/8bf8bcf7390296f79432d7a257fa85e095cc6277)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 784ac134a56..076eaeada9b 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.38.1" +__version__ = "15.38.2" def get_default_company(user=None): From 928b6b1510b13fbf15e7d3a76fe204a3bcd12f30 Mon Sep 17 00:00:00 2001 From: Abdeali Chharchhoda Date: Sun, 13 Oct 2024 19:58:20 +0530 Subject: [PATCH 335/734] fix: Use `ref_doc.get()` for `party_account_currency` (cherry picked from commit b79549422aff1c9b404e759a0f14efc01823c1b5) --- erpnext/accounts/doctype/payment_request/payment_request.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index 8c370ba9d8c..fd07551897a 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -571,7 +571,7 @@ def make_payment_request(**args): ) party_type = args.get("party_type") or "Customer" - party_account_currency = ref_doc.party_account_currency + party_account_currency = ref_doc.get("party_account_currency") if not party_account_currency: party_account = get_party_account(party_type, ref_doc.get(party_type.lower()), ref_doc.company) From f3ceb4ac7d50969d2dcece661078e1ab0af575ac Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 14 Oct 2024 15:03:35 +0530 Subject: [PATCH 336/734] fix: quotation to so frappe crm (backport #43644) (#43646) fix: quotation to so frappe crm (#43644) (cherry picked from commit d57624b18242f2a3da0d6ee4d1fdfba4a157e8b2) Co-authored-by: Nihantra C. Patel <141945075+Nihantra-Patel@users.noreply.github.com> --- .../selling/doctype/quotation/quotation.py | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index 2d7fef2d6e0..a5994756c46 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -369,24 +369,25 @@ def _make_sales_order(source_name, target_doc=None, ignore_permissions=False): if customer: target.customer = customer.name target.customer_name = customer.customer_name + + # sales team + if not target.get("sales_team"): + for d in customer.get("sales_team") or []: + target.append( + "sales_team", + { + "sales_person": d.sales_person, + "allocated_percentage": d.allocated_percentage or None, + "commission_rate": d.commission_rate, + }, + ) + if source.referral_sales_partner: target.sales_partner = source.referral_sales_partner target.commission_rate = frappe.get_value( "Sales Partner", source.referral_sales_partner, "commission_rate" ) - # sales team - if not target.get("sales_team"): - for d in customer.get("sales_team") or []: - target.append( - "sales_team", - { - "sales_person": d.sales_person, - "allocated_percentage": d.allocated_percentage or None, - "commission_rate": d.commission_rate, - }, - ) - target.flags.ignore_permissions = ignore_permissions target.run_method("set_missing_values") target.run_method("calculate_taxes_and_totals") From e5aaa5b6e5ebdb340666123e86e3c3b78b57d6ce Mon Sep 17 00:00:00 2001 From: Ninad1306 Date: Fri, 11 Oct 2024 11:33:27 +0530 Subject: [PATCH 337/734] fix: ignore free item when qty is zero (cherry picked from commit 7ae98f77eeee9b44d0afb9899c826a5acd4eb126) --- erpnext/accounts/doctype/pricing_rule/utils.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index b848fde08d8..a74dfea2cca 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -657,6 +657,9 @@ def get_product_discount_rule(pricing_rule, item_details, args=None, doc=None): if pricing_rule.round_free_qty: qty = math.floor(qty) + if not qty: + return + free_item_data_args = { "item_code": free_item, "qty": qty, From 389ee909a52280f84a88509e0596cdd1a72afaf0 Mon Sep 17 00:00:00 2001 From: Ninad1306 Date: Fri, 11 Oct 2024 14:02:50 +0530 Subject: [PATCH 338/734] test: test case to validate free item is ignored when qty is zero (cherry picked from commit a2b41a0c16c9d95c6ca609e1be100d8924d8028b) --- erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py index 3fece4aeeab..b4c47a26eb1 100644 --- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py @@ -1131,6 +1131,12 @@ class TestPricingRule(FrappeTestCase): self.assertEqual(so.items[1].item_code, "_Test Item") self.assertEqual(so.items[1].qty, 3) + so = make_sales_order(item_code="_Test Item", qty=5, do_not_submit=1) + so.items[0].qty = 1 + del so.items[-1] + so.save() + self.assertEqual(len(so.items), 1) + def test_apply_multiple_pricing_rules_for_discount_percentage_and_amount(self): frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 1") frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 2") From 9d05a6ebc04a38d985b0626aa4af26d3638f9cc4 Mon Sep 17 00:00:00 2001 From: venkat102 Date: Thu, 10 Oct 2024 12:33:12 +0530 Subject: [PATCH 339/734] refactor: remove 'format:' based naming (cherry picked from commit e8e1ec0e851496a8096184a2dddebea8efcd6551) # Conflicts: # erpnext/accounts/doctype/unreconcile_payment/unreconcile_payment.json --- .../doctype/unreconcile_payment/unreconcile_payment.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/unreconcile_payment/unreconcile_payment.json b/erpnext/accounts/doctype/unreconcile_payment/unreconcile_payment.json index f906dc6cec6..8ba79581d50 100644 --- a/erpnext/accounts/doctype/unreconcile_payment/unreconcile_payment.json +++ b/erpnext/accounts/doctype/unreconcile_payment/unreconcile_payment.json @@ -1,7 +1,5 @@ { "actions": [], - "allow_rename": 1, - "autoname": "format:UNREC-{#####}", "creation": "2023-08-22 10:26:34.421423", "default_view": "List", "doctype": "DocType", @@ -58,11 +56,14 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], +<<<<<<< HEAD "modified": "2023-08-28 17:42:50.261377", +======= + "modified": "2024-10-10 12:03:50.022444", +>>>>>>> e8e1ec0e85 (refactor: remove 'format:' based naming) "modified_by": "Administrator", "module": "Accounts", "name": "Unreconcile Payment", - "naming_rule": "Expression", "owner": "Administrator", "permissions": [ { From 1fac17b36f536d3b2995a983774e035c4acae4be Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 15 Oct 2024 06:00:12 +0530 Subject: [PATCH 340/734] chore: resolve conflict --- .../doctype/unreconcile_payment/unreconcile_payment.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/erpnext/accounts/doctype/unreconcile_payment/unreconcile_payment.json b/erpnext/accounts/doctype/unreconcile_payment/unreconcile_payment.json index 8ba79581d50..f2c2f380fe9 100644 --- a/erpnext/accounts/doctype/unreconcile_payment/unreconcile_payment.json +++ b/erpnext/accounts/doctype/unreconcile_payment/unreconcile_payment.json @@ -56,11 +56,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], -<<<<<<< HEAD - "modified": "2023-08-28 17:42:50.261377", -======= "modified": "2024-10-10 12:03:50.022444", ->>>>>>> e8e1ec0e85 (refactor: remove 'format:' based naming) "modified_by": "Administrator", "module": "Accounts", "name": "Unreconcile Payment", From 930e79c3510ea7e92edf669f864024c02dbaf058 Mon Sep 17 00:00:00 2001 From: Bhavan23 Date: Tue, 8 Oct 2024 16:18:31 +0530 Subject: [PATCH 341/734] fix: update item details with actual quantity. (cherry picked from commit 9dbdfec9b7a4bcc38399a4d849dcc32b54bf9d4a) --- erpnext/selling/doctype/sales_order/sales_order.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 4804080be38..53663de1981 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -14,6 +14,7 @@ from frappe.model.mapper import get_mapped_doc from frappe.model.utils import get_fetch_values from frappe.query_builder.functions import Sum from frappe.utils import add_days, cint, cstr, flt, get_link_to_form, getdate, nowdate, strip_html +from erpnext.stock.get_item_details import get_bin_details from erpnext.accounts.doctype.sales_invoice.sales_invoice import ( unlink_inter_company_doc, @@ -838,6 +839,7 @@ def make_material_request(source_name, target_doc=None): target.project = source_parent.project target.qty = get_remaining_qty(source) target.stock_qty = flt(target.qty) * flt(target.conversion_factor) + target.actual_qty = get_bin_details(target.item_code, target.warehouse, source_parent.company, True).get("actual_qty", 0) args = target.as_dict().copy() args.update( From 86017b223ac8b75802c83c12a964a81258dd6391 Mon Sep 17 00:00:00 2001 From: Bhavan23 Date: Tue, 8 Oct 2024 16:24:28 +0530 Subject: [PATCH 342/734] test: Validate the actual quantity when creating a material request from the sales order (cherry picked from commit 17fdd426455aa3daab828444a2ecde837936faa2) --- erpnext/selling/doctype/sales_order/test_sales_order.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index 53c629a90b4..0e031cc7b51 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -8,6 +8,8 @@ import frappe.permissions from frappe.core.doctype.user_permission.test_user_permission import create_user from frappe.tests.utils import FrappeTestCase, change_settings from frappe.utils import add_days, flt, getdate, nowdate, today +from erpnext.stock.get_item_details import get_bin_details + from erpnext.accounts.test.accounts_mixin import AccountsTestMixin from erpnext.controllers.accounts_controller import update_child_qty_rate @@ -96,6 +98,10 @@ class TestSalesOrder(AccountsTestMixin, FrappeTestCase): self.assertEqual(mr.material_request_type, "Purchase") self.assertEqual(len(mr.get("items")), len(so.get("items"))) + for item in mr.get("items"): + actual_qty = get_bin_details(item.item_code, item.warehouse, mr.company, True).get("actual_qty", 0) + self.assertEqual(flt(item.actual_qty), actual_qty) + def test_make_delivery_note(self): so = make_sales_order(do_not_submit=True) From a70181e0258ae209eb7012a507ce513def51f311 Mon Sep 17 00:00:00 2001 From: Bhavan23 Date: Tue, 8 Oct 2024 17:49:12 +0530 Subject: [PATCH 343/734] fix: update formatings (cherry picked from commit 5f4a523340209b29ebd9932228b0715eb1035103) --- erpnext/selling/doctype/sales_order/sales_order.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 53663de1981..a87c1352471 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -14,7 +14,6 @@ from frappe.model.mapper import get_mapped_doc from frappe.model.utils import get_fetch_values from frappe.query_builder.functions import Sum from frappe.utils import add_days, cint, cstr, flt, get_link_to_form, getdate, nowdate, strip_html -from erpnext.stock.get_item_details import get_bin_details from erpnext.accounts.doctype.sales_invoice.sales_invoice import ( unlink_inter_company_doc, @@ -36,7 +35,7 @@ from erpnext.stock.doctype.stock_reservation_entry.stock_reservation_entry impor get_sre_reserved_qty_details_for_voucher, has_reserved_stock, ) -from erpnext.stock.get_item_details import get_default_bom, get_price_list_rate +from erpnext.stock.get_item_details import get_bin_details, get_default_bom, get_price_list_rate from erpnext.stock.stock_balance import get_reserved_qty, update_bin_qty form_grid_templates = {"items": "templates/form_grid/item_grid.html"} @@ -839,7 +838,9 @@ def make_material_request(source_name, target_doc=None): target.project = source_parent.project target.qty = get_remaining_qty(source) target.stock_qty = flt(target.qty) * flt(target.conversion_factor) - target.actual_qty = get_bin_details(target.item_code, target.warehouse, source_parent.company, True).get("actual_qty", 0) + target.actual_qty = get_bin_details( + target.item_code, target.warehouse, source_parent.company, True + ).get("actual_qty", 0) args = target.as_dict().copy() args.update( From c2c6d27625a82fe836668773d0b8fe41426c5161 Mon Sep 17 00:00:00 2001 From: Bhavan23 Date: Tue, 8 Oct 2024 18:14:00 +0530 Subject: [PATCH 344/734] fix: update formatings (cherry picked from commit 50442973219cd246f07d6178ca3465c59778808b) --- erpnext/selling/doctype/sales_order/test_sales_order.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index 0e031cc7b51..244a6b1ddad 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -8,8 +8,6 @@ import frappe.permissions from frappe.core.doctype.user_permission.test_user_permission import create_user from frappe.tests.utils import FrappeTestCase, change_settings from frappe.utils import add_days, flt, getdate, nowdate, today -from erpnext.stock.get_item_details import get_bin_details - from erpnext.accounts.test.accounts_mixin import AccountsTestMixin from erpnext.controllers.accounts_controller import update_child_qty_rate @@ -32,6 +30,7 @@ from erpnext.selling.doctype.sales_order.sales_order import ( ) from erpnext.stock.doctype.item.test_item import make_item from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry +from erpnext.stock.get_item_details import get_bin_details class TestSalesOrder(AccountsTestMixin, FrappeTestCase): @@ -99,7 +98,9 @@ class TestSalesOrder(AccountsTestMixin, FrappeTestCase): self.assertEqual(len(mr.get("items")), len(so.get("items"))) for item in mr.get("items"): - actual_qty = get_bin_details(item.item_code, item.warehouse, mr.company, True).get("actual_qty", 0) + actual_qty = get_bin_details(item.item_code, item.warehouse, mr.company, True).get( + "actual_qty", 0 + ) self.assertEqual(flt(item.actual_qty), actual_qty) def test_make_delivery_note(self): From 85088e4aff41306bb132a1f4e22e7e4c85eb70b4 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Tue, 15 Oct 2024 11:57:02 +0530 Subject: [PATCH 345/734] fix: zero incoming rate for delivery note return (#43642) (cherry picked from commit 6087a57b0c84b4a35a4e0797a321f13f07fa8d43) --- erpnext/controllers/selling_controller.py | 10 +++++ .../delivery_note/test_delivery_note.py | 41 +++++++++++++++++++ erpnext/stock/stock_ledger.py | 9 ++++ 3 files changed, 60 insertions(+) diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index 7ad12b54eff..49710de06f6 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -473,6 +473,16 @@ class SellingController(StockController): raise_error_if_no_rate=False, ) + if ( + not d.incoming_rate + and self.get("return_against") + and self.get("is_return") + and get_valuation_method(d.item_code) == "Moving Average" + ): + d.incoming_rate = get_rate_for_return( + self.doctype, self.name, d.item_code, self.return_against, item_row=d + ) + # For internal transfers use incoming rate as the valuation rate if self.is_internal_transfer(): if self.doctype == "Delivery Note" or self.get("update_stock"): diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py index cfe550f2f27..0cfb427c670 100644 --- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py @@ -2039,6 +2039,47 @@ class TestDeliveryNote(FrappeTestCase): self.assertEqual(sn.status, "Delivered") self.assertEqual(sn.warranty_period, 100) + def test_batch_return_dn(self): + from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_return + + item_code = make_item( + "Test Batch Return DN Item 1", + properties={ + "has_batch_no": 1, + "valuation_method": "Moving Average", + "create_new_batch": 1, + "batch_number_series": "TBRDN1-.#####", + "is_stock_item": 1, + }, + ).name + + se = make_stock_entry(item_code=item_code, target="_Test Warehouse - _TC", qty=5, basic_rate=100) + + batch_no = get_batch_from_bundle(se.items[0].serial_and_batch_bundle) + dn = create_delivery_note( + item_code=item_code, + qty=5, + rate=500, + use_serial_batch_fields=1, + batch_no=batch_no, + ) + + dn_return = make_sales_return(dn.name) + dn_return.save().submit() + + self.assertEqual(dn_return.items[0].qty, 5 * -1) + + returned_batch_no = get_batch_from_bundle(dn_return.items[0].serial_and_batch_bundle) + self.assertEqual(batch_no, returned_batch_no) + + stock_value_difference = frappe.db.get_value( + "Stock Ledger Entry", + {"voucher_no": dn_return.name, "voucher_type": "Delivery Note"}, + "stock_value_difference", + ) + + self.assertEqual(stock_value_difference, 100.0 * 5) + def create_delivery_note(**args): dn = frappe.new_doc("Delivery Note") diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index b167ccf6df8..dfa60cad96e 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -1074,6 +1074,15 @@ class update_entries_after: } ) + if not rate and sle.voucher_type in ["Delivery Note", "Sales Invoice"]: + rate = get_rate_for_return( + sle.voucher_type, + sle.voucher_no, + sle.item_code, + voucher_detail_no=sle.voucher_detail_no, + sle=sle, + ) + else: rate = get_rate_for_return( sle.voucher_type, From 8668ae92d830a2eb1168739f9a8bd55281f5c451 Mon Sep 17 00:00:00 2001 From: ljain112 Date: Tue, 15 Oct 2024 11:52:44 +0530 Subject: [PATCH 346/734] fix: removed unused query (cherry picked from commit 5f590ddfa2901d483e0ee73fa5e35ff7dd369b1e) # Conflicts: # erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py --- .../tax_withholding_category/tax_withholding_category.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py index 67a33faf9bf..3f9bdea5faf 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -610,8 +610,11 @@ def get_tcs_amount(parties, inv, tax_details, vouchers, adv_vouchers): conditions.append(ple.voucher_no == ple.against_voucher_no) conditions.append(ple.company == inv.company) +<<<<<<< HEAD (qb.from_(ple).select(Abs(Sum(ple.amount))).where(Criterion.all(conditions)).run(as_list=1)) +======= +>>>>>>> 5f590ddfa2 (fix: removed unused query) advance_amt = ( qb.from_(ple).select(Abs(Sum(ple.amount))).where(Criterion.all(conditions)).run()[0][0] or 0.0 ) From 105f9ec2e1d73671cea1ccb8b2e9ab40439a5eb4 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 15 Oct 2024 13:32:29 +0530 Subject: [PATCH 347/734] chore: resolve conflict --- .../tax_withholding_category/tax_withholding_category.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py index 3f9bdea5faf..a56a7f045c8 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -610,11 +610,6 @@ def get_tcs_amount(parties, inv, tax_details, vouchers, adv_vouchers): conditions.append(ple.voucher_no == ple.against_voucher_no) conditions.append(ple.company == inv.company) -<<<<<<< HEAD - (qb.from_(ple).select(Abs(Sum(ple.amount))).where(Criterion.all(conditions)).run(as_list=1)) - -======= ->>>>>>> 5f590ddfa2 (fix: removed unused query) advance_amt = ( qb.from_(ple).select(Abs(Sum(ple.amount))).where(Criterion.all(conditions)).run()[0][0] or 0.0 ) From 5d6fc71556ba70adec566c0711a401cfadbaf432 Mon Sep 17 00:00:00 2001 From: ljain112 Date: Tue, 8 Oct 2024 12:54:28 +0530 Subject: [PATCH 348/734] fix: delete invalid pricing rule on change of applicable_for (cherry picked from commit 42746fc630eb3b4707ee62f16bf73268799deb2b) --- .../promotional_scheme/promotional_scheme.py | 52 +++++++++++++++---- 1 file changed, 41 insertions(+), 11 deletions(-) diff --git a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py index 86bd2135515..4cc87394b4f 100644 --- a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py +++ b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py @@ -5,6 +5,8 @@ import frappe from frappe import _ from frappe.model.document import Document +from frappe.query_builder import Criterion +from frappe.query_builder.functions import IfNull pricing_rule_fields = [ "apply_on", @@ -162,22 +164,50 @@ class PromotionalScheme(Document): if self.is_new(): return - transaction_exists = False - docnames = [] + invalid_pricing_rule = self.get_invalid_pricing_rules() - # If user has changed applicable for - if self.get_doc_before_save() and self.get_doc_before_save().applicable_for == self.applicable_for: + if not invalid_pricing_rule: return - docnames = frappe.get_all("Pricing Rule", filters={"promotional_scheme": self.name}) + if frappe.db.exists( + "Pricing Rule Detail", + { + "pricing_rule": ["in", invalid_pricing_rule], + "docstatus": ["<", 2], + }, + ): + raise_for_transaction_exists(self.name) - for docname in docnames: - if frappe.db.exists("Pricing Rule Detail", {"pricing_rule": docname.name, "docstatus": ("<", 2)}): - raise_for_transaction_exists(self.name) + for doc in invalid_pricing_rule: + frappe.delete_doc("Pricing Rule", doc) - if docnames and not transaction_exists: - for docname in docnames: - frappe.delete_doc("Pricing Rule", docname.name) + frappe.msgprint( + _("The following invalid Pricing Rules are deleted:") + + "

  • " + + "
  • ".join(invalid_pricing_rule) + + "
" + ) + + def get_invalid_pricing_rules(self): + pr = frappe.qb.DocType("Pricing Rule") + conditions = [] + conditions.append(pr.promotional_scheme == self.name) + + if self.applicable_for: + applicable_for = frappe.scrub(self.applicable_for) + applicable_for_list = [d.get(applicable_for) for d in self.get(applicable_for)] + + conditions.append( + (IfNull(pr.applicable_for, "") != self.applicable_for) + | ( + (IfNull(pr.applicable_for, "") == self.applicable_for) + & IfNull(pr[applicable_for], "").notin(applicable_for_list) + ) + ) + else: + conditions.append(IfNull(pr.applicable_for, "") != "") + + return frappe.qb.from_(pr).select(pr.name).where(Criterion.all(conditions)).run(pluck=True) def on_update(self): self.validate() From 4dbee00b82b5eed04e350281b54251801686ba30 Mon Sep 17 00:00:00 2001 From: ljain112 Date: Tue, 8 Oct 2024 17:06:05 +0530 Subject: [PATCH 349/734] test: added test for change in applicable_for_value in promotional scheme (cherry picked from commit 2613bdd868ac22bb2165dda439a3d9d0d0a29cea) --- .../test_promotional_scheme.py | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/erpnext/accounts/doctype/promotional_scheme/test_promotional_scheme.py b/erpnext/accounts/doctype/promotional_scheme/test_promotional_scheme.py index 74ba6cf923c..a4ea81f0d9f 100644 --- a/erpnext/accounts/doctype/promotional_scheme/test_promotional_scheme.py +++ b/erpnext/accounts/doctype/promotional_scheme/test_promotional_scheme.py @@ -90,6 +90,31 @@ class TestPromotionalScheme(unittest.TestCase): price_rules = frappe.get_all("Pricing Rule", filters={"promotional_scheme": ps.name}) self.assertEqual(price_rules, []) + def test_change_applicable_for_values_in_promotional_scheme(self): + ps = make_promotional_scheme(applicable_for="Customer", customer="_Test Customer") + ps.append("customer", {"customer": "_Test Customer 2"}) + ps.save() + + price_rules = frappe.get_all( + "Pricing Rule", filters={"promotional_scheme": ps.name, "applicable_for": "Customer"} + ) + self.assertTrue(len(price_rules), 2) + + ps.set("customer", []) + ps.append("customer", {"customer": "_Test Customer 2"}) + ps.save() + + price_rules = frappe.get_all( + "Pricing Rule", + filters={ + "promotional_scheme": ps.name, + "applicable_for": "Customer", + "customer": "_Test Customer", + }, + ) + self.assertEqual(price_rules, []) + frappe.delete_doc("Promotional Scheme", ps.name) + def test_min_max_amount_configuration(self): ps = make_promotional_scheme() ps.price_discount_slabs[0].min_amount = 10 From c490a6654090b30de9e2aa9398859fbb6d2bd9da Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 15 Oct 2024 16:04:36 +0530 Subject: [PATCH 350/734] fix: incorrect warehouse in the serial no selector for rejection (backport #43671) (#43673) fix: incorrect warehouse in the serial no selector for rejection (#43671) (cherry picked from commit 29ff682ecaddad9450a735e02f7a631f555b145e) Co-authored-by: rohitwaghchaure --- erpnext/public/js/utils/serial_no_batch_selector.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/public/js/utils/serial_no_batch_selector.js b/erpnext/public/js/utils/serial_no_batch_selector.js index 1045965e43d..8329e5c9d55 100644 --- a/erpnext/public/js/utils/serial_no_batch_selector.js +++ b/erpnext/public/js/utils/serial_no_batch_selector.js @@ -678,6 +678,10 @@ erpnext.SerialBatchPackageSelector = class SerialNoBatchBundleUpdate { } get_warehouse() { + if (this.item?.is_rejected) { + return this.item.rejected_warehouse; + } + return this.item?.type_of_transaction === "Outward" ? this.item.warehouse || this.item.s_warehouse : this.item.warehouse || this.item.t_warehouse; From b2deb8982646deccd4a4b0bef4c67796a64c09d8 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 15 Oct 2024 16:04:48 +0530 Subject: [PATCH 351/734] fix: conversion factor issue (backport #43645) (#43674) fix: conversion factor issue (#43645) (cherry picked from commit a52756f1d471ff313b0c932aeb5d0634be2113d4) Co-authored-by: rohitwaghchaure --- .../request_for_quotation.py | 1 + erpnext/controllers/stock_controller.py | 15 +++++++++++++++ .../purchase_receipt/test_purchase_receipt.py | 15 +++++++++++++++ 3 files changed, 31 insertions(+) diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py index e123822f386..3a71733a003 100644 --- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py +++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py @@ -456,6 +456,7 @@ def create_rfq_items(sq_doc, supplier, data): "material_request", "material_request_item", "stock_qty", + "uom", ]: args[field] = data.get(field) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 537b37facf4..0714bdd3a63 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -63,6 +63,21 @@ class StockController(AccountsController): self.set_rate_of_stock_uom() self.validate_internal_transfer() self.validate_putaway_capacity() + self.reset_conversion_factor() + + def reset_conversion_factor(self): + for row in self.get("items"): + if row.uom != row.stock_uom: + continue + + if row.conversion_factor != 1.0: + row.conversion_factor = 1.0 + frappe.msgprint( + _( + "Conversion factor for item {0} has been reset to 1.0 as the uom {1} is same as stock uom {2}." + ).format(bold(row.item_code), bold(row.uom), bold(row.stock_uom)), + alert=True, + ) def validate_items_exist(self): if not self.get("items"): diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index 4cb53e753e7..6d0fe27033f 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -3657,6 +3657,21 @@ class TestPurchaseReceipt(FrappeTestCase): self.assertEqual(data[0].get("bal_qty"), 50.0) + def test_same_stock_and_transaction_uom_conversion_factor(self): + item_code = "Test Item for Same Stock and Transaction UOM Conversion Factor" + create_item(item_code) + + pr = make_purchase_receipt( + item_code=item_code, + qty=10, + rate=100, + stock_uom="Nos", + transaction_uom="Nos", + conversion_factor=10, + ) + + self.assertEqual(pr.items[0].conversion_factor, 1.0) + def prepare_data_for_internal_transfer(): from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier From e10a58074f2f58c1fbcca7a85a41deca1f6db076 Mon Sep 17 00:00:00 2001 From: ljain112 Date: Thu, 10 Oct 2024 11:27:26 +0530 Subject: [PATCH 352/734] fix: added string for translation in bank reconciliation statement (cherry picked from commit c99d9f70373d3c833229e828eb9596f93140c3b2) --- .../bank_reconciliation_statement.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.js b/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.js index 9335a8cd65a..2684c87a22a 100644 --- a/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.js +++ b/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.js @@ -47,7 +47,7 @@ frappe.query_reports["Bank Reconciliation Statement"] = { }, ], formatter: function (value, row, column, data, default_formatter, filter) { - if (column.fieldname == "payment_entry" && value == "Cheques and Deposits incorrectly cleared") { + if (column.fieldname == "payment_entry" && value == __("Cheques and Deposits incorrectly cleared")) { column.link_onclick = "frappe.query_reports['Bank Reconciliation Statement'].open_utility_report()"; } From 21a7dd43a9f1fee1871e7962924330c03d7949fa Mon Sep 17 00:00:00 2001 From: ljain112 Date: Fri, 11 Oct 2024 11:07:19 +0530 Subject: [PATCH 353/734] fix: added parentheses for correct query formation for logical OR condition (cherry picked from commit c0da8f11f77c6d3bcd543e70aea6a9754427538e) --- .../period_closing_voucher/period_closing_voucher.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py index c68cd292523..f5619f8088f 100644 --- a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py +++ b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py @@ -353,9 +353,10 @@ class PeriodClosingVoucher(AccountsController): if get_opening_entries: query = query.where( - gl_entry.posting_date.between(self.get("year_start_date"), self.posting_date) - | gl_entry.is_opening - == "Yes" + ( # noqa: UP034 + (gl_entry.posting_date.between(self.get("year_start_date"), self.posting_date)) + | (gl_entry.is_opening == "Yes") + ) ) else: query = query.where( From 492ba539e803b2b13abaf41443812d8ac172f858 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 15 Oct 2024 16:47:40 +0530 Subject: [PATCH 354/734] =?UTF-8?q?fix:=20refetch=20items=20from=20BOM=20i?= =?UTF-8?q?f=20'Use=20Multi-Level=20BOM'=20has=20changed=20usin=E2=80=A6?= =?UTF-8?q?=20(backport=20#43672)=20(#43676)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix: refetch items from BOM if 'Use Multi-Level BOM' has changed usin… (#43672) fix: refetch items from BOM if 'Use Multi-Level BOM' has changed using api (cherry picked from commit 05915415de3b13545cab4e2d89c33b526ae7db39) Co-authored-by: rohitwaghchaure --- erpnext/manufacturing/doctype/work_order/work_order.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index 218ab2f2bf8..d13cd27a095 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -158,11 +158,20 @@ class WorkOrder(Document): self.validate_operation_time() self.status = self.get_status() self.validate_workstation_type() + self.reset_use_multi_level_bom() validate_uom_is_integer(self, "stock_uom", ["qty", "produced_qty"]) self.set_required_items(reset_only_qty=len(self.get("required_items"))) + def reset_use_multi_level_bom(self): + if self.is_new(): + return + + before_save_obj = self.get_doc_before_save() + if before_save_obj.use_multi_level_bom != self.use_multi_level_bom: + self.get_items_and_operations_from_bom() + def validate_workstation_type(self): for row in self.operations: if not row.workstation and not row.workstation_type: From ea12897ce96b8ba36f1a8db8ddbb192526f78141 Mon Sep 17 00:00:00 2001 From: ljain112 Date: Tue, 15 Oct 2024 12:42:48 +0530 Subject: [PATCH 355/734] fix: run gl_entries and closing voucher processes in same function (cherry picked from commit af4daa5b0f68338fee20ac15c35b6f721d78a459) --- .../period_closing_voucher.py | 54 +++++++------------ 1 file changed, 20 insertions(+), 34 deletions(-) diff --git a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py index f5619f8088f..dd8a5ff16c3 100644 --- a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py +++ b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py @@ -60,7 +60,7 @@ class PeriodClosingVoucher(AccountsController): ) if gle_count > 5000: frappe.enqueue( - make_reverse_gl_entries, + process_cancellation, voucher_type="Period Closing Voucher", voucher_no=self.name, queue="long", @@ -71,9 +71,7 @@ class PeriodClosingVoucher(AccountsController): alert=True, ) else: - make_reverse_gl_entries(voucher_type="Period Closing Voucher", voucher_no=self.name) - - self.delete_closing_entries() + process_cancellation(voucher_type="Period Closing Voucher", voucher_no=self.name) def validate_future_closing_vouchers(self): if frappe.db.exists( @@ -86,12 +84,6 @@ class PeriodClosingVoucher(AccountsController): ) ) - def delete_closing_entries(self): - closing_balance = frappe.qb.DocType("Account Closing Balance") - frappe.qb.from_(closing_balance).delete().where( - closing_balance.period_closing_voucher == self.name - ).run() - def validate_account_head(self): closing_account_type = frappe.get_cached_value("Account", self.closing_account_head, "root_type") @@ -166,14 +158,7 @@ class PeriodClosingVoucher(AccountsController): closing_entries = self.get_grouped_gl_entries(get_opening_entries=get_opening_entries) if len(gl_entries + closing_entries) > 3000: frappe.enqueue( - process_gl_entries, - gl_entries=gl_entries, - voucher_name=self.name, - timeout=3000, - ) - - frappe.enqueue( - process_closing_entries, + process_gl_and_closing_entries, gl_entries=gl_entries, closing_entries=closing_entries, voucher_name=self.name, @@ -187,8 +172,9 @@ class PeriodClosingVoucher(AccountsController): alert=True, ) else: - process_gl_entries(gl_entries, self.name) - process_closing_entries(gl_entries, closing_entries, self.name, self.company, self.posting_date) + process_gl_and_closing_entries( + gl_entries, closing_entries, self.name, self.company, self.posting_date + ) def get_grouped_gl_entries(self, get_opening_entries=False): closing_entries = [] @@ -374,12 +360,16 @@ class PeriodClosingVoucher(AccountsController): return query.run(as_dict=1) -def process_gl_entries(gl_entries, voucher_name): +def process_gl_and_closing_entries(gl_entries, closing_entries, voucher_name, company, closing_date): + from erpnext.accounts.doctype.account_closing_balance.account_closing_balance import ( + make_closing_entries, + ) from erpnext.accounts.general_ledger import make_gl_entries try: if gl_entries: make_gl_entries(gl_entries, merge_entries=False) + make_closing_entries(gl_entries + closing_entries, voucher_name, company, closing_date) frappe.db.set_value("Period Closing Voucher", voucher_name, "gle_processing_status", "Completed") except Exception as e: frappe.db.rollback() @@ -387,25 +377,21 @@ def process_gl_entries(gl_entries, voucher_name): frappe.db.set_value("Period Closing Voucher", voucher_name, "gle_processing_status", "Failed") -def process_closing_entries(gl_entries, closing_entries, voucher_name, company, closing_date): - from erpnext.accounts.doctype.account_closing_balance.account_closing_balance import ( - make_closing_entries, - ) - - try: - make_closing_entries(gl_entries + closing_entries, voucher_name, company, closing_date) - except Exception as e: - frappe.db.rollback() - frappe.log_error(e) - - -def make_reverse_gl_entries(voucher_type, voucher_no): +def process_cancellation(voucher_type, voucher_no): from erpnext.accounts.general_ledger import make_reverse_gl_entries try: make_reverse_gl_entries(voucher_type=voucher_type, voucher_no=voucher_no) + delete_closing_entries(voucher_no) frappe.db.set_value("Period Closing Voucher", voucher_no, "gle_processing_status", "Completed") except Exception as e: frappe.db.rollback() frappe.log_error(e) frappe.db.set_value("Period Closing Voucher", voucher_no, "gle_processing_status", "Failed") + + +def delete_closing_entries(voucher_no): + closing_balance = frappe.qb.DocType("Account Closing Balance") + frappe.qb.from_(closing_balance).delete().where( + closing_balance.period_closing_voucher == voucher_no + ).run() From 4db12fe2da4a3bdacf786c8a6c2ab47711ffecef Mon Sep 17 00:00:00 2001 From: ljain112 Date: Tue, 15 Oct 2024 11:41:00 +0530 Subject: [PATCH 356/734] fix: missing child company accounts in consolidated balance sheet (cherry picked from commit 7fae9d57d24de1a075a4c6bcca73ed95888603d9) --- .../consolidated_financial_statement.py | 72 +++++++++---------- 1 file changed, 35 insertions(+), 37 deletions(-) diff --git a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py index e6aa215924d..d287b30bf0b 100644 --- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py +++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py @@ -469,10 +469,13 @@ def update_parent_account_names(accounts): for d in accounts: if d.account_number: - account_name = d.account_number + " - " + d.account_name + account_key = d.account_number + " - " + d.account_name else: - account_name = d.account_name - name_to_account_map[d.name] = account_name + account_key = d.account_name + + d.account_key = account_key + + name_to_account_map[d.name] = account_key for account in accounts: if account.parent_account: @@ -505,33 +508,26 @@ def get_subsidiary_companies(company): def get_accounts(root_type, companies): accounts = [] - added_accounts = [] for company in companies: - for account in frappe.get_all( - "Account", - fields=[ - "name", - "is_group", - "company", - "parent_account", - "lft", - "rgt", - "root_type", - "report_type", - "account_name", - "account_number", - ], - filters={"company": company, "root_type": root_type}, - ): - if account.account_number: - account_key = account.account_number + "-" + account.account_name - else: - account_key = account.account_name - - if account_key not in added_accounts: - accounts.append(account) - added_accounts.append(account_key) + accounts.extend( + frappe.get_all( + "Account", + fields=[ + "name", + "is_group", + "company", + "parent_account", + "lft", + "rgt", + "root_type", + "report_type", + "account_name", + "account_number", + ], + filters={"company": company, "root_type": root_type}, + ) + ) return accounts @@ -770,15 +766,17 @@ def add_total_row(out, root_type, balance_must_be, companies, company_currency): def filter_accounts(accounts, depth=10): parent_children_map = {} accounts_by_name = {} - for d in accounts: - if d.account_number: - account_name = d.account_number + " - " + d.account_name - else: - account_name = d.account_name - d["company_wise_opening_bal"] = defaultdict(float) - accounts_by_name[account_name] = d + added_accounts = [] - parent_children_map.setdefault(d.parent_account or None, []).append(d) + for d in accounts: + if d.account_key in added_accounts: + continue + + added_accounts.append(d.account_key) + d["company_wise_opening_bal"] = defaultdict(float) + accounts_by_name[d.account_key] = d + + parent_children_map.setdefault(d.parent_account_name or None, []).append(d) filtered_accounts = [] @@ -790,7 +788,7 @@ def filter_accounts(accounts, depth=10): for child in children: child.indent = level filtered_accounts.append(child) - add_to_list(child.name, level + 1) + add_to_list(child.account_key, level + 1) add_to_list(None, 0) From cd9f949b1288708876dd908403616ce0c2e7b6b8 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 15 Oct 2024 17:45:35 +0530 Subject: [PATCH 357/734] fix(stock): Grab posting date/time from SABB (backport #43493) (#43502) fix(stock): Grab posting date/time from SABB (#43493) (cherry picked from commit ade121dac614612405f976014707045ed97181a9) Co-authored-by: Corentin Forler <10946971+cogk@users.noreply.github.com> --- erpnext/stock/stock_ledger.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index dfa60cad96e..0f058d0a64b 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -1740,6 +1740,9 @@ def get_valuation_rate( # Get moving average rate of a specific batch number if warehouse and serial_and_batch_bundle: + sabb = frappe.db.get_value( + "Serial and Batch Bundle", serial_and_batch_bundle, ["posting_date", "posting_time"], as_dict=True + ) batch_obj = BatchNoValuation( sle=frappe._dict( { @@ -1747,6 +1750,8 @@ def get_valuation_rate( "warehouse": warehouse, "actual_qty": -1, "serial_and_batch_bundle": serial_and_batch_bundle, + "posting_date": sabb.posting_date, + "posting_time": sabb.posting_time, } ) ) From 99ead94ffeecd1976604c64da1c2cbbb26e63fb9 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 16 Oct 2024 05:01:44 +0000 Subject: [PATCH 358/734] chore(release): Bumped to Version 15.38.3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## [15.38.3](https://github.com/frappe/erpnext/compare/v15.38.2...v15.38.3) (2024-10-16) ### Bug Fixes * added parentheses for correct query formation for logical OR condition ([21a7dd4](https://github.com/frappe/erpnext/commit/21a7dd43a9f1fee1871e7962924330c03d7949fa)) * added string for translation in bank reconciliation statement ([e10a580](https://github.com/frappe/erpnext/commit/e10a58074f2f58c1fbcca7a85a41deca1f6db076)) * conversion factor issue (backport [#43645](https://github.com/frappe/erpnext/issues/43645)) ([#43674](https://github.com/frappe/erpnext/issues/43674)) ([b2deb89](https://github.com/frappe/erpnext/commit/b2deb8982646deccd4a4b0bef4c67796a64c09d8)) * delete invalid pricing rule on change of applicable_for ([5d6fc71](https://github.com/frappe/erpnext/commit/5d6fc71556ba70adec566c0711a401cfadbaf432)) * don't update reference to SI / PI on advances ([b72906a](https://github.com/frappe/erpnext/commit/b72906a7a1de57d9344384226cf8e9061ab47cfa)) * ignore free item when qty is zero ([e5aaa5b](https://github.com/frappe/erpnext/commit/e5aaa5b6e5ebdb340666123e86e3c3b78b57d6ce)) * incorrect warehouse in the serial no selector for rejection (backport [#43671](https://github.com/frappe/erpnext/issues/43671)) ([#43673](https://github.com/frappe/erpnext/issues/43673)) ([c490a66](https://github.com/frappe/erpnext/commit/c490a6654090b30de9e2aa9398859fbb6d2bd9da)) * Link opportunity from RFQ to supplier quotation ([eb1f125](https://github.com/frappe/erpnext/commit/eb1f1255ebb7f1424e982864bd2b08ceb54e86a2)) * missing child company accounts in consolidated balance sheet ([4db12fe](https://github.com/frappe/erpnext/commit/4db12fe2da4a3bdacf786c8a6c2ab47711ffecef)) * quotation to so frappe crm (backport [#43644](https://github.com/frappe/erpnext/issues/43644)) ([#43646](https://github.com/frappe/erpnext/issues/43646)) ([f3ceb4a](https://github.com/frappe/erpnext/commit/f3ceb4ac7d50969d2dcece661078e1ab0af575ac)) * refetch items from BOM if 'Use Multi-Level BOM' has changed usin… (backport [#43672](https://github.com/frappe/erpnext/issues/43672)) ([#43676](https://github.com/frappe/erpnext/issues/43676)) ([492ba53](https://github.com/frappe/erpnext/commit/492ba539e803b2b13abaf41443812d8ac172f858)) * removed unused query ([8668ae9](https://github.com/frappe/erpnext/commit/8668ae92d830a2eb1168739f9a8bd55281f5c451)) * run gl_entries and closing voucher processes in same function ([ea12897](https://github.com/frappe/erpnext/commit/ea12897ce96b8ba36f1a8db8ddbb192526f78141)) * show incorrect entries filter in Stock Ledger Invariant Check report (backport [#43619](https://github.com/frappe/erpnext/issues/43619)) ([#43622](https://github.com/frappe/erpnext/issues/43622)) ([d604b12](https://github.com/frappe/erpnext/commit/d604b12d5111e9dd5e0fcd3bc35f06c87284c349)) * **stock:** Grab posting date/time from SABB (backport [#43493](https://github.com/frappe/erpnext/issues/43493)) ([#43502](https://github.com/frappe/erpnext/issues/43502)) ([cd9f949](https://github.com/frappe/erpnext/commit/cd9f949b1288708876dd908403616ce0c2e7b6b8)) * update formatings ([c2c6d27](https://github.com/frappe/erpnext/commit/c2c6d27625a82fe836668773d0b8fe41426c5161)) * update formatings ([a70181e](https://github.com/frappe/erpnext/commit/a70181e0258ae209eb7012a507ce513def51f311)) * update item details with actual quantity. ([930e79c](https://github.com/frappe/erpnext/commit/930e79c3510ea7e92edf669f864024c02dbaf058)) * Use `ref_doc.get()` for `party_account_currency` ([928b6b1](https://github.com/frappe/erpnext/commit/928b6b1510b13fbf15e7d3a76fe204a3bcd12f30)) * zero incoming rate for delivery note return ([#43642](https://github.com/frappe/erpnext/issues/43642)) ([85088e4](https://github.com/frappe/erpnext/commit/85088e4aff41306bb132a1f4e22e7e4c85eb70b4)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 076eaeada9b..df1511b8847 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.38.2" +__version__ = "15.38.3" def get_default_company(user=None): From a33d5535a7c88de749bfd47855717ac1410b65fb Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 16 Oct 2024 14:57:57 +0530 Subject: [PATCH 359/734] fix: list view and form status not same for purchase order (backport #43690) (#43692) * fix: list view and form status not same for purchase order (#43690) (cherry picked from commit a671fe13d432de3a1b76f79284855172b36dcde8) # Conflicts: # erpnext/buying/doctype/purchase_order/purchase_order.js # erpnext/buying/doctype/purchase_order/purchase_order_list.js * chore: fix conflicts * chore: fix conflicts --------- Co-authored-by: rohitwaghchaure --- .../doctype/purchase_order/purchase_order.js | 4 ++-- .../purchase_order/purchase_order_list.js | 17 +++++------------ 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js index ac2aa43f23d..de335bd6292 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.js +++ b/erpnext/buying/doctype/purchase_order/purchase_order.js @@ -382,7 +382,7 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends ( } if (doc.status != "Closed") { if (doc.status != "On Hold") { - if (flt(doc.per_received, 2) < 100 && allow_receipt) { + if (flt(doc.per_received) < 100 && allow_receipt) { cur_frm.add_custom_button( __("Purchase Receipt"), this.make_purchase_receipt, @@ -408,7 +408,7 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends ( } } } - if (flt(doc.per_billed, 2) < 100) + if (flt(doc.per_billed) < 100) cur_frm.add_custom_button( __("Purchase Invoice"), this.make_purchase_invoice, diff --git a/erpnext/buying/doctype/purchase_order/purchase_order_list.js b/erpnext/buying/doctype/purchase_order/purchase_order_list.js index c1bf1f3b8d9..7b37987b926 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order_list.js +++ b/erpnext/buying/doctype/purchase_order/purchase_order_list.js @@ -10,14 +10,15 @@ frappe.listview_settings["Purchase Order"] = { "status", ], get_indicator: function (doc) { + // Please do not add precision in the flt function if (doc.status === "Closed") { return [__("Closed"), "green", "status,=,Closed"]; } else if (doc.status === "On Hold") { return [__("On Hold"), "orange", "status,=,On Hold"]; } else if (doc.status === "Delivered") { return [__("Delivered"), "green", "status,=,Closed"]; - } else if (flt(doc.per_received, 2) < 100 && doc.status !== "Closed") { - if (flt(doc.per_billed, 2) < 100) { + } else if (flt(doc.per_received) < 100 && doc.status !== "Closed") { + if (flt(doc.per_billed) < 100) { return [ __("To Receive and Bill"), "orange", @@ -26,17 +27,9 @@ frappe.listview_settings["Purchase Order"] = { } else { return [__("To Receive"), "orange", "per_received,<,100|per_billed,=,100|status,!=,Closed"]; } - } else if ( - flt(doc.per_received, 2) >= 100 && - flt(doc.per_billed, 2) < 100 && - doc.status !== "Closed" - ) { + } else if (flt(doc.per_received) >= 100 && flt(doc.per_billed) < 100 && doc.status !== "Closed") { return [__("To Bill"), "orange", "per_received,=,100|per_billed,<,100|status,!=,Closed"]; - } else if ( - flt(doc.per_received, 2) >= 100 && - flt(doc.per_billed, 2) == 100 && - doc.status !== "Closed" - ) { + } else if (flt(doc.per_received) >= 100 && flt(doc.per_billed) == 100 && doc.status !== "Closed") { return [__("Completed"), "green", "per_received,=,100|per_billed,=,100|status,!=,Closed"]; } }, From 752d175d22db3bb3c38b0ee68622640d1597bc29 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 16 Oct 2024 14:57:57 +0530 Subject: [PATCH 360/734] fix: list view and form status not same for purchase order (backport #43690) (#43692) * fix: list view and form status not same for purchase order (#43690) (cherry picked from commit a671fe13d432de3a1b76f79284855172b36dcde8) # Conflicts: # erpnext/buying/doctype/purchase_order/purchase_order.js # erpnext/buying/doctype/purchase_order/purchase_order_list.js * chore: fix conflicts * chore: fix conflicts --------- Co-authored-by: rohitwaghchaure (cherry picked from commit a33d5535a7c88de749bfd47855717ac1410b65fb) --- .../doctype/purchase_order/purchase_order.js | 4 ++-- .../purchase_order/purchase_order_list.js | 17 +++++------------ 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js index ac2aa43f23d..de335bd6292 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.js +++ b/erpnext/buying/doctype/purchase_order/purchase_order.js @@ -382,7 +382,7 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends ( } if (doc.status != "Closed") { if (doc.status != "On Hold") { - if (flt(doc.per_received, 2) < 100 && allow_receipt) { + if (flt(doc.per_received) < 100 && allow_receipt) { cur_frm.add_custom_button( __("Purchase Receipt"), this.make_purchase_receipt, @@ -408,7 +408,7 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends ( } } } - if (flt(doc.per_billed, 2) < 100) + if (flt(doc.per_billed) < 100) cur_frm.add_custom_button( __("Purchase Invoice"), this.make_purchase_invoice, diff --git a/erpnext/buying/doctype/purchase_order/purchase_order_list.js b/erpnext/buying/doctype/purchase_order/purchase_order_list.js index c1bf1f3b8d9..7b37987b926 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order_list.js +++ b/erpnext/buying/doctype/purchase_order/purchase_order_list.js @@ -10,14 +10,15 @@ frappe.listview_settings["Purchase Order"] = { "status", ], get_indicator: function (doc) { + // Please do not add precision in the flt function if (doc.status === "Closed") { return [__("Closed"), "green", "status,=,Closed"]; } else if (doc.status === "On Hold") { return [__("On Hold"), "orange", "status,=,On Hold"]; } else if (doc.status === "Delivered") { return [__("Delivered"), "green", "status,=,Closed"]; - } else if (flt(doc.per_received, 2) < 100 && doc.status !== "Closed") { - if (flt(doc.per_billed, 2) < 100) { + } else if (flt(doc.per_received) < 100 && doc.status !== "Closed") { + if (flt(doc.per_billed) < 100) { return [ __("To Receive and Bill"), "orange", @@ -26,17 +27,9 @@ frappe.listview_settings["Purchase Order"] = { } else { return [__("To Receive"), "orange", "per_received,<,100|per_billed,=,100|status,!=,Closed"]; } - } else if ( - flt(doc.per_received, 2) >= 100 && - flt(doc.per_billed, 2) < 100 && - doc.status !== "Closed" - ) { + } else if (flt(doc.per_received) >= 100 && flt(doc.per_billed) < 100 && doc.status !== "Closed") { return [__("To Bill"), "orange", "per_received,=,100|per_billed,<,100|status,!=,Closed"]; - } else if ( - flt(doc.per_received, 2) >= 100 && - flt(doc.per_billed, 2) == 100 && - doc.status !== "Closed" - ) { + } else if (flt(doc.per_received) >= 100 && flt(doc.per_billed) == 100 && doc.status !== "Closed") { return [__("Completed"), "green", "per_received,=,100|per_billed,=,100|status,!=,Closed"]; } }, From ab20344141e60c43464263454a7c531cc0fd2b5e Mon Sep 17 00:00:00 2001 From: venkat102 Date: Thu, 17 Oct 2024 12:13:15 +0530 Subject: [PATCH 361/734] fix: show total amount on report summary --- .../accounts/report/balance_sheet/balance_sheet.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/erpnext/accounts/report/balance_sheet/balance_sheet.py b/erpnext/accounts/report/balance_sheet/balance_sheet.py index 274c8a7a371..ab2f45d4f8b 100644 --- a/erpnext/accounts/report/balance_sheet/balance_sheet.py +++ b/erpnext/accounts/report/balance_sheet/balance_sheet.py @@ -122,13 +122,13 @@ def get_provisional_profit_loss( for period in period_list: key = period if consolidated else period.key - total_assets = flt(asset[0].get(key)) + total_assets = flt(asset[-2].get(key)) effective_liability = 0.00 - if liability: - effective_liability += flt(liability[0].get(key)) - if equity: - effective_liability += flt(equity[0].get(key)) + if liability and liability[-1] == {}: + effective_liability += flt(liability[-2].get(key)) + if equity and equity[-1] == {}: + effective_liability += flt(equity[-2].get(key)) provisional_profit_loss[key] = total_assets - effective_liability total_row[key] = provisional_profit_loss[key] + effective_liability @@ -195,9 +195,9 @@ def get_report_summary( key = period if consolidated else period.key if asset: net_asset += asset[-2].get(key) - if liability: + if liability and liability[-1] == {}: net_liability += liability[-2].get(key) - if equity: + if equity and equity[-1] == {}: net_equity += equity[-2].get(key) if provisional_profit_loss: net_provisional_profit_loss += provisional_profit_loss.get(key) From 60ffcd0574bebbfa3e91fe82744c14792f9d8ad2 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 17 Oct 2024 21:34:54 +0530 Subject: [PATCH 362/734] fix: added validation for UOM must be whole number (backport #43710) (#43712) fix: added validation for UOM must be whole number (#43710) (cherry picked from commit 4fd4a37dc931b48526a5e00b0a905a735b435778) Co-authored-by: rohitwaghchaure --- erpnext/controllers/accounts_controller.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 73923464ed9..ff178fab59e 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -3521,6 +3521,9 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil parent.update_billing_percentage() parent.set_status() + parent.validate_uom_is_integer("uom", "qty") + parent.validate_uom_is_integer("stock_uom", "stock_qty") + # Cancel and Recreate Stock Reservation Entries. if parent_doctype == "Sales Order": from erpnext.stock.doctype.stock_reservation_entry.stock_reservation_entry import ( From 08cabd1717a0a08875edc94e9c48e104465336c9 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Thu, 17 Oct 2024 16:05:56 +0000 Subject: [PATCH 363/734] chore(release): Bumped to Version 15.38.4 ## [15.38.4](https://github.com/frappe/erpnext/compare/v15.38.3...v15.38.4) (2024-10-17) ### Bug Fixes * list view and form status not same for purchase order (backport [#43690](https://github.com/frappe/erpnext/issues/43690)) ([#43692](https://github.com/frappe/erpnext/issues/43692)) ([752d175](https://github.com/frappe/erpnext/commit/752d175d22db3bb3c38b0ee68622640d1597bc29)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index df1511b8847..e28b6e9bb91 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.38.3" +__version__ = "15.38.4" def get_default_company(user=None): From 52be45c5dff5f0fcfb3573ba94f177816c3fa67b Mon Sep 17 00:00:00 2001 From: NIYAZ RAZAK <76736615+niyazrazak@users.noreply.github.com> Date: Sat, 24 Aug 2024 12:56:33 +0300 Subject: [PATCH 364/734] fix: incorrect amount in bank clearance (cherry picked from commit 9a11df59fc3b415d4b15a71d10df4656a0b339d8) --- erpnext/accounts/doctype/bank_clearance/bank_clearance.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/bank_clearance/bank_clearance.py b/erpnext/accounts/doctype/bank_clearance/bank_clearance.py index 92abb8cea89..ac7883fce13 100644 --- a/erpnext/accounts/doctype/bank_clearance/bank_clearance.py +++ b/erpnext/accounts/doctype/bank_clearance/bank_clearance.py @@ -168,7 +168,7 @@ def get_payment_entries_for_bank_clearance( "Payment Entry" as payment_document, name as payment_entry, reference_no as cheque_number, reference_date as cheque_date, if(paid_from=%(account)s, paid_amount + total_taxes_and_charges, 0) as credit, - if(paid_from=%(account)s, 0, received_amount) as debit, + if(paid_from=%(account)s, 0, received_amount + total_taxes_and_charges) as debit, posting_date, ifnull(party,if(paid_from=%(account)s,paid_to,paid_from)) as against_account, clearance_date, if(paid_to=%(account)s, paid_to_account_currency, paid_from_account_currency) as account_currency from `tabPayment Entry` From 1281d9d21dd2628fe755881b6d7189c0c5848ee7 Mon Sep 17 00:00:00 2001 From: devdiogenes Date: Wed, 11 Sep 2024 20:42:45 +0000 Subject: [PATCH 365/734] fix: Call super onload_post_render inside pos_invoice.js (cherry picked from commit 4a3eca963c32b6a46dfe713339e34661ac0eb27e) --- erpnext/accounts/doctype/pos_invoice/pos_invoice.js | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.js b/erpnext/accounts/doctype/pos_invoice/pos_invoice.js index 8707ee88860..deb8bd7529d 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.js +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.js @@ -57,6 +57,7 @@ erpnext.selling.POSInvoiceController = class POSInvoiceController extends erpnex } onload_post_render(frm) { + super.onload_post_render(); this.pos_profile(frm); } From f70506fc92a3b149d71ede3abd879d7ee6ce6535 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Do=C4=9Fancan?= <37839267+doancan@users.noreply.github.com> Date: Thu, 19 Sep 2024 08:04:39 +0300 Subject: [PATCH 366/734] refactor: update default_success_action.py The _(doctype) inside get_message is removed from the .format() method. The reason is that _(doctype) would attempt to translate the doctype itself, which is generally not required since the doctypes in doctype_list are system-level terms. The main string "{0} has been submitted successfully" should be translated, and then it should receive the doctype name as an argument. (cherry picked from commit 804558e5bf6ecb6cad54c22233728b50bc5f2fb7) --- erpnext/setup/default_success_action.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/setup/default_success_action.py b/erpnext/setup/default_success_action.py index 2b9e75c3265..dba20548184 100644 --- a/erpnext/setup/default_success_action.py +++ b/erpnext/setup/default_success_action.py @@ -11,14 +11,17 @@ doctype_list = [ def get_message(doctype): - return _("{0} has been submitted successfully").format(_(doctype)) + # Properly format the string with translated doctype + return _("{0} has been submitted successfully").format(doctype) def get_first_success_message(doctype): + # Reuse the get_message function for consistency return get_message(doctype) def get_default_success_action(): + # Loop through each doctype in the list and return formatted actions return [ { "doctype": "Success Action", From ddb38db5c46fb20cecd1fd6dcca2ef2168d8a9f9 Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Fri, 18 Oct 2024 16:47:57 +0530 Subject: [PATCH 367/734] fix: do not make new depreciation for fully depreciated asset (cherry picked from commit 25de412371d6fe9787bcec34546e142b27970b2c) --- .../doctype/sales_invoice/sales_invoice.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 529086228de..c680bd46130 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1359,14 +1359,15 @@ class SalesInvoice(SellingController): else: if asset.calculate_depreciation: - notes = _( - "This schedule was created when Asset {0} was sold through Sales Invoice {1}." - ).format( - get_link_to_form(asset.doctype, asset.name), - get_link_to_form(self.doctype, self.get("name")), - ) - depreciate_asset(asset, self.posting_date, notes) - asset.reload() + if not asset.status == "Fully Depreciated": + notes = _( + "This schedule was created when Asset {0} was sold through Sales Invoice {1}." + ).format( + get_link_to_form(asset.doctype, asset.name), + get_link_to_form(self.doctype, self.get("name")), + ) + depreciate_asset(asset, self.posting_date, notes) + asset.reload() fixed_asset_gl_entries = get_gl_entries_on_asset_disposal( asset, From b673377b70210f2004df0cbc9eab29f86e17d778 Mon Sep 17 00:00:00 2001 From: venkat102 Date: Fri, 18 Oct 2024 18:42:45 +0530 Subject: [PATCH 368/734] fix: get party advance amount based on account (cherry picked from commit d7fa95dd2fd9d682736d0dc7b1b67d0d9eddf4d2) --- erpnext/accounts/party.py | 44 ++++++++++++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index 06e285e4ada..65054aec53f 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -881,16 +881,17 @@ def get_party_shipping_address(doctype: str, name: str) -> str | None: def get_partywise_advanced_payment_amount( party_type, posting_date=None, future_payment=0, company=None, party=None ): + account_type = frappe.get_cached_value("Party Type", party_type, "account_type") + ple = frappe.qb.DocType("Payment Ledger Entry") + acc = frappe.qb.DocType("Account") + query = ( frappe.qb.from_(ple) - .select(ple.party, Abs(Sum(ple.amount).as_("amount"))) - .where( - (ple.party_type.isin(party_type)) - & (ple.amount < 0) - & (ple.against_voucher_no == ple.voucher_no) - & (ple.delinked == 0) - ) + .inner_join(acc) + .on(ple.account == acc.name) + .select(ple.party) + .where((ple.party_type.isin(party_type)) & (acc.account_type == account_type) & (ple.delinked == 0)) .groupby(ple.party) ) @@ -909,9 +910,32 @@ def get_partywise_advanced_payment_amount( if invoice_doctypes := frappe.get_hooks("invoice_doctypes"): query = query.where(ple.voucher_type.notin(invoice_doctypes)) - data = query.run() - if data: - return frappe._dict(data) + # Get advance amount from Receivable / Payable Account + party_ledger = query.select(Abs(Sum(ple.amount).as_("amount"))) + party_ledger = party_ledger.where(ple.amount < 0) + party_ledger = party_ledger.where(ple.against_voucher_no == ple.voucher_no) + party_ledger = party_ledger.where( + acc.root_type == ("Liability" if account_type == "Payable" else "Asset") + ) + + data = party_ledger.run() + data = frappe._dict(data or {}) + + # Get advance amount from Advance Account + advance_ledger = query.select(Sum(ple.amount).as_("amount"), ple.account) + advance_ledger = advance_ledger.where( + acc.root_type == ("Asset" if account_type == "Payable" else "Liability") + ) + advance_ledger = advance_ledger.groupby(ple.account) + advance_ledger = advance_ledger.having(Sum(ple.amount) < 0) + + advance_data = advance_ledger.run() + + for row in advance_data: + data.setdefault(row[0], 0) + data[row[0]] += abs(row[1]) + + return data def get_default_contact(doctype: str, name: str) -> str | None: From f7717c91bc48869a03ba5d76b0ac1e8879dfc074 Mon Sep 17 00:00:00 2001 From: ljain112 Date: Fri, 18 Oct 2024 18:02:25 +0530 Subject: [PATCH 369/734] fix: "show_remarks" checkbox in Process statement of accounts (cherry picked from commit f4600df1f718440698aa98d460178f3e1f9ab430) --- .../process_statement_of_accounts.json | 9 ++++++++- .../process_statement_of_accounts.py | 2 ++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json index 22be5299280..763607c22a1 100644 --- a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json +++ b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json @@ -25,6 +25,7 @@ "payment_terms_template", "sales_partner", "sales_person", + "show_remarks", "based_on_payment_terms", "section_break_3", "customer_collection", @@ -390,10 +391,16 @@ "fieldname": "ignore_cr_dr_notes", "fieldtype": "Check", "label": "Ignore System Generated Credit / Debit Notes" + }, + { + "default": "0", + "fieldname": "show_remarks", + "fieldtype": "Check", + "label": "Show Remarks" } ], "links": [], - "modified": "2024-08-13 10:41:18.381165", + "modified": "2024-10-18 17:51:39.108481", "modified_by": "Administrator", "module": "Accounts", "name": "Process Statement Of Accounts", diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py index 509199ccae6..bc3ba26beb7 100644 --- a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py +++ b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py @@ -70,6 +70,7 @@ class ProcessStatementOfAccounts(Document): sales_person: DF.Link | None sender: DF.Link | None show_net_values_in_party_account: DF.Check + show_remarks: DF.Check start_date: DF.Date | None subject: DF.Data | None terms_and_conditions: DF.Link | None @@ -187,6 +188,7 @@ def get_common_filters(doc): "finance_book": doc.finance_book if doc.finance_book else None, "account": [doc.account] if doc.account else None, "cost_center": [cc.cost_center_name for cc in doc.cost_center], + "show_remarks": doc.show_remarks, } ) From 148d7e798bbe66cee8c8f3e5e0ed1db7dca8d734 Mon Sep 17 00:00:00 2001 From: venkat102 Date: Fri, 18 Oct 2024 18:00:40 +0530 Subject: [PATCH 370/734] fix: get period estimate till service end date (cherry picked from commit a7ba7e9c28e3946ba771516c8ef3ce397d6b0e2e) --- .../deferred_revenue_and_expense.py | 33 ++++++++++--------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.py b/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.py index c6d9eac5966..377777ab2a3 100644 --- a/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.py +++ b/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.py @@ -122,21 +122,24 @@ class Deferred_Item: """ simulate future posting by creating dummy gl entries. starts from the last posting date. """ - if self.service_start_date != self.service_end_date: - if add_days(self.last_entry_date, 1) < self.period_list[-1].to_date: - self.estimate_for_period_list = get_period_list( - self.filters.from_fiscal_year, - self.filters.to_fiscal_year, - add_days(self.last_entry_date, 1), - self.period_list[-1].to_date, - "Date Range", - "Monthly", - company=self.filters.company, - ) - for period in self.estimate_for_period_list: - amount = self.calculate_amount(period.from_date, period.to_date) - gle = self.make_dummy_gle(period.key, period.to_date, amount) - self.gle_entries.append(gle) + if ( + self.service_start_date != self.service_end_date + and add_days(self.last_entry_date, 1) < self.service_end_date + ): + self.estimate_for_period_list = get_period_list( + self.filters.from_fiscal_year, + self.filters.to_fiscal_year, + add_days(self.last_entry_date, 1), + self.service_end_date, + "Date Range", + "Monthly", + company=self.filters.company, + ) + + for period in self.estimate_for_period_list: + amount = self.calculate_amount(period.from_date, period.to_date) + gle = self.make_dummy_gle(period.key, period.to_date, amount) + self.gle_entries.append(gle) def calculate_item_revenue_expense_for_period(self): """ From 04fbcc64ff661689e5b8e7a62569ef54674b64f1 Mon Sep 17 00:00:00 2001 From: ljain112 Date: Fri, 18 Oct 2024 13:28:46 +0530 Subject: [PATCH 371/734] fix: party_balance based on company in payment entry (cherry picked from commit 97c9adf06b04df4a76e71e7cdc6b0331e6c84847) --- erpnext/accounts/doctype/payment_entry/payment_entry.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 7885d825129..b645d92cb64 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -2486,7 +2486,9 @@ def get_party_details(company, party_type, party, date, cost_center=None): account_balance = get_balance_on(party_account, date, cost_center=cost_center) _party_name = "title" if party_type == "Shareholder" else party_type.lower() + "_name" party_name = frappe.db.get_value(party_type, party, _party_name) - party_balance = get_balance_on(party_type=party_type, party=party, cost_center=cost_center) + party_balance = get_balance_on( + party_type=party_type, party=party, company=company, cost_center=cost_center + ) if party_type in ["Customer", "Supplier"]: party_bank_account = get_party_bank_account(party_type, party) bank_account = get_default_company_bank_account(company, party_type, party) From f343d5a24dbcc38b63b9c94182d69cdea701b6db Mon Sep 17 00:00:00 2001 From: HarryPaulo Date: Wed, 24 Jul 2024 08:57:27 -0300 Subject: [PATCH 372/734] fix: Freeze Screen on load invoices on POS Closing Entry (cherry picked from commit 486d396174216c83c00f0218dda94aff803b8693) --- erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js index c171713dc61..5711b27da04 100644 --- a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js +++ b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js @@ -80,8 +80,10 @@ frappe.ui.form.on("POS Closing Entry", { ) { reset_values(frm); frappe.run_serially([ + () => frappe.dom.freeze(__("Loading Invoices! Please Wait...")), () => frm.trigger("set_opening_amounts"), () => frm.trigger("get_pos_invoices"), + () => frappe.dom.unfreeze(), ]); } }, From 1fa9030aee13c39c6aade9779145d86ff976ce90 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 21 Oct 2024 17:15:34 +0530 Subject: [PATCH 373/734] fix: Workspace link for Work Order Consumed Materials report (backport #43753) (#43754) fix: Workspace link for Work Order Consumed Materials report (#43753) (cherry picked from commit e94ffb87cd463aa23cda78c55f60e62001954129) Co-authored-by: Nabin Hait --- .../manufacturing/manufacturing.json | 157 +++++++++--------- 1 file changed, 80 insertions(+), 77 deletions(-) diff --git a/erpnext/manufacturing/workspace/manufacturing/manufacturing.json b/erpnext/manufacturing/workspace/manufacturing/manufacturing.json index d2520d6b7eb..06b9f1b0759 100644 --- a/erpnext/manufacturing/workspace/manufacturing/manufacturing.json +++ b/erpnext/manufacturing/workspace/manufacturing/manufacturing.json @@ -1,4 +1,5 @@ { + "app": "erpnext", "charts": [], "content": "[{\"id\":\"csBCiDglCE\",\"type\":\"header\",\"data\":{\"text\":\"Your Shortcuts\",\"col\":12}},{\"id\":\"YHCQG3wAGv\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"BOM Creator\",\"col\":3}},{\"id\":\"xit0dg7KvY\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"BOM\",\"col\":3}},{\"id\":\"LRhGV9GAov\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Production Plan\",\"col\":3}},{\"id\":\"69KKosI6Hg\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Work Order\",\"col\":3}},{\"id\":\"PwndxuIpB3\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Job Card\",\"col\":3}},{\"id\":\"Ubj6zXcmIQ\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Plant Floor\",\"col\":3}},{\"id\":\"OtMcArFRa5\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"BOM Stock Report\",\"col\":3}},{\"id\":\"76yYsI5imF\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Production Planning Report\",\"col\":3}},{\"id\":\"PIQJYZOMnD\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Learn Manufacturing\",\"col\":3}},{\"id\":\"OaiDqTT03Y\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Forecasting\",\"col\":3}},{\"id\":\"bN_6tHS-Ct\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"yVEFZMqVwd\",\"type\":\"header\",\"data\":{\"text\":\"Reports & Masters\",\"col\":12}},{\"id\":\"rwrmsTI58-\",\"type\":\"card\",\"data\":{\"card_name\":\"Production\",\"col\":4}},{\"id\":\"6dnsyX-siZ\",\"type\":\"card\",\"data\":{\"card_name\":\"Bill of Materials\",\"col\":4}},{\"id\":\"CIq-v5f5KC\",\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}},{\"id\":\"8RRiQeYr0G\",\"type\":\"card\",\"data\":{\"card_name\":\"Tools\",\"col\":4}},{\"id\":\"Pu8z7-82rT\",\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}}]", "creation": "2020-03-02 17:11:37.032604", @@ -124,11 +125,86 @@ "onboard": 0, "type": "Link" }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Bill of Materials", + "link_count": 6, + "onboard": 0, + "type": "Card Break" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Item", + "link_count": 0, + "link_to": "Item", + "link_type": "DocType", + "onboard": 1, + "type": "Link" + }, + { + "dependencies": "Item", + "hidden": 0, + "is_query_report": 0, + "label": "Bill of Materials", + "link_count": 0, + "link_to": "BOM", + "link_type": "DocType", + "onboard": 1, + "type": "Link" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Workstation Type", + "link_count": 0, + "link_to": "Workstation Type", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Workstation", + "link_count": 0, + "link_to": "Workstation", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Operation", + "link_count": 0, + "link_to": "Operation", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "Work Order", + "hidden": 0, + "is_query_report": 1, + "label": "Routing", + "link_count": 0, + "link_to": "Routing", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, { "hidden": 0, "is_query_report": 0, "label": "Reports", "link_count": 10, + "link_type": "DocType", "onboard": 0, "type": "Card Break" }, @@ -233,90 +309,16 @@ }, { "hidden": 0, - "is_query_report": 0, + "is_query_report": 1, "label": "Work Order Consumed Materials", "link_count": 0, "link_to": "Work Order Consumed Materials", "link_type": "Report", "onboard": 0, "type": "Link" - }, - { - "hidden": 0, - "is_query_report": 0, - "label": "Bill of Materials", - "link_count": 6, - "onboard": 0, - "type": "Card Break" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Item", - "link_count": 0, - "link_to": "Item", - "link_type": "DocType", - "onboard": 1, - "type": "Link" - }, - { - "dependencies": "Item", - "hidden": 0, - "is_query_report": 0, - "label": "Bill of Materials", - "link_count": 0, - "link_to": "BOM", - "link_type": "DocType", - "onboard": 1, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Workstation Type", - "link_count": 0, - "link_to": "Workstation Type", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Workstation", - "link_count": 0, - "link_to": "Workstation", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Operation", - "link_count": 0, - "link_to": "Operation", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Work Order", - "hidden": 0, - "is_query_report": 1, - "label": "Routing", - "link_count": 0, - "link_to": "Routing", - "link_type": "DocType", - "onboard": 0, - "type": "Link" } ], - "modified": "2024-01-30 21:49:58.577218", + "modified": "2024-10-21 14:13:38.777556", "modified_by": "Administrator", "module": "Manufacturing", "name": "Manufacturing", @@ -398,5 +400,6 @@ "type": "Report" } ], - "title": "Manufacturing" + "title": "Manufacturing", + "type": "Workspace" } \ No newline at end of file From 9e56f213a3c7149a2067944f4adb274af69598f6 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Mon, 21 Oct 2024 20:16:35 +0530 Subject: [PATCH 374/734] fix: lead create opp from connection not working (cherry picked from commit 0dc518b1c3ad6e3539bfbc8486eea5a6fc6a9812) --- erpnext/crm/doctype/lead/lead.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/crm/doctype/lead/lead.js b/erpnext/crm/doctype/lead/lead.js index e50cf9e4dd0..022d1906e84 100644 --- a/erpnext/crm/doctype/lead/lead.js +++ b/erpnext/crm/doctype/lead/lead.js @@ -7,9 +7,9 @@ cur_frm.email_field = "email_id"; erpnext.LeadController = class LeadController extends frappe.ui.form.Controller { setup() { this.frm.make_methods = { - Customer: this.make_customer, - Quotation: this.make_quotation, - Opportunity: this.make_opportunity, + Customer: this.make_customer.bind(this), + Quotation: this.make_quotation.bind(this), + Opportunity: this.make_opportunity.bind(this), }; // For avoiding integration issues. From a3b8f9759d79d9ad45173248ab4bf3bb343aafc6 Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Tue, 24 Sep 2024 17:43:04 +0530 Subject: [PATCH 375/734] feat: added assignee email field in asset maintenance log (cherry picked from commit 5911934dc75eb77f11fce49cd6621f9f554217f5) # Conflicts: # erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.json # erpnext/patches.txt --- .../asset_maintenance/asset_maintenance.py | 1 + .../asset_maintenance_log.json | 12 ++++++++++++ .../asset_maintenance_log.py | 1 + erpnext/patches.txt | 5 +++++ ...nee_email_field_in_asset_maintenance_log.py | 18 ++++++++++++++++++ 5 files changed, 37 insertions(+) create mode 100644 erpnext/patches/v15_0/update_task_assignee_email_field_in_asset_maintenance_log.py diff --git a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py index b44164f2dae..70ab7fdc8e8 100644 --- a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py +++ b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py @@ -144,6 +144,7 @@ def update_maintenance_log(asset_maintenance, item_code, item_name, task): "has_certificate": task.certificate_required, "description": task.description, "assign_to_name": task.assign_to_name, + "task_assignee_email": task.assign_to, "periodicity": str(task.periodicity), "maintenance_type": task.maintenance_type, "due_date": task.next_due_date, diff --git a/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.json b/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.json index 7d33176e2f3..cf6d94ee567 100644 --- a/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.json +++ b/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.json @@ -23,6 +23,7 @@ "column_break_6", "maintenance_status", "assign_to_name", + "task_assignee_email", "due_date", "completion_date", "description", @@ -168,15 +169,26 @@ "in_preview": 1, "label": "Task Name", "read_only": 1 + }, + { + "fieldname": "task_assignee_email", + "fieldtype": "Data", + "label": "Task Assignee Email", + "read_only": 1 } ], "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], +<<<<<<< HEAD "modified": "2021-01-22 12:33:45.888124", +======= + "modified": "2024-09-24 15:12:37.497853", +>>>>>>> 5911934dc7 (feat: added assignee email field in asset maintenance log) "modified_by": "Administrator", "module": "Assets", "name": "Asset Maintenance Log", + "naming_rule": "By \"Naming Series\" field", "owner": "Administrator", "permissions": [ { diff --git a/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.py b/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.py index 95d02714c5b..140eb1af27e 100644 --- a/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.py +++ b/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.py @@ -37,6 +37,7 @@ class AssetMaintenanceLog(Document): naming_series: DF.Literal["ACC-AML-.YYYY.-"] periodicity: DF.Data | None task: DF.Link | None + task_assignee_email: DF.Data | None task_name: DF.Data | None # end: auto-generated types diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 333fca1d1da..32e56be77ea 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -377,4 +377,9 @@ erpnext.patches.v15_0.drop_index_posting_datetime_from_sle erpnext.patches.v15_0.add_disassembly_order_stock_entry_type #1 erpnext.patches.v15_0.set_standard_stock_entry_type erpnext.patches.v15_0.link_purchase_item_to_asset_doc +<<<<<<< HEAD erpnext.patches.v14_0.update_currency_exchange_settings_for_frankfurter +======= +erpnext.patches.v15_0.migrate_to_utm_analytics +erpnext.patches.v15_0.update_task_assignee_email_field_in_asset_maintenance_log +>>>>>>> 5911934dc7 (feat: added assignee email field in asset maintenance log) diff --git a/erpnext/patches/v15_0/update_task_assignee_email_field_in_asset_maintenance_log.py b/erpnext/patches/v15_0/update_task_assignee_email_field_in_asset_maintenance_log.py new file mode 100644 index 00000000000..a6eda9c2e17 --- /dev/null +++ b/erpnext/patches/v15_0/update_task_assignee_email_field_in_asset_maintenance_log.py @@ -0,0 +1,18 @@ +import frappe +from frappe.query_builder import DocType + + +def execute(): + if frappe.db.has_column("Asset Maintenance Log", "task_assignee_email"): + asset_maintenance_log = DocType("Asset Maintenance Log") + asset_maintenance_task = DocType("Asset Maintenance Task") + try: + ( + frappe.qb.update(asset_maintenance_log) + .set(asset_maintenance_log.task_assignee_email, asset_maintenance_task.assign_to) + .join(asset_maintenance_task) + .on(asset_maintenance_log.task == asset_maintenance_task.name) + .run() + ) + except Exception: + frappe.log_error("Failed to update Task Assignee Email Field.") From 0a030761484946a567dfa23b72cfc8833680dbc6 Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Tue, 22 Oct 2024 01:17:33 +0530 Subject: [PATCH 376/734] chore: resolved conflicts --- .../asset_maintenance_log/asset_maintenance_log.json | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.json b/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.json index cf6d94ee567..c948630869b 100644 --- a/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.json +++ b/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.json @@ -180,11 +180,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], -<<<<<<< HEAD - "modified": "2021-01-22 12:33:45.888124", -======= "modified": "2024-09-24 15:12:37.497853", ->>>>>>> 5911934dc7 (feat: added assignee email field in asset maintenance log) "modified_by": "Administrator", "module": "Assets", "name": "Asset Maintenance Log", @@ -211,4 +207,4 @@ "sort_order": "DESC", "track_changes": 1, "track_seen": 1 -} \ No newline at end of file +} From 1d5345abc16b228d89a2c8c8c264a10e7c969d6f Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Tue, 22 Oct 2024 01:23:49 +0530 Subject: [PATCH 377/734] chore: resolved conflicts --- erpnext/patches.txt | 3 --- 1 file changed, 3 deletions(-) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 32e56be77ea..3013c3c9750 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -377,9 +377,6 @@ erpnext.patches.v15_0.drop_index_posting_datetime_from_sle erpnext.patches.v15_0.add_disassembly_order_stock_entry_type #1 erpnext.patches.v15_0.set_standard_stock_entry_type erpnext.patches.v15_0.link_purchase_item_to_asset_doc -<<<<<<< HEAD erpnext.patches.v14_0.update_currency_exchange_settings_for_frankfurter -======= erpnext.patches.v15_0.migrate_to_utm_analytics erpnext.patches.v15_0.update_task_assignee_email_field_in_asset_maintenance_log ->>>>>>> 5911934dc7 (feat: added assignee email field in asset maintenance log) From 2e0cf36901b52369cd95c73a331269d123f94612 Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Tue, 22 Oct 2024 02:06:41 +0530 Subject: [PATCH 378/734] fix: removed unmerged patches --- erpnext/patches.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 3013c3c9750..e59938909c7 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -378,5 +378,4 @@ erpnext.patches.v15_0.add_disassembly_order_stock_entry_type #1 erpnext.patches.v15_0.set_standard_stock_entry_type erpnext.patches.v15_0.link_purchase_item_to_asset_doc erpnext.patches.v14_0.update_currency_exchange_settings_for_frankfurter -erpnext.patches.v15_0.migrate_to_utm_analytics erpnext.patches.v15_0.update_task_assignee_email_field_in_asset_maintenance_log From aeaadb1e300b622abf54d35364a471463b504a2e Mon Sep 17 00:00:00 2001 From: "bhaveshkumar.j" Date: Mon, 21 Oct 2024 18:22:30 +0000 Subject: [PATCH 379/734] fix: coupon code validation logic (cherry picked from commit d04257a32dd705868cc662399dcc893003a5b59c) # Conflicts: # erpnext/accounts/doctype/pricing_rule/utils.py --- erpnext/accounts/doctype/pricing_rule/utils.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index a74dfea2cca..4093462d645 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -728,6 +728,7 @@ def get_pricing_rule_items(pr_doc, other_items=False) -> list: def validate_coupon_code(coupon_name): coupon = frappe.get_doc("Coupon Code", coupon_name) +<<<<<<< HEAD if coupon.valid_from: if coupon.valid_from > getdate(today()): @@ -736,9 +737,17 @@ def validate_coupon_code(coupon_name): if coupon.valid_upto < getdate(today()): frappe.throw(_("Sorry, this coupon code's validity has expired")) elif coupon.used >= coupon.maximum_use: +======= + if coupon.valid_from and coupon.valid_from > getdate(today()): + frappe.throw(_("Sorry, this coupon code's validity has not started")) + elif coupon.valid_upto and coupon.valid_upto < getdate(today()): + frappe.throw(_("Sorry, this coupon code's validity has expired")) + elif coupon.maximum_use and coupon.used >= coupon.maximum_use: +>>>>>>> d04257a32d (fix: coupon code validation logic) frappe.throw(_("Sorry, this coupon code is no longer valid")) + def update_coupon_code_count(coupon_name, transaction_type): coupon = frappe.get_doc("Coupon Code", coupon_name) if coupon: From 50dd8d9df756fa1bf64fa0d86b719b6aeffd0228 Mon Sep 17 00:00:00 2001 From: "bhaveshkumar.j" Date: Mon, 21 Oct 2024 18:23:39 +0000 Subject: [PATCH 380/734] fix: remove extra space (cherry picked from commit 1561a9e1bf6b179dddae2861160d224b6983ced1) --- erpnext/accounts/doctype/pricing_rule/utils.py | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index 4093462d645..98ef8910143 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -747,7 +747,6 @@ def validate_coupon_code(coupon_name): frappe.throw(_("Sorry, this coupon code is no longer valid")) - def update_coupon_code_count(coupon_name, transaction_type): coupon = frappe.get_doc("Coupon Code", coupon_name) if coupon: From 853ca1fcee8a5176fa700599c0bb34813858d177 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 22 Oct 2024 10:15:28 +0530 Subject: [PATCH 381/734] chore: resolve conflict --- erpnext/accounts/doctype/pricing_rule/utils.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index 98ef8910143..572529580e8 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -728,22 +728,11 @@ def get_pricing_rule_items(pr_doc, other_items=False) -> list: def validate_coupon_code(coupon_name): coupon = frappe.get_doc("Coupon Code", coupon_name) -<<<<<<< HEAD - - if coupon.valid_from: - if coupon.valid_from > getdate(today()): - frappe.throw(_("Sorry, this coupon code's validity has not started")) - elif coupon.valid_upto: - if coupon.valid_upto < getdate(today()): - frappe.throw(_("Sorry, this coupon code's validity has expired")) - elif coupon.used >= coupon.maximum_use: -======= if coupon.valid_from and coupon.valid_from > getdate(today()): frappe.throw(_("Sorry, this coupon code's validity has not started")) elif coupon.valid_upto and coupon.valid_upto < getdate(today()): frappe.throw(_("Sorry, this coupon code's validity has expired")) elif coupon.maximum_use and coupon.used >= coupon.maximum_use: ->>>>>>> d04257a32d (fix: coupon code validation logic) frappe.throw(_("Sorry, this coupon code is no longer valid")) From 88f5e3f160cacf2c86b393a2036d109647cb6506 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 22 Oct 2024 11:59:57 +0530 Subject: [PATCH 382/734] refactor: allow unreconcile on bank and cash entry type journals (cherry picked from commit 2c4f37f488031d98c9b8297758295422520a9eea) --- erpnext/public/js/utils/unreconcile.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/public/js/utils/unreconcile.js b/erpnext/public/js/utils/unreconcile.js index c6ee8a330c7..de20f468ccb 100644 --- a/erpnext/public/js/utils/unreconcile.js +++ b/erpnext/public/js/utils/unreconcile.js @@ -4,7 +4,8 @@ erpnext.accounts.unreconcile_payment = { add_unreconcile_btn(frm) { if (frm.doc.docstatus == 1) { if ( - (frm.doc.doctype == "Journal Entry" && frm.doc.voucher_type != "Journal Entry") || + (frm.doc.doctype == "Journal Entry" && + !["Journal Entry", "Bank Entry", "Cash Entry"].includes(frm.doc.voucher_type)) || !["Purchase Invoice", "Sales Invoice", "Journal Entry", "Payment Entry"].includes( frm.doc.doctype ) From fac27d9dff8fbd6babcbf92ede6a94d51484e36f Mon Sep 17 00:00:00 2001 From: Corentin Forler Date: Wed, 9 Oct 2024 09:56:17 +0200 Subject: [PATCH 383/734] fix(deferred_revenue): Escape account in query (cherry picked from commit c7b3ae41d4a0b1506ef50d470a9eed3f76f85a22) --- erpnext/accounts/deferred_revenue.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/deferred_revenue.py b/erpnext/accounts/deferred_revenue.py index a48ce9b4c63..cd34bf7f850 100644 --- a/erpnext/accounts/deferred_revenue.py +++ b/erpnext/accounts/deferred_revenue.py @@ -58,7 +58,7 @@ def build_conditions(process_type, account, company): ) if account: - conditions += f"AND {deferred_account}='{account}'" + conditions += f"AND {deferred_account}='{frappe.db.escape(account)}'" elif company: conditions += f"AND p.company = {frappe.db.escape(company)}" From 8d9796666247c089a3799445b9520c491d782c26 Mon Sep 17 00:00:00 2001 From: Raffael Meyer <14891507+barredterra@users.noreply.github.com> Date: Tue, 22 Oct 2024 13:44:28 +0200 Subject: [PATCH 384/734] refactor: validate_return_against_account (#43778) (cherry picked from commit c4faa0e101f5c9e3c885eedc08c7ae318b555717) --- erpnext/controllers/accounts_controller.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index ff178fab59e..21adab8bc1e 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -393,12 +393,17 @@ class AccountsController(TransactionBase): def validate_return_against_account(self): if self.doctype in ["Sales Invoice", "Purchase Invoice"] and self.is_return and self.return_against: cr_dr_account_field = "debit_to" if self.doctype == "Sales Invoice" else "credit_to" - cr_dr_account_label = "Debit To" if self.doctype == "Sales Invoice" else "Credit To" + cr_dr_account_label = self.meta.get_label(cr_dr_account_field) cr_dr_account = self.get(cr_dr_account_field) - if frappe.get_value(self.doctype, self.return_against, cr_dr_account_field) != cr_dr_account: + original_account = frappe.get_value(self.doctype, self.return_against, cr_dr_account_field) + if original_account != cr_dr_account: frappe.throw( - _("'{0}' account: '{1}' should match the Return Against Invoice").format( - frappe.bold(cr_dr_account_label), frappe.bold(cr_dr_account) + _( + "Please set {0} to {1}, the same account that was used in the original invoice {2}." + ).format( + frappe.bold(_(cr_dr_account_label, context=self.doctype)), + frappe.bold(cr_dr_account), + frappe.bold(self.return_against), ) ) From 2205ae8e54126a868825bb6cb6fa54d01498b298 Mon Sep 17 00:00:00 2001 From: Ninad Parikh <109862100+Ninad1306@users.noreply.github.com> Date: Tue, 22 Oct 2024 19:28:02 +0530 Subject: [PATCH 385/734] fix: Required Changes to Support e-Waybill Generation for Material Transfer Return (#43061) (cherry picked from commit 004c4e21d4732fd64fe0d6b24908baf385d4e685) --- .../controllers/subcontracting_controller.py | 18 ++++++++++++------ .../stock/doctype/stock_entry/stock_entry.js | 6 +++++- .../stock/doctype/stock_entry/stock_entry.json | 12 ++++++------ 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/erpnext/controllers/subcontracting_controller.py b/erpnext/controllers/subcontracting_controller.py index f6f6742cc87..3c7f8b0ccca 100644 --- a/erpnext/controllers/subcontracting_controller.py +++ b/erpnext/controllers/subcontracting_controller.py @@ -1235,6 +1235,17 @@ def add_items_in_ste(ste_doc, row, qty, rm_details, rm_detail_field="sco_rm_deta def make_return_stock_entry_for_subcontract( available_materials, order_doc, rm_details, order_doctype="Subcontracting Order" ): + def post_process(source_doc, target_doc): + target_doc.purpose = "Material Transfer" + + if source_doc.doctype == "Purchase Order": + target_doc.purchase_order = source_doc.name + else: + target_doc.subcontracting_order = source_doc.name + + target_doc.company = source_doc.company + target_doc.is_return = 1 + ste_doc = get_mapped_doc( order_doctype, order_doc.name, @@ -1245,18 +1256,13 @@ def make_return_stock_entry_for_subcontract( }, }, ignore_child_tables=True, + postprocess=post_process, ) - ste_doc.purpose = "Material Transfer" - if order_doctype == "Purchase Order": - ste_doc.purchase_order = order_doc.name rm_detail_field = "po_detail" else: - ste_doc.subcontracting_order = order_doc.name rm_detail_field = "sco_rm_detail" - ste_doc.company = order_doc.company - ste_doc.is_return = 1 for _key, value in available_materials.items(): if not value.qty: diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js index cb442f6666d..3e82ef51653 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry.js @@ -1298,7 +1298,7 @@ erpnext.stock.StockEntry = class StockEntry extends erpnext.stock.StockControlle this.frm.cscript.toggle_enable_bom(); - if (doc.purpose == "Send to Subcontractor") { + if (erpnext.stock.is_subcontracting_or_return_transfer(doc)) { doc.customer = doc.customer_name = doc.customer_address = @@ -1364,6 +1364,10 @@ erpnext.stock.select_batch_and_serial_no = (frm, item) => { }); }; +erpnext.stock.is_subcontracting_or_return_transfer = (doc) => { + return doc.purpose == "Send to Subcontractor" || (doc.purpose == "Material Transfer" && doc.is_return); +}; + function attach_bom_items(bom_no) { if (!bom_no) { return; diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.json b/erpnext/stock/doctype/stock_entry/stock_entry.json index 5ba9f2973a1..f5fdfafe778 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.json +++ b/erpnext/stock/doctype/stock_entry/stock_entry.json @@ -154,14 +154,14 @@ "search_index": 1 }, { - "depends_on": "eval:doc.purpose==\"Send to Subcontractor\"", + "depends_on": "eval: erpnext.stock.is_subcontracting_or_return_transfer(doc)", "fieldname": "purchase_order", "fieldtype": "Link", "label": "Purchase Order", "options": "Purchase Order" }, { - "depends_on": "eval:doc.purpose==\"Send to Subcontractor\"", + "depends_on": "eval: erpnext.stock.is_subcontracting_or_return_transfer(doc)", "fieldname": "subcontracting_order", "fieldtype": "Link", "label": "Subcontracting Order", @@ -427,13 +427,13 @@ }, { "collapsible": 1, - "depends_on": "eval:doc.purpose === \"Send to Subcontractor\"", + "depends_on": "eval: erpnext.stock.is_subcontracting_or_return_transfer(doc)", "fieldname": "contact_section", "fieldtype": "Section Break", "label": "Supplier Details" }, { - "depends_on": "eval:doc.purpose === \"Send to Subcontractor\"", + "depends_on": "eval: erpnext.stock.is_subcontracting_or_return_transfer(doc)", "fieldname": "supplier", "fieldtype": "Link", "label": "Supplier", @@ -445,7 +445,7 @@ }, { "bold": 1, - "depends_on": "eval:doc.purpose === \"Send to Subcontractor\"", + "depends_on": "eval: erpnext.stock.is_subcontracting_or_return_transfer(doc)", "fieldname": "supplier_name", "fieldtype": "Data", "label": "Supplier Name", @@ -455,7 +455,7 @@ "read_only": 1 }, { - "depends_on": "eval:doc.purpose === \"Send to Subcontractor\"", + "depends_on": "eval: erpnext.stock.is_subcontracting_or_return_transfer(doc)", "fieldname": "supplier_address", "fieldtype": "Link", "label": "Supplier Address", From 39387e9f54ddd27f77ddfb268a7d92e0c3e66f7b Mon Sep 17 00:00:00 2001 From: Vishakh Desai Date: Tue, 1 Oct 2024 12:25:04 +0530 Subject: [PATCH 386/734] fix: get stock accounts from the doc instead of db in validate_stock_accounts (cherry picked from commit 30954586d8d8f63869cce9446b93ba6089ad4899) --- erpnext/accounts/doctype/journal_entry/journal_entry.py | 2 +- erpnext/accounts/utils.py | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index 593fa48e856..47626492e84 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -259,7 +259,7 @@ class JournalEntry(AccountsController): frappe.throw(_("Journal Entry type should be set as Depreciation Entry for asset depreciation")) def validate_stock_accounts(self): - stock_accounts = get_stock_accounts(self.company, self.doctype, self.name) + stock_accounts = get_stock_accounts(self.company, accounts=self.accounts) for account in stock_accounts: account_bal, stock_bal, warehouse_list = get_stock_and_account_balance( account, self.posting_date, self.company diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 1d75fafc1a9..91904059824 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -1547,12 +1547,16 @@ def compare_existing_and_expected_gle(existing_gle, expected_gle, precision): return matched -def get_stock_accounts(company, voucher_type=None, voucher_no=None): +def get_stock_accounts(company, voucher_type=None, voucher_no=None, accounts=None): stock_accounts = [ d.name for d in frappe.db.get_all("Account", {"account_type": "Stock", "company": company, "is_group": 0}) ] - if voucher_type and voucher_no: + + if accounts: + stock_accounts = [row.account for row in accounts if row.account in stock_accounts] + + elif voucher_type and voucher_no: if voucher_type == "Journal Entry": stock_accounts = [ d.account From d2e5b2aa1d22cdcaccc62b412732b8d12f9b1aa3 Mon Sep 17 00:00:00 2001 From: ljain112 Date: Tue, 22 Oct 2024 19:49:37 +0530 Subject: [PATCH 387/734] fix: only show pay button for specific doctype in portal --- .../doctype/payment_request/payment_request.py | 12 ++++++++++++ erpnext/templates/pages/order.py | 10 ++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index fd07551897a..462cae95ba5 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -20,6 +20,15 @@ from erpnext.accounts.party import get_party_account, get_party_bank_account from erpnext.accounts.utils import get_account_currency, get_currency_precision from erpnext.utilities import payment_app_import_guard +ALLOWED_DOCTYPES_FOR_PAYMENT_REQUEST = [ + "Sales Order", + "Purchase Order", + "Sales Invoice", + "Purchase Invoice", + "POS Invoice", + "Fees", +] + def _get_payment_gateway_controller(*args, **kwargs): with payment_app_import_guard(): @@ -525,6 +534,9 @@ def make_payment_request(**args): args = frappe._dict(args) + if args.dt not in ALLOWED_DOCTYPES_FOR_PAYMENT_REQUEST: + frappe.throw(_("Payment Requests cannot be created against: {0}").format(frappe.bold(args.dt))) + ref_doc = frappe.get_doc(args.dt, args.dn) gateway_account = get_gateway_details(args) or frappe._dict() diff --git a/erpnext/templates/pages/order.py b/erpnext/templates/pages/order.py index 505399f4a5b..dca5a0c7497 100644 --- a/erpnext/templates/pages/order.py +++ b/erpnext/templates/pages/order.py @@ -4,6 +4,10 @@ import frappe from frappe import _ +from erpnext.accounts.doctype.payment_request.payment_request import ( + ALLOWED_DOCTYPES_FOR_PAYMENT_REQUEST, +) + def get_context(context): context.no_cache = 1 @@ -46,8 +50,10 @@ def get_context(context): ) context.available_loyalty_points = int(loyalty_program_details.get("loyalty_points")) - context.show_pay_button = "payments" in frappe.get_installed_apps() and frappe.db.get_single_value( - "Buying Settings", "show_pay_button" + context.show_pay_button = ( + "payments" in frappe.get_installed_apps() + and frappe.db.get_single_value("Buying Settings", "show_pay_button") + and context.doc.doctype in ALLOWED_DOCTYPES_FOR_PAYMENT_REQUEST ) context.show_make_pi_button = False if context.doc.get("supplier"): From 58a3ef7aa6b5d446460bb25ba7cb0188cbdab66b Mon Sep 17 00:00:00 2001 From: Ninad1306 Date: Tue, 22 Oct 2024 12:16:59 +0530 Subject: [PATCH 388/734] fix: map doc from purchase order (cherry picked from commit 60ceb91ace8fbb8ddd127a00e7eadd431d4e250c) --- .../subcontracting_receipt.py | 144 +++++++++--------- 1 file changed, 71 insertions(+), 73 deletions(-) diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py index db912514988..13010d8448c 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py @@ -687,86 +687,84 @@ def make_purchase_receipt(source_name, target_doc=None, save=False, submit=False else: source_doc = source_name - if not source_doc.is_return: - if not target_doc: - target_doc = frappe.new_doc("Purchase Receipt") - target_doc.is_subcontracted = 1 - target_doc.is_old_subcontracting_flow = 0 + if source_doc.is_return: + return - target_doc = get_mapped_doc( - "Subcontracting Receipt", - source_doc.name, - { - "Subcontracting Receipt": { - "doctype": "Purchase Receipt", - "field_map": { - "posting_date": "posting_date", - "posting_time": "posting_time", - "name": "subcontracting_receipt", - "supplier_warehouse": "supplier_warehouse", - }, - "field_no_map": ["total_qty", "total"], + po_doc = frappe.get_doc("Purchase Order", source_doc.items[0].purchase_order) + target_doc = get_mapped_doc( + "Purchase Order", + po_doc.name, + { + "Purchase Order": { + "doctype": "Purchase Receipt", + "validation": { + "docstatus": ["=", 1], }, + "field_no_map": ["items"], }, - target_doc, - ignore_child_tables=True, + "Purchase Taxes and Charges": {"doctype": "Purchase Taxes and Charges", "reset_value": True}, + }, + ) + + target_doc.update( + { + "posting_date": source_doc.posting_date, + "posting_time": source_doc.posting_time, + "subcontracting_receipt": source_doc.name, + "supplier_warehouse": source_doc.supplier_warehouse, + "is_subcontracted": 1, + "is_old_subcontracting_flow": 0, + "currency": frappe.get_cached_value("Company", target_doc.company, "default_currency"), + } + ) + + po_items_details = {po_item.name: po_item for po_item in po_doc.items} + + for item in source_doc.items: + if po_item := po_items_details.get(item.purchase_order_item): + conversion_factor = flt(po_item.qty) / flt(po_item.fg_item_qty) + item_row = { + "item_code": po_item.item_code, + "item_name": po_item.item_name, + "conversion_factor": conversion_factor, + "qty": flt(item.qty) * conversion_factor, + "rejected_qty": flt(item.rejected_qty) * conversion_factor, + "uom": po_item.uom, + "rate": po_item.rate, + "warehouse": item.warehouse, + "rejected_warehouse": item.rejected_warehouse, + "purchase_order": po_doc.name, + "purchase_order_item": item.purchase_order_item, + "subcontracting_receipt_item": item.name, + "project": po_item.project, + } + target_doc.append("items", item_row) + + if not target_doc.items: + frappe.throw( + _("Purchase Order Item reference is missing in Subcontracting Receipt {0}").format( + source_doc.name + ) ) - target_doc.currency = frappe.get_cached_value("Company", target_doc.company, "default_currency") + target_doc.set_missing_values() - po_items_details = {} - for item in source_doc.items: - if item.purchase_order and item.purchase_order_item: - if item.purchase_order not in po_items_details: - po_doc = frappe.get_doc("Purchase Order", item.purchase_order) - po_items_details[item.purchase_order] = { - po_item.name: po_item for po_item in po_doc.items - } + if (save or submit) and frappe.has_permission(target_doc.doctype, "create"): + target_doc.save() - if po_item := po_items_details[item.purchase_order].get(item.purchase_order_item): - conversion_factor = flt(po_item.qty) / flt(po_item.fg_item_qty) - item_row = { - "item_code": po_item.item_code, - "item_name": po_item.item_name, - "conversion_factor": conversion_factor, - "qty": flt(item.qty) * conversion_factor, - "rejected_qty": flt(item.rejected_qty) * conversion_factor, - "uom": po_item.uom, - "rate": po_item.rate, - "warehouse": item.warehouse, - "rejected_warehouse": item.rejected_warehouse, - "purchase_order": item.purchase_order, - "purchase_order_item": item.purchase_order_item, - "subcontracting_receipt_item": item.name, - "project": po_item.project, - } - target_doc.append("items", item_row) + if submit and frappe.has_permission(target_doc.doctype, "submit", target_doc): + try: + target_doc.submit() + except Exception as e: + target_doc.add_comment("Comment", _("Submit Action Failed") + "

" + str(e)) - if not target_doc.items: - frappe.throw( - _("Purchase Order Item reference is missing in Subcontracting Receipt {0}").format( - source_doc.name - ) + if notify: + frappe.msgprint( + _("Purchase Receipt {0} created.").format( + get_link_to_form(target_doc.doctype, target_doc.name) + ), + indicator="green", + alert=True, ) - target_doc.set_missing_values() - - if (save or submit) and frappe.has_permission(target_doc.doctype, "create"): - target_doc.save() - - if submit and frappe.has_permission(target_doc.doctype, "submit", target_doc): - try: - target_doc.submit() - except Exception as e: - target_doc.add_comment("Comment", _("Submit Action Failed") + "

" + str(e)) - - if notify: - frappe.msgprint( - _("Purchase Receipt {0} created.").format( - get_link_to_form(target_doc.doctype, target_doc.name) - ), - indicator="green", - alert=True, - ) - - return target_doc + return target_doc From 40927c7413985e9b2880f9b657b5efb2d5738238 Mon Sep 17 00:00:00 2001 From: Ninad1306 Date: Tue, 22 Oct 2024 17:00:11 +0530 Subject: [PATCH 389/734] test: auto create purchase receipt (cherry picked from commit 59887bbc13c223739750eb27599be5e251bca91e) --- .../test_subcontracting_receipt.py | 43 +++++++++++++++++-- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py index 27ad7dbebdf..e0fa7923ef9 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py @@ -1075,18 +1075,42 @@ class TestSubcontractingReceipt(FrappeTestCase): @change_settings("Buying Settings", {"auto_create_purchase_receipt": 1}) def test_auto_create_purchase_receipt(self): + from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order + fg_item = "Subcontracted Item SA1" service_items = [ { "warehouse": "_Test Warehouse - _TC", "item_code": "Subcontracted Service Item 1", - "qty": 5, + "qty": 10, "rate": 100, "fg_item": fg_item, "fg_item_qty": 5, }, ] - sco = get_subcontracting_order(service_items=service_items) + + po = create_purchase_order( + rm_items=service_items, + is_subcontracted=1, + supplier_warehouse="_Test Warehouse 1 - _TC", + do_not_submit=True, + ) + po.append( + "taxes", + { + "account_head": "_Test Account Excise Duty - _TC", + "charge_type": "On Net Total", + "cost_center": "_Test Cost Center - _TC", + "description": "Excise Duty", + "doctype": "Purchase Taxes and Charges", + "rate": 10, + }, + ) + po.save() + po.submit() + + sco = get_subcontracting_order(po_name=po.name) + rm_items = get_rm_items(sco.supplied_items) itemwise_details = make_stock_in_entry(rm_items=rm_items) make_stock_transfer_entry( @@ -1094,11 +1118,24 @@ class TestSubcontractingReceipt(FrappeTestCase): rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details), ) + scr = make_subcontracting_receipt(sco.name) + scr.items[0].qty = 3 scr.save() scr.submit() - self.assertTrue(frappe.db.get_value("Purchase Receipt", {"subcontracting_receipt": scr.name})) + pr_details = frappe.get_all( + "Purchase Receipt", + filters={"subcontracting_receipt": scr.name}, + fields=["name", "total_taxes_and_charges"], + ) + + self.assertTrue(pr_details) + + pr_qty = frappe.db.get_value("Purchase Receipt Item", {"parent": pr_details[0]["name"]}, "qty") + self.assertEqual(pr_qty, 6) + + self.assertEqual(pr_details[0]["total_taxes_and_charges"], 60) def test_use_serial_batch_fields_for_subcontracting_receipt(self): fg_item = make_item( From 41db040a60c88afb8d61b52327227ad282488c3b Mon Sep 17 00:00:00 2001 From: Ninad1306 Date: Tue, 22 Oct 2024 17:24:46 +0530 Subject: [PATCH 390/734] fix: better implementation, handle missing purchase order (cherry picked from commit 66211dafd69ecbdc56a52d8391b6fdcf02c3154a) --- .../subcontracting_receipt.py | 119 ++++++++++-------- 1 file changed, 69 insertions(+), 50 deletions(-) diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py index 13010d8448c..50ce270c166 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py @@ -690,64 +690,83 @@ def make_purchase_receipt(source_name, target_doc=None, save=False, submit=False if source_doc.is_return: return - po_doc = frappe.get_doc("Purchase Order", source_doc.items[0].purchase_order) - target_doc = get_mapped_doc( - "Purchase Order", - po_doc.name, - { - "Purchase Order": { - "doctype": "Purchase Receipt", - "validation": { - "docstatus": ["=", 1], - }, - "field_no_map": ["items"], - }, - "Purchase Taxes and Charges": {"doctype": "Purchase Taxes and Charges", "reset_value": True}, - }, - ) - - target_doc.update( - { - "posting_date": source_doc.posting_date, - "posting_time": source_doc.posting_time, - "subcontracting_receipt": source_doc.name, - "supplier_warehouse": source_doc.supplier_warehouse, - "is_subcontracted": 1, - "is_old_subcontracting_flow": 0, - "currency": frappe.get_cached_value("Company", target_doc.company, "default_currency"), - } - ) - - po_items_details = {po_item.name: po_item for po_item in po_doc.items} - + po_sr_item_dict = {} + po_name = None for item in source_doc.items: - if po_item := po_items_details.get(item.purchase_order_item): - conversion_factor = flt(po_item.qty) / flt(po_item.fg_item_qty) - item_row = { - "item_code": po_item.item_code, - "item_name": po_item.item_name, - "conversion_factor": conversion_factor, - "qty": flt(item.qty) * conversion_factor, - "rejected_qty": flt(item.rejected_qty) * conversion_factor, - "uom": po_item.uom, - "rate": po_item.rate, - "warehouse": item.warehouse, - "rejected_warehouse": item.rejected_warehouse, - "purchase_order": po_doc.name, - "purchase_order_item": item.purchase_order_item, - "subcontracting_receipt_item": item.name, - "project": po_item.project, - } - target_doc.append("items", item_row) + if not item.purchase_order: + continue - if not target_doc.items: + if not po_name: + po_name = item.purchase_order + + po_sr_item_dict[item.purchase_order_item] = { + "qty": flt(item.qty), + "rejected_qty": flt(item.rejected_qty), + "warehouse": item.warehouse, + "rejected_warehouse": item.rejected_warehouse, + "subcontracting_receipt_item": item.name, + } + + if not po_name: frappe.throw( _("Purchase Order Item reference is missing in Subcontracting Receipt {0}").format( source_doc.name ) ) - target_doc.set_missing_values() + def update_item(obj, target, source_parent): + sr_item_details = po_sr_item_dict.get(obj.name) + ratio = flt(obj.qty) / flt(obj.fg_item_qty) + + target.update( + { + "qty": ratio * sr_item_details["qty"], + "rejected_qty": ratio * sr_item_details["rejected_qty"], + "warehouse": sr_item_details["warehouse"], + "rejected_warehouse": sr_item_details["rejected_warehouse"], + "subcontracting_receipt_item": sr_item_details["subcontracting_receipt_item"], + } + ) + + def post_process(source, target): + target.set_missing_values() + target.update( + { + "posting_date": source_doc.posting_date, + "posting_time": source_doc.posting_time, + "subcontracting_receipt": source_doc.name, + "supplier_warehouse": source_doc.supplier_warehouse, + "is_subcontracted": 1, + "is_old_subcontracting_flow": 0, + "currency": frappe.get_cached_value("Company", target.company, "default_currency"), + } + ) + + target_doc = get_mapped_doc( + "Purchase Order", + po_name, + { + "Purchase Order": { + "doctype": "Purchase Receipt", + "field_map": {"supplier_warehouse": "supplier_warehouse"}, + "validation": { + "docstatus": ["=", 1], + }, + }, + "Purchase Order Item": { + "doctype": "Purchase Receipt Item", + "field_map": { + "name": "purchase_order_item", + "parent": "purchase_order", + "bom": "bom", + }, + "postprocess": update_item, + "condition": lambda doc: doc.name in po_sr_item_dict, + }, + "Purchase Taxes and Charges": {"doctype": "Purchase Taxes and Charges", "reset_value": True}, + }, + postprocess=post_process, + ) if (save or submit) and frappe.has_permission(target_doc.doctype, "create"): target_doc.save() From 879b2b778a6421c62b977cac82f9568da172ed04 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 22 Oct 2024 19:43:16 +0200 Subject: [PATCH 391/734] fix: use correct variable in error message (backport #43790) (#43792) Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com> fix: use correct variable in error message (#43790) --- erpnext/controllers/accounts_controller.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 21adab8bc1e..188cbf0587c 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -393,16 +393,14 @@ class AccountsController(TransactionBase): def validate_return_against_account(self): if self.doctype in ["Sales Invoice", "Purchase Invoice"] and self.is_return and self.return_against: cr_dr_account_field = "debit_to" if self.doctype == "Sales Invoice" else "credit_to" - cr_dr_account_label = self.meta.get_label(cr_dr_account_field) - cr_dr_account = self.get(cr_dr_account_field) original_account = frappe.get_value(self.doctype, self.return_against, cr_dr_account_field) - if original_account != cr_dr_account: + if original_account != self.get(cr_dr_account_field): frappe.throw( _( "Please set {0} to {1}, the same account that was used in the original invoice {2}." ).format( - frappe.bold(_(cr_dr_account_label, context=self.doctype)), - frappe.bold(cr_dr_account), + frappe.bold(_(self.meta.get_label(cr_dr_account_field), context=self.doctype)), + frappe.bold(original_account), frappe.bold(self.return_against), ) ) From e0a45a5a5481f6c5328bed01a449994279a376e2 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 23 Oct 2024 04:48:23 +0000 Subject: [PATCH 392/734] chore(release): Bumped to Version 15.39.0 # [15.39.0](https://github.com/frappe/erpnext/compare/v15.38.4...v15.39.0) (2024-10-23) ### Bug Fixes * "show_remarks" checkbox in Process statement of accounts ([f7717c9](https://github.com/frappe/erpnext/commit/f7717c91bc48869a03ba5d76b0ac1e8879dfc074)) * added validation for UOM must be whole number (backport [#43710](https://github.com/frappe/erpnext/issues/43710)) ([#43712](https://github.com/frappe/erpnext/issues/43712)) ([60ffcd0](https://github.com/frappe/erpnext/commit/60ffcd0574bebbfa3e91fe82744c14792f9d8ad2)) * Call super onload_post_render inside pos_invoice.js ([1281d9d](https://github.com/frappe/erpnext/commit/1281d9d21dd2628fe755881b6d7189c0c5848ee7)) * coupon code validation logic ([aeaadb1](https://github.com/frappe/erpnext/commit/aeaadb1e300b622abf54d35364a471463b504a2e)) * **deferred_revenue:** Escape account in query ([fac27d9](https://github.com/frappe/erpnext/commit/fac27d9dff8fbd6babcbf92ede6a94d51484e36f)) * do not make new depreciation for fully depreciated asset ([ddb38db](https://github.com/frappe/erpnext/commit/ddb38db5c46fb20cecd1fd6dcca2ef2168d8a9f9)) * Freeze Screen on load invoices on POS Closing Entry ([f343d5a](https://github.com/frappe/erpnext/commit/f343d5a24dbcc38b63b9c94182d69cdea701b6db)) * get party advance amount based on account ([b673377](https://github.com/frappe/erpnext/commit/b673377b70210f2004df0cbc9eab29f86e17d778)) * get period estimate till service end date ([148d7e7](https://github.com/frappe/erpnext/commit/148d7e798bbe66cee8c8f3e5e0ed1db7dca8d734)) * get stock accounts from the doc instead of db in validate_stock_accounts ([39387e9](https://github.com/frappe/erpnext/commit/39387e9f54ddd27f77ddfb268a7d92e0c3e66f7b)) * incorrect amount in bank clearance ([52be45c](https://github.com/frappe/erpnext/commit/52be45c5dff5f0fcfb3573ba94f177816c3fa67b)) * lead create opp from connection not working ([9e56f21](https://github.com/frappe/erpnext/commit/9e56f213a3c7149a2067944f4adb274af69598f6)) * list view and form status not same for purchase order (backport [#43690](https://github.com/frappe/erpnext/issues/43690)) ([#43692](https://github.com/frappe/erpnext/issues/43692)) ([a33d553](https://github.com/frappe/erpnext/commit/a33d5535a7c88de749bfd47855717ac1410b65fb)) * only show pay button for specific doctype in portal ([d2e5b2a](https://github.com/frappe/erpnext/commit/d2e5b2aa1d22cdcaccc62b412732b8d12f9b1aa3)) * party_balance based on company in payment entry ([04fbcc6](https://github.com/frappe/erpnext/commit/04fbcc64ff661689e5b8e7a62569ef54674b64f1)) * remove extra space ([50dd8d9](https://github.com/frappe/erpnext/commit/50dd8d9df756fa1bf64fa0d86b719b6aeffd0228)) * removed unmerged patches ([2e0cf36](https://github.com/frappe/erpnext/commit/2e0cf36901b52369cd95c73a331269d123f94612)) * Required Changes to Support e-Waybill Generation for Material Transfer Return ([#43061](https://github.com/frappe/erpnext/issues/43061)) ([2205ae8](https://github.com/frappe/erpnext/commit/2205ae8e54126a868825bb6cb6fa54d01498b298)) * show total amount on report summary ([ab20344](https://github.com/frappe/erpnext/commit/ab20344141e60c43464263454a7c531cc0fd2b5e)) * use correct variable in error message (backport [#43790](https://github.com/frappe/erpnext/issues/43790)) ([#43792](https://github.com/frappe/erpnext/issues/43792)) ([879b2b7](https://github.com/frappe/erpnext/commit/879b2b778a6421c62b977cac82f9568da172ed04)) * Workspace link for Work Order Consumed Materials report (backport [#43753](https://github.com/frappe/erpnext/issues/43753)) ([#43754](https://github.com/frappe/erpnext/issues/43754)) ([1fa9030](https://github.com/frappe/erpnext/commit/1fa9030aee13c39c6aade9779145d86ff976ce90)) ### Features * added assignee email field in asset maintenance log ([a3b8f97](https://github.com/frappe/erpnext/commit/a3b8f9759d79d9ad45173248ab4bf3bb343aafc6)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index e28b6e9bb91..a5b8609d1d9 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.38.4" +__version__ = "15.39.0" def get_default_company(user=None): From ced76ca5c0b2e66e13781353ed71f1d95368bf87 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 23 Oct 2024 13:07:16 +0530 Subject: [PATCH 393/734] perf: performance optimizations for accounting reports by refactoring account closing balance and period closing voucher (#43798) * fix: Gl Entry form cleanup * fix: Added indexes in gl entry table * perf: Refactored period closing voucher to handle large volume of gle * fix: fixes as per new period start and end date fields in PCV * perf: performance optimization for accounting reports * perf: performance optimizations for account closing balance patch * fix: test cases * fix: lenter issues - direct use of sql query * fix: test cases * fix: test cases * fix: test cases * fix: wrong fieldname * fix: test cases --- .../account_closing_balance.py | 4 +- .../accounting_period/accounting_period.py | 2 + .../accounts/doctype/gl_entry/gl_entry.json | 106 ++- erpnext/accounts/doctype/gl_entry/gl_entry.py | 3 +- .../test_payment_reconciliation.py | 6 +- .../period_closing_voucher.js | 18 + .../period_closing_voucher.json | 38 +- .../period_closing_voucher.py | 721 ++++++++++-------- .../test_period_closing_voucher.py | 8 +- .../repost_accounting_ledger.py | 4 +- .../test_repost_accounting_ledger.py | 6 +- erpnext/accounts/general_ledger.py | 34 +- .../accounts_receivable.py | 29 +- .../accounts/report/financial_statements.py | 143 ++-- .../report/trial_balance/trial_balance.py | 20 +- erpnext/patches.txt | 1 + .../fix_additional_cost_in_mfg_stock_entry.py | 2 +- .../v14_0/set_period_start_end_date_in_pcv.py | 17 + .../patches/v14_0/single_to_multi_dunning.py | 2 +- .../patches/v14_0/update_closing_balances.py | 105 +-- .../doctype/delivery_note/delivery_note.py | 25 +- .../repost_item_valuation.py | 2 +- .../stock/doctype/stock_entry/stock_entry.py | 8 +- erpnext/stock/report/test_reports.py | 3 +- erpnext/tests/test_perf.py | 2 +- 25 files changed, 772 insertions(+), 537 deletions(-) create mode 100644 erpnext/patches/v14_0/set_period_start_end_date_in_pcv.py diff --git a/erpnext/accounts/doctype/account_closing_balance/account_closing_balance.py b/erpnext/accounts/doctype/account_closing_balance/account_closing_balance.py index 82821e140ea..6d5e023f039 100644 --- a/erpnext/accounts/doctype/account_closing_balance/account_closing_balance.py +++ b/erpnext/accounts/doctype/account_closing_balance/account_closing_balance.py @@ -113,9 +113,9 @@ def get_previous_closing_entries(company, closing_date, accounting_dimensions): entries = [] last_period_closing_voucher = frappe.db.get_all( "Period Closing Voucher", - filters={"docstatus": 1, "company": company, "posting_date": ("<", closing_date)}, + filters={"docstatus": 1, "company": company, "period_end_date": ("<", closing_date)}, fields=["name"], - order_by="posting_date desc", + order_by="period_end_date desc", limit=1, ) diff --git a/erpnext/accounts/doctype/accounting_period/accounting_period.py b/erpnext/accounts/doctype/accounting_period/accounting_period.py index 172ef93f14d..300d216618e 100644 --- a/erpnext/accounts/doctype/accounting_period/accounting_period.py +++ b/erpnext/accounts/doctype/accounting_period/accounting_period.py @@ -101,6 +101,8 @@ def validate_accounting_period_on_doc_save(doc, method=None): date = doc.available_for_use_date elif doc.doctype == "Asset Repair": date = doc.completion_date + elif doc.doctype == "Period Closing Voucher": + date = doc.period_end_date else: date = doc.posting_date diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.json b/erpnext/accounts/doctype/gl_entry/gl_entry.json index 2d106ad8cee..c285a33f73e 100644 --- a/erpnext/accounts/doctype/gl_entry/gl_entry.json +++ b/erpnext/accounts/doctype/gl_entry/gl_entry.json @@ -6,38 +6,50 @@ "document_type": "Document", "engine": "InnoDB", "field_order": [ + "dates_section", "posting_date", "transaction_date", + "column_break_avko", + "fiscal_year", + "due_date", + "account_details_section", "account", + "account_currency", + "column_break_ifvf", + "against", "party_type", "party", - "cost_center", - "debit", - "credit", - "account_currency", - "debit_in_account_currency", - "credit_in_account_currency", - "against", + "transaction_details_section", + "voucher_type", + "voucher_no", + "voucher_subtype", + "transaction_currency", + "column_break_dpsx", "against_voucher_type", "against_voucher", - "voucher_type", - "voucher_subtype", - "voucher_no", "voucher_detail_no", + "transaction_exchange_rate", + "amounts_section", + "debit_in_account_currency", + "debit", + "debit_in_transaction_currency", + "column_break_bm1w", + "credit_in_account_currency", + "credit", + "credit_in_transaction_currency", + "dimensions_section", + "cost_center", + "column_break_lmnm", "project", - "remarks", + "more_info_section", + "finance_book", + "company", "is_opening", "is_advance", - "fiscal_year", - "company", - "finance_book", + "column_break_8abq", "to_rename", - "due_date", "is_cancelled", - "transaction_currency", - "debit_in_transaction_currency", - "credit_in_transaction_currency", - "transaction_exchange_rate" + "remarks" ], "fields": [ { @@ -285,13 +297,67 @@ "fieldname": "voucher_subtype", "fieldtype": "Small Text", "label": "Voucher Subtype" + }, + { + "fieldname": "dates_section", + "fieldtype": "Section Break", + "label": "Dates" + }, + { + "fieldname": "column_break_avko", + "fieldtype": "Column Break" + }, + { + "fieldname": "account_details_section", + "fieldtype": "Section Break", + "label": "Account Details" + }, + { + "fieldname": "column_break_ifvf", + "fieldtype": "Column Break" + }, + { + "fieldname": "transaction_details_section", + "fieldtype": "Section Break", + "label": "Transaction Details" + }, + { + "fieldname": "amounts_section", + "fieldtype": "Section Break", + "label": "Amounts" + }, + { + "fieldname": "column_break_dpsx", + "fieldtype": "Column Break" + }, + { + "fieldname": "more_info_section", + "fieldtype": "Section Break", + "label": "More Info" + }, + { + "fieldname": "column_break_bm1w", + "fieldtype": "Column Break" + }, + { + "fieldname": "dimensions_section", + "fieldtype": "Section Break", + "label": "Dimensions" + }, + { + "fieldname": "column_break_lmnm", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_8abq", + "fieldtype": "Column Break" } ], "icon": "fa fa-list", "idx": 1, "in_create": 1, "links": [], - "modified": "2024-07-02 14:31:51.496466", + "modified": "2024-08-22 13:03:39.997475", "modified_by": "Administrator", "module": "Accounts", "name": "GL Entry", diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py index d74224c4aa2..a7e7edb098d 100644 --- a/erpnext/accounts/doctype/gl_entry/gl_entry.py +++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py @@ -430,8 +430,9 @@ def update_against_account(voucher_type, voucher_no): def on_doctype_update(): - frappe.db.add_index("GL Entry", ["against_voucher_type", "against_voucher"]) frappe.db.add_index("GL Entry", ["voucher_type", "voucher_no"]) + frappe.db.add_index("GL Entry", ["posting_date", "company"]) + frappe.db.add_index("GL Entry", ["party_type", "party"]) def rename_gle_sle_docs(): diff --git a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py index 883c638398c..1b19949bb7e 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py @@ -1986,13 +1986,15 @@ def make_period_closing_voucher(company, cost_center, posting_date=None, submit= parent_account=parent_account, doctype="Account", ) + fy = get_fiscal_year(posting_date, company=company) pcv = frappe.get_doc( { "doctype": "Period Closing Voucher", "transaction_date": posting_date or today(), - "posting_date": posting_date or today(), + "period_start_date": fy[1], + "period_end_date": fy[2], "company": company, - "fiscal_year": get_fiscal_year(posting_date or today(), company=company)[0], + "fiscal_year": fy[0], "cost_center": cost_center, "closing_account_head": surplus_account, "remarks": "test", diff --git a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.js b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.js index 82d8cb37fe7..095310c7e70 100644 --- a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.js +++ b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.js @@ -19,6 +19,24 @@ frappe.ui.form.on("Period Closing Voucher", { }); }, + fiscal_year: function (frm) { + if (frm.doc.fiscal_year) { + frappe.call({ + method: "erpnext.accounts.doctype.period_closing_voucher.period_closing_voucher.get_period_start_end_date", + args: { + fiscal_year: frm.doc.fiscal_year, + company: frm.doc.company, + }, + callback: function (r) { + if (r.message) { + frm.set_value("period_start_date", r.message[0]); + frm.set_value("period_end_date", r.message[1]); + } + }, + }); + } + }, + refresh: function (frm) { if (frm.doc.docstatus > 0) { frm.add_custom_button( diff --git a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.json b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.json index 624b5f82f64..f41cff0e0d8 100644 --- a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.json +++ b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.json @@ -6,39 +6,32 @@ "engine": "InnoDB", "field_order": [ "transaction_date", - "posting_date", - "fiscal_year", - "year_start_date", - "amended_from", "company", + "fiscal_year", + "period_start_date", + "period_end_date", + "amended_from", "column_break1", "closing_account_head", - "remarks", "gle_processing_status", + "remarks", "error_message" ], "fields": [ { + "default": "Today", "fieldname": "transaction_date", "fieldtype": "Date", "label": "Transaction Date", "oldfieldname": "transaction_date", "oldfieldtype": "Date" }, - { - "fieldname": "posting_date", - "fieldtype": "Date", - "label": "Posting Date", - "oldfieldname": "posting_date", - "oldfieldtype": "Date", - "reqd": 1 - }, { "fieldname": "fiscal_year", "fieldtype": "Link", "in_list_view": 1, "in_standard_filter": 1, - "label": "Closing Fiscal Year", + "label": "Fiscal Year", "oldfieldname": "fiscal_year", "oldfieldtype": "Select", "options": "Fiscal Year", @@ -103,16 +96,25 @@ "read_only": 1 }, { - "fieldname": "year_start_date", + "fieldname": "period_end_date", "fieldtype": "Date", - "label": "Year Start Date" + "label": "Period End Date", + "reqd": 1 + }, + { + "fieldname": "period_start_date", + "fieldtype": "Date", + "label": "Period Start Date", + "oldfieldname": "posting_date", + "oldfieldtype": "Date", + "reqd": 1 } ], "icon": "fa fa-file-text", "idx": 1, "is_submittable": 1, "links": [], - "modified": "2023-09-11 20:19:11.810533", + "modified": "2024-09-15 17:22:45.291628", "modified_by": "Administrator", "module": "Accounts", "name": "Period Closing Voucher", @@ -148,7 +150,7 @@ "write": 1 } ], - "search_fields": "posting_date, fiscal_year", + "search_fields": "fiscal_year, period_start_date, period_end_date", "sort_field": "modified", "sort_order": "DESC", "states": [], diff --git a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py index dd8a5ff16c3..d6bd217650b 100644 --- a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py +++ b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py @@ -2,15 +2,20 @@ # License: GNU General Public License v3. See license.txt +import copy + import frappe from frappe import _ from frappe.query_builder.functions import Sum -from frappe.utils import add_days, flt +from frappe.utils import add_days, flt, formatdate, getdate +from erpnext.accounts.doctype.account_closing_balance.account_closing_balance import ( + make_closing_entries, +) from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( get_accounting_dimensions, ) -from erpnext.accounts.utils import get_account_currency, get_fiscal_year, validate_fiscal_year +from erpnext.accounts.utils import get_account_currency, get_fiscal_year from erpnext.controllers.accounts_controller import AccountsController @@ -29,36 +34,386 @@ class PeriodClosingVoucher(AccountsController): error_message: DF.Text | None fiscal_year: DF.Link gle_processing_status: DF.Literal["In Progress", "Completed", "Failed"] - posting_date: DF.Date + period_end_date: DF.Date + period_start_date: DF.Date remarks: DF.SmallText transaction_date: DF.Date | None - year_start_date: DF.Date | None # end: auto-generated types def validate(self): - self.validate_account_head() - self.validate_posting_date() + self.validate_start_and_end_date() + self.check_if_previous_year_closed() + self.block_if_future_closing_voucher_exists() + self.check_closing_account_type() + self.check_closing_account_currency() + + def validate_start_and_end_date(self): + self.fy_start_date, self.fy_end_date = frappe.db.get_value( + "Fiscal Year", self.fiscal_year, ["year_start_date", "year_end_date"] + ) + + prev_closed_period_end_date = get_previous_closed_period_in_current_year( + self.fiscal_year, self.company + ) + valid_start_date = ( + add_days(prev_closed_period_end_date, 1) if prev_closed_period_end_date else self.fy_start_date + ) + + if getdate(self.period_start_date) != getdate(valid_start_date): + frappe.throw(_("Period Start Date must be {0}").format(formatdate(valid_start_date))) + + if getdate(self.period_start_date) > getdate(self.period_end_date): + frappe.throw(_("Period Start Date cannot be greater than Period End Date")) + + if getdate(self.period_end_date) > getdate(self.fy_end_date): + frappe.throw(_("Period End Date cannot be greater than Fiscal Year End Date")) + + def check_if_previous_year_closed(self): + last_year_closing = add_days(self.fy_start_date, -1) + previous_fiscal_year = get_fiscal_year(last_year_closing, company=self.company, boolean=True) + if not previous_fiscal_year: + return + + previous_fiscal_year_start_date = previous_fiscal_year[0][1] + gle_exists_in_previous_year = frappe.db.exists( + "GL Entry", + { + "posting_date": ("between", [previous_fiscal_year_start_date, last_year_closing]), + "company": self.company, + "is_cancelled": 0, + }, + ) + if not gle_exists_in_previous_year: + return + + previous_fiscal_year_closed = frappe.db.exists( + "Period Closing Voucher", + { + "period_end_date": ("between", [previous_fiscal_year_start_date, last_year_closing]), + "docstatus": 1, + "company": self.company, + }, + ) + if not previous_fiscal_year_closed: + frappe.throw(_("Previous Year is not closed, please close it first")) + + def block_if_future_closing_voucher_exists(self): + future_closing_voucher = self.get_future_closing_voucher() + if future_closing_voucher and future_closing_voucher[0][0]: + action = "cancel" if self.docstatus == 2 else "create" + frappe.throw( + _( + "You cannot {0} this document because another Period Closing Entry {1} exists after {2}" + ).format(action, future_closing_voucher[0][0], self.period_end_date) + ) + + def get_future_closing_voucher(self): + return frappe.db.get_value( + "Period Closing Voucher", + {"period_end_date": (">", self.period_end_date), "docstatus": 1, "company": self.company}, + "name", + ) + + def check_closing_account_type(self): + closing_account_type = frappe.get_cached_value("Account", self.closing_account_head, "root_type") + + if closing_account_type not in ["Liability", "Equity"]: + frappe.throw( + _("Closing Account {0} must be of type Liability / Equity").format(self.closing_account_head) + ) + + def check_closing_account_currency(self): + account_currency = get_account_currency(self.closing_account_head) + company_currency = frappe.get_cached_value("Company", self.company, "default_currency") + if account_currency != company_currency: + frappe.throw(_("Currency of the Closing Account must be {0}").format(company_currency)) def on_submit(self): self.db_set("gle_processing_status", "In Progress") - get_opening_entries = False - - if not frappe.db.exists( - "Period Closing Voucher", {"company": self.company, "docstatus": 1, "name": ("!=", self.name)} - ): - get_opening_entries = True - - self.make_gl_entries(get_opening_entries=get_opening_entries) + self.make_gl_entries() def on_cancel(self): - self.validate_future_closing_vouchers() - self.db_set("gle_processing_status", "In Progress") self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry", "Payment Ledger Entry") - gle_count = frappe.db.count( + self.block_if_future_closing_voucher_exists() + self.db_set("gle_processing_status", "In Progress") + self.cancel_gl_entries() + + def make_gl_entries(self): + if self.get_gle_count_in_selected_period() > 5000: + frappe.enqueue( + process_gl_and_closing_entries, + doc=self, + timeout=1800, + ) + frappe.msgprint( + _( + "The GL Entries and closing balances will be processed in the background, it can take a few minutes." + ), + alert=True, + ) + else: + process_gl_and_closing_entries(self) + + def get_gle_count_in_selected_period(self): + return frappe.db.count( "GL Entry", - {"voucher_type": "Period Closing Voucher", "voucher_no": self.name, "is_cancelled": 0}, + { + "posting_date": ["between", [self.period_start_date, self.period_end_date]], + "company": self.company, + "is_cancelled": 0, + }, ) - if gle_count > 5000: + + def get_pcv_gl_entries(self): + self.pl_accounts_reverse_gle = [] + self.closing_account_gle = [] + + pl_account_balances = self.get_account_balances_based_on_dimensions(report_type="Profit and Loss") + for dimensions, account_balances in pl_account_balances.items(): + for acc, balances in account_balances.items(): + balance_in_company_currency = flt(balances.debit_in_account_currency) - flt( + balances.credit_in_account_currency + ) + if balance_in_company_currency and acc != "balances": + self.pl_accounts_reverse_gle.append( + self.get_gle_for_pl_account(acc, balances, dimensions) + ) + + # closing liability account + self.closing_account_gle.append( + self.get_gle_for_closing_account(account_balances["balances"], dimensions) + ) + + return self.pl_accounts_reverse_gle + self.closing_account_gle + + def get_gle_for_pl_account(self, acc, balances, dimensions): + balance_in_account_currency = flt(balances.debit_in_account_currency) - flt( + balances.credit_in_account_currency + ) + balance_in_company_currency = flt(balances.debit) - flt(balances.credit) + gl_entry = frappe._dict( + { + "company": self.company, + "posting_date": self.period_end_date, + "account": acc, + "account_currency": balances.account_currency, + "debit_in_account_currency": abs(balance_in_account_currency) + if balance_in_account_currency < 0 + else 0, + "debit": abs(balance_in_company_currency) if balance_in_company_currency < 0 else 0, + "credit_in_account_currency": abs(balance_in_account_currency) + if balance_in_account_currency > 0 + else 0, + "credit": abs(balance_in_company_currency) if balance_in_company_currency > 0 else 0, + "is_period_closing_voucher_entry": 1, + "voucher_type": "Period Closing Voucher", + "voucher_no": self.name, + "fiscal_year": self.fiscal_year, + "remarks": self.remarks, + "is_opening": "No", + } + ) + self.update_default_dimensions(gl_entry, dimensions) + return gl_entry + + def get_gle_for_closing_account(self, dimension_balance, dimensions): + balance_in_account_currency = flt(dimension_balance.balance_in_account_currency) + balance_in_company_currency = flt(dimension_balance.balance_in_company_currency) + gl_entry = frappe._dict( + { + "company": self.company, + "posting_date": self.period_end_date, + "account": self.closing_account_head, + "account_currency": frappe.db.get_value( + "Account", self.closing_account_head, "account_currency" + ), + "debit_in_account_currency": balance_in_account_currency + if balance_in_account_currency > 0 + else 0, + "debit": balance_in_company_currency if balance_in_company_currency > 0 else 0, + "credit_in_account_currency": abs(balance_in_account_currency) + if balance_in_account_currency < 0 + else 0, + "credit": abs(balance_in_company_currency) if balance_in_company_currency < 0 else 0, + "is_period_closing_voucher_entry": 1, + "voucher_type": "Period Closing Voucher", + "voucher_no": self.name, + "fiscal_year": self.fiscal_year, + "remarks": self.remarks, + "is_opening": "No", + } + ) + self.update_default_dimensions(gl_entry, dimensions) + return gl_entry + + def update_default_dimensions(self, gl_entry, dimensions): + for i, dimension in enumerate(self.accounting_dimension_fields): + gl_entry[dimension] = dimensions[i] + + def get_account_balances_based_on_dimensions(self, report_type): + """Get balance for dimension-wise pl accounts""" + self.get_accounting_dimension_fields() + acc_bal_dict = frappe._dict() + gl_entries = [] + + with frappe.db.unbuffered_cursor(): + gl_entries = self.get_gl_entries_for_current_period(report_type, as_iterator=True) + for gle in gl_entries: + acc_bal_dict = self.set_account_balance_dict(gle, acc_bal_dict) + + if report_type == "Balance Sheet" and self.is_first_period_closing_voucher(): + opening_entries = self.get_gl_entries_for_current_period(report_type, only_opening_entries=True) + for gle in opening_entries: + acc_bal_dict = self.set_account_balance_dict(gle, acc_bal_dict) + + return acc_bal_dict + + def get_accounting_dimension_fields(self): + default_dimensions = ["cost_center", "finance_book", "project"] + self.accounting_dimension_fields = default_dimensions + get_accounting_dimensions() + + def get_gl_entries_for_current_period(self, report_type, only_opening_entries=False, as_iterator=False): + date_condition = "" + if only_opening_entries: + date_condition = "is_opening = 'Yes'" + else: + date_condition = f"posting_date BETWEEN '{self.period_start_date}' AND '{self.period_end_date}' and is_opening = 'No'" + + # nosemgrep + return frappe.db.sql( + """ + SELECT + name, + posting_date, + account, + account_currency, + debit_in_account_currency, + credit_in_account_currency, + debit, + credit, + {} + FROM `tabGL Entry` + WHERE + {} + AND company = %s + AND voucher_type != 'Period Closing Voucher' + AND EXISTS(SELECT name FROM `tabAccount` WHERE name = account AND report_type = %s) + AND is_cancelled = 0 + """.format( + ", ".join(self.accounting_dimension_fields), + date_condition, + ), + (self.company, report_type), + as_dict=1, + as_iterator=as_iterator, + ) + + def set_account_balance_dict(self, gle, acc_bal_dict): + key = self.get_key(gle) + + acc_bal_dict.setdefault(key, frappe._dict()).setdefault( + gle.account, + frappe._dict( + { + "debit_in_account_currency": 0, + "credit_in_account_currency": 0, + "debit": 0, + "credit": 0, + "account_currency": gle.account_currency, + } + ), + ) + + acc_bal_dict[key][gle.account].debit_in_account_currency += flt(gle.debit_in_account_currency) + acc_bal_dict[key][gle.account].credit_in_account_currency += flt(gle.credit_in_account_currency) + acc_bal_dict[key][gle.account].debit += flt(gle.debit) + acc_bal_dict[key][gle.account].credit += flt(gle.credit) + + # dimension-wise total balances + acc_bal_dict[key].setdefault( + "balances", + frappe._dict( + { + "balance_in_account_currency": 0, + "balance_in_company_currency": 0, + } + ), + ) + + balance_in_account_currency = flt(gle.debit_in_account_currency) - flt(gle.credit_in_account_currency) + balance_in_company_currency = flt(gle.debit) - flt(gle.credit) + + acc_bal_dict[key]["balances"].balance_in_account_currency += balance_in_account_currency + acc_bal_dict[key]["balances"].balance_in_company_currency += balance_in_company_currency + + return acc_bal_dict + + def get_key(self, gle): + return tuple([gle.get(dimension) for dimension in self.accounting_dimension_fields]) + + def get_account_closing_balances(self): + pl_closing_entries = self.get_closing_entries_for_pl_accounts() + bs_closing_entries = self.get_closing_entries_for_balance_sheet_accounts() + closing_entries = pl_closing_entries + bs_closing_entries + return closing_entries + + def get_closing_entries_for_pl_accounts(self): + closing_entries = copy.deepcopy(self.pl_accounts_reverse_gle) + for d in self.pl_accounts_reverse_gle: + # reverse debit and credit + gle_copy = copy.deepcopy(d) + gle_copy.debit = d.credit + gle_copy.credit = d.debit + gle_copy.debit_in_account_currency = d.credit_in_account_currency + gle_copy.credit_in_account_currency = d.debit_in_account_currency + gle_copy.is_period_closing_voucher_entry = 0 + gle_copy.period_closing_voucher = self.name + closing_entries.append(gle_copy) + + return closing_entries + + def get_closing_entries_for_balance_sheet_accounts(self): + closing_entries = [] + balance_sheet_account_balances = self.get_account_balances_based_on_dimensions( + report_type="Balance Sheet" + ) + + for dimensions, account_balances in balance_sheet_account_balances.items(): + for acc, balances in account_balances.items(): + balance_in_company_currency = flt(balances.debit_in_account_currency) - flt( + balances.credit_in_account_currency + ) + if acc != "balances" and balance_in_company_currency: + closing_entries.append(self.get_closing_entry(acc, balances, dimensions)) + + return closing_entries + + def get_closing_entry(self, account, balances, dimensions): + closing_entry = frappe._dict( + { + "company": self.company, + "closing_date": self.period_end_date, + "period_closing_voucher": self.name, + "account": account, + "account_currency": balances.account_currency, + "debit_in_account_currency": flt(balances.debit_in_account_currency), + "debit": flt(balances.debit), + "credit_in_account_currency": flt(balances.credit_in_account_currency), + "credit": flt(balances.credit), + "is_period_closing_voucher_entry": 0, + } + ) + self.update_default_dimensions(closing_entry, dimensions) + return closing_entry + + def is_first_period_closing_voucher(self): + return not frappe.db.exists( + "Period Closing Voucher", + {"company": self.company, "docstatus": 1, "name": ("!=", self.name)}, + ) + + def cancel_gl_entries(self): + if self.get_gle_count_against_current_pcv() > 5000: frappe.enqueue( process_cancellation, voucher_type="Period Closing Voucher", @@ -73,308 +428,30 @@ class PeriodClosingVoucher(AccountsController): else: process_cancellation(voucher_type="Period Closing Voucher", voucher_no=self.name) - def validate_future_closing_vouchers(self): - if frappe.db.exists( - "Period Closing Voucher", - {"posting_date": (">", self.posting_date), "docstatus": 1, "company": self.company}, - ): - frappe.throw( - _( - "You can not cancel this Period Closing Voucher, please cancel the future Period Closing Vouchers first" - ) - ) - - def validate_account_head(self): - closing_account_type = frappe.get_cached_value("Account", self.closing_account_head, "root_type") - - if closing_account_type not in ["Liability", "Equity"]: - frappe.throw( - _("Closing Account {0} must be of type Liability / Equity").format(self.closing_account_head) - ) - - account_currency = get_account_currency(self.closing_account_head) - company_currency = frappe.get_cached_value("Company", self.company, "default_currency") - if account_currency != company_currency: - frappe.throw(_("Currency of the Closing Account must be {0}").format(company_currency)) - - def validate_posting_date(self): - validate_fiscal_year( - self.posting_date, self.fiscal_year, self.company, label=_("Posting Date"), doc=self - ) - - self.year_start_date = get_fiscal_year(self.posting_date, self.fiscal_year, company=self.company)[1] - - self.check_if_previous_year_closed() - - pcv = frappe.qb.DocType("Period Closing Voucher") - existing_entry = ( - frappe.qb.from_(pcv) - .select(pcv.name) - .where( - (pcv.posting_date >= self.posting_date) - & (pcv.fiscal_year == self.fiscal_year) - & (pcv.docstatus == 1) - & (pcv.company == self.company) - ) - .run() - ) - - if existing_entry and existing_entry[0][0]: - frappe.throw( - _("Another Period Closing Entry {0} has been made after {1}").format( - existing_entry[0][0], self.posting_date - ) - ) - - def check_if_previous_year_closed(self): - last_year_closing = add_days(self.year_start_date, -1) - previous_fiscal_year = get_fiscal_year(last_year_closing, company=self.company, boolean=True) - if not previous_fiscal_year: - return - - previous_fiscal_year_start_date = previous_fiscal_year[0][1] - if not frappe.db.exists( + def get_gle_count_against_current_pcv(self): + return frappe.db.count( "GL Entry", - { - "posting_date": ("between", [previous_fiscal_year_start_date, last_year_closing]), - "company": self.company, - "is_cancelled": 0, - }, - ): - return - - if not frappe.db.exists( - "Period Closing Voucher", - { - "posting_date": ("between", [previous_fiscal_year_start_date, last_year_closing]), - "docstatus": 1, - "company": self.company, - }, - ): - frappe.throw(_("Previous Year is not closed, please close it first")) - - def make_gl_entries(self, get_opening_entries=False): - gl_entries = self.get_gl_entries() - closing_entries = self.get_grouped_gl_entries(get_opening_entries=get_opening_entries) - if len(gl_entries + closing_entries) > 3000: - frappe.enqueue( - process_gl_and_closing_entries, - gl_entries=gl_entries, - closing_entries=closing_entries, - voucher_name=self.name, - company=self.company, - closing_date=self.posting_date, - timeout=3000, - ) - - frappe.msgprint( - _("The GL Entries will be processed in the background, it can take a few minutes."), - alert=True, - ) - else: - process_gl_and_closing_entries( - gl_entries, closing_entries, self.name, self.company, self.posting_date - ) - - def get_grouped_gl_entries(self, get_opening_entries=False): - closing_entries = [] - for acc in self.get_balances_based_on_dimensions( - group_by_account=True, for_aggregation=True, get_opening_entries=get_opening_entries - ): - closing_entries.append(self.get_closing_entries(acc)) - - return closing_entries - - def get_gl_entries(self): - gl_entries = [] - - # pl account - for acc in self.get_balances_based_on_dimensions( - group_by_account=True, report_type="Profit and Loss" - ): - if flt(acc.bal_in_company_currency): - gl_entries.append(self.get_gle_for_pl_account(acc)) - - # closing liability account - for acc in self.get_balances_based_on_dimensions( - group_by_account=False, report_type="Profit and Loss" - ): - if flt(acc.bal_in_company_currency): - gl_entries.append(self.get_gle_for_closing_account(acc)) - - return gl_entries - - def get_gle_for_pl_account(self, acc): - gl_entry = self.get_gl_dict( - { - "company": self.company, - "closing_date": self.posting_date, - "account": acc.account, - "cost_center": acc.cost_center, - "finance_book": acc.finance_book, - "account_currency": acc.account_currency, - "debit_in_account_currency": abs(flt(acc.bal_in_account_currency)) - if flt(acc.bal_in_account_currency) < 0 - else 0, - "debit": abs(flt(acc.bal_in_company_currency)) if flt(acc.bal_in_company_currency) < 0 else 0, - "credit_in_account_currency": abs(flt(acc.bal_in_account_currency)) - if flt(acc.bal_in_account_currency) > 0 - else 0, - "credit": abs(flt(acc.bal_in_company_currency)) - if flt(acc.bal_in_company_currency) > 0 - else 0, - "is_period_closing_voucher_entry": 1, - }, - item=acc, - ) - self.update_default_dimensions(gl_entry, acc) - return gl_entry - - def get_gle_for_closing_account(self, acc): - gl_entry = self.get_gl_dict( - { - "company": self.company, - "closing_date": self.posting_date, - "account": self.closing_account_head, - "cost_center": acc.cost_center, - "finance_book": acc.finance_book, - "account_currency": acc.account_currency, - "debit_in_account_currency": abs(flt(acc.bal_in_account_currency)) - if flt(acc.bal_in_account_currency) > 0 - else 0, - "debit": abs(flt(acc.bal_in_company_currency)) if flt(acc.bal_in_company_currency) > 0 else 0, - "credit_in_account_currency": abs(flt(acc.bal_in_account_currency)) - if flt(acc.bal_in_account_currency) < 0 - else 0, - "credit": abs(flt(acc.bal_in_company_currency)) - if flt(acc.bal_in_company_currency) < 0 - else 0, - "is_period_closing_voucher_entry": 1, - }, - item=acc, - ) - self.update_default_dimensions(gl_entry, acc) - return gl_entry - - def get_closing_entries(self, acc): - closing_entry = self.get_gl_dict( - { - "company": self.company, - "closing_date": self.posting_date, - "period_closing_voucher": self.name, - "account": acc.account, - "cost_center": acc.cost_center, - "finance_book": acc.finance_book, - "account_currency": acc.account_currency, - "debit_in_account_currency": flt(acc.debit_in_account_currency), - "debit": flt(acc.debit), - "credit_in_account_currency": flt(acc.credit_in_account_currency), - "credit": flt(acc.credit), - }, - item=acc, + {"voucher_type": "Period Closing Voucher", "voucher_no": self.name, "is_cancelled": 0}, ) - for dimension in self.accounting_dimensions: - closing_entry.update({dimension: acc.get(dimension)}) - return closing_entry - - def update_default_dimensions(self, gl_entry, acc): - if not self.accounting_dimensions: - self.accounting_dimensions = get_accounting_dimensions() - - for dimension in self.accounting_dimensions: - gl_entry.update({dimension: acc.get(dimension)}) - - def get_balances_based_on_dimensions( - self, group_by_account=False, report_type=None, for_aggregation=False, get_opening_entries=False - ): - """Get balance for dimension-wise pl accounts""" - - qb_dimension_fields = ["cost_center", "finance_book", "project"] - - self.accounting_dimensions = get_accounting_dimensions() - for dimension in self.accounting_dimensions: - qb_dimension_fields.append(dimension) - - if group_by_account: - qb_dimension_fields.append("account") - - account_filters = { - "company": self.company, - "is_group": 0, - } - - if report_type: - account_filters.update({"report_type": report_type}) - - accounts = frappe.get_all("Account", filters=account_filters, pluck="name") - - gl_entry = frappe.qb.DocType("GL Entry") - query = frappe.qb.from_(gl_entry).select(gl_entry.account, gl_entry.account_currency) - - if not for_aggregation: - query = query.select( - (Sum(gl_entry.debit_in_account_currency) - Sum(gl_entry.credit_in_account_currency)).as_( - "bal_in_account_currency" - ), - (Sum(gl_entry.debit) - Sum(gl_entry.credit)).as_("bal_in_company_currency"), - ) - else: - query = query.select( - (Sum(gl_entry.debit_in_account_currency)).as_("debit_in_account_currency"), - (Sum(gl_entry.credit_in_account_currency)).as_("credit_in_account_currency"), - (Sum(gl_entry.debit)).as_("debit"), - (Sum(gl_entry.credit)).as_("credit"), - ) - - for dimension in qb_dimension_fields: - query = query.select(gl_entry[dimension]) - - query = query.where( - (gl_entry.company == self.company) - & (gl_entry.is_cancelled == 0) - & (gl_entry.account.isin(accounts)) - ) - - if get_opening_entries: - query = query.where( - ( # noqa: UP034 - (gl_entry.posting_date.between(self.get("year_start_date"), self.posting_date)) - | (gl_entry.is_opening == "Yes") - ) - ) - else: - query = query.where( - gl_entry.posting_date.between(self.get("year_start_date"), self.posting_date) - & gl_entry.is_opening - == "No" - ) - - if for_aggregation: - query = query.where(gl_entry.voucher_type != "Period Closing Voucher") - - for dimension in qb_dimension_fields: - query = query.groupby(gl_entry[dimension]) - - return query.run(as_dict=1) - - -def process_gl_and_closing_entries(gl_entries, closing_entries, voucher_name, company, closing_date): - from erpnext.accounts.doctype.account_closing_balance.account_closing_balance import ( - make_closing_entries, - ) +def process_gl_and_closing_entries(doc): from erpnext.accounts.general_ledger import make_gl_entries try: + gl_entries = doc.get_pcv_gl_entries() if gl_entries: make_gl_entries(gl_entries, merge_entries=False) - make_closing_entries(gl_entries + closing_entries, voucher_name, company, closing_date) - frappe.db.set_value("Period Closing Voucher", voucher_name, "gle_processing_status", "Completed") + + closing_entries = doc.get_account_closing_balances() + if closing_entries: + make_closing_entries(closing_entries, doc.name, doc.company, doc.period_end_date) + + frappe.db.set_value(doc.doctype, doc.name, "gle_processing_status", "Completed") except Exception as e: frappe.db.rollback() frappe.log_error(e) - frappe.db.set_value("Period Closing Voucher", voucher_name, "gle_processing_status", "Failed") + frappe.db.set_value(doc.doctype, doc.name, "gle_processing_status", "Failed") def process_cancellation(voucher_type, voucher_no): @@ -395,3 +472,29 @@ def delete_closing_entries(voucher_no): frappe.qb.from_(closing_balance).delete().where( closing_balance.period_closing_voucher == voucher_no ).run() + + +@frappe.whitelist() +def get_period_start_end_date(fiscal_year, company): + fy_start_date, fy_end_date = frappe.db.get_value( + "Fiscal Year", fiscal_year, ["year_start_date", "year_end_date"] + ) + prev_closed_period_end_date = get_previous_closed_period_in_current_year(fiscal_year, company) + period_start_date = ( + add_days(prev_closed_period_end_date, 1) if prev_closed_period_end_date else fy_start_date + ) + return period_start_date, fy_end_date + + +def get_previous_closed_period_in_current_year(fiscal_year, company): + prev_closed_period_end_date = frappe.db.get_value( + "Period Closing Voucher", + filters={ + "company": company, + "fiscal_year": fiscal_year, + "docstatus": 1, + }, + fieldname=["period_end_date"], + order_by="period_end_date desc", + ) + return prev_closed_period_end_date diff --git a/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py b/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py index 1bd565e1b36..e9d65f7f856 100644 --- a/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py +++ b/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py @@ -317,16 +317,18 @@ class TestPeriodClosingVoucher(unittest.TestCase): repost_doc.posting_date = today() repost_doc.save() - def make_period_closing_voucher(self, posting_date=None, submit=True): + def make_period_closing_voucher(self, posting_date, submit=True): surplus_account = create_account() cost_center = create_cost_center("Test Cost Center 1") + fy = get_fiscal_year(posting_date, company="Test PCV Company") pcv = frappe.get_doc( { "doctype": "Period Closing Voucher", "transaction_date": posting_date or today(), - "posting_date": posting_date or today(), + "period_start_date": fy[1], + "period_end_date": fy[2], "company": "Test PCV Company", - "fiscal_year": get_fiscal_year(today(), company="Test PCV Company")[0], + "fiscal_year": fy[0], "cost_center": cost_center, "closing_account_head": surplus_account, "remarks": "test", 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 8c8ba633df0..f37e542dd89 100644 --- a/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py +++ b/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py @@ -46,8 +46,8 @@ class RepostAccountingLedger(Document): frappe.db.get_all( "Period Closing Voucher", filters={"company": self.company}, - order_by="posting_date desc", - pluck="posting_date", + order_by="period_end_date desc", + pluck="period_end_date", limit=1, ) or None 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 f631ef437d6..9f906bb7647 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 @@ -129,13 +129,15 @@ class TestRepostAccountingLedger(AccountsTestMixin, FrappeTestCase): cost_center=self.cost_center, rate=100, ) + fy = get_fiscal_year(today(), company=self.company) pcv = frappe.get_doc( { "doctype": "Period Closing Voucher", "transaction_date": today(), - "posting_date": today(), + "period_start_date": fy[1], + "period_end_date": today(), "company": self.company, - "fiscal_year": get_fiscal_year(today(), company=self.company)[0], + "fiscal_year": fy[0], "cost_center": self.cost_center, "closing_account_head": self.retained_earnings, "remarks": "test", diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py index ad8cc97e101..856e2b96af0 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -37,13 +37,14 @@ def make_gl_entries( validate_disabled_accounts(gl_map) gl_map = process_gl_map(gl_map, merge_entries) if gl_map and len(gl_map) > 1: - create_payment_ledger_entry( - gl_map, - cancel=0, - adv_adj=adv_adj, - update_outstanding=update_outstanding, - from_repost=from_repost, - ) + if gl_map[0].voucher_type != "Period Closing Voucher": + create_payment_ledger_entry( + gl_map, + cancel=0, + adv_adj=adv_adj, + update_outstanding=update_outstanding, + 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 elif gl_map: @@ -116,17 +117,16 @@ def get_accounting_dimensions_for_offsetting_entry(gl_map, company): def validate_disabled_accounts(gl_map): accounts = [d.account for d in gl_map if d.account] - Account = frappe.qb.DocType("Account") + disabled_accounts = frappe.get_all( + "Account", + filters={"disabled": 1, "is_group": 0, "company": gl_map[0].company}, + fields=["name"], + ) - disabled_accounts = ( - frappe.qb.from_(Account) - .where(Account.name.isin(accounts) & Account.disabled == 1) - .select(Account.name, Account.disabled) - ).run(as_dict=True) - - if disabled_accounts: + used_disabled_accounts = set(accounts).intersection(set([d.name for d in disabled_accounts])) + if used_disabled_accounts: account_list = "
" - account_list += ", ".join([frappe.bold(d.name) for d in disabled_accounts]) + account_list += ", ".join([frappe.bold(d) for d in used_disabled_accounts]) frappe.throw( _("Cannot create accounting entries against disabled accounts: {0}").format(account_list), title=_("Disabled Account Selected"), @@ -708,7 +708,7 @@ def validate_against_pcv(is_opening, posting_date, company): ) last_pcv_date = frappe.db.get_value( - "Period Closing Voucher", {"docstatus": 1, "company": company}, "max(posting_date)" + "Period Closing Voucher", {"docstatus": 1, "company": company}, "max(period_end_date)" ) if last_pcv_date and getdate(posting_date) <= getdate(last_pcv_date): diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index 43100c81285..8d4a8579ae3 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -385,6 +385,7 @@ class ReceivablePayableReport: self.delivery_notes = frappe._dict() # delivery note link inside sales invoice + # nosemgrep si_against_dn = frappe.db.sql( """ select parent, delivery_note @@ -400,6 +401,7 @@ class ReceivablePayableReport: if d.delivery_note: self.delivery_notes.setdefault(d.parent, set()).add(d.delivery_note) + # nosemgrep dn_against_si = frappe.db.sql( """ select distinct parent, against_sales_invoice @@ -417,13 +419,16 @@ class ReceivablePayableReport: def get_invoice_details(self): self.invoice_details = frappe._dict() if self.account_type == "Receivable": + # nosemgrep si_list = frappe.db.sql( """ select name, due_date, po_no from `tabSales Invoice` where posting_date <= %s + and company = %s + and docstatus = 1 """, - self.filters.report_date, + (self.filters.report_date, self.filters.company), as_dict=1, ) for d in si_list: @@ -431,6 +436,7 @@ class ReceivablePayableReport: # Get Sales Team if self.filters.show_sales_person: + # nosemgrep sales_team = frappe.db.sql( """ select parent, sales_person @@ -445,25 +451,33 @@ class ReceivablePayableReport: ) if self.account_type == "Payable": + # nosemgrep for pi in frappe.db.sql( """ select name, due_date, bill_no, bill_date from `tabPurchase Invoice` - where posting_date <= %s + where + posting_date <= %s + and company = %s + and docstatus = 1 """, - self.filters.report_date, + (self.filters.report_date, self.filters.company), as_dict=1, ): self.invoice_details.setdefault(pi.name, pi) # Invoices booked via Journal Entries + # nosemgrep journal_entries = frappe.db.sql( """ select name, due_date, bill_no, bill_date from `tabJournal Entry` - where posting_date <= %s + where + posting_date <= %s + and company = %s + and docstatus = 1 """, - self.filters.report_date, + (self.filters.report_date, self.filters.company), as_dict=1, ) @@ -472,6 +486,8 @@ class ReceivablePayableReport: self.invoice_details.setdefault(je.name, je) def set_party_details(self, row): + if not row.party: + return # customer / supplier name party_details = self.get_party_details(row.party) or {} row.update(party_details) @@ -496,6 +512,7 @@ class ReceivablePayableReport: def get_payment_terms(self, row): # build payment_terms for row + # nosemgrep payment_terms_details = frappe.db.sql( f""" select @@ -708,6 +725,7 @@ class ReceivablePayableReport: def get_return_entries(self): doctype = "Sales Invoice" if self.account_type == "Receivable" else "Purchase Invoice" filters = { + "posting_date": ("<=", self.filters.report_date), "is_return": 1, "docstatus": 1, "company": self.filters.company, @@ -815,6 +833,7 @@ class ReceivablePayableReport: if self.filters.get("sales_person"): lft, rgt = frappe.db.get_value("Sales Person", self.filters.get("sales_person"), ["lft", "rgt"]) + # nosemgrep records = frappe.db.sql( """ select distinct parent, parenttype diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py index 6d7635979bb..c233f3c7e2b 100644 --- a/erpnext/accounts/report/financial_statements.py +++ b/erpnext/accounts/report/financial_statements.py @@ -9,6 +9,7 @@ import re import frappe from frappe import _ from frappe.utils import add_days, add_months, cint, cstr, flt, formatdate, get_first_day, getdate +from pypika.terms import ExistsCriterion from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( get_accounting_dimensions, @@ -181,12 +182,12 @@ def get_data( company, period_list[0]["year_start_date"] if only_current_fiscal_year else None, period_list[-1]["to_date"], - root.lft, - root.rgt, filters, gl_entries_by_account, - ignore_closing_entries=ignore_closing_entries, + root.lft, + root.rgt, root_type=root_type, + ignore_closing_entries=ignore_closing_entries, ) calculate_values( @@ -419,93 +420,78 @@ def set_gl_entries_by_account( company, from_date, to_date, - root_lft, - root_rgt, filters, gl_entries_by_account, + root_lft=None, + root_rgt=None, + root_type=None, ignore_closing_entries=False, ignore_opening_entries=False, - root_type=None, ): """Returns a dict like { "account": [gl entries], ... }""" gl_entries = [] - account_filters = { - "company": company, - "is_group": 0, - "lft": (">=", root_lft), - "rgt": ("<=", root_rgt), - } - - if root_type: - account_filters.update( - { - "root_type": root_type, - } + # For balance sheet + ignore_closing_balances = frappe.db.get_single_value( + "Accounts Settings", "ignore_account_closing_balance" + ) + if not from_date and not ignore_closing_balances: + last_period_closing_voucher = frappe.db.get_all( + "Period Closing Voucher", + filters={ + "docstatus": 1, + "company": filters.company, + "period_end_date": ("<", filters["period_start_date"]), + }, + fields=["period_end_date", "name"], + order_by="period_end_date desc", + limit=1, ) + if last_period_closing_voucher: + gl_entries += get_accounting_entries( + "Account Closing Balance", + from_date, + to_date, + filters, + root_lft, + root_rgt, + root_type, + ignore_closing_entries, + last_period_closing_voucher[0].name, + ) + from_date = add_days(last_period_closing_voucher[0].period_end_date, 1) + ignore_opening_entries = True - accounts_list = frappe.db.get_all( - "Account", - filters=account_filters, - pluck="name", + gl_entries += get_accounting_entries( + "GL Entry", + from_date, + to_date, + filters, + root_lft, + root_rgt, + root_type, + ignore_closing_entries, + ignore_opening_entries=ignore_opening_entries, ) - if accounts_list: - # For balance sheet - ignore_closing_balances = frappe.db.get_single_value( - "Accounts Settings", "ignore_account_closing_balance" - ) - if not from_date and not ignore_closing_balances: - last_period_closing_voucher = frappe.db.get_all( - "Period Closing Voucher", - filters={ - "docstatus": 1, - "company": filters.company, - "posting_date": ("<", filters["period_start_date"]), - }, - fields=["posting_date", "name"], - order_by="posting_date desc", - limit=1, - ) - if last_period_closing_voucher: - gl_entries += get_accounting_entries( - "Account Closing Balance", - from_date, - to_date, - accounts_list, - filters, - ignore_closing_entries, - last_period_closing_voucher[0].name, - ) - from_date = add_days(last_period_closing_voucher[0].posting_date, 1) - ignore_opening_entries = True + if filters and filters.get("presentation_currency"): + convert_to_presentation_currency(gl_entries, get_currency(filters)) - gl_entries += get_accounting_entries( - "GL Entry", - from_date, - to_date, - accounts_list, - filters, - ignore_closing_entries, - ignore_opening_entries=ignore_opening_entries, - ) + for entry in gl_entries: + gl_entries_by_account.setdefault(entry.account, []).append(entry) - if filters and filters.get("presentation_currency"): - convert_to_presentation_currency(gl_entries, get_currency(filters)) - - for entry in gl_entries: - gl_entries_by_account.setdefault(entry.account, []).append(entry) - - return gl_entries_by_account + return gl_entries_by_account def get_accounting_entries( doctype, from_date, to_date, - accounts, filters, - ignore_closing_entries, + root_lft=None, + root_rgt=None, + root_type=None, + ignore_closing_entries=None, period_closing_voucher=None, ignore_opening_entries=False, ): @@ -535,13 +521,30 @@ def get_accounting_entries( query = query.where(gl_entry.period_closing_voucher == period_closing_voucher) query = apply_additional_conditions(doctype, query, from_date, ignore_closing_entries, filters) - query = query.where(gl_entry.account.isin(accounts)) + + if (root_lft and root_rgt) or root_type: + account_filter_query = get_account_filter_query(root_lft, root_rgt, root_type, gl_entry) + query = query.where(ExistsCriterion(account_filter_query)) entries = query.run(as_dict=True) return entries +def get_account_filter_query(root_lft, root_rgt, root_type, gl_entry): + acc = frappe.qb.DocType("Account") + exists_query = ( + frappe.qb.from_(acc).select(acc.name).where(acc.name == gl_entry.account).where(acc.is_group == 0) + ) + if root_lft and root_rgt: + exists_query = exists_query.where(acc.lft >= root_lft).where(acc.rgt <= root_rgt) + + if root_type: + exists_query = exists_query.where(acc.root_type == root_type) + + return exists_query + + def apply_additional_conditions(doctype, query, from_date, ignore_closing_entries, filters): gl_entry = frappe.qb.DocType(doctype) accounting_dimensions = get_accounting_dimensions(as_list=False) diff --git a/erpnext/accounts/report/trial_balance/trial_balance.py b/erpnext/accounts/report/trial_balance/trial_balance.py index f216ecea15a..8ca850f301e 100644 --- a/erpnext/accounts/report/trial_balance/trial_balance.py +++ b/erpnext/accounts/report/trial_balance/trial_balance.py @@ -94,12 +94,6 @@ def get_data(filters): accounts, accounts_by_name, parent_children_map = filter_accounts(accounts) - min_lft, max_rgt = frappe.db.sql( - """select min(lft), max(rgt) from `tabAccount` - where company=%s""", - (filters.company,), - )[0] - gl_entries_by_account = {} opening_balances = get_opening_balances(filters) @@ -112,10 +106,10 @@ def get_data(filters): filters.company, filters.from_date, filters.to_date, - min_lft, - max_rgt, filters, gl_entries_by_account, + root_lft=None, + root_rgt=None, ignore_closing_entries=not flt(filters.with_period_closing_entry_for_current_period), ignore_opening_entries=True, ) @@ -150,9 +144,9 @@ def get_rootwise_opening_balances(filters, report_type): if not ignore_closing_balances: last_period_closing_voucher = frappe.db.get_all( "Period Closing Voucher", - filters={"docstatus": 1, "company": filters.company, "posting_date": ("<", filters.from_date)}, - fields=["posting_date", "name"], - order_by="posting_date desc", + filters={"docstatus": 1, "company": filters.company, "period_end_date": ("<", filters.from_date)}, + fields=["period_end_date", "name"], + order_by="period_end_date desc", limit=1, ) @@ -168,8 +162,8 @@ def get_rootwise_opening_balances(filters, report_type): ) # Report getting generate from the mid of a fiscal year - if getdate(last_period_closing_voucher[0].posting_date) < getdate(add_days(filters.from_date, -1)): - start_date = add_days(last_period_closing_voucher[0].posting_date, 1) + if getdate(last_period_closing_voucher[0].period_end_date) < getdate(add_days(filters.from_date, -1)): + start_date = add_days(last_period_closing_voucher[0].period_end_date, 1) gle += get_opening_balance( "GL Entry", filters, report_type, accounting_dimensions, start_date=start_date ) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index e59938909c7..515a299504a 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -314,6 +314,7 @@ erpnext.patches.v13_0.update_docs_link erpnext.patches.v15_0.update_asset_value_for_manual_depr_entries erpnext.patches.v15_0.update_gpa_and_ndb_for_assdeprsch erpnext.patches.v14_0.create_accounting_dimensions_for_closing_balance +erpnext.patches.v14_0.set_period_start_end_date_in_pcv erpnext.patches.v14_0.update_closing_balances #14-07-2023 execute:frappe.db.set_single_value("Accounts Settings", "merge_similar_account_heads", 0) erpnext.patches.v14_0.update_reference_type_in_journal_entry_accounts diff --git a/erpnext/patches/v13_0/fix_additional_cost_in_mfg_stock_entry.py b/erpnext/patches/v13_0/fix_additional_cost_in_mfg_stock_entry.py index e305b375c7f..5609b6bb895 100644 --- a/erpnext/patches/v13_0/fix_additional_cost_in_mfg_stock_entry.py +++ b/erpnext/patches/v13_0/fix_additional_cost_in_mfg_stock_entry.py @@ -15,7 +15,7 @@ def execute(): def find_broken_stock_entries() -> list[StockEntryCode]: period_closing_date = frappe.db.get_value( - "Period Closing Voucher", {"docstatus": 1}, "posting_date", order_by="posting_date desc" + "Period Closing Voucher", {"docstatus": 1}, "period_end_date", order_by="period_end_date desc" ) stock_entries_to_patch = frappe.db.sql( diff --git a/erpnext/patches/v14_0/set_period_start_end_date_in_pcv.py b/erpnext/patches/v14_0/set_period_start_end_date_in_pcv.py new file mode 100644 index 00000000000..8020a286f69 --- /dev/null +++ b/erpnext/patches/v14_0/set_period_start_end_date_in_pcv.py @@ -0,0 +1,17 @@ +# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + + +import frappe + + +def execute(): + # nosemgrep + frappe.db.sql( + """ + UPDATE `tabPeriod Closing Voucher` + SET + period_start_date = (select year_start_date from `tabFiscal Year` where name = fiscal_year), + period_end_date = posting_date + """ + ) diff --git a/erpnext/patches/v14_0/single_to_multi_dunning.py b/erpnext/patches/v14_0/single_to_multi_dunning.py index 3b01871d437..98be0204518 100644 --- a/erpnext/patches/v14_0/single_to_multi_dunning.py +++ b/erpnext/patches/v14_0/single_to_multi_dunning.py @@ -66,7 +66,7 @@ def get_accounts_closing_date(): ) # always returns datetime.date period_closing_date = frappe.db.get_value( - "Period Closing Voucher", {"docstatus": 1}, "posting_date", order_by="posting_date desc" + "Period Closing Voucher", {"docstatus": 1}, "period_end_date", order_by="period_end_date desc" ) # Set most recent frozen/closing date as filter diff --git a/erpnext/patches/v14_0/update_closing_balances.py b/erpnext/patches/v14_0/update_closing_balances.py index cfc29c87fa1..a2670717ee9 100644 --- a/erpnext/patches/v14_0/update_closing_balances.py +++ b/erpnext/patches/v14_0/update_closing_balances.py @@ -7,67 +7,68 @@ import frappe from erpnext.accounts.doctype.account_closing_balance.account_closing_balance import ( make_closing_entries, ) +from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( + get_accounting_dimensions, +) from erpnext.accounts.utils import get_fiscal_year def execute(): frappe.db.truncate("Account Closing Balance") + gle_fields = get_gle_fields() + for company in frappe.get_all("Company", pluck="name"): i = 0 company_wise_order = {} - for pcv in frappe.db.get_all( - "Period Closing Voucher", - fields=["company", "posting_date", "name"], - filters={"docstatus": 1, "company": company}, - order_by="posting_date", - ): + for pcv in get_period_closing_vouchers(company): company_wise_order.setdefault(pcv.company, []) - if pcv.posting_date not in company_wise_order[pcv.company]: - pcv_doc = frappe.get_doc("Period Closing Voucher", pcv.name) - pcv_doc.year_start_date = get_fiscal_year( - pcv.posting_date, pcv.fiscal_year, company=pcv.company - )[1] + if pcv.period_end_date not in company_wise_order[pcv.company]: + pcv.pl_accounts_reverse_gle = get_pcv_gl_entries(pcv, gle_fields) + closing_entries = pcv.get_account_closing_balances() + if closing_entries: + make_closing_entries(closing_entries, pcv.name, pcv.company, pcv.period_end_date) - # get gl entries against pcv - gl_entries = frappe.db.get_all( - "GL Entry", filters={"voucher_no": pcv.name, "is_cancelled": 0}, fields=["*"] - ) - for entry in gl_entries: - entry["is_period_closing_voucher_entry"] = 1 - entry["closing_date"] = pcv_doc.posting_date - entry["period_closing_voucher"] = pcv_doc.name - - closing_entries = [] - - if pcv.posting_date not in company_wise_order[pcv.company]: - # get all gl entries for the year - closing_entries = frappe.db.get_all( - "GL Entry", - filters={ - "is_cancelled": 0, - "voucher_no": ["!=", pcv.name], - "posting_date": ["between", [pcv_doc.year_start_date, pcv.posting_date]], - "is_opening": "No", - "company": company, - }, - fields=["*"], - ) - - if i == 0: - # add opening entries only for the first pcv - closing_entries += frappe.db.get_all( - "GL Entry", - filters={"is_cancelled": 0, "is_opening": "Yes", "company": company}, - fields=["*"], - ) - - for entry in closing_entries: - entry["closing_date"] = pcv_doc.posting_date - entry["period_closing_voucher"] = pcv_doc.name - - entries = gl_entries + closing_entries - - make_closing_entries(entries, pcv.name, pcv.company, pcv.posting_date) - company_wise_order[pcv.company].append(pcv.posting_date) + company_wise_order[pcv.company].append(pcv.period_end_date) i += 1 + + +def get_gle_fields(): + default_diemnsion_fields = ["cost_center", "finance_book", "project"] + accounting_dimension_fields = get_accounting_dimensions() + gle_fields = [ + "name", + "posting_date", + "account", + "account_currency", + "debit", + "credit", + "debit_in_account_currency", + "credit_in_account_currency", + *default_diemnsion_fields, + *accounting_dimension_fields, + ] + + return gle_fields + + +def get_period_closing_vouchers(company): + return frappe.db.get_all( + "Period Closing Voucher", + fields=["name", "closing_account_head", "period_start_date", "period_end_date", "company"], + filters={"docstatus": 1, "company": company}, + order_by="period_end_date", + ) + + +def get_pcv_gl_entries(pcv, gle_fields): + gl_entries = frappe.db.get_all( + "GL Entry", + filters={"voucher_no": pcv.name, "account": ["!=", pcv.closing_account_head], "is_cancelled": 0}, + fields=gle_fields, + ) + for entry in gl_entries: + entry["is_period_closing_voucher_entry"] = 1 + entry["closing_date"] = pcv.period_end_date + entry["period_closing_voucher"] = pcv.name + return gl_entries diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index d203b979c61..cb006bb3e99 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -1188,18 +1188,19 @@ def make_shipment(source_name, target_doc=None): # As we are using session user details in the pickup_contact then pickup_contact_person will be session user target.pickup_contact_person = frappe.session.user - contact = frappe.db.get_value( - "Contact", source.contact_person, ["email_id", "phone", "mobile_no"], as_dict=1 - ) - delivery_contact_display = f"{source.contact_display}" - if contact: - if contact.email_id: - delivery_contact_display += "
" + contact.email_id - if contact.phone: - delivery_contact_display += "
" + contact.phone - if contact.mobile_no and not contact.phone: - delivery_contact_display += "
" + contact.mobile_no - target.delivery_contact = delivery_contact_display + if source.contact_person: + contact = frappe.db.get_value( + "Contact", source.contact_person, ["email_id", "phone", "mobile_no"], as_dict=1 + ) + delivery_contact_display = f"{source.contact_display}" + if contact: + if contact.email_id: + delivery_contact_display += "
" + contact.email_id + if contact.phone: + delivery_contact_display += "
" + contact.phone + if contact.mobile_no and not contact.phone: + delivery_contact_display += "
" + contact.mobile_no + target.delivery_contact = delivery_contact_display if source.shipping_address_name: target.delivery_address_name = source.shipping_address_name diff --git a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py index c20eadeb78d..0c81a296d5e 100644 --- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py +++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py @@ -125,7 +125,7 @@ class RepostItemValuation(Document): query = ( frappe.qb.from_(table) - .select(Max(table.posting_date)) + .select(Max(table.period_end_date)) .where((table.company == company) & (table.docstatus == 1)) ).run() diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index ae6ca4e25d0..5993580032f 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -1309,10 +1309,10 @@ class StockEntry(StockController): 3. Check FG Item and Qty against WO if present (mfg) """ production_item, wo_qty, finished_items = None, 0, [] - - wo_details = frappe.db.get_value("Work Order", self.work_order, ["production_item", "qty"]) - if wo_details: - production_item, wo_qty = wo_details + if self.work_order: + wo_details = frappe.db.get_value("Work Order", self.work_order, ["production_item", "qty"]) + if wo_details: + production_item, wo_qty = wo_details for d in self.get("items"): if d.is_finished_item: diff --git a/erpnext/stock/report/test_reports.py b/erpnext/stock/report/test_reports.py index 85337a3bf54..ad2b46b393f 100644 --- a/erpnext/stock/report/test_reports.py +++ b/erpnext/stock/report/test_reports.py @@ -1,6 +1,7 @@ import unittest import frappe +from frappe.utils.make_random import get_random from erpnext.tests.utils import ReportFilters, ReportName, execute_script_report @@ -11,7 +12,7 @@ DEFAULT_FILTERS = { } -batch = frappe.db.get_value("Batch", fieldname=["name"], as_dict=True, order_by="creation desc") +batch = get_random("Batch") REPORT_FILTER_TEST_CASES: list[tuple[ReportName, ReportFilters]] = [ ("Stock Ledger", {"_optional": True}), diff --git a/erpnext/tests/test_perf.py b/erpnext/tests/test_perf.py index fc17b1dcbda..db54ca97395 100644 --- a/erpnext/tests/test_perf.py +++ b/erpnext/tests/test_perf.py @@ -3,7 +3,7 @@ from frappe.tests.utils import FrappeTestCase INDEXED_FIELDS = { "Bin": ["item_code"], - "GL Entry": ["voucher_type", "against_voucher_type"], + "GL Entry": ["voucher_no", "posting_date", "company", "party"], "Purchase Order Item": ["item_code"], "Stock Ledger Entry": ["warehouse"], } From d392660d455f0be92cc423eca611e40f96100c07 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 23 Oct 2024 14:27:10 +0530 Subject: [PATCH 394/734] chore: release version 15.39.1 (#43800) * fix: map doc from purchase order (cherry picked from commit 60ceb91ace8fbb8ddd127a00e7eadd431d4e250c) * test: auto create purchase receipt (cherry picked from commit 59887bbc13c223739750eb27599be5e251bca91e) * fix: better implementation, handle missing purchase order (cherry picked from commit 66211dafd69ecbdc56a52d8391b6fdcf02c3154a) * perf: performance optimizations for accounting reports by refactoring account closing balance and period closing voucher (#43798) * fix: Gl Entry form cleanup * fix: Added indexes in gl entry table * perf: Refactored period closing voucher to handle large volume of gle * fix: fixes as per new period start and end date fields in PCV * perf: performance optimization for accounting reports * perf: performance optimizations for account closing balance patch * fix: test cases * fix: lenter issues - direct use of sql query * fix: test cases * fix: test cases * fix: test cases * fix: wrong fieldname * fix: test cases --------- Co-authored-by: Ninad1306 Co-authored-by: Smit Vora --- .../account_closing_balance.py | 4 +- .../accounting_period/accounting_period.py | 2 + .../accounts/doctype/gl_entry/gl_entry.json | 106 ++- erpnext/accounts/doctype/gl_entry/gl_entry.py | 3 +- .../test_payment_reconciliation.py | 6 +- .../period_closing_voucher.js | 18 + .../period_closing_voucher.json | 38 +- .../period_closing_voucher.py | 721 ++++++++++-------- .../test_period_closing_voucher.py | 8 +- .../repost_accounting_ledger.py | 4 +- .../test_repost_accounting_ledger.py | 6 +- erpnext/accounts/general_ledger.py | 34 +- .../accounts_receivable.py | 29 +- .../accounts/report/financial_statements.py | 143 ++-- .../report/trial_balance/trial_balance.py | 20 +- erpnext/patches.txt | 1 + .../fix_additional_cost_in_mfg_stock_entry.py | 2 +- .../v14_0/set_period_start_end_date_in_pcv.py | 17 + .../patches/v14_0/single_to_multi_dunning.py | 2 +- .../patches/v14_0/update_closing_balances.py | 105 +-- .../doctype/delivery_note/delivery_note.py | 25 +- .../repost_item_valuation.py | 2 +- .../stock/doctype/stock_entry/stock_entry.py | 8 +- erpnext/stock/report/test_reports.py | 3 +- .../subcontracting_receipt.py | 167 ++-- .../test_subcontracting_receipt.py | 43 +- erpnext/tests/test_perf.py | 2 +- 27 files changed, 904 insertions(+), 615 deletions(-) create mode 100644 erpnext/patches/v14_0/set_period_start_end_date_in_pcv.py diff --git a/erpnext/accounts/doctype/account_closing_balance/account_closing_balance.py b/erpnext/accounts/doctype/account_closing_balance/account_closing_balance.py index 82821e140ea..6d5e023f039 100644 --- a/erpnext/accounts/doctype/account_closing_balance/account_closing_balance.py +++ b/erpnext/accounts/doctype/account_closing_balance/account_closing_balance.py @@ -113,9 +113,9 @@ def get_previous_closing_entries(company, closing_date, accounting_dimensions): entries = [] last_period_closing_voucher = frappe.db.get_all( "Period Closing Voucher", - filters={"docstatus": 1, "company": company, "posting_date": ("<", closing_date)}, + filters={"docstatus": 1, "company": company, "period_end_date": ("<", closing_date)}, fields=["name"], - order_by="posting_date desc", + order_by="period_end_date desc", limit=1, ) diff --git a/erpnext/accounts/doctype/accounting_period/accounting_period.py b/erpnext/accounts/doctype/accounting_period/accounting_period.py index 172ef93f14d..300d216618e 100644 --- a/erpnext/accounts/doctype/accounting_period/accounting_period.py +++ b/erpnext/accounts/doctype/accounting_period/accounting_period.py @@ -101,6 +101,8 @@ def validate_accounting_period_on_doc_save(doc, method=None): date = doc.available_for_use_date elif doc.doctype == "Asset Repair": date = doc.completion_date + elif doc.doctype == "Period Closing Voucher": + date = doc.period_end_date else: date = doc.posting_date diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.json b/erpnext/accounts/doctype/gl_entry/gl_entry.json index 2d106ad8cee..c285a33f73e 100644 --- a/erpnext/accounts/doctype/gl_entry/gl_entry.json +++ b/erpnext/accounts/doctype/gl_entry/gl_entry.json @@ -6,38 +6,50 @@ "document_type": "Document", "engine": "InnoDB", "field_order": [ + "dates_section", "posting_date", "transaction_date", + "column_break_avko", + "fiscal_year", + "due_date", + "account_details_section", "account", + "account_currency", + "column_break_ifvf", + "against", "party_type", "party", - "cost_center", - "debit", - "credit", - "account_currency", - "debit_in_account_currency", - "credit_in_account_currency", - "against", + "transaction_details_section", + "voucher_type", + "voucher_no", + "voucher_subtype", + "transaction_currency", + "column_break_dpsx", "against_voucher_type", "against_voucher", - "voucher_type", - "voucher_subtype", - "voucher_no", "voucher_detail_no", + "transaction_exchange_rate", + "amounts_section", + "debit_in_account_currency", + "debit", + "debit_in_transaction_currency", + "column_break_bm1w", + "credit_in_account_currency", + "credit", + "credit_in_transaction_currency", + "dimensions_section", + "cost_center", + "column_break_lmnm", "project", - "remarks", + "more_info_section", + "finance_book", + "company", "is_opening", "is_advance", - "fiscal_year", - "company", - "finance_book", + "column_break_8abq", "to_rename", - "due_date", "is_cancelled", - "transaction_currency", - "debit_in_transaction_currency", - "credit_in_transaction_currency", - "transaction_exchange_rate" + "remarks" ], "fields": [ { @@ -285,13 +297,67 @@ "fieldname": "voucher_subtype", "fieldtype": "Small Text", "label": "Voucher Subtype" + }, + { + "fieldname": "dates_section", + "fieldtype": "Section Break", + "label": "Dates" + }, + { + "fieldname": "column_break_avko", + "fieldtype": "Column Break" + }, + { + "fieldname": "account_details_section", + "fieldtype": "Section Break", + "label": "Account Details" + }, + { + "fieldname": "column_break_ifvf", + "fieldtype": "Column Break" + }, + { + "fieldname": "transaction_details_section", + "fieldtype": "Section Break", + "label": "Transaction Details" + }, + { + "fieldname": "amounts_section", + "fieldtype": "Section Break", + "label": "Amounts" + }, + { + "fieldname": "column_break_dpsx", + "fieldtype": "Column Break" + }, + { + "fieldname": "more_info_section", + "fieldtype": "Section Break", + "label": "More Info" + }, + { + "fieldname": "column_break_bm1w", + "fieldtype": "Column Break" + }, + { + "fieldname": "dimensions_section", + "fieldtype": "Section Break", + "label": "Dimensions" + }, + { + "fieldname": "column_break_lmnm", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_8abq", + "fieldtype": "Column Break" } ], "icon": "fa fa-list", "idx": 1, "in_create": 1, "links": [], - "modified": "2024-07-02 14:31:51.496466", + "modified": "2024-08-22 13:03:39.997475", "modified_by": "Administrator", "module": "Accounts", "name": "GL Entry", diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py index d74224c4aa2..a7e7edb098d 100644 --- a/erpnext/accounts/doctype/gl_entry/gl_entry.py +++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py @@ -430,8 +430,9 @@ def update_against_account(voucher_type, voucher_no): def on_doctype_update(): - frappe.db.add_index("GL Entry", ["against_voucher_type", "against_voucher"]) frappe.db.add_index("GL Entry", ["voucher_type", "voucher_no"]) + frappe.db.add_index("GL Entry", ["posting_date", "company"]) + frappe.db.add_index("GL Entry", ["party_type", "party"]) def rename_gle_sle_docs(): diff --git a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py index 883c638398c..1b19949bb7e 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py @@ -1986,13 +1986,15 @@ def make_period_closing_voucher(company, cost_center, posting_date=None, submit= parent_account=parent_account, doctype="Account", ) + fy = get_fiscal_year(posting_date, company=company) pcv = frappe.get_doc( { "doctype": "Period Closing Voucher", "transaction_date": posting_date or today(), - "posting_date": posting_date or today(), + "period_start_date": fy[1], + "period_end_date": fy[2], "company": company, - "fiscal_year": get_fiscal_year(posting_date or today(), company=company)[0], + "fiscal_year": fy[0], "cost_center": cost_center, "closing_account_head": surplus_account, "remarks": "test", diff --git a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.js b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.js index 82d8cb37fe7..095310c7e70 100644 --- a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.js +++ b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.js @@ -19,6 +19,24 @@ frappe.ui.form.on("Period Closing Voucher", { }); }, + fiscal_year: function (frm) { + if (frm.doc.fiscal_year) { + frappe.call({ + method: "erpnext.accounts.doctype.period_closing_voucher.period_closing_voucher.get_period_start_end_date", + args: { + fiscal_year: frm.doc.fiscal_year, + company: frm.doc.company, + }, + callback: function (r) { + if (r.message) { + frm.set_value("period_start_date", r.message[0]); + frm.set_value("period_end_date", r.message[1]); + } + }, + }); + } + }, + refresh: function (frm) { if (frm.doc.docstatus > 0) { frm.add_custom_button( diff --git a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.json b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.json index 624b5f82f64..f41cff0e0d8 100644 --- a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.json +++ b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.json @@ -6,39 +6,32 @@ "engine": "InnoDB", "field_order": [ "transaction_date", - "posting_date", - "fiscal_year", - "year_start_date", - "amended_from", "company", + "fiscal_year", + "period_start_date", + "period_end_date", + "amended_from", "column_break1", "closing_account_head", - "remarks", "gle_processing_status", + "remarks", "error_message" ], "fields": [ { + "default": "Today", "fieldname": "transaction_date", "fieldtype": "Date", "label": "Transaction Date", "oldfieldname": "transaction_date", "oldfieldtype": "Date" }, - { - "fieldname": "posting_date", - "fieldtype": "Date", - "label": "Posting Date", - "oldfieldname": "posting_date", - "oldfieldtype": "Date", - "reqd": 1 - }, { "fieldname": "fiscal_year", "fieldtype": "Link", "in_list_view": 1, "in_standard_filter": 1, - "label": "Closing Fiscal Year", + "label": "Fiscal Year", "oldfieldname": "fiscal_year", "oldfieldtype": "Select", "options": "Fiscal Year", @@ -103,16 +96,25 @@ "read_only": 1 }, { - "fieldname": "year_start_date", + "fieldname": "period_end_date", "fieldtype": "Date", - "label": "Year Start Date" + "label": "Period End Date", + "reqd": 1 + }, + { + "fieldname": "period_start_date", + "fieldtype": "Date", + "label": "Period Start Date", + "oldfieldname": "posting_date", + "oldfieldtype": "Date", + "reqd": 1 } ], "icon": "fa fa-file-text", "idx": 1, "is_submittable": 1, "links": [], - "modified": "2023-09-11 20:19:11.810533", + "modified": "2024-09-15 17:22:45.291628", "modified_by": "Administrator", "module": "Accounts", "name": "Period Closing Voucher", @@ -148,7 +150,7 @@ "write": 1 } ], - "search_fields": "posting_date, fiscal_year", + "search_fields": "fiscal_year, period_start_date, period_end_date", "sort_field": "modified", "sort_order": "DESC", "states": [], diff --git a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py index dd8a5ff16c3..d6bd217650b 100644 --- a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py +++ b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py @@ -2,15 +2,20 @@ # License: GNU General Public License v3. See license.txt +import copy + import frappe from frappe import _ from frappe.query_builder.functions import Sum -from frappe.utils import add_days, flt +from frappe.utils import add_days, flt, formatdate, getdate +from erpnext.accounts.doctype.account_closing_balance.account_closing_balance import ( + make_closing_entries, +) from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( get_accounting_dimensions, ) -from erpnext.accounts.utils import get_account_currency, get_fiscal_year, validate_fiscal_year +from erpnext.accounts.utils import get_account_currency, get_fiscal_year from erpnext.controllers.accounts_controller import AccountsController @@ -29,36 +34,386 @@ class PeriodClosingVoucher(AccountsController): error_message: DF.Text | None fiscal_year: DF.Link gle_processing_status: DF.Literal["In Progress", "Completed", "Failed"] - posting_date: DF.Date + period_end_date: DF.Date + period_start_date: DF.Date remarks: DF.SmallText transaction_date: DF.Date | None - year_start_date: DF.Date | None # end: auto-generated types def validate(self): - self.validate_account_head() - self.validate_posting_date() + self.validate_start_and_end_date() + self.check_if_previous_year_closed() + self.block_if_future_closing_voucher_exists() + self.check_closing_account_type() + self.check_closing_account_currency() + + def validate_start_and_end_date(self): + self.fy_start_date, self.fy_end_date = frappe.db.get_value( + "Fiscal Year", self.fiscal_year, ["year_start_date", "year_end_date"] + ) + + prev_closed_period_end_date = get_previous_closed_period_in_current_year( + self.fiscal_year, self.company + ) + valid_start_date = ( + add_days(prev_closed_period_end_date, 1) if prev_closed_period_end_date else self.fy_start_date + ) + + if getdate(self.period_start_date) != getdate(valid_start_date): + frappe.throw(_("Period Start Date must be {0}").format(formatdate(valid_start_date))) + + if getdate(self.period_start_date) > getdate(self.period_end_date): + frappe.throw(_("Period Start Date cannot be greater than Period End Date")) + + if getdate(self.period_end_date) > getdate(self.fy_end_date): + frappe.throw(_("Period End Date cannot be greater than Fiscal Year End Date")) + + def check_if_previous_year_closed(self): + last_year_closing = add_days(self.fy_start_date, -1) + previous_fiscal_year = get_fiscal_year(last_year_closing, company=self.company, boolean=True) + if not previous_fiscal_year: + return + + previous_fiscal_year_start_date = previous_fiscal_year[0][1] + gle_exists_in_previous_year = frappe.db.exists( + "GL Entry", + { + "posting_date": ("between", [previous_fiscal_year_start_date, last_year_closing]), + "company": self.company, + "is_cancelled": 0, + }, + ) + if not gle_exists_in_previous_year: + return + + previous_fiscal_year_closed = frappe.db.exists( + "Period Closing Voucher", + { + "period_end_date": ("between", [previous_fiscal_year_start_date, last_year_closing]), + "docstatus": 1, + "company": self.company, + }, + ) + if not previous_fiscal_year_closed: + frappe.throw(_("Previous Year is not closed, please close it first")) + + def block_if_future_closing_voucher_exists(self): + future_closing_voucher = self.get_future_closing_voucher() + if future_closing_voucher and future_closing_voucher[0][0]: + action = "cancel" if self.docstatus == 2 else "create" + frappe.throw( + _( + "You cannot {0} this document because another Period Closing Entry {1} exists after {2}" + ).format(action, future_closing_voucher[0][0], self.period_end_date) + ) + + def get_future_closing_voucher(self): + return frappe.db.get_value( + "Period Closing Voucher", + {"period_end_date": (">", self.period_end_date), "docstatus": 1, "company": self.company}, + "name", + ) + + def check_closing_account_type(self): + closing_account_type = frappe.get_cached_value("Account", self.closing_account_head, "root_type") + + if closing_account_type not in ["Liability", "Equity"]: + frappe.throw( + _("Closing Account {0} must be of type Liability / Equity").format(self.closing_account_head) + ) + + def check_closing_account_currency(self): + account_currency = get_account_currency(self.closing_account_head) + company_currency = frappe.get_cached_value("Company", self.company, "default_currency") + if account_currency != company_currency: + frappe.throw(_("Currency of the Closing Account must be {0}").format(company_currency)) def on_submit(self): self.db_set("gle_processing_status", "In Progress") - get_opening_entries = False - - if not frappe.db.exists( - "Period Closing Voucher", {"company": self.company, "docstatus": 1, "name": ("!=", self.name)} - ): - get_opening_entries = True - - self.make_gl_entries(get_opening_entries=get_opening_entries) + self.make_gl_entries() def on_cancel(self): - self.validate_future_closing_vouchers() - self.db_set("gle_processing_status", "In Progress") self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry", "Payment Ledger Entry") - gle_count = frappe.db.count( + self.block_if_future_closing_voucher_exists() + self.db_set("gle_processing_status", "In Progress") + self.cancel_gl_entries() + + def make_gl_entries(self): + if self.get_gle_count_in_selected_period() > 5000: + frappe.enqueue( + process_gl_and_closing_entries, + doc=self, + timeout=1800, + ) + frappe.msgprint( + _( + "The GL Entries and closing balances will be processed in the background, it can take a few minutes." + ), + alert=True, + ) + else: + process_gl_and_closing_entries(self) + + def get_gle_count_in_selected_period(self): + return frappe.db.count( "GL Entry", - {"voucher_type": "Period Closing Voucher", "voucher_no": self.name, "is_cancelled": 0}, + { + "posting_date": ["between", [self.period_start_date, self.period_end_date]], + "company": self.company, + "is_cancelled": 0, + }, ) - if gle_count > 5000: + + def get_pcv_gl_entries(self): + self.pl_accounts_reverse_gle = [] + self.closing_account_gle = [] + + pl_account_balances = self.get_account_balances_based_on_dimensions(report_type="Profit and Loss") + for dimensions, account_balances in pl_account_balances.items(): + for acc, balances in account_balances.items(): + balance_in_company_currency = flt(balances.debit_in_account_currency) - flt( + balances.credit_in_account_currency + ) + if balance_in_company_currency and acc != "balances": + self.pl_accounts_reverse_gle.append( + self.get_gle_for_pl_account(acc, balances, dimensions) + ) + + # closing liability account + self.closing_account_gle.append( + self.get_gle_for_closing_account(account_balances["balances"], dimensions) + ) + + return self.pl_accounts_reverse_gle + self.closing_account_gle + + def get_gle_for_pl_account(self, acc, balances, dimensions): + balance_in_account_currency = flt(balances.debit_in_account_currency) - flt( + balances.credit_in_account_currency + ) + balance_in_company_currency = flt(balances.debit) - flt(balances.credit) + gl_entry = frappe._dict( + { + "company": self.company, + "posting_date": self.period_end_date, + "account": acc, + "account_currency": balances.account_currency, + "debit_in_account_currency": abs(balance_in_account_currency) + if balance_in_account_currency < 0 + else 0, + "debit": abs(balance_in_company_currency) if balance_in_company_currency < 0 else 0, + "credit_in_account_currency": abs(balance_in_account_currency) + if balance_in_account_currency > 0 + else 0, + "credit": abs(balance_in_company_currency) if balance_in_company_currency > 0 else 0, + "is_period_closing_voucher_entry": 1, + "voucher_type": "Period Closing Voucher", + "voucher_no": self.name, + "fiscal_year": self.fiscal_year, + "remarks": self.remarks, + "is_opening": "No", + } + ) + self.update_default_dimensions(gl_entry, dimensions) + return gl_entry + + def get_gle_for_closing_account(self, dimension_balance, dimensions): + balance_in_account_currency = flt(dimension_balance.balance_in_account_currency) + balance_in_company_currency = flt(dimension_balance.balance_in_company_currency) + gl_entry = frappe._dict( + { + "company": self.company, + "posting_date": self.period_end_date, + "account": self.closing_account_head, + "account_currency": frappe.db.get_value( + "Account", self.closing_account_head, "account_currency" + ), + "debit_in_account_currency": balance_in_account_currency + if balance_in_account_currency > 0 + else 0, + "debit": balance_in_company_currency if balance_in_company_currency > 0 else 0, + "credit_in_account_currency": abs(balance_in_account_currency) + if balance_in_account_currency < 0 + else 0, + "credit": abs(balance_in_company_currency) if balance_in_company_currency < 0 else 0, + "is_period_closing_voucher_entry": 1, + "voucher_type": "Period Closing Voucher", + "voucher_no": self.name, + "fiscal_year": self.fiscal_year, + "remarks": self.remarks, + "is_opening": "No", + } + ) + self.update_default_dimensions(gl_entry, dimensions) + return gl_entry + + def update_default_dimensions(self, gl_entry, dimensions): + for i, dimension in enumerate(self.accounting_dimension_fields): + gl_entry[dimension] = dimensions[i] + + def get_account_balances_based_on_dimensions(self, report_type): + """Get balance for dimension-wise pl accounts""" + self.get_accounting_dimension_fields() + acc_bal_dict = frappe._dict() + gl_entries = [] + + with frappe.db.unbuffered_cursor(): + gl_entries = self.get_gl_entries_for_current_period(report_type, as_iterator=True) + for gle in gl_entries: + acc_bal_dict = self.set_account_balance_dict(gle, acc_bal_dict) + + if report_type == "Balance Sheet" and self.is_first_period_closing_voucher(): + opening_entries = self.get_gl_entries_for_current_period(report_type, only_opening_entries=True) + for gle in opening_entries: + acc_bal_dict = self.set_account_balance_dict(gle, acc_bal_dict) + + return acc_bal_dict + + def get_accounting_dimension_fields(self): + default_dimensions = ["cost_center", "finance_book", "project"] + self.accounting_dimension_fields = default_dimensions + get_accounting_dimensions() + + def get_gl_entries_for_current_period(self, report_type, only_opening_entries=False, as_iterator=False): + date_condition = "" + if only_opening_entries: + date_condition = "is_opening = 'Yes'" + else: + date_condition = f"posting_date BETWEEN '{self.period_start_date}' AND '{self.period_end_date}' and is_opening = 'No'" + + # nosemgrep + return frappe.db.sql( + """ + SELECT + name, + posting_date, + account, + account_currency, + debit_in_account_currency, + credit_in_account_currency, + debit, + credit, + {} + FROM `tabGL Entry` + WHERE + {} + AND company = %s + AND voucher_type != 'Period Closing Voucher' + AND EXISTS(SELECT name FROM `tabAccount` WHERE name = account AND report_type = %s) + AND is_cancelled = 0 + """.format( + ", ".join(self.accounting_dimension_fields), + date_condition, + ), + (self.company, report_type), + as_dict=1, + as_iterator=as_iterator, + ) + + def set_account_balance_dict(self, gle, acc_bal_dict): + key = self.get_key(gle) + + acc_bal_dict.setdefault(key, frappe._dict()).setdefault( + gle.account, + frappe._dict( + { + "debit_in_account_currency": 0, + "credit_in_account_currency": 0, + "debit": 0, + "credit": 0, + "account_currency": gle.account_currency, + } + ), + ) + + acc_bal_dict[key][gle.account].debit_in_account_currency += flt(gle.debit_in_account_currency) + acc_bal_dict[key][gle.account].credit_in_account_currency += flt(gle.credit_in_account_currency) + acc_bal_dict[key][gle.account].debit += flt(gle.debit) + acc_bal_dict[key][gle.account].credit += flt(gle.credit) + + # dimension-wise total balances + acc_bal_dict[key].setdefault( + "balances", + frappe._dict( + { + "balance_in_account_currency": 0, + "balance_in_company_currency": 0, + } + ), + ) + + balance_in_account_currency = flt(gle.debit_in_account_currency) - flt(gle.credit_in_account_currency) + balance_in_company_currency = flt(gle.debit) - flt(gle.credit) + + acc_bal_dict[key]["balances"].balance_in_account_currency += balance_in_account_currency + acc_bal_dict[key]["balances"].balance_in_company_currency += balance_in_company_currency + + return acc_bal_dict + + def get_key(self, gle): + return tuple([gle.get(dimension) for dimension in self.accounting_dimension_fields]) + + def get_account_closing_balances(self): + pl_closing_entries = self.get_closing_entries_for_pl_accounts() + bs_closing_entries = self.get_closing_entries_for_balance_sheet_accounts() + closing_entries = pl_closing_entries + bs_closing_entries + return closing_entries + + def get_closing_entries_for_pl_accounts(self): + closing_entries = copy.deepcopy(self.pl_accounts_reverse_gle) + for d in self.pl_accounts_reverse_gle: + # reverse debit and credit + gle_copy = copy.deepcopy(d) + gle_copy.debit = d.credit + gle_copy.credit = d.debit + gle_copy.debit_in_account_currency = d.credit_in_account_currency + gle_copy.credit_in_account_currency = d.debit_in_account_currency + gle_copy.is_period_closing_voucher_entry = 0 + gle_copy.period_closing_voucher = self.name + closing_entries.append(gle_copy) + + return closing_entries + + def get_closing_entries_for_balance_sheet_accounts(self): + closing_entries = [] + balance_sheet_account_balances = self.get_account_balances_based_on_dimensions( + report_type="Balance Sheet" + ) + + for dimensions, account_balances in balance_sheet_account_balances.items(): + for acc, balances in account_balances.items(): + balance_in_company_currency = flt(balances.debit_in_account_currency) - flt( + balances.credit_in_account_currency + ) + if acc != "balances" and balance_in_company_currency: + closing_entries.append(self.get_closing_entry(acc, balances, dimensions)) + + return closing_entries + + def get_closing_entry(self, account, balances, dimensions): + closing_entry = frappe._dict( + { + "company": self.company, + "closing_date": self.period_end_date, + "period_closing_voucher": self.name, + "account": account, + "account_currency": balances.account_currency, + "debit_in_account_currency": flt(balances.debit_in_account_currency), + "debit": flt(balances.debit), + "credit_in_account_currency": flt(balances.credit_in_account_currency), + "credit": flt(balances.credit), + "is_period_closing_voucher_entry": 0, + } + ) + self.update_default_dimensions(closing_entry, dimensions) + return closing_entry + + def is_first_period_closing_voucher(self): + return not frappe.db.exists( + "Period Closing Voucher", + {"company": self.company, "docstatus": 1, "name": ("!=", self.name)}, + ) + + def cancel_gl_entries(self): + if self.get_gle_count_against_current_pcv() > 5000: frappe.enqueue( process_cancellation, voucher_type="Period Closing Voucher", @@ -73,308 +428,30 @@ class PeriodClosingVoucher(AccountsController): else: process_cancellation(voucher_type="Period Closing Voucher", voucher_no=self.name) - def validate_future_closing_vouchers(self): - if frappe.db.exists( - "Period Closing Voucher", - {"posting_date": (">", self.posting_date), "docstatus": 1, "company": self.company}, - ): - frappe.throw( - _( - "You can not cancel this Period Closing Voucher, please cancel the future Period Closing Vouchers first" - ) - ) - - def validate_account_head(self): - closing_account_type = frappe.get_cached_value("Account", self.closing_account_head, "root_type") - - if closing_account_type not in ["Liability", "Equity"]: - frappe.throw( - _("Closing Account {0} must be of type Liability / Equity").format(self.closing_account_head) - ) - - account_currency = get_account_currency(self.closing_account_head) - company_currency = frappe.get_cached_value("Company", self.company, "default_currency") - if account_currency != company_currency: - frappe.throw(_("Currency of the Closing Account must be {0}").format(company_currency)) - - def validate_posting_date(self): - validate_fiscal_year( - self.posting_date, self.fiscal_year, self.company, label=_("Posting Date"), doc=self - ) - - self.year_start_date = get_fiscal_year(self.posting_date, self.fiscal_year, company=self.company)[1] - - self.check_if_previous_year_closed() - - pcv = frappe.qb.DocType("Period Closing Voucher") - existing_entry = ( - frappe.qb.from_(pcv) - .select(pcv.name) - .where( - (pcv.posting_date >= self.posting_date) - & (pcv.fiscal_year == self.fiscal_year) - & (pcv.docstatus == 1) - & (pcv.company == self.company) - ) - .run() - ) - - if existing_entry and existing_entry[0][0]: - frappe.throw( - _("Another Period Closing Entry {0} has been made after {1}").format( - existing_entry[0][0], self.posting_date - ) - ) - - def check_if_previous_year_closed(self): - last_year_closing = add_days(self.year_start_date, -1) - previous_fiscal_year = get_fiscal_year(last_year_closing, company=self.company, boolean=True) - if not previous_fiscal_year: - return - - previous_fiscal_year_start_date = previous_fiscal_year[0][1] - if not frappe.db.exists( + def get_gle_count_against_current_pcv(self): + return frappe.db.count( "GL Entry", - { - "posting_date": ("between", [previous_fiscal_year_start_date, last_year_closing]), - "company": self.company, - "is_cancelled": 0, - }, - ): - return - - if not frappe.db.exists( - "Period Closing Voucher", - { - "posting_date": ("between", [previous_fiscal_year_start_date, last_year_closing]), - "docstatus": 1, - "company": self.company, - }, - ): - frappe.throw(_("Previous Year is not closed, please close it first")) - - def make_gl_entries(self, get_opening_entries=False): - gl_entries = self.get_gl_entries() - closing_entries = self.get_grouped_gl_entries(get_opening_entries=get_opening_entries) - if len(gl_entries + closing_entries) > 3000: - frappe.enqueue( - process_gl_and_closing_entries, - gl_entries=gl_entries, - closing_entries=closing_entries, - voucher_name=self.name, - company=self.company, - closing_date=self.posting_date, - timeout=3000, - ) - - frappe.msgprint( - _("The GL Entries will be processed in the background, it can take a few minutes."), - alert=True, - ) - else: - process_gl_and_closing_entries( - gl_entries, closing_entries, self.name, self.company, self.posting_date - ) - - def get_grouped_gl_entries(self, get_opening_entries=False): - closing_entries = [] - for acc in self.get_balances_based_on_dimensions( - group_by_account=True, for_aggregation=True, get_opening_entries=get_opening_entries - ): - closing_entries.append(self.get_closing_entries(acc)) - - return closing_entries - - def get_gl_entries(self): - gl_entries = [] - - # pl account - for acc in self.get_balances_based_on_dimensions( - group_by_account=True, report_type="Profit and Loss" - ): - if flt(acc.bal_in_company_currency): - gl_entries.append(self.get_gle_for_pl_account(acc)) - - # closing liability account - for acc in self.get_balances_based_on_dimensions( - group_by_account=False, report_type="Profit and Loss" - ): - if flt(acc.bal_in_company_currency): - gl_entries.append(self.get_gle_for_closing_account(acc)) - - return gl_entries - - def get_gle_for_pl_account(self, acc): - gl_entry = self.get_gl_dict( - { - "company": self.company, - "closing_date": self.posting_date, - "account": acc.account, - "cost_center": acc.cost_center, - "finance_book": acc.finance_book, - "account_currency": acc.account_currency, - "debit_in_account_currency": abs(flt(acc.bal_in_account_currency)) - if flt(acc.bal_in_account_currency) < 0 - else 0, - "debit": abs(flt(acc.bal_in_company_currency)) if flt(acc.bal_in_company_currency) < 0 else 0, - "credit_in_account_currency": abs(flt(acc.bal_in_account_currency)) - if flt(acc.bal_in_account_currency) > 0 - else 0, - "credit": abs(flt(acc.bal_in_company_currency)) - if flt(acc.bal_in_company_currency) > 0 - else 0, - "is_period_closing_voucher_entry": 1, - }, - item=acc, - ) - self.update_default_dimensions(gl_entry, acc) - return gl_entry - - def get_gle_for_closing_account(self, acc): - gl_entry = self.get_gl_dict( - { - "company": self.company, - "closing_date": self.posting_date, - "account": self.closing_account_head, - "cost_center": acc.cost_center, - "finance_book": acc.finance_book, - "account_currency": acc.account_currency, - "debit_in_account_currency": abs(flt(acc.bal_in_account_currency)) - if flt(acc.bal_in_account_currency) > 0 - else 0, - "debit": abs(flt(acc.bal_in_company_currency)) if flt(acc.bal_in_company_currency) > 0 else 0, - "credit_in_account_currency": abs(flt(acc.bal_in_account_currency)) - if flt(acc.bal_in_account_currency) < 0 - else 0, - "credit": abs(flt(acc.bal_in_company_currency)) - if flt(acc.bal_in_company_currency) < 0 - else 0, - "is_period_closing_voucher_entry": 1, - }, - item=acc, - ) - self.update_default_dimensions(gl_entry, acc) - return gl_entry - - def get_closing_entries(self, acc): - closing_entry = self.get_gl_dict( - { - "company": self.company, - "closing_date": self.posting_date, - "period_closing_voucher": self.name, - "account": acc.account, - "cost_center": acc.cost_center, - "finance_book": acc.finance_book, - "account_currency": acc.account_currency, - "debit_in_account_currency": flt(acc.debit_in_account_currency), - "debit": flt(acc.debit), - "credit_in_account_currency": flt(acc.credit_in_account_currency), - "credit": flt(acc.credit), - }, - item=acc, + {"voucher_type": "Period Closing Voucher", "voucher_no": self.name, "is_cancelled": 0}, ) - for dimension in self.accounting_dimensions: - closing_entry.update({dimension: acc.get(dimension)}) - return closing_entry - - def update_default_dimensions(self, gl_entry, acc): - if not self.accounting_dimensions: - self.accounting_dimensions = get_accounting_dimensions() - - for dimension in self.accounting_dimensions: - gl_entry.update({dimension: acc.get(dimension)}) - - def get_balances_based_on_dimensions( - self, group_by_account=False, report_type=None, for_aggregation=False, get_opening_entries=False - ): - """Get balance for dimension-wise pl accounts""" - - qb_dimension_fields = ["cost_center", "finance_book", "project"] - - self.accounting_dimensions = get_accounting_dimensions() - for dimension in self.accounting_dimensions: - qb_dimension_fields.append(dimension) - - if group_by_account: - qb_dimension_fields.append("account") - - account_filters = { - "company": self.company, - "is_group": 0, - } - - if report_type: - account_filters.update({"report_type": report_type}) - - accounts = frappe.get_all("Account", filters=account_filters, pluck="name") - - gl_entry = frappe.qb.DocType("GL Entry") - query = frappe.qb.from_(gl_entry).select(gl_entry.account, gl_entry.account_currency) - - if not for_aggregation: - query = query.select( - (Sum(gl_entry.debit_in_account_currency) - Sum(gl_entry.credit_in_account_currency)).as_( - "bal_in_account_currency" - ), - (Sum(gl_entry.debit) - Sum(gl_entry.credit)).as_("bal_in_company_currency"), - ) - else: - query = query.select( - (Sum(gl_entry.debit_in_account_currency)).as_("debit_in_account_currency"), - (Sum(gl_entry.credit_in_account_currency)).as_("credit_in_account_currency"), - (Sum(gl_entry.debit)).as_("debit"), - (Sum(gl_entry.credit)).as_("credit"), - ) - - for dimension in qb_dimension_fields: - query = query.select(gl_entry[dimension]) - - query = query.where( - (gl_entry.company == self.company) - & (gl_entry.is_cancelled == 0) - & (gl_entry.account.isin(accounts)) - ) - - if get_opening_entries: - query = query.where( - ( # noqa: UP034 - (gl_entry.posting_date.between(self.get("year_start_date"), self.posting_date)) - | (gl_entry.is_opening == "Yes") - ) - ) - else: - query = query.where( - gl_entry.posting_date.between(self.get("year_start_date"), self.posting_date) - & gl_entry.is_opening - == "No" - ) - - if for_aggregation: - query = query.where(gl_entry.voucher_type != "Period Closing Voucher") - - for dimension in qb_dimension_fields: - query = query.groupby(gl_entry[dimension]) - - return query.run(as_dict=1) - - -def process_gl_and_closing_entries(gl_entries, closing_entries, voucher_name, company, closing_date): - from erpnext.accounts.doctype.account_closing_balance.account_closing_balance import ( - make_closing_entries, - ) +def process_gl_and_closing_entries(doc): from erpnext.accounts.general_ledger import make_gl_entries try: + gl_entries = doc.get_pcv_gl_entries() if gl_entries: make_gl_entries(gl_entries, merge_entries=False) - make_closing_entries(gl_entries + closing_entries, voucher_name, company, closing_date) - frappe.db.set_value("Period Closing Voucher", voucher_name, "gle_processing_status", "Completed") + + closing_entries = doc.get_account_closing_balances() + if closing_entries: + make_closing_entries(closing_entries, doc.name, doc.company, doc.period_end_date) + + frappe.db.set_value(doc.doctype, doc.name, "gle_processing_status", "Completed") except Exception as e: frappe.db.rollback() frappe.log_error(e) - frappe.db.set_value("Period Closing Voucher", voucher_name, "gle_processing_status", "Failed") + frappe.db.set_value(doc.doctype, doc.name, "gle_processing_status", "Failed") def process_cancellation(voucher_type, voucher_no): @@ -395,3 +472,29 @@ def delete_closing_entries(voucher_no): frappe.qb.from_(closing_balance).delete().where( closing_balance.period_closing_voucher == voucher_no ).run() + + +@frappe.whitelist() +def get_period_start_end_date(fiscal_year, company): + fy_start_date, fy_end_date = frappe.db.get_value( + "Fiscal Year", fiscal_year, ["year_start_date", "year_end_date"] + ) + prev_closed_period_end_date = get_previous_closed_period_in_current_year(fiscal_year, company) + period_start_date = ( + add_days(prev_closed_period_end_date, 1) if prev_closed_period_end_date else fy_start_date + ) + return period_start_date, fy_end_date + + +def get_previous_closed_period_in_current_year(fiscal_year, company): + prev_closed_period_end_date = frappe.db.get_value( + "Period Closing Voucher", + filters={ + "company": company, + "fiscal_year": fiscal_year, + "docstatus": 1, + }, + fieldname=["period_end_date"], + order_by="period_end_date desc", + ) + return prev_closed_period_end_date diff --git a/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py b/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py index 1bd565e1b36..e9d65f7f856 100644 --- a/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py +++ b/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py @@ -317,16 +317,18 @@ class TestPeriodClosingVoucher(unittest.TestCase): repost_doc.posting_date = today() repost_doc.save() - def make_period_closing_voucher(self, posting_date=None, submit=True): + def make_period_closing_voucher(self, posting_date, submit=True): surplus_account = create_account() cost_center = create_cost_center("Test Cost Center 1") + fy = get_fiscal_year(posting_date, company="Test PCV Company") pcv = frappe.get_doc( { "doctype": "Period Closing Voucher", "transaction_date": posting_date or today(), - "posting_date": posting_date or today(), + "period_start_date": fy[1], + "period_end_date": fy[2], "company": "Test PCV Company", - "fiscal_year": get_fiscal_year(today(), company="Test PCV Company")[0], + "fiscal_year": fy[0], "cost_center": cost_center, "closing_account_head": surplus_account, "remarks": "test", 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 8c8ba633df0..f37e542dd89 100644 --- a/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py +++ b/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py @@ -46,8 +46,8 @@ class RepostAccountingLedger(Document): frappe.db.get_all( "Period Closing Voucher", filters={"company": self.company}, - order_by="posting_date desc", - pluck="posting_date", + order_by="period_end_date desc", + pluck="period_end_date", limit=1, ) or None 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 f631ef437d6..9f906bb7647 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 @@ -129,13 +129,15 @@ class TestRepostAccountingLedger(AccountsTestMixin, FrappeTestCase): cost_center=self.cost_center, rate=100, ) + fy = get_fiscal_year(today(), company=self.company) pcv = frappe.get_doc( { "doctype": "Period Closing Voucher", "transaction_date": today(), - "posting_date": today(), + "period_start_date": fy[1], + "period_end_date": today(), "company": self.company, - "fiscal_year": get_fiscal_year(today(), company=self.company)[0], + "fiscal_year": fy[0], "cost_center": self.cost_center, "closing_account_head": self.retained_earnings, "remarks": "test", diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py index ad8cc97e101..856e2b96af0 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -37,13 +37,14 @@ def make_gl_entries( validate_disabled_accounts(gl_map) gl_map = process_gl_map(gl_map, merge_entries) if gl_map and len(gl_map) > 1: - create_payment_ledger_entry( - gl_map, - cancel=0, - adv_adj=adv_adj, - update_outstanding=update_outstanding, - from_repost=from_repost, - ) + if gl_map[0].voucher_type != "Period Closing Voucher": + create_payment_ledger_entry( + gl_map, + cancel=0, + adv_adj=adv_adj, + update_outstanding=update_outstanding, + 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 elif gl_map: @@ -116,17 +117,16 @@ def get_accounting_dimensions_for_offsetting_entry(gl_map, company): def validate_disabled_accounts(gl_map): accounts = [d.account for d in gl_map if d.account] - Account = frappe.qb.DocType("Account") + disabled_accounts = frappe.get_all( + "Account", + filters={"disabled": 1, "is_group": 0, "company": gl_map[0].company}, + fields=["name"], + ) - disabled_accounts = ( - frappe.qb.from_(Account) - .where(Account.name.isin(accounts) & Account.disabled == 1) - .select(Account.name, Account.disabled) - ).run(as_dict=True) - - if disabled_accounts: + used_disabled_accounts = set(accounts).intersection(set([d.name for d in disabled_accounts])) + if used_disabled_accounts: account_list = "
" - account_list += ", ".join([frappe.bold(d.name) for d in disabled_accounts]) + account_list += ", ".join([frappe.bold(d) for d in used_disabled_accounts]) frappe.throw( _("Cannot create accounting entries against disabled accounts: {0}").format(account_list), title=_("Disabled Account Selected"), @@ -708,7 +708,7 @@ def validate_against_pcv(is_opening, posting_date, company): ) last_pcv_date = frappe.db.get_value( - "Period Closing Voucher", {"docstatus": 1, "company": company}, "max(posting_date)" + "Period Closing Voucher", {"docstatus": 1, "company": company}, "max(period_end_date)" ) if last_pcv_date and getdate(posting_date) <= getdate(last_pcv_date): diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index 43100c81285..8d4a8579ae3 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -385,6 +385,7 @@ class ReceivablePayableReport: self.delivery_notes = frappe._dict() # delivery note link inside sales invoice + # nosemgrep si_against_dn = frappe.db.sql( """ select parent, delivery_note @@ -400,6 +401,7 @@ class ReceivablePayableReport: if d.delivery_note: self.delivery_notes.setdefault(d.parent, set()).add(d.delivery_note) + # nosemgrep dn_against_si = frappe.db.sql( """ select distinct parent, against_sales_invoice @@ -417,13 +419,16 @@ class ReceivablePayableReport: def get_invoice_details(self): self.invoice_details = frappe._dict() if self.account_type == "Receivable": + # nosemgrep si_list = frappe.db.sql( """ select name, due_date, po_no from `tabSales Invoice` where posting_date <= %s + and company = %s + and docstatus = 1 """, - self.filters.report_date, + (self.filters.report_date, self.filters.company), as_dict=1, ) for d in si_list: @@ -431,6 +436,7 @@ class ReceivablePayableReport: # Get Sales Team if self.filters.show_sales_person: + # nosemgrep sales_team = frappe.db.sql( """ select parent, sales_person @@ -445,25 +451,33 @@ class ReceivablePayableReport: ) if self.account_type == "Payable": + # nosemgrep for pi in frappe.db.sql( """ select name, due_date, bill_no, bill_date from `tabPurchase Invoice` - where posting_date <= %s + where + posting_date <= %s + and company = %s + and docstatus = 1 """, - self.filters.report_date, + (self.filters.report_date, self.filters.company), as_dict=1, ): self.invoice_details.setdefault(pi.name, pi) # Invoices booked via Journal Entries + # nosemgrep journal_entries = frappe.db.sql( """ select name, due_date, bill_no, bill_date from `tabJournal Entry` - where posting_date <= %s + where + posting_date <= %s + and company = %s + and docstatus = 1 """, - self.filters.report_date, + (self.filters.report_date, self.filters.company), as_dict=1, ) @@ -472,6 +486,8 @@ class ReceivablePayableReport: self.invoice_details.setdefault(je.name, je) def set_party_details(self, row): + if not row.party: + return # customer / supplier name party_details = self.get_party_details(row.party) or {} row.update(party_details) @@ -496,6 +512,7 @@ class ReceivablePayableReport: def get_payment_terms(self, row): # build payment_terms for row + # nosemgrep payment_terms_details = frappe.db.sql( f""" select @@ -708,6 +725,7 @@ class ReceivablePayableReport: def get_return_entries(self): doctype = "Sales Invoice" if self.account_type == "Receivable" else "Purchase Invoice" filters = { + "posting_date": ("<=", self.filters.report_date), "is_return": 1, "docstatus": 1, "company": self.filters.company, @@ -815,6 +833,7 @@ class ReceivablePayableReport: if self.filters.get("sales_person"): lft, rgt = frappe.db.get_value("Sales Person", self.filters.get("sales_person"), ["lft", "rgt"]) + # nosemgrep records = frappe.db.sql( """ select distinct parent, parenttype diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py index 6d7635979bb..c233f3c7e2b 100644 --- a/erpnext/accounts/report/financial_statements.py +++ b/erpnext/accounts/report/financial_statements.py @@ -9,6 +9,7 @@ import re import frappe from frappe import _ from frappe.utils import add_days, add_months, cint, cstr, flt, formatdate, get_first_day, getdate +from pypika.terms import ExistsCriterion from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( get_accounting_dimensions, @@ -181,12 +182,12 @@ def get_data( company, period_list[0]["year_start_date"] if only_current_fiscal_year else None, period_list[-1]["to_date"], - root.lft, - root.rgt, filters, gl_entries_by_account, - ignore_closing_entries=ignore_closing_entries, + root.lft, + root.rgt, root_type=root_type, + ignore_closing_entries=ignore_closing_entries, ) calculate_values( @@ -419,93 +420,78 @@ def set_gl_entries_by_account( company, from_date, to_date, - root_lft, - root_rgt, filters, gl_entries_by_account, + root_lft=None, + root_rgt=None, + root_type=None, ignore_closing_entries=False, ignore_opening_entries=False, - root_type=None, ): """Returns a dict like { "account": [gl entries], ... }""" gl_entries = [] - account_filters = { - "company": company, - "is_group": 0, - "lft": (">=", root_lft), - "rgt": ("<=", root_rgt), - } - - if root_type: - account_filters.update( - { - "root_type": root_type, - } + # For balance sheet + ignore_closing_balances = frappe.db.get_single_value( + "Accounts Settings", "ignore_account_closing_balance" + ) + if not from_date and not ignore_closing_balances: + last_period_closing_voucher = frappe.db.get_all( + "Period Closing Voucher", + filters={ + "docstatus": 1, + "company": filters.company, + "period_end_date": ("<", filters["period_start_date"]), + }, + fields=["period_end_date", "name"], + order_by="period_end_date desc", + limit=1, ) + if last_period_closing_voucher: + gl_entries += get_accounting_entries( + "Account Closing Balance", + from_date, + to_date, + filters, + root_lft, + root_rgt, + root_type, + ignore_closing_entries, + last_period_closing_voucher[0].name, + ) + from_date = add_days(last_period_closing_voucher[0].period_end_date, 1) + ignore_opening_entries = True - accounts_list = frappe.db.get_all( - "Account", - filters=account_filters, - pluck="name", + gl_entries += get_accounting_entries( + "GL Entry", + from_date, + to_date, + filters, + root_lft, + root_rgt, + root_type, + ignore_closing_entries, + ignore_opening_entries=ignore_opening_entries, ) - if accounts_list: - # For balance sheet - ignore_closing_balances = frappe.db.get_single_value( - "Accounts Settings", "ignore_account_closing_balance" - ) - if not from_date and not ignore_closing_balances: - last_period_closing_voucher = frappe.db.get_all( - "Period Closing Voucher", - filters={ - "docstatus": 1, - "company": filters.company, - "posting_date": ("<", filters["period_start_date"]), - }, - fields=["posting_date", "name"], - order_by="posting_date desc", - limit=1, - ) - if last_period_closing_voucher: - gl_entries += get_accounting_entries( - "Account Closing Balance", - from_date, - to_date, - accounts_list, - filters, - ignore_closing_entries, - last_period_closing_voucher[0].name, - ) - from_date = add_days(last_period_closing_voucher[0].posting_date, 1) - ignore_opening_entries = True + if filters and filters.get("presentation_currency"): + convert_to_presentation_currency(gl_entries, get_currency(filters)) - gl_entries += get_accounting_entries( - "GL Entry", - from_date, - to_date, - accounts_list, - filters, - ignore_closing_entries, - ignore_opening_entries=ignore_opening_entries, - ) + for entry in gl_entries: + gl_entries_by_account.setdefault(entry.account, []).append(entry) - if filters and filters.get("presentation_currency"): - convert_to_presentation_currency(gl_entries, get_currency(filters)) - - for entry in gl_entries: - gl_entries_by_account.setdefault(entry.account, []).append(entry) - - return gl_entries_by_account + return gl_entries_by_account def get_accounting_entries( doctype, from_date, to_date, - accounts, filters, - ignore_closing_entries, + root_lft=None, + root_rgt=None, + root_type=None, + ignore_closing_entries=None, period_closing_voucher=None, ignore_opening_entries=False, ): @@ -535,13 +521,30 @@ def get_accounting_entries( query = query.where(gl_entry.period_closing_voucher == period_closing_voucher) query = apply_additional_conditions(doctype, query, from_date, ignore_closing_entries, filters) - query = query.where(gl_entry.account.isin(accounts)) + + if (root_lft and root_rgt) or root_type: + account_filter_query = get_account_filter_query(root_lft, root_rgt, root_type, gl_entry) + query = query.where(ExistsCriterion(account_filter_query)) entries = query.run(as_dict=True) return entries +def get_account_filter_query(root_lft, root_rgt, root_type, gl_entry): + acc = frappe.qb.DocType("Account") + exists_query = ( + frappe.qb.from_(acc).select(acc.name).where(acc.name == gl_entry.account).where(acc.is_group == 0) + ) + if root_lft and root_rgt: + exists_query = exists_query.where(acc.lft >= root_lft).where(acc.rgt <= root_rgt) + + if root_type: + exists_query = exists_query.where(acc.root_type == root_type) + + return exists_query + + def apply_additional_conditions(doctype, query, from_date, ignore_closing_entries, filters): gl_entry = frappe.qb.DocType(doctype) accounting_dimensions = get_accounting_dimensions(as_list=False) diff --git a/erpnext/accounts/report/trial_balance/trial_balance.py b/erpnext/accounts/report/trial_balance/trial_balance.py index f216ecea15a..8ca850f301e 100644 --- a/erpnext/accounts/report/trial_balance/trial_balance.py +++ b/erpnext/accounts/report/trial_balance/trial_balance.py @@ -94,12 +94,6 @@ def get_data(filters): accounts, accounts_by_name, parent_children_map = filter_accounts(accounts) - min_lft, max_rgt = frappe.db.sql( - """select min(lft), max(rgt) from `tabAccount` - where company=%s""", - (filters.company,), - )[0] - gl_entries_by_account = {} opening_balances = get_opening_balances(filters) @@ -112,10 +106,10 @@ def get_data(filters): filters.company, filters.from_date, filters.to_date, - min_lft, - max_rgt, filters, gl_entries_by_account, + root_lft=None, + root_rgt=None, ignore_closing_entries=not flt(filters.with_period_closing_entry_for_current_period), ignore_opening_entries=True, ) @@ -150,9 +144,9 @@ def get_rootwise_opening_balances(filters, report_type): if not ignore_closing_balances: last_period_closing_voucher = frappe.db.get_all( "Period Closing Voucher", - filters={"docstatus": 1, "company": filters.company, "posting_date": ("<", filters.from_date)}, - fields=["posting_date", "name"], - order_by="posting_date desc", + filters={"docstatus": 1, "company": filters.company, "period_end_date": ("<", filters.from_date)}, + fields=["period_end_date", "name"], + order_by="period_end_date desc", limit=1, ) @@ -168,8 +162,8 @@ def get_rootwise_opening_balances(filters, report_type): ) # Report getting generate from the mid of a fiscal year - if getdate(last_period_closing_voucher[0].posting_date) < getdate(add_days(filters.from_date, -1)): - start_date = add_days(last_period_closing_voucher[0].posting_date, 1) + if getdate(last_period_closing_voucher[0].period_end_date) < getdate(add_days(filters.from_date, -1)): + start_date = add_days(last_period_closing_voucher[0].period_end_date, 1) gle += get_opening_balance( "GL Entry", filters, report_type, accounting_dimensions, start_date=start_date ) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index e59938909c7..515a299504a 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -314,6 +314,7 @@ erpnext.patches.v13_0.update_docs_link erpnext.patches.v15_0.update_asset_value_for_manual_depr_entries erpnext.patches.v15_0.update_gpa_and_ndb_for_assdeprsch erpnext.patches.v14_0.create_accounting_dimensions_for_closing_balance +erpnext.patches.v14_0.set_period_start_end_date_in_pcv erpnext.patches.v14_0.update_closing_balances #14-07-2023 execute:frappe.db.set_single_value("Accounts Settings", "merge_similar_account_heads", 0) erpnext.patches.v14_0.update_reference_type_in_journal_entry_accounts diff --git a/erpnext/patches/v13_0/fix_additional_cost_in_mfg_stock_entry.py b/erpnext/patches/v13_0/fix_additional_cost_in_mfg_stock_entry.py index e305b375c7f..5609b6bb895 100644 --- a/erpnext/patches/v13_0/fix_additional_cost_in_mfg_stock_entry.py +++ b/erpnext/patches/v13_0/fix_additional_cost_in_mfg_stock_entry.py @@ -15,7 +15,7 @@ def execute(): def find_broken_stock_entries() -> list[StockEntryCode]: period_closing_date = frappe.db.get_value( - "Period Closing Voucher", {"docstatus": 1}, "posting_date", order_by="posting_date desc" + "Period Closing Voucher", {"docstatus": 1}, "period_end_date", order_by="period_end_date desc" ) stock_entries_to_patch = frappe.db.sql( diff --git a/erpnext/patches/v14_0/set_period_start_end_date_in_pcv.py b/erpnext/patches/v14_0/set_period_start_end_date_in_pcv.py new file mode 100644 index 00000000000..8020a286f69 --- /dev/null +++ b/erpnext/patches/v14_0/set_period_start_end_date_in_pcv.py @@ -0,0 +1,17 @@ +# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + + +import frappe + + +def execute(): + # nosemgrep + frappe.db.sql( + """ + UPDATE `tabPeriod Closing Voucher` + SET + period_start_date = (select year_start_date from `tabFiscal Year` where name = fiscal_year), + period_end_date = posting_date + """ + ) diff --git a/erpnext/patches/v14_0/single_to_multi_dunning.py b/erpnext/patches/v14_0/single_to_multi_dunning.py index 3b01871d437..98be0204518 100644 --- a/erpnext/patches/v14_0/single_to_multi_dunning.py +++ b/erpnext/patches/v14_0/single_to_multi_dunning.py @@ -66,7 +66,7 @@ def get_accounts_closing_date(): ) # always returns datetime.date period_closing_date = frappe.db.get_value( - "Period Closing Voucher", {"docstatus": 1}, "posting_date", order_by="posting_date desc" + "Period Closing Voucher", {"docstatus": 1}, "period_end_date", order_by="period_end_date desc" ) # Set most recent frozen/closing date as filter diff --git a/erpnext/patches/v14_0/update_closing_balances.py b/erpnext/patches/v14_0/update_closing_balances.py index cfc29c87fa1..a2670717ee9 100644 --- a/erpnext/patches/v14_0/update_closing_balances.py +++ b/erpnext/patches/v14_0/update_closing_balances.py @@ -7,67 +7,68 @@ import frappe from erpnext.accounts.doctype.account_closing_balance.account_closing_balance import ( make_closing_entries, ) +from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( + get_accounting_dimensions, +) from erpnext.accounts.utils import get_fiscal_year def execute(): frappe.db.truncate("Account Closing Balance") + gle_fields = get_gle_fields() + for company in frappe.get_all("Company", pluck="name"): i = 0 company_wise_order = {} - for pcv in frappe.db.get_all( - "Period Closing Voucher", - fields=["company", "posting_date", "name"], - filters={"docstatus": 1, "company": company}, - order_by="posting_date", - ): + for pcv in get_period_closing_vouchers(company): company_wise_order.setdefault(pcv.company, []) - if pcv.posting_date not in company_wise_order[pcv.company]: - pcv_doc = frappe.get_doc("Period Closing Voucher", pcv.name) - pcv_doc.year_start_date = get_fiscal_year( - pcv.posting_date, pcv.fiscal_year, company=pcv.company - )[1] + if pcv.period_end_date not in company_wise_order[pcv.company]: + pcv.pl_accounts_reverse_gle = get_pcv_gl_entries(pcv, gle_fields) + closing_entries = pcv.get_account_closing_balances() + if closing_entries: + make_closing_entries(closing_entries, pcv.name, pcv.company, pcv.period_end_date) - # get gl entries against pcv - gl_entries = frappe.db.get_all( - "GL Entry", filters={"voucher_no": pcv.name, "is_cancelled": 0}, fields=["*"] - ) - for entry in gl_entries: - entry["is_period_closing_voucher_entry"] = 1 - entry["closing_date"] = pcv_doc.posting_date - entry["period_closing_voucher"] = pcv_doc.name - - closing_entries = [] - - if pcv.posting_date not in company_wise_order[pcv.company]: - # get all gl entries for the year - closing_entries = frappe.db.get_all( - "GL Entry", - filters={ - "is_cancelled": 0, - "voucher_no": ["!=", pcv.name], - "posting_date": ["between", [pcv_doc.year_start_date, pcv.posting_date]], - "is_opening": "No", - "company": company, - }, - fields=["*"], - ) - - if i == 0: - # add opening entries only for the first pcv - closing_entries += frappe.db.get_all( - "GL Entry", - filters={"is_cancelled": 0, "is_opening": "Yes", "company": company}, - fields=["*"], - ) - - for entry in closing_entries: - entry["closing_date"] = pcv_doc.posting_date - entry["period_closing_voucher"] = pcv_doc.name - - entries = gl_entries + closing_entries - - make_closing_entries(entries, pcv.name, pcv.company, pcv.posting_date) - company_wise_order[pcv.company].append(pcv.posting_date) + company_wise_order[pcv.company].append(pcv.period_end_date) i += 1 + + +def get_gle_fields(): + default_diemnsion_fields = ["cost_center", "finance_book", "project"] + accounting_dimension_fields = get_accounting_dimensions() + gle_fields = [ + "name", + "posting_date", + "account", + "account_currency", + "debit", + "credit", + "debit_in_account_currency", + "credit_in_account_currency", + *default_diemnsion_fields, + *accounting_dimension_fields, + ] + + return gle_fields + + +def get_period_closing_vouchers(company): + return frappe.db.get_all( + "Period Closing Voucher", + fields=["name", "closing_account_head", "period_start_date", "period_end_date", "company"], + filters={"docstatus": 1, "company": company}, + order_by="period_end_date", + ) + + +def get_pcv_gl_entries(pcv, gle_fields): + gl_entries = frappe.db.get_all( + "GL Entry", + filters={"voucher_no": pcv.name, "account": ["!=", pcv.closing_account_head], "is_cancelled": 0}, + fields=gle_fields, + ) + for entry in gl_entries: + entry["is_period_closing_voucher_entry"] = 1 + entry["closing_date"] = pcv.period_end_date + entry["period_closing_voucher"] = pcv.name + return gl_entries diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index d203b979c61..cb006bb3e99 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -1188,18 +1188,19 @@ def make_shipment(source_name, target_doc=None): # As we are using session user details in the pickup_contact then pickup_contact_person will be session user target.pickup_contact_person = frappe.session.user - contact = frappe.db.get_value( - "Contact", source.contact_person, ["email_id", "phone", "mobile_no"], as_dict=1 - ) - delivery_contact_display = f"{source.contact_display}" - if contact: - if contact.email_id: - delivery_contact_display += "
" + contact.email_id - if contact.phone: - delivery_contact_display += "
" + contact.phone - if contact.mobile_no and not contact.phone: - delivery_contact_display += "
" + contact.mobile_no - target.delivery_contact = delivery_contact_display + if source.contact_person: + contact = frappe.db.get_value( + "Contact", source.contact_person, ["email_id", "phone", "mobile_no"], as_dict=1 + ) + delivery_contact_display = f"{source.contact_display}" + if contact: + if contact.email_id: + delivery_contact_display += "
" + contact.email_id + if contact.phone: + delivery_contact_display += "
" + contact.phone + if contact.mobile_no and not contact.phone: + delivery_contact_display += "
" + contact.mobile_no + target.delivery_contact = delivery_contact_display if source.shipping_address_name: target.delivery_address_name = source.shipping_address_name diff --git a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py index c20eadeb78d..0c81a296d5e 100644 --- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py +++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py @@ -125,7 +125,7 @@ class RepostItemValuation(Document): query = ( frappe.qb.from_(table) - .select(Max(table.posting_date)) + .select(Max(table.period_end_date)) .where((table.company == company) & (table.docstatus == 1)) ).run() diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index ae6ca4e25d0..5993580032f 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -1309,10 +1309,10 @@ class StockEntry(StockController): 3. Check FG Item and Qty against WO if present (mfg) """ production_item, wo_qty, finished_items = None, 0, [] - - wo_details = frappe.db.get_value("Work Order", self.work_order, ["production_item", "qty"]) - if wo_details: - production_item, wo_qty = wo_details + if self.work_order: + wo_details = frappe.db.get_value("Work Order", self.work_order, ["production_item", "qty"]) + if wo_details: + production_item, wo_qty = wo_details for d in self.get("items"): if d.is_finished_item: diff --git a/erpnext/stock/report/test_reports.py b/erpnext/stock/report/test_reports.py index 85337a3bf54..ad2b46b393f 100644 --- a/erpnext/stock/report/test_reports.py +++ b/erpnext/stock/report/test_reports.py @@ -1,6 +1,7 @@ import unittest import frappe +from frappe.utils.make_random import get_random from erpnext.tests.utils import ReportFilters, ReportName, execute_script_report @@ -11,7 +12,7 @@ DEFAULT_FILTERS = { } -batch = frappe.db.get_value("Batch", fieldname=["name"], as_dict=True, order_by="creation desc") +batch = get_random("Batch") REPORT_FILTER_TEST_CASES: list[tuple[ReportName, ReportFilters]] = [ ("Stock Ledger", {"_optional": True}), diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py index db912514988..50ce270c166 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py @@ -687,86 +687,103 @@ def make_purchase_receipt(source_name, target_doc=None, save=False, submit=False else: source_doc = source_name - if not source_doc.is_return: - if not target_doc: - target_doc = frappe.new_doc("Purchase Receipt") - target_doc.is_subcontracted = 1 - target_doc.is_old_subcontracting_flow = 0 + if source_doc.is_return: + return - target_doc = get_mapped_doc( - "Subcontracting Receipt", - source_doc.name, - { - "Subcontracting Receipt": { - "doctype": "Purchase Receipt", - "field_map": { - "posting_date": "posting_date", - "posting_time": "posting_time", - "name": "subcontracting_receipt", - "supplier_warehouse": "supplier_warehouse", - }, - "field_no_map": ["total_qty", "total"], - }, - }, - target_doc, - ignore_child_tables=True, + po_sr_item_dict = {} + po_name = None + for item in source_doc.items: + if not item.purchase_order: + continue + + if not po_name: + po_name = item.purchase_order + + po_sr_item_dict[item.purchase_order_item] = { + "qty": flt(item.qty), + "rejected_qty": flt(item.rejected_qty), + "warehouse": item.warehouse, + "rejected_warehouse": item.rejected_warehouse, + "subcontracting_receipt_item": item.name, + } + + if not po_name: + frappe.throw( + _("Purchase Order Item reference is missing in Subcontracting Receipt {0}").format( + source_doc.name + ) ) - target_doc.currency = frappe.get_cached_value("Company", target_doc.company, "default_currency") + def update_item(obj, target, source_parent): + sr_item_details = po_sr_item_dict.get(obj.name) + ratio = flt(obj.qty) / flt(obj.fg_item_qty) - po_items_details = {} - for item in source_doc.items: - if item.purchase_order and item.purchase_order_item: - if item.purchase_order not in po_items_details: - po_doc = frappe.get_doc("Purchase Order", item.purchase_order) - po_items_details[item.purchase_order] = { - po_item.name: po_item for po_item in po_doc.items - } + target.update( + { + "qty": ratio * sr_item_details["qty"], + "rejected_qty": ratio * sr_item_details["rejected_qty"], + "warehouse": sr_item_details["warehouse"], + "rejected_warehouse": sr_item_details["rejected_warehouse"], + "subcontracting_receipt_item": sr_item_details["subcontracting_receipt_item"], + } + ) - if po_item := po_items_details[item.purchase_order].get(item.purchase_order_item): - conversion_factor = flt(po_item.qty) / flt(po_item.fg_item_qty) - item_row = { - "item_code": po_item.item_code, - "item_name": po_item.item_name, - "conversion_factor": conversion_factor, - "qty": flt(item.qty) * conversion_factor, - "rejected_qty": flt(item.rejected_qty) * conversion_factor, - "uom": po_item.uom, - "rate": po_item.rate, - "warehouse": item.warehouse, - "rejected_warehouse": item.rejected_warehouse, - "purchase_order": item.purchase_order, - "purchase_order_item": item.purchase_order_item, - "subcontracting_receipt_item": item.name, - "project": po_item.project, - } - target_doc.append("items", item_row) + def post_process(source, target): + target.set_missing_values() + target.update( + { + "posting_date": source_doc.posting_date, + "posting_time": source_doc.posting_time, + "subcontracting_receipt": source_doc.name, + "supplier_warehouse": source_doc.supplier_warehouse, + "is_subcontracted": 1, + "is_old_subcontracting_flow": 0, + "currency": frappe.get_cached_value("Company", target.company, "default_currency"), + } + ) - if not target_doc.items: - frappe.throw( - _("Purchase Order Item reference is missing in Subcontracting Receipt {0}").format( - source_doc.name - ) + target_doc = get_mapped_doc( + "Purchase Order", + po_name, + { + "Purchase Order": { + "doctype": "Purchase Receipt", + "field_map": {"supplier_warehouse": "supplier_warehouse"}, + "validation": { + "docstatus": ["=", 1], + }, + }, + "Purchase Order Item": { + "doctype": "Purchase Receipt Item", + "field_map": { + "name": "purchase_order_item", + "parent": "purchase_order", + "bom": "bom", + }, + "postprocess": update_item, + "condition": lambda doc: doc.name in po_sr_item_dict, + }, + "Purchase Taxes and Charges": {"doctype": "Purchase Taxes and Charges", "reset_value": True}, + }, + postprocess=post_process, + ) + + if (save or submit) and frappe.has_permission(target_doc.doctype, "create"): + target_doc.save() + + if submit and frappe.has_permission(target_doc.doctype, "submit", target_doc): + try: + target_doc.submit() + except Exception as e: + target_doc.add_comment("Comment", _("Submit Action Failed") + "

" + str(e)) + + if notify: + frappe.msgprint( + _("Purchase Receipt {0} created.").format( + get_link_to_form(target_doc.doctype, target_doc.name) + ), + indicator="green", + alert=True, ) - target_doc.set_missing_values() - - if (save or submit) and frappe.has_permission(target_doc.doctype, "create"): - target_doc.save() - - if submit and frappe.has_permission(target_doc.doctype, "submit", target_doc): - try: - target_doc.submit() - except Exception as e: - target_doc.add_comment("Comment", _("Submit Action Failed") + "

" + str(e)) - - if notify: - frappe.msgprint( - _("Purchase Receipt {0} created.").format( - get_link_to_form(target_doc.doctype, target_doc.name) - ), - indicator="green", - alert=True, - ) - - return target_doc + return target_doc diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py index 27ad7dbebdf..e0fa7923ef9 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py @@ -1075,18 +1075,42 @@ class TestSubcontractingReceipt(FrappeTestCase): @change_settings("Buying Settings", {"auto_create_purchase_receipt": 1}) def test_auto_create_purchase_receipt(self): + from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order + fg_item = "Subcontracted Item SA1" service_items = [ { "warehouse": "_Test Warehouse - _TC", "item_code": "Subcontracted Service Item 1", - "qty": 5, + "qty": 10, "rate": 100, "fg_item": fg_item, "fg_item_qty": 5, }, ] - sco = get_subcontracting_order(service_items=service_items) + + po = create_purchase_order( + rm_items=service_items, + is_subcontracted=1, + supplier_warehouse="_Test Warehouse 1 - _TC", + do_not_submit=True, + ) + po.append( + "taxes", + { + "account_head": "_Test Account Excise Duty - _TC", + "charge_type": "On Net Total", + "cost_center": "_Test Cost Center - _TC", + "description": "Excise Duty", + "doctype": "Purchase Taxes and Charges", + "rate": 10, + }, + ) + po.save() + po.submit() + + sco = get_subcontracting_order(po_name=po.name) + rm_items = get_rm_items(sco.supplied_items) itemwise_details = make_stock_in_entry(rm_items=rm_items) make_stock_transfer_entry( @@ -1094,11 +1118,24 @@ class TestSubcontractingReceipt(FrappeTestCase): rm_items=rm_items, itemwise_details=copy.deepcopy(itemwise_details), ) + scr = make_subcontracting_receipt(sco.name) + scr.items[0].qty = 3 scr.save() scr.submit() - self.assertTrue(frappe.db.get_value("Purchase Receipt", {"subcontracting_receipt": scr.name})) + pr_details = frappe.get_all( + "Purchase Receipt", + filters={"subcontracting_receipt": scr.name}, + fields=["name", "total_taxes_and_charges"], + ) + + self.assertTrue(pr_details) + + pr_qty = frappe.db.get_value("Purchase Receipt Item", {"parent": pr_details[0]["name"]}, "qty") + self.assertEqual(pr_qty, 6) + + self.assertEqual(pr_details[0]["total_taxes_and_charges"], 60) def test_use_serial_batch_fields_for_subcontracting_receipt(self): fg_item = make_item( diff --git a/erpnext/tests/test_perf.py b/erpnext/tests/test_perf.py index fc17b1dcbda..db54ca97395 100644 --- a/erpnext/tests/test_perf.py +++ b/erpnext/tests/test_perf.py @@ -3,7 +3,7 @@ from frappe.tests.utils import FrappeTestCase INDEXED_FIELDS = { "Bin": ["item_code"], - "GL Entry": ["voucher_type", "against_voucher_type"], + "GL Entry": ["voucher_no", "posting_date", "company", "party"], "Purchase Order Item": ["item_code"], "Stock Ledger Entry": ["warehouse"], } From 33fa1e45ad27e41654335e65ca579a3099c5519d Mon Sep 17 00:00:00 2001 From: IamSaiyyamChhetri Date: Mon, 21 Oct 2024 12:28:27 +0530 Subject: [PATCH 395/734] fix: add company filter for project - In Project dt Sales Order field - In Sales Order dt Project field (cherry picked from commit 9909d760a529192e0fe26eeae03ed6c7e1b5375b) --- erpnext/projects/doctype/project/project.js | 1 + erpnext/public/js/utils/sales_common.js | 1 + 2 files changed, 2 insertions(+) diff --git a/erpnext/projects/doctype/project/project.js b/erpnext/projects/doctype/project/project.js index d03ab786cc1..643e3b21782 100644 --- a/erpnext/projects/doctype/project/project.js +++ b/erpnext/projects/doctype/project/project.js @@ -45,6 +45,7 @@ frappe.ui.form.on("Project", { frm.set_query("sales_order", function () { var filters = { project: ["in", frm.doc.__islocal ? [""] : [frm.doc.name, ""]], + company: frm.doc.company, }; if (frm.doc.customer) { diff --git a/erpnext/public/js/utils/sales_common.js b/erpnext/public/js/utils/sales_common.js index e503fb3339c..ac4ecf52e63 100644 --- a/erpnext/public/js/utils/sales_common.js +++ b/erpnext/public/js/utils/sales_common.js @@ -29,6 +29,7 @@ erpnext.sales_common = { query: "erpnext.controllers.queries.get_project_name", filters: { customer: doc.customer, + company: doc.company, }, }; }); From 2fb441763a74bd4c5233e77f39611ee0c95b7efa Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 23 Oct 2024 17:08:52 +0530 Subject: [PATCH 396/734] fix: consider gle based on balances in company currency (#43805) --- .../doctype/period_closing_voucher/period_closing_voucher.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py index d6bd217650b..99b42089501 100644 --- a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py +++ b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py @@ -380,9 +380,7 @@ class PeriodClosingVoucher(AccountsController): for dimensions, account_balances in balance_sheet_account_balances.items(): for acc, balances in account_balances.items(): - balance_in_company_currency = flt(balances.debit_in_account_currency) - flt( - balances.credit_in_account_currency - ) + balance_in_company_currency = flt(balances.debit) - flt(balances.credit) if acc != "balances" and balance_in_company_currency: closing_entries.append(self.get_closing_entry(acc, balances, dimensions)) From 1c4eef2ef6fc34c3e2cf95306e15f2073d228bb6 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 23 Oct 2024 17:41:41 +0530 Subject: [PATCH 397/734] fix: consider gle based on balances in company currency (copy #43805) (#43809) fix: consider gle based on balances in company currency (#43805) (cherry picked from commit 2fb441763a74bd4c5233e77f39611ee0c95b7efa) Co-authored-by: Nabin Hait --- .../doctype/period_closing_voucher/period_closing_voucher.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py index d6bd217650b..99b42089501 100644 --- a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py +++ b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py @@ -380,9 +380,7 @@ class PeriodClosingVoucher(AccountsController): for dimensions, account_balances in balance_sheet_account_balances.items(): for acc, balances in account_balances.items(): - balance_in_company_currency = flt(balances.debit_in_account_currency) - flt( - balances.credit_in_account_currency - ) + balance_in_company_currency = flt(balances.debit) - flt(balances.credit) if acc != "balances" and balance_in_company_currency: closing_entries.append(self.get_closing_entry(acc, balances, dimensions)) From 188645bfd6cadfc05d506667e9a256245450e696 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 23 Oct 2024 12:12:56 +0000 Subject: [PATCH 398/734] chore(release): Bumped to Version 15.39.2 ## [15.39.2](https://github.com/frappe/erpnext/compare/v15.39.1...v15.39.2) (2024-10-23) ### Bug Fixes * consider gle based on balances in company currency (copy [#43805](https://github.com/frappe/erpnext/issues/43805)) ([#43809](https://github.com/frappe/erpnext/issues/43809)) ([1c4eef2](https://github.com/frappe/erpnext/commit/1c4eef2ef6fc34c3e2cf95306e15f2073d228bb6)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index a5b8609d1d9..fea98a77374 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.39.0" +__version__ = "15.39.2" def get_default_company(user=None): From cf25f4c579295c6e0d28226a125e5681a86ecfa9 Mon Sep 17 00:00:00 2001 From: Raffael Meyer <14891507+barredterra@users.noreply.github.com> Date: Thu, 24 Oct 2024 00:52:42 +0200 Subject: [PATCH 399/734] chore(Timesheet): add type hints (#43793) (cherry picked from commit fe1e1b12c3e5ceb8389408f88ee25dcd9c91d0e6) --- .../projects/doctype/timesheet/timesheet.py | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/erpnext/projects/doctype/timesheet/timesheet.py b/erpnext/projects/doctype/timesheet/timesheet.py index fb15b507efb..70494e9e966 100644 --- a/erpnext/projects/doctype/timesheet/timesheet.py +++ b/erpnext/projects/doctype/timesheet/timesheet.py @@ -22,6 +22,46 @@ class OverWorkLoggedError(frappe.ValidationError): class Timesheet(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from frappe.types import DF + + from erpnext.projects.doctype.timesheet_detail.timesheet_detail import TimesheetDetail + + amended_from: DF.Link | None + base_total_billable_amount: DF.Currency + base_total_billed_amount: DF.Currency + base_total_costing_amount: DF.Currency + company: DF.Link | None + currency: DF.Link | None + customer: DF.Link | None + department: DF.Link | None + employee: DF.Link | None + employee_name: DF.Data | None + end_date: DF.Date | None + exchange_rate: DF.Float + naming_series: DF.Literal["TS-.YYYY.-"] + note: DF.TextEditor | None + parent_project: DF.Link | None + per_billed: DF.Percent + sales_invoice: DF.Link | None + start_date: DF.Date | None + status: DF.Literal["Draft", "Submitted", "Billed", "Payslip", "Completed", "Cancelled"] + time_logs: DF.Table[TimesheetDetail] + title: DF.Data | None + total_billable_amount: DF.Currency + total_billable_hours: DF.Float + total_billed_amount: DF.Currency + total_billed_hours: DF.Float + total_costing_amount: DF.Currency + total_hours: DF.Float + user: DF.Link | None + # end: auto-generated types + def validate(self): self.set_status() self.validate_dates() From bf4fb53575be594e2e13e0c53cd4218fe4215ac5 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 24 Oct 2024 13:37:57 +0530 Subject: [PATCH 400/734] fix: Unnecessary validation for non deferred sales invoices (#43816) fix: Unnecessary validation for non deferred sales invoices (#43816) (cherry picked from commit af472054f6a8f77aca8811e3f575893ba7ea1c00) Co-authored-by: Deepesh Garg --- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index c680bd46130..ebdbb01fdc2 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -297,8 +297,11 @@ class SalesInvoice(SellingController): self.update_current_stock() self.validate_delivery_note() + is_deferred_invoice = any(d.get("enable_deferred_revenue") for d in self.get("items")) + # validate service stop date to lie in between start and end date - validate_service_stop_date(self) + if is_deferred_invoice: + validate_service_stop_date(self) if not self.is_opening: self.is_opening = "No" From a79bc4d35abbfb7597d7c01dbdb4f4cb798eeff6 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 24 Oct 2024 13:37:57 +0530 Subject: [PATCH 401/734] fix: Unnecessary validation for non deferred sales invoices (#43816) fix: Unnecessary validation for non deferred sales invoices (#43816) (cherry picked from commit af472054f6a8f77aca8811e3f575893ba7ea1c00) Co-authored-by: Deepesh Garg (cherry picked from commit bf4fb53575be594e2e13e0c53cd4218fe4215ac5) --- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index c680bd46130..ebdbb01fdc2 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -297,8 +297,11 @@ class SalesInvoice(SellingController): self.update_current_stock() self.validate_delivery_note() + is_deferred_invoice = any(d.get("enable_deferred_revenue") for d in self.get("items")) + # validate service stop date to lie in between start and end date - validate_service_stop_date(self) + if is_deferred_invoice: + validate_service_stop_date(self) if not self.is_opening: self.is_opening = "No" From f48ce906582cee31272300414b4387da56d47307 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Thu, 24 Oct 2024 08:22:31 +0000 Subject: [PATCH 402/734] chore(release): Bumped to Version 15.39.3 ## [15.39.3](https://github.com/frappe/erpnext/compare/v15.39.2...v15.39.3) (2024-10-24) ### Bug Fixes * Unnecessary validation for non deferred sales invoices ([#43816](https://github.com/frappe/erpnext/issues/43816)) ([a79bc4d](https://github.com/frappe/erpnext/commit/a79bc4d35abbfb7597d7c01dbdb4f4cb798eeff6)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index fea98a77374..645337290fb 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.39.2" +__version__ = "15.39.3" def get_default_company(user=None): From 81297ce1683ec413a8f9b2fe153124452b7d1883 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 25 Oct 2024 13:30:28 +0530 Subject: [PATCH 403/734] fix: set bill_no before `against_voucher` gets concatenated (cherry picked from commit 7bade7f1fe08370d655e12929b2376814dac018a) --- .../accounts/report/general_ledger/general_ledger.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py index 1c1d10c087b..04df22c9c9c 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.py +++ b/erpnext/accounts/report/general_ledger/general_ledger.py @@ -348,10 +348,18 @@ def get_accounts_with_children(accounts): return frappe.qb.from_(doctype).select(doctype.name).where(Criterion.any(conditions)).run(pluck=True) +def set_bill_no(gl_entries): + inv_details = get_supplier_invoice_details() + for gl in gl_entries: + gl["bill_no"] = inv_details.get(gl.get("against_voucher"), "") + + def get_data_with_opening_closing(filters, account_details, accounting_dimensions, gl_entries): data = [] totals_dict = get_totals_dict() + set_bill_no(gl_entries) + gle_map = initialize_gle_map(gl_entries, filters, totals_dict) totals, entries = get_accountwise_gle(filters, accounting_dimensions, gl_entries, gle_map, totals_dict) @@ -539,7 +547,6 @@ def get_account_type_map(company): def get_result_as_list(data, filters): balance, _balance_in_account_currency = 0, 0 - inv_details = get_supplier_invoice_details() for d in data: if not d.get("posting_date"): @@ -549,7 +556,6 @@ def get_result_as_list(data, filters): d["balance"] = balance d["account_currency"] = filters.account_currency - d["bill_no"] = inv_details.get(d.get("against_voucher"), "") return data From 20478b632f82bd9d44bed28e37359b2dd2c7d989 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 25 Oct 2024 20:21:01 +0530 Subject: [PATCH 404/734] fix: basic rate not editable in Stock Entry Detail (backport #43837) (#43838) fix: basic rate not editable in Stock Entry Detail (#43837) (cherry picked from commit 5a967bc868566eae06ce44df894cbb33bc98f869) Co-authored-by: rohitwaghchaure --- erpnext/stock/doctype/stock_entry/stock_entry.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js index 3e82ef51653..ea47ffd79ea 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry.js @@ -830,6 +830,15 @@ frappe.ui.form.on("Stock Entry", { }); frappe.ui.form.on("Stock Entry Detail", { + set_basic_rate_manually(frm, cdt, cdn) { + let row = locals[cdt][cdn]; + frm.fields_dict.items.grid.update_docfield_property( + "basic_rate", + "read_only", + row?.set_basic_rate_manually ? 0 : 1 + ); + }, + qty(frm, cdt, cdn) { frm.events.set_basic_rate(frm, cdt, cdn); }, From 07aaef2af2413ff6c39b85514659d572096a6c73 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sat, 26 Oct 2024 19:43:23 +0200 Subject: [PATCH 405/734] fix(RFQ): make strings translatable (backport #43843) (#43848) Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com> fix(RFQ): make strings translatable (#43843) --- .../request_for_quotation/request_for_quotation.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js index b8689d29a56..d7c2c3f24b1 100644 --- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js +++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js @@ -146,8 +146,8 @@ frappe.ui.form.on("Request for Quotation", { return; } }, - "Download PDF for Supplier", - "Download" + __("Download PDF for Supplier"), + __("Download") ); }, __("Tools") @@ -272,9 +272,10 @@ frappe.ui.form.on("Request for Quotation", { }); }; - dialog.fields_dict.note.$wrapper - .append(`

This is a preview of the email to be sent. A PDF of the document will - automatically be attached with the email.

`); + const msg = __( + "This is a preview of the email to be sent. A PDF of the document will automatically be attached with the email." + ); + dialog.fields_dict.note.$wrapper.append(`

${msg}

`); dialog.show(); }, From 11dd1968c78ea1608edd8850854f7dcd4f998648 Mon Sep 17 00:00:00 2001 From: Raffael Meyer <14891507+barredterra@users.noreply.github.com> Date: Sat, 26 Oct 2024 20:25:30 +0200 Subject: [PATCH 406/734] fix: backport translations from develop (#43849) --- erpnext/translations/de.csv | 182 +++ erpnext/translations/es.csv | 2525 ++++++++++++++++++++++++++++ erpnext/translations/fa.csv | 2532 ++++++++++++++++++++++++++++ erpnext/translations/sv.csv | 3083 +++++++++++++++++++++++++++++++++++ 4 files changed, 8322 insertions(+) diff --git a/erpnext/translations/de.csv b/erpnext/translations/de.csv index ed05b8d6695..9ef1d4bc63a 100644 --- a/erpnext/translations/de.csv +++ b/erpnext/translations/de.csv @@ -11929,3 +11929,185 @@ Website Theme,Webseiten-Thema, Workflow Action,Workflow-Aktion, Workflow State,Workflow-Status, {0} is not running. Cannot trigger events for this Document,{0} lƤuft nicht. Ereignisse für dieses Dokument kƶnnen nicht ausgelƶst werden, +"""SN-01::10"" for ""SN-01"" to ""SN-10""","""SN-01::10"" für ""SN-01"" bis ""SN-10""", +"Masters & Reports","Stammdaten & Berichte", +"Quick Access","Schnellzugriff", +"Reports & Masters","Berichte & Stammdaten", +"Reports & Masters","Berichte & Stammdaten", +"Settings","Einstellungen", +"Shortcuts","Verknüpfungen", +"Your Shortcuts + + + + + + ","Ihre Verknüpfungen + + + + + + ", +"Your Shortcuts","Ihre Verknüpfungen", +Grand Total: {0},Gesamtsumme:{0}, +Outstanding Amount: {0},Ausstehender Betrag: {0}, +Against Customer Order {0},Gegen Kundenauftrag {0}, +Against Supplier Invoice {0},Gegen Lieferantenrechnung {0}, +Ageing Range,Alterungsbereich, +Allocate Payment Request,Zahlungsanfrage zuweisen, +Amount in party's bank account currency,Betrag in der WƤhrung des Bankkontos des Beteiligten, +Amount in transaction currency,Betrag in TransaktionswƤhrung, +BIN Qty,BIN Menge, +BOM and Production,Stückliste und Produktion, +Balance Stock Value,Bestandswert, +Base Cost Per Unit,Grundkosten pro Einheit, +Base Rate,Basispreis, +Base Tax Withholding Net Total,Basis-Steuereinbehalt-Nettosumme, +Base Total,Basis-Summe, +Base Total Billable Amount,Basis Gesamter abrechenbarer Betrag, +Batch Expiry Date,Ablaufdatum der Charge, +Bill for Rejected Quantity in Purchase Invoice,Rechnung für abgelehnte Menge in der Eingangsrechnung, +Calculate daily depreciation using total days in depreciation period,TƤgliche Abschreibung anhand der Gesamttage im Abschreibungszeitraum berechnen, +Call Schedule Row {0}: To time slot should always be ahead of From time slot.,Anrufplanzeile {0}: Das Zeitfenster Bis sollte immer vor dem Zeitfenster Von liegen., +Cannot find a default warehouse for item {0}. Please set one in the Item Master or in Stock Settings.,Es wurde kein Standardlager für den Artikel {0} gefunden. Bitte legen Sie eines im Artikelstamm oder in den Lagereinstellungen fest., +Cannot {0} from {2} without any negative outstanding invoice,Kann nicht {0} von {2} ohne negative ausstehende Rechnung, +Cheques and Deposits Incorrectly cleared,Falsch verrechnete Schecks und Einzahlungen, +Columns are not according to template. Please compare the uploaded file with standard template,Die Spalten stimmen nicht mit der Vorlage überein. Bitte vergleichen Sie die hochgeladene Datei mit der Standardvorlage, +Company is mandatory,Unternehmen ist obligatorisch, +Completion Date can not be before Failure Date. Please adjust the dates accordingly.,Das Fertigstellungsdatum kann nicht vor dem Ausfalldatum liegen. Bitte passen Sie die Daten entsprechend an., +Consumed Stock Items or Consumed Asset Items are mandatory for creating new composite asset,Verbrauchte Lagerartikel oder verbrauchte Vermƶgensgegenstand-Artikel sind für die Erstellung obligatorisch, +Convert to Item Based Reposting,Umstellung auf artikelbasiertes Umbuchen, +Create Workstation,Arbeitsplatz erstellen, +Creating Journal Entries...,JournaleintrƤge erstellen..., +Creating Purchase Invoices ...,Eingangsrechnungen erstellen ..., +Creating Sales Invoices ...,Ausgangsrechnungen erstellen ..., +Currency Exchange Settings Result,WƤhrungsumtauscheinstellungen Ergebnis, +Deal Owner,Besitzer des Deals, +Decapitalized,Dekapitalisiert, +Dependant SLE Voucher Detail No,Unterhaltsberechtigter SLE Beleg Detail Nr., +Disassemble,Demontage, +Documents: {0} have deferred revenue/expense enabled for them. Cannot repost.,Dokumente: {0} hat vertagte Einnahmen/Ausgaben aktiviert. Kann nicht erneut posten., +Don't Reserve Sales Order Qty on Sales Return,Menge des Auftrags nicht bei der Rücksendung reservieren, +Enter Manually,Manuell eingeben, +Failed to post depreciation entries,Abschreibungsbuchungen fehlgeschlagen, +Filters missing,Filter fehlen, +"For Return Invoices with Stock effect, '0' qty Items are not allowed. Following rows are affected: {0}",Bei Retourenrechnungen mit Lagereffekt sind Artikel mit einer Menge von '0' nicht zulƤssig. Folgende Zeilen sind betroffen: {0}, +"For the item {0}, the quantity should be {1} according to the BOM {2}.",Für den Artikel {0} sollte die Menge gemäß Stückliste {2} {1} betragen., +"For the {0}, no stock is available for the return in the warehouse {1}.",Für {0} ist im Lager {1} kein Bestand für die Retoure verfügbar., +Force-Fetch Subscription Updates,Abonnement-Updates erzwingen, +From Date is mandatory,Von-Datum ist obligatorisch, +From Prospect,Von Interessenten, +Gross Purchase Amount Too Low: {0} cannot be depreciated over {1} cycles with a frequency of {2} depreciations.,Bruttokaufbetrag zu niedrig: {0} kann nicht über {1} Zyklen mit einer HƤufigkeit von {2} Abschreibungen abgeschrieben werden., +If enabled then system won't apply the pricing rule on the delivery note which will be create from the pick list,"Falls aktiviert, wird das System die Preisregel nicht auf den Lieferschein anwenden, der aus der Pickliste erstellt wird", +Impairment,Wertminderung, +Include Closed Orders,Geschlossene AuftrƤge/Bestellungen einbeziehen, +Invalid Allocated Amount,Ungültiger zugewiesener Betrag, +Invalid Amount,Ungültiger Betrag, +Is Standard,Ist Standard, +Item {0} does not exist.,Artikel {0} existiert nicht., +Items {0} do not exist in the Item master.,Artikel {0} sind nicht im Artikelstamm vorhanden., +Journal entries have been created,JournaleintrƤge wurden erstellt, +Net total calculation precision loss,PrƤzisionsverlust bei Berechnung der Nettosumme, +Only Deduct Tax On Excess Amount ,Nur den überschüssigen Betrag versteuern , +Payment Ledger Entry,Zahlungsbucheintrag, +Payment Requests cannot be created against: {0},Zahlungsanforderungen kƶnnen nicht erstellt werden für: {0}, +Period Closing Entry For Current Period,Periodenabschlussbuchung für aktuelle Periode, +Provisional Account,VorlƤufiges Konto, +Rate of Stock UOM,Einzelpreis der Lager-ME, +Raw Materials Consumption ,Rohstoffverbrauch , +Recalculating Purchase Cost against this Project...,Neuberechnung der Anschaffungskosten für dieses Projekt..., +Reference DocType,Referenz DocType, +Round Tax Amount Row-wise,Steuerbetrag zeilenweise runden, +Rounding gain/loss Entry for Stock Transfer,Rundungsgewinn/-verlustbuchung für Umlagerung, +Row #{0}: Only {1} available to reserve for the Item {2},Zeile #{0}: Nur {1} zur Reservierung für den Artikel {2} verfügbar, +Row #{}: The original Invoice {} of return invoice {} is not consolidated.,Zeile #{}: Die ursprüngliche Rechnung {} der Rechnungskorrektur {} ist nicht konsolidiert., +Row {0}: Item {1} must be a subcontracted item.,Zeile {0}: Artikel {1} muss ein an Dritte vergebener Artikel sein., +Row {0}: Please provide a valid Delivery Note Item or Packed Item reference.,Zeile {0}: Bitte geben Sie einen gültigen Lieferschein Artikel oder verpackten Artikel an., +Row {0}: Target Warehouse is mandatory for internal transfers,Zeile {0}: Ziellager ist für interne Transfers obligatorisch, +Row({0}): Outstanding Amount cannot be greater than actual Outstanding Amount {1} in {2},Zeile({0}): Ausstehender Betrag kann nicht größer sein als der tatsƤchliche ausstehende Betrag {1} in {2}, +Rows with Same Account heads will be merged on Ledger,Zeilen mit denselben Konten werden im Hauptbuch zusammengefasst, +Select Warehouses to get Stock for Materials Planning,"WƤhlen Sie Lager aus, um BestƤnde für die Materialplanung zu erhalten", +Select an invoice to load summary data,"WƤhlen Sie eine Rechnung aus, um die Zusammenfassung zu laden", +Serial / Batch Bundle,Serien- / Chargenbündel, +Serial / Batch Bundle Missing,Serien- / Chargenbündel fehlt, +Serial No Range,Seriennummernbereich, +Serial and Batch Details,Serien- und Chargendetails, +Sets 'Accepted Warehouse' in each row of the Items table.,Legt in jeder Zeile der Artikeltabelle das Annahmelager fest., +Sets 'Rejected Warehouse' in each row of the Items table.,Legt in jeder Zeile der Artikeltabelle das ā€žAusschusslagerā€œ fest., +Shelf Life in Days,Haltbarkeitsdauer in Tagen, +Show Disabled Warehouses,Deaktivierte Lager anzeigen, +Show GL Balance,Hauptbuchsaldo anzeigen, +Show Pay Button in Purchase Order Portal,SchaltflƤche ā€žBezahlenā€œ im Bestellportal anzeigen, +Show Taxes as Table in Print,Steuern als Tabelle im Druck anzeigen, +Show net values in opening and closing columns,Nettowerte in Erƶffnungs- und Abschlussspalten anzeigen, +Show with upcoming revenue/expense,Mit kommenden Einnahmen/Ausgaben anzeigen, +Something went wrong please try again,"Etwas ist schief gelaufen, bitte versuchen Sie es erneut", +South Africa VAT Account,Südafrika Mehrwertsteuer-Konto, +South Africa VAT Settings,Südafrika Mehrwertsteuer-Einstellungen, +Start Date should be lower than End Date,Das Startdatum muss vor dem Enddatum liegen, +Start Deletion,Lƶschen starten, +Start Time can't be greater than or equal to End Time for {0}.,Die Startzeit kann nicht größer oder gleich der Endzeit für {0} sein., +Started a background job to create {1} {0},Hintergrundjob zum Erstellen von {1} {0} gestartet, +Status set to rejected as there are one or more rejected readings.,"Der Status wurde auf abgelehnt gesetzt, da es einen oder mehrere abgelehnte Messwerte gibt.", +Stock Consumed During Repair,WƤhrend der Reparatur verbrauchter Bestand, +Stock Consumption Details,Details zum Lagerverbrauch, +Stock Planning,Bestandsplanung, +Stock Reservation,Bestandsreservierung, +Stock Reservation Entries Cancelled,Bestandsreservierungen storniert, +Stock Reservation Entries Created,Bestandsreservierungen erstellt, +Stock Reservation Entry,Bestandsreservierungseintrag, +Stock Reservation Entry cannot be updated as it has been delivered.,"Der Bestandsreservierungseintrag kann nicht aktualisiert werden, da er bereits geliefert wurde.", +"Stock Reservation Entry created against a Pick List cannot be updated. If you need to make changes, we recommend canceling the existing entry and creating a new one.","Ein anhand einer Kommissionierliste erstellter Bestandsreservierungseintrag kann nicht aktualisiert werden. Wenn Sie Ƅnderungen vornehmen müssen, empfehlen wir, den vorhandenen Eintrag zu stornieren und einen neuen zu erstellen.", +Stock Reservation can only be created against {0}.,Bestandsreservierungen kƶnnen nur gegen {0} erstellt werden., +Stock Reserved Qty (in Stock UOM),Reservierter Bestand (in Lager-ME), +Stock Unreservation,Aufhebung der Bestandsreservierung, +Stock/Accounts can not be frozen as processing of backdated entries is going on. Please try again later.,"LagerbestƤnde/Konten kƶnnen nicht eingefroren werden, da die Verarbeitung rückwirkender EintrƤge noch lƤuft. Bitte versuchen Sie es spƤter erneut.", +Supplied Item,Gelieferter Artikel, +Supplies subject to the reverse charge provision,"Lieferungen, die der Reverse-Charge-Regelung unterliegen", +Task {0} depends on Task {1}. Please add Task {1} to the Tasks list.,Aufgabe {0} hƤngt von Aufgabe {1} ab. Bitte fügen Sie Aufgabe {1} zur Aufgabenliste hinzu., +Tax Amount will be rounded on a row(items) level,Der Steuerbetrag wird auf (Artikel-)Zeilenebene gerundet, +Tax Refunds provided to Tourists under the Tax Refunds for Tourists Scheme,Steuererstattungen für Touristen im Rahmen der Steuererstattungsregelung für Touristen, +"Tax detail table fetched from item master as a string and stored in this field. +Used for Taxes and Charges","Steuerdetailtabelle, die aus dem Artikelstamm als Zeichenfolge abgerufen und in diesem Feld gespeichert wird. +Wird für Steuern und Gebühren verwendet", +"The Payment Request {0} is already paid, cannot process payment twice","Die Auszahlungsanforderung {0} ist bereits bezahlt, die Zahlung kann nicht zweimal verarbeitet werden", +The Serial No at Row #{0}: {1} is not available in warehouse {2}.,Die Seriennummer in Zeile #{0}: {1} ist im Lager {2} nicht verfügbar., +The Work Order is mandatory for Disassembly Order,Der Arbeitsauftrag ist obligatorisch für den Demontageauftrag, +The allocated amount is greater than the outstanding amount of Payment Request {0},Der zugewiesene Betrag ist größer als der ausstehende Betrag der Zahlungsanforderung {0}, +The field {0} in row {1} is not set,Das Feld {0} in der Zeile {1} ist nicht gesetzt, +The following invalid Pricing Rules are deleted:,Die folgenden ungültigen Preisregeln werden gelƶscht:, +The original invoice should be consolidated before or along with the return invoice.,Die Originalrechnung sollte vor oder zusammen mit der Erstattungsrechnung konsolidiert werden., +"The sync has started in the background, please check the {0} list for new records.",Die Synchronisierung wurde im Hintergrund gestartet. Bitte überprüfen Sie die Liste {0} auf neue DatensƤtze., +"The users with this Role are allowed to create/modify a stock transaction, even though the transaction is frozen.","Die Benutzer mit dieser Rolle dürfen eine Lagerbewegungen erstellen/Ƥndern, auch wenn die Transaktion eingefroren ist.", +There are no active Fiscal Years for which Demo Data can be generated.,"Es gibt keine aktiven GeschƤftsjahre, für die Demodaten erstellt werden kƶnnen.", +There were issues unlinking payment entry {0}.,Es gab Probleme bei der Aufhebung der Verknüpfung der Zahlung {0}., +"This is enabled by default. If you want to plan materials for sub-assemblies of the Item you're manufacturing leave this enabled. If you plan and manufacture the sub-assemblies separately, you can disable this checkbox.","Diese Option ist standardmäßig aktiviert. Wenn Sie Materialien für Unterbaugruppen des Artikels, den Sie herstellen, planen mƶchten, lassen Sie diese Option aktiviert. Wenn Sie die Unterbaugruppen separat planen und herstellen, kƶnnen Sie dieses KontrollkƤstchen deaktivieren.", +To Date is mandatory,Bis Datum ist obligatorisch, +To Delivery Date,Bis Liefertermin, +To Due Date,Bis FƤlligkeitsdatum, +To Reference Date,Bis Stichtag, +To cancel a {} you need to cancel the POS Closing Entry {}.,"Um einen {} zu stornieren, müssen Sie die POS-Abschlussbuchung {} stornieren.", +Total Incoming Value (Receipt),Gesamter eingehender Wert (Empfang), +Total Number of Booked Depreciations ,Gesamtzahl der gebuchten Abschreibungen , +Total Operation Time,Gesamtbetriebszeit, +Total Other Charges,Sonstige Kosten insgesamt, +Total Purchase Amount,Gesamtkaufbetrag, +Total Purchase Cost has been updated,Die Gesamteinkaufskosten wurden aktualisiert, +UnReconcile,Zuordnung aufheben, +Unrealized Profit / Loss account for intra-company transfers,Konto für nicht realisierte Gewinne/Verluste aus konzerninternen Transfers, +Unrealized Profit/Loss account for intra-company transfers,Konto für nicht realisierte Gewinne/Verluste aus konzerninternen Transfers, +Validate Components Quantities Per BOM,Anzahl der Komponenten pro Stückliste überprüfen, +Validate Pricing Rule,Preisregel validieren, +Validate Stock on Save,Lagerbestand beim Speichern validieren, +Warning on Negative Stock,Warnung vor negativem Bestand, +You cannot create a {0} within the closed Accounting Period {1},Sie kƶnnen innerhalb der abgeschlossenen Abrechnungsperiode {1} kein(e) {0} erstellen, +dated {0},von {0}, +must be between 0 and 100,muss zwischen 0 und 100 liegen, +or its descendants,oder seine Nachkommen, +subscription is already cancelled.,abonnement ist bereits storniert., +{0} Account not found against Customer {1}.,{0} Konto für Kunde {1} nicht gefunden., +{0} Transaction(s) Reconciled,{0} Transaktion(en) Abgestimmt, +{0} cannot be zero,{0} kann nicht Null sein, +{0} is already running for {1},{0} lƤuft bereits für {1}, +{0} units of Item {1} is not available in any of the warehouses.,{0} Einheiten des Artikels {1} sind in keinem der Lager verfügbar., diff --git a/erpnext/translations/es.csv b/erpnext/translations/es.csv index 1b0468d2077..dcb95fe5466 100644 --- a/erpnext/translations/es.csv +++ b/erpnext/translations/es.csv @@ -8743,3 +8743,2528 @@ WhatsApp,WhatsApp, Make a call,Haz una llamada, Approve,Aprobar, Reject,Rechazar, + Address, Dirección, + Amount, Importe, + Is Child Table, Es una tabla secundaria, + Name, Nombre, + Rate, Precio, + Summary, Resumen, +"""SN-01::10"" for ""SN-01"" to ""SN-10""","""SN-01::10"" para ""SN-01"" a ""SN-10""", +# In Stock,# En stock, +# Req'd Items,# ArtĆ­culos Requeridos, +% Finished Item Quantity,% Cantidad de ArtĆ­culos Terminados, +% Occupied,% Ocupado, +% Picked,% Seleccionado, +% Process Loss,% PĆ©rdida por Proceso, +% Returned,% Devuelto, +'Account' in the Accounting section of Customer {0},'Cuenta' en la sección Contabilidad de Cliente {0}, +'Allow Multiple Sales Orders Against a Customer's Purchase Order','Permitir mĆŗltiples órdenes de venta contra la orden de compra de un cliente', +'Default {0} Account' in Company {1},'Cuenta {0} Predeterminada' en la CompaƱƭa {1}, +'To Package No.' cannot be less than 'From Package No.','Al paquete n.°' no puede ser menor que 'Desde el paquete n.°', +'{0}' account is already used by {1}. Use another account.,La cuenta de '{0}' ya estĆ” siendo utilizada por {1}. Utilice otra cuenta., +'{0}' should be in company currency {1}.,'{0}' debe estar en la moneda de la empresa {1}., +(A) Qty After Transaction,(A) Cant. despuĆ©s de la transacción, +(B) Expected Qty After Transaction,(B) Cant. esperada despuĆ©s de la transacción, +(C) Total Qty in Queue,(C) Cant. total en cola, +(C) Total qty in queue,(C) Cant. total en cola, +(D) Balance Stock Value,(D) Valor del balance de las existencias, +(E) Balance Stock Value in Queue,(E) Valor del balance de las existencias en cola, +(F) Change in Stock Value,(F) Cambio en el Valor de Stock, +(G) Sum of Change in Stock Value,(G) Suma del Cambio en el Valor de Stock, +(H) Change in Stock Value (FIFO Queue),(H) Cambio en Valor de Stock (Cola FIFO), +(H) Valuation Rate,(H) Tasa de valoración, +(I) Valuation Rate,(I) Tasa de valoración, +(J) Valuation Rate as per FIFO,(J) Tasa de valoración segĆŗn FIFO, +(K) Valuation = Value (D) Ć· Qty (A),(K) Valoración = Valor (D) Ć· Cant. (A), +", with the inventory {0}: {1}",", con el inventario {0}: {1}", +0-30 Days,0-30 dĆ­as, +1000+,mĆ”s de 1.000, +11-50,11 a 50, +3 Yearly,3 Anual, +30-60 Days,30-60 dĆ­as, +60-90 Days,60-90 dĆ­as, +90 Above,Superior a 90, +"
+

Note

+
    +
  • +You can use Jinja tags in Subject and Body fields for dynamic values. +
  • + All fields in this doctype are available under the doc object and all fields for the customer to whom the mail will go to is available under the customer object. +
+

Examples

+ +
    +
  • Subject:

    Statement Of Accounts for {{ customer.customer_name }}

  • +
  • Body:

    +
    Hello {{ customer.customer_name }},
    PFA your Statement Of Accounts from {{ doc.from_date }} to {{ doc.to_date }}.
  • +
+","
+

Nota

+
    +
  • +Puede usar las etiquetas Jinja en los campos Asunto y Cuerpo para valores dinĆ”micos. +
  • + Todos los campos de este doctype estĆ”n disponibles en el objeto doc y todos los campos del cliente al que se enviarĆ” el correo estĆ”n disponibles en el objeto customer . +
+

Ejemplos

+ +
    +
  • Asunto:

    Estado de Cuentas para {{ customer.customer_name }}

  • +
  • Cuerpo:

    +
    Hola {{ customer.customer_name }},
    PFA tu Estado de Cuenta del {{ doc.from_date }} al {{ doc.to_date }}.
  • +
+", +"
Other Details
","
Otros detalles
", +"
No Matching Bank Transactions Found
","
No se han encontrado transacciones bancarias coincidentes
", +"
+

All dimensions in centimeter only

+
","
+

Todas las dimensiones solo en centĆ­metros

+
", +"

About Product Bundle

+ +

Aggregate group of Items into another Item. This is useful if you are bundling a certain Items into a package and you maintain stock of the packed Items and not the aggregate Item.

+

The package Item will have Is Stock Item as No and Is Sales Item as Yes.

+

Example:

+

If you are selling Laptops and Backpacks separately and have a special price if the customer buys both, then the Laptop + Backpack will be a new Product Bundle Item.

","

Acerca de la agrupación de productos

+ +

Agregue un grupo de ArtĆ­culos en otro ArtĆ­culo. Esto es Ćŗtil si estĆ” agrupando ciertos ArtĆ­culos en un paquete y mantiene existencias de los ArtĆ­culos empaquetados y no del ArtĆ­culo agregado.

+

El ArtĆ­culo del paquete tendrĆ” Es ArtĆ­culo de Stock como No y Es ArtĆ­culo de Venta como SĆ­.

+

Ejemplo:

+

Si estƔ vendiendo PortƔtiles y Mochilas por separado y tiene un precio especial si el cliente compra ambos, entonces el PortƔtil + Mochila serƔ un nuevo Artƭculo de Paquete de Productos.

", +"

Currency Exchange Settings Help

+

There are 3 variables that could be used within the endpoint, result key and in values of the parameter.

+

Exchange rate between {from_currency} and {to_currency} on {transaction_date} is fetched by the API.

+

Example: If your endpoint is exchange.com/2021-08-01, then, you will have to input exchange.com/{transaction_date}

","

Ayuda para la configuración del cambio de divisas

+

Hay 3 variables que se pueden utilizar dentro del endpoint, clave de resultado y en valores del parƔmetro.

+

El tipo de cambio entre {from_currency} y {to_currency} en {transaction_date} es obtenido por la API.

+

Ejemplo: Si su endpoint es exchange.com/2021-08-01, entonces, tendrĆ” que introducir exchange.com/{transaction_date}

", +"

Body Text and Closing Text Example

+ +
We have noticed that you have not yet paid invoice {{sales_invoice}} for {{frappe.db.get_value(""Currency"", currency, ""symbol"")}} {{outstanding_amount}}. This is a friendly reminder that the invoice was due on {{due_date}}. Please pay the amount due immediately to avoid any further dunning cost.
+ +

How to get fieldnames

+ +

The fieldnames you can use in your template are the fields in the document. You can find out the fields of any documents via Setup > Customize Form View and selecting the document type (e.g. Sales Invoice)

+ +

Templating

+ +

Templates are compiled using the Jinja Templating Language. To learn more about Jinja, read this documentation.

","

Ejemplo de cuerpo de texto y texto de cierre

+ +
Hemos observado que aún no ha pagado la factura {{sales_invoice}} correspondiente a {{frappe.db.get_value(""Currency"", currency, ""symbol"")}} {{outstanding_amount}}. Este es un recordatorio amistoso de que la factura vencía el {{due_date}}. Le rogamos que abone inmediatamente el importe adeudado para evitar posibles gastos de reclamación.
+ +

Cómo obtener nombres de campo

+ +

Los nombres de campo que puede utilizar en su plantilla son los campos del documento. Puede averiguar los campos de cualquier documento a través de Configuración > Personalizar vista de formulario y seleccionando el tipo de documento (por ejemplo, Factura de venta)

+ +

Plantillas

+ +

Las plantillas se compilan utilizando el lenguaje de plantillas Jinja. Para saber mÔs sobre Jinja, lea esta documentación.

", +"

Contract Template Example

+ +
Contract for Customer {{ party_name }}
+
+-Valid From : {{ start_date }} 
+-Valid To : {{ end_date }}
+
+ +

How to get fieldnames

+ +

The field names you can use in your Contract Template are the fields in the Contract for which you are creating the template. You can find out the fields of any documents via Setup > Customize Form View and selecting the document type (e.g. Contract)

+ +

Templating

+ +

Templates are compiled using the Jinja Templating Language. To learn more about Jinja, read this documentation.

","

Ejemplo de plantilla de contrato

+ +
Contrato para cliente {{ party_name }}
+
+-VƔlido desde : {{ start_date }} 
+-VƔlido hasta : {{ end_date }}
+
+ +

Cómo obtener los nombres de campo

+ +

Los nombres de campo que puede utilizar en su Plantilla de Contrato son los campos del Contrato para el que estÔ creando la plantilla. Puede averiguar los campos de cualquier documento a través de Configuración > Personalizar vista de formulario y seleccionando el tipo de documento (por ejemplo, Contrato)

+ +

Creación de plantillas

+ +

Las plantillas se compilan utilizando el lenguaje de plantillas Jinja. Para saber mÔs sobre Jinja, lea esta documentación.

", +"

Standard Terms and Conditions Example

+ +
Delivery Terms for Order number {{ name }}
+
+-Order Date : {{ transaction_date }} 
+-Expected Delivery Date : {{ delivery_date }}
+
+ +

How to get fieldnames

+ +

The fieldnames you can use in your email template are the fields in the document from which you are sending the email. You can find out the fields of any documents via Setup > Customize Form View and selecting the document type (e.g. Sales Invoice)

+ +

Templating

+ +

Templates are compiled using the Jinja Templating Language. To learn more about Jinja, read this documentation.

","

Ejemplo de condiciones generales

+ +
Condiciones de entrega para el nĆŗmero de pedido {{ name }}
+
+-Fecha de pedido : {{ transaction_date }} 
+-Fecha de entrega prevista : {{ delivery_date }}
+
+ +

Cómo obtener los nombres de campo

+ +

Los nombres de campo que puede utilizar en su plantilla de correo electrónico son los campos del documento desde el que estÔ enviando el correo electrónico. Puede averiguar los campos de cualquier documento a través de Configuración > Personalizar vista de formulario y seleccionando el tipo de documento (por ejemplo, Factura de venta)

+ +

Plantillas

+ +

Las plantillas se compilan utilizando el lenguaje de plantillas Jinja. Para saber mÔs sobre Jinja, lea esta documentación.

", +"
Or
","
O
", +"","", +"","", +"","", +"

In your Email Template, you can use the following special variables: +

+
    +
  • + {{ update_password_link }}: A link where your supplier can set a new password to log into your portal. +
  • +
  • + {{ portal_link }}: A link to this RFQ in your supplier portal. +
  • +
  • + {{ supplier_name }}: The company name of your supplier. +
  • +
  • + {{ contact.salutation }} {{ contact.last_name }}: The contact person of your supplier. +
  • + {{ user_fullname }}: Your full name. +
  • +
+

+

Apart from these, you can access all values in this RFQ, like {{ message_for_supplier }} or {{ terms }}.

","

En su plantilla de correo electrónico, puede utilizar las siguientes variables especiales: +

+
    +
  • + {{ update_password_link }}: Un enlace donde su proveedor puede establecer una nueva contraseƱa para acceder a su portal. +
  • +
  • + {{ portal_link }}: Un enlace a esta petición de oferta en su portal de proveedores. +
  • +
  • + {{ supplier_name }}: El nombre de la empresa de su proveedor. +
  • +
  • + {{ contact.salutation }} {{ contact.last_name }}: La persona de contacto de su proveedor. +
  • + {{ user_fullname }}: Su nombre completo. +
  • +
+

+

Aparte de éstos, puede acceder a todos los valores de esta petición de oferta, como {{ message_for_supplier }} o {{ terms }}.

", +"
Message Example
+ +<p> Thank You for being a part of {{ doc.company }}! We hope you are enjoying the service.</p> + +<p> Please find enclosed the E Bill statement. The outstanding amount is {{ doc.grand_total }}.</p> + +<p> We don't want you to be spending time running around in order to pay for your Bill.
After all, life is beautiful and the time you have in hand should be spent to enjoy it!
So here are our little ways to help you get more time for life! </p> + +<a href=""{{ payment_url }}""> click here to pay </a> + +
+","
Ejemplo de mensaje
+ +<p> ”Gracias por formar parte de {{ doc.company }}! Esperamos que esté disfrutando del servicio.</p> + +<p> Le adjuntamos el extracto de la factura E. El importe pendiente es de {{ doc.grand_total }}.</p> + +<p> No queremos que pierda tiempo dando vueltas para pagar su Factura.
”Después de todo, la vida es bella y el tiempo de que dispone debe emplearlo en disfrutarla!
”Así que aquí tiene nuestras pequeñas maneras de ayudarle a tener mÔs tiempo para la vida! </p> + +<a href=""{{ payment_url }}""> pulse aquí para pagar </a> + +
+", +"
Message Example
+ +<p>Dear {{ doc.contact_person }},</p> + +<p>Requesting payment for {{ doc.doctype }}, {{ doc.name }} for {{ doc.grand_total }}.</p> + +<a href=""{{ payment_url }}""> click here to pay </a> + +
+","
Ejemplo de mensaje
+ +<p>Estimado {{ doc.contact_person }},</p> + +<p>Solicitando pago por {{ doc.doctype }}, {{ doc.name }} por {{ doc.grand_total }}.</p> + +<a href=""{{ payment_url }}""> Haga clic aquĆ­ para pagar </a> + +
+", +"Masters & Reports","Datos Maestros & Informes", +"Quick Access","Acceso rÔpido", +"Reports & Masters","Informes y Datos Maestros", +"Reports & Masters","Informes & Datos Maestros", +"Settings","Configuración", +"Shortcuts","Accesos directos", +"Your Shortcuts + + + + + + ","Tus accesos directos + + + + + + ", +"Your Shortcuts","Tus accesos directos", +Grand Total: {0},Total general: {0}, +Outstanding Amount: {0},Importe pendiente: {0}, +" + + + + + + + + + + + + + + + + + +
Child DocumentNon Child Document
+

To access parent document field use parent.fieldname and to access child table document field use doc.fieldname

+ +
+

To access document field use doc.fieldname

+
+

Example: parent.doctype == ""Stock Entry"" and doc.item_code == ""Test""

+ +
+

Example: doc.doctype == ""Stock Entry"" and doc.purpose == ""Manufacture""

+
+ + + + + + +"," + + + + + + + + + + + + + + + + + +
Documento secundarioDocumento no secundario
+

Para acceder al campo del documento principal, utilice parent.fieldname y para acceder al campo del documento de la tabla secundaria, utilice doc.fieldname

+ +
+

Para acceder al campo del documento, utilice doc.fieldname

+
+

Ejemplo: parent.doctype == ""Entrada de stock"" y doc.item_code == ""Prueba""

+ +
+

Ejemplo: doc.doctype == ""Entrada de stock"" y doc.purpose == ""Fabricación""

+
+ + + + + + +", +A - B,A-B, +A - C,A-C, +A Holiday List can be added to exclude counting these days for the Workstation.,Se puede aƱadir una lista de dĆ­as festivos para excluir el cómputo de estos dĆ­as para el puesto de trabajo., +A Packing Slip can only be created for Draft Delivery Note.,Solo se puede crear un albarĆ”n para un borrador de nota de entrega., +"A Price List is a collection of Item Prices either Selling, Buying, or both","Una lista de precios es una colección de Precios de Productos, ya sea de Venta, de Compra o ambos", +A Reconciliation Job {0} is running for the same filters. Cannot reconcile now,Se estĆ” ejecutando un trabajo de reconciliación {0} para los mismos filtros. No se puede reconciliar ahora., +A Transaction Deletion Document: {0} is triggered for {0},Un documento de borrado de transacciones: {0} se activa para {0}, +A customer must have primary contact email.,Un cliente debe tener un correo electrónico de contacto principal., +A driver must be set to submit.,Debe seleccionar un conductor antes de confirmar., +A template with tax category {0} already exists. Only one template is allowed with each tax category,Ya existe una plantilla con categorĆ­a de impuestos {0}. Sólo se permite una plantilla con cada categorĆ­a de impuestos, +API Details,Detalles de la API, +AWB Number,NĆŗmero AWB, +Abbreviation: {0} must appear only once,Abreviación: {0} debe aparecer sólo una vez, +About Us Settings,Configuración de información de la compaƱƭa, +About {0} minute remaining,Quedan aproximadamente {0} minutos, +About {0} minutes remaining,Quedan aproximadamente {0} minutos, +About {0} seconds remaining,Quedan aproximadamente {0} segundos, +Acceptance Criteria Formula,Fórmula de Criterio de Aceptación, +Acceptance Criteria Value,Valor de los Criterios de Aceptación, +Accepted Qty in Stock UOM,Cantidad Aceptada en UdM de Stock, +Access Key,Clave de Acceso, +Access Key is required for Service Provider: {0},Se requiere clave de acceso para el proveedor de servicios: {0}, +Account Balance (From),Saldo de cuenta (Desde), +Account Balance (To),Saldo de cuenta (Para), +Account Closing Balance,Balance de Cierre de Cuenta, +Account Currency (From),Moneda de la Cuenta (De), +Account Currency (To),Divisa de la Cuenta (De), +Account Opening Balance,Saldo de Apertura de Cuenta, +Account not Found,Cuenta no encontrada, +Account {0} added multiple times,Cuenta {0} agregada varias veces, +Accounting Dimension Filter,Filtro de dimensión contable, +Accounting Dimensions Filter,Filtro de dimensiones contables, +Accounting Entry for {0},Entrada contable para {0}, +Accounts Closing,Cierre de Cuentas, +Accounts Missing Error,Error de Cuentas Faltantes, +Accounts Receivable/Payable,Cuentas por Cobrar/Pagar, +Accounts to Merge,Cuentas a fusionar, +Action If Quality Inspection Is Rejected,Acción si se rechaza la inspección de calidad, +Action If Same Rate is Not Maintained,Acción si no se mantiene la misma tasa, +Action if Same Rate is Not Maintained Throughout Sales Cycle,Si no se mantiene la misma tarifa durante todo el ciclo de ventas, +Active Status,Estado activo, +Actual Balance Qty,Cantidad de Saldo Actual, +Actual Expense,Gasto actual, +Actual Posting,Asiento Actual, +Actual Qty in Warehouse,Cantidad real en AlmacĆ©n, +Actual Time,Tiempo actual, +Add Columns in Transaction Currency,AƱadir Columnas en Moneda de Transacción, +Add Corrective Operation Cost in Finished Good Valuation,AƱadir Costo de Operación Correctiva en la Valoración de Productos Terminados, +Add Discount,Agregar descuento, +Add Items in the Purpose Table,AƱadir Elementos en la Tabla de Propósitos, +Add Lead to Prospect,AƱadir cliente potencial a prospecto, +Add Local Holidays,Agregar dĆ­as festivos locales, +Add Manually,AƱadir manualmente, +Add Or Deduct,AƱadir o deducir, +Add Serial / Batch Bundle,AƱadir Nro. Serie/Lote, +Add Serial / Batch No,AƱadir Nro Serie/Lote, +Add Serial / Batch No (Rejected Qty),AƱadir Nro Serie/Lote (Cant Rechazada), +Add Stock,AƱadir Inventario, +Add Sub Assembly,AƱadir sub ensamblaje, +Add Template,AƱadir plantilla, +Add a Note,AƱadir Nota, +Add details,AƱadir detalles, +Add to Prospect,AƱadir a prospectos, +Added By,AƱadido por, +Added On,AƱadido el, +Added Supplier Role to User {0}.,AƱadido el Rol de Proveedor al Usuario {0}., +Added {1} Role to User {0}.,Se agregó el Rol {1} al Usuario {0}., +Adding Lead to Prospect...,Agregando cliente potencial a prospecto..., +Additional,Adicional, +Additional Asset Cost,Costo Adicional del Activo, +Additional Cost Per Qty,Costo adicional por cantidad, +Additional Info,Información Adicional, +Address And Contacts,Dirección y Contactos, +Adjust Asset Value,Ajustar el valor del activo, +Adjustment Against,Ajuste contra, +Adjustment based on Purchase Invoice rate,Ajuste basado en la tarifa de la Factura de Compra, +Advance Account: {0} must be in either customer billing currency: {1} or Company default currency: {2},La cuenta de anticipo: {0} debe estar en la moneda de facturación del cliente: {1} o en la moneda predeterminada de la empresa: {2}, +Advance Payment,Pago adelantado, +Advance Tax,Impuesto anticipado, +Advance Taxes and Charges,Impuestos y Cargos anticipados, +Advance paid against {0} {1} cannot be greater than Grand Total {2},El anticipo pagado contra {0} {1} no puede ser mayor que el total general {2}., +Advance payments allocated against orders will only be fetched,Los pagos anticipados asignados a pedidos solo se recuperarĆ”n., +Affected Transactions,Transacciones Afectadas, +Against Customer Order {0},Contra pedido del cliente {0}, +Against Supplier Invoice {0},Contra factura del proveedor {0}, +Against Voucher No,Contra el NĆŗmero de Comprobante, +Age ({0}),Edad ({0}), +Ageing Range,Rango de antigüedad, +Agent Busy Message,Mensaje de agente ocupado, +Agent Group,Grupo de agentes, +Agent Unavailable Message,Mensaje de agente no disponible, +Aggregate a group of Items into another Item. This is useful if you are maintaining the stock of the packed items and not the bundled item,Agregue un grupo de ArtĆ­culos en otro ArtĆ­culo. Esto es Ćŗtil si estĆ” manteniendo el stock de los artĆ­culos empaquetados y no del artĆ­culo agrupado, +Algorithm,Algoritmo, +All Activities,Todas las Actividades, +All Activities HTML,Todas las actividades HTML, +All Items,Todos los Productos, +All Sales Transactions can be tagged against multiple Sales Persons so that you can set and monitor targets.,Todas las transacciones de ventas se pueden etiquetar contra varias personas de ventas para que pueda establecer y supervisar los objetivos., +All allocations have been successfully reconciled,Todas las asignaciones se han reconciliado con Ć©xito., +All items have already been received,Todos los artĆ­culos ya han sido recibidos, +All items in this document already have a linked Quality Inspection.,Todos los artĆ­culos de este documento ya tienen una Inspección de Calidad vinculada., +All the Comments and Emails will be copied from one document to another newly created document(Lead -> Opportunity -> Quotation) throughout the CRM documents.,Todos los comentarios y correos electrónicos se copiarĆ”n de un documento a otro reciĆ©n creado (Cliente potencial -> Oportunidad -> Oferta) en todos los documentos del CRM., +"All the required items (raw materials) will be fetched from BOM and populated in this table. Here you can also change the Source Warehouse for any item. And during the production, you can track transferred raw materials from this table.","Todos los artĆ­culos necesarios (materias primas) se obtendrĆ”n de la lista de materiales y se rellenarĆ”n en esta tabla. AquĆ­ tambiĆ©n puede cambiar el AlmacĆ©n de Origen para cualquier artĆ­culo. Y durante la producción, puede hacer un seguimiento de las materias primas transferidas desde esta tabla.", +Allocate Payment Request,Asignar solicitud de pago, +Allocated Entries,Entradas Asignadas, +Allocated To:,Asignado a:, +Allocations,Asignaciones, +Allow,Permitir, +Allow Alternative Item must be checked on Item {},Permitir elemento alternativo debe estar marcado en Elemento {}, +Allow Continuous Material Consumption,Permitir el consumo continuo de material, +Allow Excess Material Transfer,Permitir la transferencia de material sobrante, +Allow Internal Transfers at Arm's Length Price,Permitir Transferencias Internas a Precio de Mercado, +Allow Item to be Added Multiple Times in a Transaction,Permitir que un artĆ­culo se aƱada varias veces en una transacción, +Allow Lead Duplication based on Emails,Permitir la duplicación de clientes potenciales basada en correos electrónicos, +Allow Negative rates for Items,Permitir tarifas negativas para Productos, +Allow Or Restrict Dimension,Permitir o Restringir dimensión, +Allow Partial Reservation,Permitir reserva parcial, +Allow Purchase,Permitir Compra, +Allow Sales,Permitir Ventas, +Allow Sales Order Creation For Expired Quotation,Permitir la creación de Ɠrdenes de Venta para Cotizaciones vencidas, +Allow User to Edit Discount,Permitir al usuario editar el descuento, +Allow User to Edit Rate,Permitir al usuario editar la tarifa, +Allow Zero Rate,Permitir Tarifa Cero, +Allow material consumptions without immediately manufacturing finished goods against a Work Order,Permitir consumos de material sin fabricar inmediatamente productos acabados contra una Orden de Trabajo, +Allow multi-currency invoices against single party account ,Permitir facturas en mĆŗltiples monedas contra una cuenta de una sola parte., +Allow to Edit Stock UOM Qty for Purchase Documents,Permitir editar la cantidad de UdM de stock para documentos de compras, +Allow to Edit Stock UOM Qty for Sales Documents,Permitir editar la cantidad de UdM de stock para documentos de ventas, +Allow transferring raw materials even after the Required Quantity is fulfilled,Permitir la transferencia de materias primas incluso despuĆ©s de cumplir la cantidad requerida, +Allowed,Permitido, +Allowed Dimension,Dimensión permitida, +Allowed Doctypes,Doctypes permitidos, +Allowed Items,Productos Permitidos, +Allowed primary roles are 'Customer' and 'Supplier'. Please select one of these roles only.,"Los roles principales permitidos son 'Cliente' y 'Proveedor'. Por favor, seleccione sólo uno de estos roles.", +Allows to keep aside a specific quantity of inventory for a particular order.,Permite reservar una cantidad especĆ­fica de existencias para un pedido concreto., +Already Picked,Ya ha sido seleccionado, +Alternative Items,ƍtems Alternativos, +"Alternatively, you can download the template and fill your data in.",TambiĆ©n puede descargar la plantilla y rellenar sus datos., +Amount (AED),Importe del impuesto (AED), +Amount Eligible for Commission,Importe elegible para la comisión, +Amount in Account Currency,Importe en Moneda de la Cuenta, +Amount in party's bank account currency,Importe en la divisa de la cuenta bancaria., +Amount in transaction currency,Importe en la divisa de la transacción, +An Item Group is a way to classify items based on types.,Un Grupo de Producto es una forma de clasificar Productos segĆŗn sus tipos., +An error has been appeared while reposting item valuation via {0},Se ha producido un error al volver a publicar la valoración del artĆ­culo a travĆ©s de {0}, +An error has occurred during {0}. Check {1} for more details,Se ha producido un error durante {0}. Consulte {1} para obtener mĆ”s detalles.,Error Log +Annual Revenue,Ingresos Anuales, +"Another Cost Center Allocation record {0} applicable from {1}, hence this allocation will be applicable upto {2}","Otro registro de Asignación de Centro de Coste {0} aplicable desde {1}, por lo tanto esta asignación serĆ” aplicable hasta {2}", +"Any one of following filters required: warehouse, Item Code, Item Group","Se requiere cualquiera de los siguientes filtros: almacĆ©n, código de artĆ­culo, grupo de artĆ­culos", +Applicable Dimension,Dimensión aplicable, +Applicable On Account,Aplicable en Cuenta, +Applied on each reading.,Se aplica en cada lectura., +Applied putaway rules.,Reglas de almacenamiento aplicadas., +Apply Putaway Rule,Aplicar la regla de almacenamiento, +Apply Recursion Over (As Per Transaction UOM),Aplicar recursión sobre (segĆŗn UdM de transacción), +Apply SLA for Resolution Time,Aplicar SLA para el tiempo de resolución, +Apply TDS,Aplicar a, +Apply Tax Withholding Amount ,Aplicar Monto de Retención de Impuestos , +Apply restriction on dimension values,Aplicar restricción a los valores de dimensión, +Apply to All Inventory Documents,Aplicar a todos los documentos de inventario, +Apply to Document,Aplicar al documento, +Appointment Created Successfully,Cita creada exitosamente, +Appointment Scheduling Disabled,Programación de citas deshabilitada, +Appointment Scheduling has been disabled for this site,La programación de citas ha sido desactivada para este sitio, +Appointment was created. But no lead was found. Please check the email to confirm,"Se creó la cita, pero no se encontró ningĆŗn cliente potencial. Por favor, revise el correo electrónico para confirmar.", +Approximately match the description/party name against parties,Comparar aproximadamente la descripción/nombre de la parte con las partes., +Are you sure you want to clear all demo data?,ĀæEstĆ” seguro de que desea borrar todos los datos de la demostración?, +Are you sure you want to delete this Item?,ĀæEstĆ” seguro de que desea borrar este elemento?, +Are you sure you want to restart this subscription?,ĀæEstĆ” seguro de que desea reiniciar esta suscripción?, +As on Date,A fecha, +"As there are existing submitted transactions against item {0}, you can not change the value of {1}.","Como ya existen transacciones validadas contra el artĆ­culo {0}, no puede cambiar el valor de {1}.", +"As there are negative stock, you can not enable {0}.","Como hay existencias negativas, no puede habilitar {0}.", +"As there are reserved stock, you cannot disable {0}.","Como hay stock reservado, no puedes desactivar {0}.", +"As there are sufficient Sub Assembly Items, Work Order is not required for Warehouse {0}.","Dado que hay suficientes artĆ­culos de sub ensamblaje, no se requiere una orden de trabajo para el almacĆ©n {0}.", +"As {0} is enabled, you can not enable {1}.","Como {0} estĆ” habilitado, no puedes habilitar {1}.", +Assembly Items,ArtĆ­culos de montaje, +Asset Activity,Actividad de Activos, +Asset Capitalization,Capitalización de Activos, +Asset Capitalization Asset Item,Elemento de Capitalización de Activos, +Asset Capitalization Service Item,Elemento de servicio de capitalización de activos, +Asset Capitalization Stock Item,Elemento de stock de capitalización de activos, +Asset Depreciation Details,Detalles de depreciación de activos, +Asset Depreciation Schedule,Calendario de depreciación de activos, +Asset Depreciation Schedule for Asset {0} and Finance Book {1} is not using shift based depreciation,Calendario de depreciación de activos para el activo {0} y el libro de finanzas {1} no estĆ” utilizando la depreciación por turnos, +Asset Depreciation Schedule not found for Asset {0} and Finance Book {1},El Calendario de amortización de activos no encontrado para el activo {0} y el libro de finanzas {1}, +Asset Depreciation Schedule {0} for Asset {1} already exists.,El Calendario de amortización de activos {0} para el activo {1} ya existe., +Asset Depreciation Schedule {0} for Asset {1} and Finance Book {2} already exists.,El Calendario de Depreciación de Activos {0} para el Activo {1} y el Libro Financiero {2} ya existe., +"Asset Depreciation Schedules created:
{0}

Please check, edit if needed, and submit the Asset.","Programas de depreciación de activos creados:
{0}

Verifique, edite si es necesario y valide el activo.", +Asset ID,Id de Activo, +Asset Quantity,Cantidad de Activos, +Asset Repair Consumed Item,ArtĆ­culo Consumido en Reparación de Activos, +Asset Settings,Configuración de Activos, +Asset Shift Allocation,Asignación de Turnos de Activos, +Asset Shift Factor,Factor de cambio de activos, +Asset Shift Factor {0} is set as default currently. Please change it first.,El factor de cambio de activos {0} estĆ” configurado como predeterminado actualmente. CĆ”mbielo primero., +Asset cancelled,Activo cancelado, +Asset capitalized after Asset Capitalization {0} was submitted,El Activo capitalizado fue validado despuĆ©s de la Capitalización de Activos {0}, +Asset created,Activo creado, +Asset created after Asset Capitalization {0} was submitted,El Activo creado fue validado despuĆ©s del la Capitalización de Activos {0}, +Asset created after being split from Asset {0},Activo creado despuĆ©s de ser separado del Activo {0}, +Asset decapitalized after Asset Capitalization {0} was submitted,El Activo descapitalizado fue validado despuĆ©s de la Capitalización de Activos {0}, +Asset deleted,Activo eliminado, +Asset issued to Employee {0},Activo emitido al empleado {0}, +Asset out of order due to Asset Repair {0},Activo fuera de servicio debido a la reparación del activo {0}, +Asset received at Location {0} and issued to Employee {1},Activo recibido en el lugar {0} y entregado al empleado {1}, +Asset restored,Activo restituido, +Asset restored after Asset Capitalization {0} was cancelled,Activo restituido despuĆ©s de la Capitalización de Activos {0} fue cancelada, +Asset returned,Activo devuelto, +Asset scrapped,Activo desechado, +Asset sold,Activo vendido, +Asset submitted,Activo validado, +Asset transferred to Location {0},Activo transferido a la ubicación {0}, +Asset updated after being split into Asset {0},Activo actualizado tras ser dividido en Activo {0}, +Asset updated after cancellation of Asset Repair {0},Activo actualizado tras la anulación de la reparación de activos {0}, +Asset updated after completion of Asset Repair {0},Activo actualizado tras la finalización de la reparación del activo {0}, +Asset {0} cannot be received at a location and given to an employee in a single movement,El activo {0} no puede recibirse en un lugar y entregarse a un empleado en un solo movimiento, +Asset {0} does not belong to Item {1},Activo {0} no pertenece al Producto {1}, +Asset {0} does not exist,Activo {0} no existe, +Asset {0} has been created. Please set the depreciation details if any and submit it.,"Se ha creado el activo {0}. Por favor, establezca los detalles de depreciación si los hay y valĆ­delo.", +Asset {0} has been updated. Please set the depreciation details if any and submit it.,"El activo {0} ha sido actualizado. Por favor, establezca los detalles de depreciación si los hay y valĆ­delo.", +Asset's depreciation schedule updated after Asset Shift Allocation {0},Calendario de amortización del activo actualizado tras la asignación del cambio de activo {0}, +Asset's value adjusted after cancellation of Asset Value Adjustment {0},Valor del activo ajustado tras la cancelación del ajuste del valor del activo {0}, +Asset's value adjusted after submission of Asset Value Adjustment {0},Valor del activo ajustado tras la presentación del ajuste del valor del activo {0}, +Assign Job to Employee,Asignar trabajo a un empleado, +Assignment,Asignación, +Assignment Conditions,Condiciones de asignación, +At Row #{0}: The picked quantity {1} for the item {2} is greater than available stock {3} for the batch {4} in the warehouse {5}.,En la fila #{0}: La cantidad seleccionada {1} para el artĆ­culo {2} es mayor que el stock disponible {3} para el lote {4} en el almacĆ©n {5}., +At Row #{0}: The picked quantity {1} for the item {2} is greater than available stock {3} in the warehouse {4}.,En la fila #{0}: La cantidad seleccionada {1} para el artĆ­culo {2} es mayor que el stock disponible {3} en el almacĆ©n {4}., +At row {0}: Batch No is mandatory for Item {1},En la fila {0}: el NĆŗm. de Lote es obligatorio para el Producto {1}, +At row {0}: Parent Row No cannot be set for item {1},En la fila {0}: No se puede establecer el nĀŗ de fila padre para el artĆ­culo {1}, +At row {0}: Qty is mandatory for the batch {1},En la fila {0}: La cant. es obligatoria para el lote {1}, +At row {0}: Serial No is mandatory for Item {1},En la fila {0}: el NĆŗm. Serial es obligatorio para el Producto {1}, +At row {0}: Serial and Batch Bundle {1} has already created. Please remove the values from the serial no or batch no fields.,"En la fila {0}: El paquete de serie y lote {1} ya estĆ” creado. Por favor, elimine los valores de los campos nĀŗ de serie o nĀŗ de lote.", +At row {0}: set Parent Row No for item {1},En la fila {0}: establezca el nĀŗ de fila padre para el artĆ­culo {1}, +Attach CSV File,Adjuntar archivo CSV, +Attendance & Leaves,Asistencia y Salidas, +Attribute value: {0} must appear only once,Valor del atributo: {0} debe aparecer sólo una vez, +Auto Create Exchange Rate Revaluation,Creación automĆ”tica de la revalorización del Tipo de Cambio, +Auto Create Purchase Receipt,Creación automĆ”tica de Recibo de Compra, +Auto Create Serial and Batch Bundle For Outward,Crear AutomĆ”ticamente Lote y Serie para Salida, +Auto Create Subcontracting Order,Crear orden de subcontratación automĆ”ticamente, +Auto Created Serial and Batch Bundle,Creación automĆ”tica de series y lotes, +Auto Creation of Contact,Creación automĆ”tica de Contacto, +Auto Email Report,Reporte de Correo Electrónico AutomĆ”tico, +Auto Insert Item Price If Missing,Insertar automĆ”ticamente el precio del artĆ­culo si falta, +Auto Name,Nombre AutomĆ”tico, +Auto Reconcile,Reconciliación AutomĆ”tica, +Auto Reconcile Payments,Reconciliación AutomĆ”tica de Pagos, +Auto Reconciliation,Reconciliación AutomĆ”tica, +Auto Reconciliation of Payments has been disabled. Enable it through {0},Reconciliación automĆ”tica de pagos ha sido desactivada. HabilĆ­telo a travĆ©s de {0}, +Auto Reserve Serial and Batch Nos,Reserva automĆ”tica de nĆŗmeros de serie y de lote, +Auto Reserve Stock for Sales Order on Purchase,Reserva automĆ”tica de stock para órdenes de venta en el momento de la compra, +Auto close Opportunity Replied after the no. of days mentioned above,Cierre automĆ”tico Oportunidad Respondida despuĆ©s del nĆŗmero de dĆ­as mencionado anteriormente, +Auto match and set the Party in Bank Transactions,Coincidencia automĆ”tica y fijación de la entidad en las Transacciones Bancarias, +Auto write off precision loss while consolidation,Cancelación automĆ”tica de la pĆ©rdida de precisión durante la consolidación, +Automatically Add Filtered Item To Cart,AƱadir automĆ”ticamente el artĆ­culo filtrado a la cesta, +Automatically Fetch Payment Terms from Order,Obtenga automĆ”ticamente las condiciones de pago del pedido, +Automatically post balancing accounting entry,Registrar automĆ”ticamente el asiento contable de balance, +Available Batch Report,Informe de lotes disponibles, +Available Qty For Consumption,Cantidad disponible para consumo, +Available Qty at Company,Cant. disponible en CompaƱƭa, +Available Qty at Target Warehouse,Cantidad disponible en AlmacĆ©n de destino, +Available Qty to Reserve,Cantidad disponible para reservar, +Average Completion,Promedio de completado, +Avg Rate,Tasa promedio, +Avg Rate (Balance Stock),Tasa media (Balance Stock), +BFS,BFS (BĆŗsqueda en Amplitud), +BIN Qty,Cant. BIN, +BOM Created,LdM Creado, +BOM Creator,Creador LdM, +BOM Creator Item,Creador de elementos de lista de materiales, +BOM Info,Información de LdM, +BOM Level,Nivel de lista de materiales, +BOM Tree,Ɓrbol LdM, +BOM UoM,LdM UdM, +BOM Update Batch,Lote de actualización de lista de materiales, +BOM Update Initiated,Actualización de lista de materiales iniciada, +BOM Update Log,Registro de actualización de lista de materiales, +BOM Update Tool Log with job status maintained,Registro de la herramienta de actualización de lista de materiales con el estado del trabajo mantenido, +BOM Updation already in progress. Please wait until {0} is complete.,La actualización de la lista de materiales ya estĆ” en curso. Espere hasta que se complete {0} ., +BOM Updation is queued and may take a few minutes. Check {0} for progress.,La actualización de la lista de materiales estĆ” en cola y puede tardar unos minutos. Verifique {0} para ver el progreso., +BOM and Production,Lista de materiales y producción, +BOM recursion: {1} cannot be parent or child of {0},Recursión de la lista de materiales: {1} no puede ser padre o hijo de {0}, +BOMs Updated,Listas de materiales actualizadas, +BOMs created successfully,Listas de materiales creadas con Ć©xito, +BOMs creation failed,La creación de listas de materiales falló, +"BOMs creation has been enqueued, kindly check the status after some time","La creación de listas de materiales se ha puesto en cola, compruebe el estado despuĆ©s de algĆŗn tiempo", +Balance Qty (Stock),Cantidad (stock), +Balance Sheet Summary,Resumen del balance general, +Balance Stock Value,Valor de stock, +Bank Reconciliation Tool,Herramienta de Reconciliación Bancaria, +Bank Statement Import,Importación de extractos bancarios, +Bank Transaction {0} Matched,Transacción bancaria {0} Cotejada, +Bank Transaction {0} added as Journal Entry,Transacción bancaria {0} aƱadida como asiento, +Bank Transaction {0} added as Payment Entry,Transacción bancaria {0} aƱadida como asiento de pago, +Bank Transaction {0} is already fully reconciled,Transacción bancaria {0} ya estĆ” totalmente conciliada, +Bank Transaction {0} updated,Transacción bancaria {0} actualizada, +Bank/Cash Account,Cuenta Banco/Efectivo, +Bank/Cash Account {0} doesn't belong to company {1},La Cuenta Banco/Efectivo {0} no pertenece a la compaƱƭa {1}, +Base Amount,Importe base, +Base Cost Per Unit,Coste base por unidad, +Base Rate,Tarifa base, +Base Tax Withholding Net Total,Base Imponible Retención Neta Total, +Base Total,Total base, +Base Total Billable Amount,Base Importe total facturable, +Base Total Billed Amount,Base Importe total facturado, +Base Total Costing Amount,Base Importe total del cĆ”lculo de costes, +Based On Value,Basado en el Valor, +"Based on your HR Policy, select your leave allocation period's end date","BasĆ”ndose en su polĆ­tica de RRHH, seleccione la fecha de finalización del perĆ­odo de asignación de vacaciones", +"Based on your HR Policy, select your leave allocation period's start date","BasĆ”ndose en su polĆ­tica de RRHH, seleccione la fecha de inicio de su perĆ­odo de asignación de vacaciones", +Batch Expiry Date,Fecha de caducidad del lote, +Batch No is mandatory,El nĆŗmero de lote es obligatorio, +Batch No {0} does not exists,Lote nĆŗm. {0} no existe, +Batch No {0} is linked with Item {1} which has serial no. Please scan serial no instead.,"El lote nĀŗ {0} estĆ” vinculado con el artĆ­culo {1} que tiene nĀŗ de serie. Por favor, escanee el nĀŗ de serie en su lugar.", +Batch No.,NĀŗ de Lote, +Batch Nos,NĆŗmeros de Lote, +Batch Nos are created successfully,Los NĆŗm. de Lote se crearon correctamente, +Batch Not Available for Return,Lote no disponible para devolución, +Batch Qty,Cantidad de lote, +Batch and Serial No,NĆŗm. de Lote y Serie, +Batch not created for item {} since it does not have a batch series.,"Lote no creado para el artĆ­culo {}, ya que no tiene serie de lote.", +Batch {0} and Warehouse,Lote {0} y almacĆ©n, +Batch {0} is not available in warehouse {1},El lote {0} no estĆ” disponible en el almacĆ©n {1}, +Batchwise Valuation,Valoración por lotes, +Beginning of the current subscription period,Inicio del periodo de suscripción actual, +Below Subscription Plans are of different currency to the party default billing currency/Company currency: {0},Los siguientes planes de suscripción tienen una moneda diferente a la moneda de facturación predeterminada de la parte/moneda de la empresa: {0}, +Bill for Rejected Quantity in Purchase Invoice,Facturación de la cantidad rechazada en la factura de compra, +Billed Items To Be Received,ArtĆ­culos facturados por recibir, +"Billed, Received & Returned","Facturado, Recibido y Devuelto", +Billing Address Details,Detalles de la dirección de facturación, +Billing Interval in Subscription Plan must be Month to follow calendar months,El intervalo de facturación en el plan de suscripción debe ser Mes para seguir los meses naturales, +Bisect Accounting Statements,Estados contables divididos, +Bisect Left,Dividir a la izquierda, +Bisect Nodes,Dividir nodos, +Bisect Right,Dividir a la derecha, +Bisecting From,Dividir desde, +Bisecting Left ...,Dividir a la izquierda..., +Bisecting Right ...,Dividir a la derecha..., +Bisecting To,Dividir a, +Blanket Order Allowance (%),Asignación de pedidos generales (%), +Bom No,Lista de materiales NĀŗ, +Book Advance Payments as Liability option is chosen. Paid From account changed from {0} to {1}.,Se eligió la opción de Reservar pagos por adelantado como pasivo. La cuenta Pagado desde cambió de {0} a {1}., +Book Advance Payments in Separate Party Account,Contabilizar los pagos anticipados en una cuenta separada, +Book Tax Loss on Early Payment Discount,PĆ©rdida de impuestos sobre descuentos de pago anticipado, +Book an appointment,Concierte una cita, +Booking stock value across multiple accounts will make it harder to track stock and account value.,Registrar el valor de las existencias en varias cuentas harĆ” que sea mĆ”s difĆ­cil realizar un seguimiento del valor de las existencias y de las cuentas., +Books have been closed till the period ending on {0},Los libros estarĆ”n cerrados hasta el perĆ­odo que finaliza el {0}, +Both Payable Account: {0} and Advance Account: {1} must be of same currency for company: {2},Tanto la Cuenta de Acreedores: {0} como la Cuenta de Anticipos: {1} deben ser de la misma moneda para la empresa: {2}, +Both Receivable Account: {0} and Advance Account: {1} must be of same currency for company: {2},Tanto la cuenta de deudores: {0} como la cuenta de anticipos: {1} deben ser de la misma moneda para la empresa: {2}, +Both {0} Account: {1} and Advance Account: {2} must be of same currency for company: {3},Tanto la cuenta {0} : {1} como la cuenta de anticipos: {2} deben ser de la misma moneda para la empresa: {3}, +Budget Exceeded,Presupuesto excedido, +Build All?,ĀæConstruir todo?, +Build Tree,Construir Ć”rbol, +Buildable Qty,Cantidad fabricable, +Bulk Transaction Log,Registro de transacciones masivas, +Bulk Transaction Log Detail,Detalle del registro de transacciones masivas, +Bulk Update,Actualización masiva, +Bundle Items,Conjunto de Productos, +Buying & Selling Settings,Configuración de Compra y Venta, +Buying and Selling,Compra y Venta, +"By default, the Supplier Name is set as per the Supplier Name entered. If you want Suppliers to be named by a Naming Series choose the 'Naming Series' option.","Por defecto, el Nombre del Proveedor se establece segĆŗn el Nombre del Proveedor introducido. Si desea que los Proveedores sean nombrados por una Serie de Nombres elija la opción 'Serie de Nombres'.", +Bypass credit check at Sales Order,Omitir verificación de crĆ©dito en Orden de Venta, +COGS By Item Group,CMV grupo de artĆ­culos, +COGS Debit,DĆ©bito CMV, +CRM Note,Nota CRM, +CRM Settings,Configuración CRM, +Calculate Product Bundle Price based on Child Items' Rates,Calcular el precio del paquete de productos en función de las tarifas de los artĆ­culos secundarios, +Calculate daily depreciation using total days in depreciation period,Calcular la depreciación diaria utilizando el total de dĆ­as del perĆ­odo de depreciación, +Call Again,Volver a llamar, +Call Ended,Llamada finalizada, +Call Handling Schedule,Horario de atención de llamadas, +Call Received By,Llamada recibida por, +Call Receiving Device,Dispositivo receptor de llamadas, +Call Routing,Enrutamiento de llamadas, +Call Schedule Row {0}: To time slot should always be ahead of From time slot.,Horario de llamadas Fila {0}: La franja horaria A debe estar siempre por delante de la franja horaria Desde., +Call Type,Tipo de llamada, +Callback,Devolver Llamada, +Campaign Item,ArtĆ­culos de campaƱa, +Can not close Work Order. Since {0} Job Cards are in Work In Progress state.,No se puede cerrar la Orden de Trabajo. Ya que {0} Las fichas de trabajo estĆ”n en estado Trabajo en curso., +"Can not filter based on Child Account, if grouped by Account",No se puede filtrar basado en la cuenta secundaria si estĆ” agrupada por cuenta, +"Can't change the valuation method, as there are transactions against some items which do not have its own valuation method","No se puede cambiar el mĆ©todo de valoración, ya que hay transacciones contra algunos artĆ­culos que no tienen su propio mĆ©todo de valoración.", +Can't disable batch wise valuation for active batches.,No se puede deshabilitar la valoración por lotes para lotes activos., +Can't disable batch wise valuation for items with FIFO valuation method.,No se puede desactivar la valoración por lotes para artĆ­culos con mĆ©todo de valoración FIFO., +Cannot Merge,No se puede fusionar, +Cannot Resubmit Ledger entries for vouchers in Closed fiscal year.,No se pueden volver a validar entradas del libro mayor para comprobantes en un aƱo fiscal cerrado., +"Cannot amend {0} {1}, please create a new one instead.","No se puede modificar {0} {1}; en su lugar, cree uno nuevo.", +Cannot apply TDS against multiple parties in one entry,No se puede aplicar Retención de impuestos en origen contra varias partes en una sola entrada, +Cannot cancel as processing of cancelled documents is pending.,No se puede cancelar porque el procesamiento de los documentos cancelados estĆ” pendiente., +Cannot cancel the transaction. Reposting of item valuation on submission is not completed yet.,"No se puede cancelar la transacción. La validación del traspaso de la valoración del artĆ­culo, aĆŗn no se ha completado.", +Cannot change Reference Document Type.,No se puede cambiar el tipo de documento de referencia., +Cannot complete task {0} as its dependant task {1} are not completed / cancelled.,No se puede completar la tarea {0} porque su tarea dependiente {1} no estĆ” completada / cancelada., +Cannot convert Task to non-group because the following child Tasks exist: {0}.,No se puede convertir una tarea a una no grupal porque existen las siguientes tareas secundarias: {0}., +Cannot convert to Group because Account Type is selected.,No se puede convertir a Grupo porque Tipo de Cuenta estĆ” seleccionado., +Cannot create Stock Reservation Entries for future dated Purchase Receipts.,No se pueden crear entradas de reserva de stock para recibos de compra con fecha futura., +Cannot create a pick list for Sales Order {0} because it has reserved stock. Please unreserve the stock in order to create a pick list.,No se puede crear una lista de selección para la orden de venta {0} porque tiene stock reservado. Anule la reserva del stock para crear una lista de selección., +Cannot create accounting entries against disabled accounts: {0},No se pueden crear asientos contables contra cuentas desactivadas: {0}, +Cannot disable batch wise valuation for FIFO valuation method.,No se puede desactivar la valoración por lotes para el mĆ©todo de valoración FIFO., +Cannot enqueue multi docs for one company. {0} is already queued/running for company: {1},No se pueden poner en cola varios documentos para una empresa. {0} ya estĆ” en cola/en ejecución para la empresa: {1}, +Cannot find a default warehouse for item {0}. Please set one in the Item Master or in Stock Settings.,No se puede encontrar un almacĆ©n predeterminado para el artĆ­culo {0}. Establezca uno en el Maestro de artĆ­culos o en la Configuración de existencias., +Cannot make any transactions until the deletion job is completed,No se puede realizar ninguna transacción hasta que se complete el trabajo de eliminación., +Cannot produce more item for {0},No se puede producir mĆ”s productos por {0}, +Cannot produce more than {0} items for {1},No se pueden producir mĆ”s de {0} productos por {1}, +Cannot receive from customer against negative outstanding,No se puede recibir del cliente contra saldos pendientes negativos, +Cannot retrieve link token for update. Check Error Log for more information,No se puede recuperar el token de enlace para la actualización. Consulte el registro de errores para obtener mĆ”s información, +Cannot retrieve link token. Check Error Log for more information,No se puede recuperar el token de enlace. Compruebe el registro de errores para obtener mĆ”s información, +Cannot {0} from {2} without any negative outstanding invoice,No se puede {0} desde {2} sin ninguna factura pendiente negativa, +Capacity (Stock UOM),Capacidad (Stock UdM), +Capacity in Stock UOM,Capacidad en stock UdM, +Capacity must be greater than 0,La capacidad debe ser superior a 0, +Capitalization,Capitalización, +Capitalization Method,MĆ©todo de Capitalización, +Capitalize Asset,Capitalizar Activo, +Capitalize Repair Cost,Capitalizar el coste de reparación, +Capitalized,Capitalizado, +Carrier,Operador, +Carrier Service,Servicio de Operador, +Carry Forward Communication and Comments,Llevar adelante la comunicación y los comentarios, +Category Details,Detalles de la categorĆ­a, +Caution: This might alter frozen accounts.,Precaución: Esto podrĆ­a alterar las cuentas congeladas., +Change in Stock Value,Cambio en el Valor de Stock, +Changed customer name to '{}' as '{}' already exists.,Se cambió el nombre del Cliente a '{}' porque '{}' ya existe., +Changes,Cambio, +Charge of type 'Actual' in row {0} cannot be included in Item Rate or Paid Amount,El cargo de tipo 'Real' en la fila {0} no puede incluirse en la Tarifa del artĆ­culo o en el Importe pagado, +Chart Of Accounts,Plan de Cuentas, +Checked On,Comprobado el, +Checking this will round off the tax amount to the nearest integer,Al marcar esta casilla se redondearĆ” el importe del impuesto al nĆŗmero entero mĆ”s próximo, +Cheques and Deposits Incorrectly cleared,Cheques y depósitos compensados incorrectamente, +Choose a WIP composite asset,Elija un activo compuesto WIP, +Clear Demo Data,Borrar datos de demostración, +Clear Notifications,Borrar Notificaciones, +Clearing Demo Data...,Borrando datos de demostración..., +Click on 'Get Finished Goods for Manufacture' to fetch the items from the above Sales Orders. Items only for which a BOM is present will be fetched.,"Haga clic en ""Obtener Productos Terminados para Fabricación"" para obtener los artĆ­culos de los Pedidos de Ventas anteriores. Sólo se obtendrĆ”n los artĆ­culos para los que exista una lista de materiales.", +Click on Add to Holidays. This will populate the holidays table with all the dates that fall on the selected weekly off. Repeat the process for populating the dates for all your weekly holidays,Haga clic en AƱadir a vacaciones. Esto rellenarĆ” la tabla de dĆ­as festivos con todas las fechas que caen en el dĆ­a festivo semanal seleccionado. Repita el proceso para rellenar las fechas de todas sus vacaciones semanales, +Click on Get Sales Orders to fetch sales orders based on the above filters.,Haga clic en Obtener pedidos de venta para obtener los pedidos de venta basados en los filtros anteriores., +Click to add email / phone,Clic para aƱadir correo / telĆ©fono, +Close Replied Opportunity After Days,Cerrar oportunidad respondida despuĆ©s de dĆ­as, +Closed Work Order can not be stopped or Re-opened,La orden de trabajo cerrada no puede detenerse ni reabrirse, +Closing,Cierre, +Closing Balance as per Bank Statement,Saldo de cierre segĆŗn extracto bancario, +Closing Balance as per ERP,Saldo de cierre segĆŗn ERP, +Closing Stock Balance,Saldo de stock de cierre, +Columns are not according to template. Please compare the uploaded file with standard template,"Las columnas no se ajustan a la plantilla. Por favor, compare el archivo subido con la plantilla estĆ”ndar", +Communication Channel,Canal de comunicación, +Company Address Display,Mostrar dirección de la empresa, +Company Billing Address,Dirección de Facturación de la CompaƱƭa, +Company Details,Detalles de la CompaƱƭa, +Company Shipping Address,Dirección de envĆ­o de la compaƱƭa, +Company Tax ID,NĆŗmero de Identificación Fiscal de la CompaƱƭa, +Company and Posting Date is mandatory,La Empresa y la Fecha de Publicación son obligatorias, +Company is mandatory,La empresa es obligatoria, +Company is mandatory for generating an invoice. Please set a default company in Global Defaults.,La empresa es obligatoria para generar una factura. Establezca una empresa predeterminada en Valores predeterminados globales., +Company which internal customer represents,CompaƱƭa a la que representa el Cliente Interno, +Company which internal customer represents.,CompaƱƭa a la que representa el Cliente Interno., +Company which internal supplier represents,Empresa a la que representa el proveedor interno, +Company {0} is added more than once,La empresa {0} se agrega mĆ”s de una vez, +Company {} does not exist yet. Taxes setup aborted.,La empresa {} aĆŗn no existe. Configuración de impuestos abortada., +Company {} does not match with POS Profile Company {},La empresa {} no coincide con el perfil de POS {}, +Competitor,Competidor, +Competitor Detail,Detalle del Competidor, +Competitor Name,Nombre del Competidor, +Competitors,Competidores, +Complete Job,Trabajo completo, +Complete Order,Pedido completo, +Completed On,Completado el, +Completed On cannot be greater than Today,Completado el no puede ser despuĆ©s de hoy, +Completed Tasks,Tareas Completadas, +Completed Time,Tiempo completado, +Completion Date can not be before Failure Date. Please adjust the dates accordingly.,La fecha de finalización no puede ser anterior a la fecha de falla. Ajuste las fechas segĆŗn corresponda., +Conditional Rule,Regla condicional, +Conditional Rule Examples,Ejemplos de reglas condicionales, +Configure Product Assembly,Configurar el ensamblaje del producto, +Configure the action to stop the transaction or just warn if the same rate is not maintained.,Configure la acción para detener la transacción o simplemente avisar si no se mantiene la misma tasa., +Connections,Conexiones, +Consider Entire Party Ledger Amount,Considerar el importe total del Libro Mayor de Partes, +Consider Minimum Order Qty,Considerar la cantidad mĆ­nima de pedido, +Consider Rejected Warehouses,Considerar los almacenes rechazados, +Considered In Paid Amount,Considerado en el importe pagado, +Consolidate Sales Order Items,Consolidar posiciones de pedido de cliente, +Consolidate Sub Assembly Items,Consolidar elementos de subensamblado, +Consumed Asset Items is mandatory for Decapitalization,Los elementos de activos consumidos son obligatorios para la descapitalización, +Consumed Asset Total Value,Valor total de los activos consumidos, +Consumed Assets,Activos consumidos, +Consumed Quantity,Calidad consumida, +Consumed Stock Items,ArtĆ­culos de stock consumidos, +Consumed Stock Items or Consumed Asset Items are mandatory for creating new composite asset,Los artĆ­culos de stock consumidos o los artĆ­culos de activos consumidos son obligatorios para crear un nuevo activo compuesto., +"Consumed Stock Items, Consumed Asset Items or Consumed Service Items is mandatory for Capitalization","Los artĆ­culos de stock consumidos, los artĆ­culos de activos consumidos o los artĆ­culos de servicios consumidos son obligatorios para la capitalización", +Consumed Stock Total Value,Valor total del stock consumido, +Consumption Rate,Tasa de consumo, +Contact Details,Detalles de contacto, +Contact Mobile,Contacto Móvil, +Contact Us Settings,Configuración de contĆ”cto, +Contacts,Contactos, +Contract Template Help,Ayuda con la plantilla de contrato, +Contribution Qty,Contribución Cantidad, +Control Historical Stock Transactions,Control de las transacciones históricas de existencias, +Conversion factor for item {0} has been reset to 1.0 as the uom {1} is same as stock uom {2}.,El factor de conversión para el artĆ­culo {0} se ha restablecido a 1.0 ya que la unidad de medida {1} es la misma que la unidad de medida de stock {2}., +Convert Item Description to Clean HTML in Transactions,Convertir la descripción del artĆ­culo a HTML limpio en las transacciones, +Convert to Group,Convertir a grupo,Warehouse +Convert to Item Based Reposting,Convertir a reenvĆ­o basado en artĆ­culos, +Convert to Ledger,Convertir a libro mayor,Warehouse +Core,NĆŗcleo, +Corrective Job Card,Ficha de trabajo correctivo, +Corrective Operation,Operación correctiva, +Corrective Operation Cost,Coste de la operación correctiva, +Cost Center Allocation,Asignación de centros de costes, +Cost Center Allocation Percentage,Porcentaje de Asignación del Centro de Costos, +Cost Center Allocation Percentages,Porcentajes de Asignación del Centro de Costo, +Cost Center For Item with Item Code {0} has been Changed to {1},El centro de costes para el artĆ­culo con código de artĆ­culo {0} se ha cambiado a {1}, +"Cost Center is a part of Cost Center Allocation, hence cannot be converted to a group","El centro de costes forma parte de la asignación de centros de costes, por lo que no puede convertirse en un grupo", +Cost Center with Allocation records can not be converted to a group,El centro de costes con registros de asignación no puede convertirse en un grupo, +Cost Center {0} cannot be used for allocation as it is used as main cost center in other allocation record.,"El centro de costes {0} no puede utilizarse para la asignación, ya que se utiliza como centro de costes principal en otro registro de asignación.", +Cost Center {} doesn't belong to Company {},Centro de costos {} no pertenece a la empresa {}, +Cost Center {} is a group cost center and group cost centers cannot be used in transactions,El centro de costes {} es un centro de costes de grupo y los centros de costes de grupo no pueden utilizarse en las transacciones, +Cost Configuration,Configuración de costes, +Cost Per Unit,Coste por unidad, +Cost of New Capitalized Asset,Coste del nuevo activo capitalizado, +Cost of Poor Quality Report,Informe sobre el coste de la mala calidad, +Cost to Company (CTC),Coste para la empresa (CTE), +Costing Details,Detalles de costos, +Could Not Delete Demo Data,No se pueden borrar los datos de la demostración, +Could not auto update shifts. Shift with shift factor {0} needed.,No se pudieron actualizar automĆ”ticamente los turnos. Se necesita un turno con un factor de turno {0} ., +Could not detect the Company for updating Bank Accounts,No se ha podido detectar la empresa para actualizar las cuentas bancarias, +Could not find path for ,No se pudo encontrar la ruta para , +Count,Contar, +Create Depreciation Entry,Crear asiento de amortización, +Create Employee records.,Crear registros de empleados., +Create Grouped Asset,Crear activos agrupados, +Create Job Card based on Batch Size,Crear tarjeta de trabajo en función del tamaƱo del lote, +Create Journal Entries,Crear asientos, +Create Ledger Entries for Change Amount,Crear entradas en el libro mayor para el importe de modificación, +Create Link,Crear enlace, +Create Multi-level BOM,Crear lista de materiales Multi-Nivel, +Create New Customer,Crear Nuevo Cliente, +Create Opportunity,Crear Oportunidad, +Create Prospect,Crear prospecto, +Create Reposting Entries,Crear entradas de reenvĆ­o, +Create Reposting Entry,Crear entrada de reenvĆ­o, +Create Stock Entry,Crear entrada de stock, +Create Workstation,Crear estación de trabajo, +Create a new composite asset,Crear un nuevo activo compuesto, +Create a variant with the template image.,Cree una variante con la imagen de la plantilla., +Create in Draft Status,Crear en estado Borrador, +Create {0} {1} ?,ĀæCrear {0} {1} ?, +Created On,Creado el, +Created {0} scorecards for {1} between:,Se crearon {0} tarjetas de puntos para {1} entre:, +Creating Delivery Note ...,Creando Nota de Entrega..., +Creating Journal Entries...,Creación de asientos de diario..., +Creating Packing Slip ...,Creando albarĆ”n..., +Creating Purchase Invoices ...,Creando facturas de compra..., +Creating Purchase Receipt ...,Creando Recibo de Compra..., +Creating Sales Invoices ...,Creando facturas de venta..., +Creating Stock Entry,Creando Entrada de Inventario, +Creating Subcontracting Order ...,Creando Orden de Subcontratación..., +Creating Subcontracting Receipt ...,Creando Recibo de Subcontratación..., +Creating User...,Creando usuario..., +Creation,Creación, +Creation of {1}(s) successful,Creación de {1}(s) exitosa, +"Creation of {0} failed. + Check Bulk Transaction Log","La creación de {0} falló. + Verificar Registro de transacciones masivas", +"Creation of {0} partially successful. + Check Bulk Transaction Log","Creación de {0} parcialmente satisfactoria. + Compruebe Registro de transacciones masivas", +Credit (Transaction),CrĆ©dito (Transacción), +Credit Amount in Transaction Currency,Importe del crĆ©dito en la moneda de la transacción, +Credit Limit Crossed,LĆ­mite de crĆ©dito sobrepasado, +Credit Limit Settings,Configuración del lĆ­mite de crĆ©dito, +"Credit Note will update it's own outstanding amount, even if ""Return Against"" is specified.","La nota de crĆ©dito actualizarĆ” su propio importe pendiente, incluso si se especifica ""Devolución contra"".", +Currency Exchange Settings Details,Detalles de la configuración del cambio de divisas, +Currency Exchange Settings Result,Resultado de la configuración de cambio de moneda, +Current Asset,Activo corriente, +Current Index,ƍndice actual, +Current Level,Nivel actual, +Current Liability,Pasivo corriente, +Current Node,Nodo actual, +Current Serial / Batch Bundle,Paquete de serie / lote actual, +Custom,Personalizar, +Custom delimiters,Delimitador personalizado, +Customer ,Cliente , +Customer / Item / Item Group,Cliente / Producto / Grupo de Productos, +Customer Defaults,Valores predeterminados del cliente, +Customer Group Item,ArtĆ­culo del grupo de clientes, +Customer Group: {0} does not exist,Grupo de Clientes: {0} no existe, +Customer Item,ArtĆ­culo del cliente, +Customer Name: ,Nombre del cliente: , +Customer Portal Users,Usuarios del Portal del Cliente, +Customer: ,Cliente: , +Daily Time to send,Tiempo diario para enviar, +Dashboard,Tablero, +Data Based On,Datos basados en, +Date ,Fecha , +Date must be between {0} and {1},La fecha debe estar entre {0} y {1}, +Days before the current subscription period,DĆ­as antes del perĆ­odo de suscripción actual, +DeLinked,Desvinculado, +Deal Owner,Propietario de la Oferta, +Debit (Transaction),DĆ©bito (Transacción), +Debit Amount in Transaction Currency,Importe del dĆ©bito en la moneda de la transacción, +"Debit Note will update it's own outstanding amount, even if ""Return Against"" is specified.","La nota de dĆ©bito actualizarĆ” su propio monto pendiente, incluso si se especifica ""Devolver contra"".", +Debit-Credit Mismatch,Desajuste dĆ©bito-crĆ©dito, +Debit-Credit mismatch,Desajuste dĆ©bito-crĆ©dito, +Decapitalization,Descapitalización, +Decapitalized,Descapitalizado, +Default Advance Account,Cuenta de anticipos por defecto, +Default Advance Paid Account,Cuenta de anticipos por defecto, +Default Advance Received Account,Cuenta de anticipos recibidos por defecto, +Default BOM not found for FG Item {0},Lista de materiales por defecto no encontrada para el artĆ­culo FG {0}, +Default Discount Account,Cuenta de descuento predeterminada, +Default In-Transit Warehouse,AlmacĆ©n en trĆ”nsito predeterminado, +Default Operating Cost Account,Cuenta de costos operativos por defecto, +Default Payment Discount Account,Cuenta de descuento por pago predeterminado, +Default Provisional Account,Cuenta provisional predeterminada, +Default Service Level Agreement for {0} already exists.,Ya existe un acuerdo de nivel de servicio predeterminado para {0} ., +Default Unit of Measure for Item {0} cannot be changed directly because you have already made some transaction(s) with another UOM. You need to either cancel the linked documents or create a new Item.,La unidad de medida predeterminada para el artĆ­culo {0} no se puede cambiar directamente porque ya ha realizado alguna transacción con otra unidad de medida. Debe cancelar los documentos vinculados o crear un artĆ­culo nuevo., +Default settings for your stock-related transactions,Configuración predeterminada para sus transacciones relacionadas con acciones, +"Default tax templates for sales, purchase and items are created.","Se crean plantillas de impuestos por defecto para ventas, compras y artĆ­culos.", +Deferred Accounting,Contabilidad diferida, +Deferred Accounting Defaults,Contabilidad diferida predeterminada, +Deferred Revenue and Expense,Ingresos y gastos diferidos, +Deferred accounting failed for some invoices:,La contabilidad diferida falló para algunas facturas:, +Delay (In Days),Retraso (en dĆ­as), +Delayed,Retrasado, +Delayed Tasks Summary,Resumen de tareas retrasadas, +Delete Accounting and Stock Ledger Entries on deletion of Transaction,Borrar asientos del libro de contabilidad y de existencias al borrar una transacción, +Delete Bins,Eliminar contenedores, +Delete Cancelled Ledger Entries,Eliminar entradas contables canceladas, +Delete Dimension,Eliminar Dimensión, +Delete Leads and Addresses,Eliminar clientes potenciales y direcciones, +Delete Transactions,Eliminar transacciones, +Deleted Documents,Documentos Eliminados, +Deletion in Progress!,Ā”Eliminación en progreso!, +Delimiter options,Opciones de delimitador, +Delivery Manager,Gerente de EnvĆ­o, +Delivery Note Packed Item,AlbarĆ”n de entrega ArtĆ­culo embalado, +Delivery Note(s) created for the Pick List,Nota(s) de entrega creada(s) para la lista de selección, +Delivery User,Usuario de EnvĆ­o, +Delivery to,Entregar a, +Demo Company,Empresa de Demostración, +Demo data cleared,Datos de demostración borrados, +Dependant SLE Voucher Detail No,Detalle del comprobante SLE dependiente N.Āŗ, +Dependent Task {0} is not a Template Task,La tarea dependiente {0} no es una tarea plantilla, +Deposit,Depósito, +Depreciate based on daily pro-rata,Depreciación basada en el prorrateo diario, +Depreciate based on shifts,Depreciar segĆŗn turnos, +Depreciation Details,Detalles de la depreciación, +Depreciation Entry Posting Status,Estado de contabilización del asiento de amortización, +Depreciation Expense Account should be an Income or Expense Account.,La cuenta de gastos de depreciación debe ser una cuenta de ingresos o de gastos., +Depreciation Posting Date cannot be before Available-for-use Date,La fecha de contabilización de la depreciación no puede ser anterior a la fecha de disponibilidad para uso, +Depreciation Row {0}: Depreciation Posting Date cannot be before Available-for-use Date,Fila de depreciación {0}: La fecha de contabilización de la depreciación no puede ser anterior a la fecha de disponibilidad para uso, +Depreciation Schedule View,Vista del calendario de amortización, +Depreciation cannot be calculated for fully depreciated assets,La amortización no puede calcularse para los activos totalmente amortizados, +Description of Content,Descripción del contenido, +Desk User,Usuario de Escritorio, +Difference In,Diferencia en, +Difference Posting Date,Fecha de publicación de la diferencia, +Difference Qty,Diferencia Cant., +Different 'Source Warehouse' and 'Target Warehouse' can be set for each row.,Se pueden configurar diferentes 'AlmacĆ©n de origen' y 'AlmacĆ©n de destino' para cada fila., +Dimension Details,Detalles de la dimensión, +Dimension Filter Help,Ayuda del filtro de dimensiones, +Dimension-wise Accounts Balance Report,Informe de balance de cuentas por dimensiones, +Direct Expense,Gastos Directos, +Disable Last Purchase Rate,Desactivar Ćŗltima tasa de compra, +Disable Serial No And Batch Selector,Desactivar selección de NĆŗn. de Serie y Lote, +Disabled Account Selected,Cuenta deshabilitada seleccionada, +Disabled Warehouse {0} cannot be used for this transaction.,El almacĆ©n deshabilitado {0} no se puede utilizar para esta transacción., +Disabled pricing rules since this {} is an internal transfer,Deshabilitado las reglas de precios ya que esta {} es una transferencia interna, +Disabled tax included prices since this {} is an internal transfer,"Precios con impuestos incluidos, ya que este {} es un traslado interno", +Disables auto-fetching of existing quantity,Desactiva la obtención automĆ”tica de la cantidad existente, +Disassemble,Desmontar, +Disassemble Order,Orden de desmontaje, +Discount Account,Cuenta de Descuento, +Discount Date,Fecha de descuento, +Discount Settings,Configuración de Descuento, +Discount Validity,Validez del descuento, +Discount Validity Based On,Validez del descuento basado en, +Discount of {} applied as per Payment Term,Descuento de {} aplicado segĆŗn la Condición de Pago, +Discounted Amount,Importe descontado, +"Discounts to be applied in sequential ranges like buy 1 get 1, buy 2 get 2, buy 3 get 3 and so on","Descuentos a aplicar en rangos secuenciales como compra 1 consigue 1, compra 2 consigue 2, compra 3 consigue 3 y asĆ­ sucesivamente", +Discrepancy between General and Payment Ledger,Discrepancia entre el Libro Mayor y el Libro de Pagos, +Dispatch Address,Dirección de Despacho, +Dispatch Address Name,Nombre de Dirección de Despacho, +Distinct Item and Warehouse,Distinto artĆ­culo y almacĆ©n, +Distribute Additional Costs Based On ,Distribuir los costes adicionales en función de , +Distribute Manually,Distribuir manualmente, +Do Not Explode,No desglosar, +Do Not Update Serial / Batch on Creation of Auto Bundle,No actualizar el nĆŗmero de serie o lote al crear un paquete automĆ”tico, +Do Not Use Batch-wise Valuation,No utilice la valoración por lotes, +Do reposting for each Stock Transaction,Realice el traspaso para cada transacción bursĆ”til, +Do you still want to enable negative inventory?,ĀæAĆŗn desea activar el inventario negativo?, +DocTypes should not be added manually to the 'Excluded DocTypes' table. You are only allowed to remove entries from it.,Los DocTypes no deben aƱadirse manualmente a la tabla 'DocTypes excluidos'. Solamente se le permite eliminar entradas de la misma., +Document Type already used as a dimension,Tipo de documento ya utilizado como dimensión, +Documents,Documentos, +Documents: {0} have deferred revenue/expense enabled for them. Cannot repost.,Documentos: {0} tienen habilitados ingresos/gastos diferidos. No se pueden volver a publicar., +Domain Settings,Configuraciones de Dominio, +Don't Reserve Sales Order Qty on Sales Return,No reserve la cantidad de la orden de venta en la devolución de ventas, +Don't Send Emails,No envĆ­e correos electrónicos, +Dont Recompute tax,No recalcular impuestos, +Download Backups,Descargar Backups, +Download CSV Template,Descargar la plantilla CSV, +Download Materials Request Plan,Descargar Plan de Solicitud de Materiales, +Download Materials Request Plan Section,Descargar Sección de Solicitud de Planos de Materiales, +Dunning Amount (Company Currency),Importe de la reclamación (moneda de la empresa), +Dunning Level,Nivel de reclamación, +Duplicate Closing Stock Balance,Saldo de stock de cierre duplicado, +Duplicate Customer Group,Grupo de clientes duplicados, +Duplicate Finance Book,Duplicado del Libro de Finanzas, +Duplicate Item Group,Grupo de Productos duplicado, +Duplicate POS Invoices found,Se encontraron facturas PV duplicadas, +Dynamic Condition,Condición DinĆ”mica, +Edit Capacity,Editar capacidad, +Edit Cart,Editar carrito, +Edit Full Form,Editar formulario completo, +Edit Note,Editar Nota, +Editing {0} is not allowed as per POS Profile settings,La edición de {0} no estĆ” permitida segĆŗn la configuración del perfil del PV, +Either 'Selling' or 'Buying' must be selected,"Debe seleccionar ""Vender"" o ""Comprar"".", +Email / Notifications,Correo / Notificaciones, +Email Address (required),Dirección de correo electrónico (obligatorio), +"Email Address must be unique, it is already used in {0}","La dirección de correo electrónico debe ser Ćŗnica, ya se utiliza en {0}", +Email Digest Recipient,Destinatario del resumen de correo electrónico, +Email Digest: {0},Resumen de correo: {0}, +Email Domain,Dominio de Correo Electrónico, +Email or Phone/Mobile of the Contact are mandatory to continue.,El correo electrónico o el telĆ©fono/móvil del contacto son obligatorios para continuar., +Email verification failed.,Error en la verificación del correo electrónico., +Employee User Id,Id. de usuario del empleado, +Enable Allow Partial Reservation in the Stock Settings to reserve partial stock.,Habilite Permitir reserva parcial en la configuración de stock para reservar stock parcial., +Enable Automatic Party Matching,Habilitar la coincidencia automĆ”tica de partes, +Enable Common Party Accounting,Habilitar la contabilidad de partes comunes, +Enable Discount Accounting for Selling,Habilitar la contabilidad de descuentos para las ventas, +Enable Fuzzy Matching,Habilitar coincidencia difusa, +Enable Health Monitor,Habilitar monitor de salud, +Enable Immutable Ledger,Habilitar libro mayor inmutable, +Enable Provisional Accounting For Non Stock Items,Habilitar contabilidad provisional para artĆ­culos que no estĆ”n en stock, +Enable Stock Reservation,Habilitar reserva de existencias, +Enable it if users want to consider rejected materials to dispatch.,HabilĆ­telo si los usuarios desean considerar los materiales rechazados para enviarlos., +Enable this checkbox even if you want to set the zero priority,Active esta casilla incluso si desea establecer la prioridad cero, +"Enable this option to calculate daily depreciation by considering the total number of days in the entire depreciation period, (including leap years) while using daily pro-rata based depreciation","Active esta opción para calcular la amortización diaria teniendo en cuenta el nĆŗmero total de dĆ­as de todo el perĆ­odo de amortización, (incluidos los aƱos bisiestos) al utilizar la amortización diaria prorrateada.", +Enable to apply SLA on every {0},Habilitar para aplicar SLA en cada {0}, +Enabling this ensures each Purchase Invoice has a unique value in Supplier Invoice No. field within a particular fiscal year,Habilitar esta opción garantiza que cada factura de compra tenga un valor Ćŗnico en el campo NĀŗ de factura del proveedor dentro de un ejercicio fiscal determinado, +Enabling this option will allow you to record -

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

2. Advances Paid in an Asset Account instead of the Liability Account,Activar esta opción le permitirÔ registrar -

1. Anticipos Recibidos en una Cuenta de Pasivo en lugar de la Cuenta de Activo

2. Anticipos Pagados en una Cuenta de Activo en lugar de la Cuenta de Pasivo, +Enabling this will allow creation of multi-currency invoices against single party account in company currency,Habilitar esta opción permitirĆ” la creación de facturas multidivisa contra la cuenta de una sola parte en la divisa de la empresa, +Enabling this will change the way how cancelled transactions are handled.,Al activar esta opción cambiarĆ” la forma en que se gestionan las transacciones canceladas., +End Transit,Fin del trĆ”nsito, +End of the current subscription period,Fin del periodo de suscripción actual, +"Enter First and Last name of Employee, based on Which Full Name will be updated. IN transactions, it will be Full Name which will be fetched.","Introduzca el Nombre y Apellidos del Empleado, en base a los cuales se actualizarĆ” el Nombre Completo. EN las transacciones, serĆ” el Nombre Completo el que se obtendrĆ”.", +Enter Manually,Introducir manualmente, +Enter Serial Nos,Ingrese Serial Nro., +Enter Visit Details,Introduzca los datos de la visita, +Enter a name for Routing.,Introduzca un nombre para el Enrutamiento., +"Enter a name for the Operation, for example, Cutting.","Introduzca un nombre para la Operación, por ejemplo, Corte.", +Enter a name for this Holiday List.,Introduzca un nombre para esta Lista de vacaciones., +"Enter an Item Code, the name will be auto-filled the same as Item Code on clicking inside the Item Name field.","Introduzca un Código de ArtĆ­culo, el nombre se autocompletarĆ” igual que Código de ArtĆ­culo al pulsar dentro del campo Nombre de ArtĆ­culo.", +Enter each serial no in a new line,Introduzca cada nĀŗ de serie en una nueva lĆ­nea, +"Enter the Operation, the table will fetch the Operation details like Hourly Rate, Workstation automatically. + + After that, set the Operation Time in minutes and the table will calculate the Operation Costs based on the Hourly Rate and Operation Time.","Introduzca la Operación, la tabla obtendrĆ” los detalles de la Operación como la Tasa Horaria, la Estación de Trabajo automĆ”ticamente. + + DespuĆ©s, fije el Tiempo de Operación en minutos y la tabla calcularĆ” los Costes de Operación basĆ”ndose en la Tarifa Horaria y el Tiempo de Operación.", +Enter the opening stock units.,Introduzca las unidades de existencias iniciales., +Enter the quantity of the Item that will be manufactured from this Bill of Materials.,Introduzca la cantidad del ArtĆ­culo que se fabricarĆ” a partir de esta Lista de Materiales., +Enter the quantity to manufacture. Raw material Items will be fetched only when this is set.,Introduzca la cantidad a fabricar. Los artĆ­culos de materia prima sólo se obtendrĆ”n cuando se haya configurado esta opción., +Error during caller information update,Error al actualizar la información de llamada, +Error while posting depreciation entries,Error al contabilizar asientos de amortización, +Error while processing deferred accounting for {0},Error al procesar la contabilidad diferida para {0}, +Error while reposting item valuation,Error al volver a publicar la valoración del artĆ­culo, +"Error: This asset already has {0} depreciation periods booked. + The `depreciation start` date must be at least {1} periods after the `available for use` date. + Please correct the dates accordingly.","Error: Este activo ya tiene contabilizados {0} periodos de amortización. + La fecha de `inicio de la amortización` debe ser al menos {1} periodos despuĆ©s de la fecha de `disponible para su uso`. + Por favor, corrija las fechas en consecuencia.", +Errors Notification,Notificación de errores, +Even invoices with apply tax withholding unchecked will be considered for checking cumulative threshold breach,Incluso las facturas que no tengan marcada la opción de aplicar la retención de impuestos se tendrĆ”n en cuenta para comprobar el incumplimiento del umbral acumulativo., +Example URL,URL de ejemplo, +Example of a linked document: {0},Ejemplo de documento vinculado: {0}, +"Example: ABCD.##### +If series is set and Serial No is not mentioned in transactions, then automatic serial number will be created based on this series. If you always want to explicitly mention Serial Nos for this item. leave this blank.","Ejemplo: ABCD. #####. Si se establece una serie y no se menciona el No de lote en las transacciones, se crearĆ” un nĆŗmero de lote automĆ”tico basado en esta serie. Si siempre quiere mencionar explĆ­citamente el No de lote para este artĆ­culo, dĆ©jelo en blanco. Nota: esta configuración tendrĆ” prioridad sobre el Prefijo de denominación de serie en Configuración de stock.", +Example: Serial No {0} reserved in {1}.,Ejemplo: NĆŗmero de serie {0} reservado en {1}., +Excess Materials Consumed,Exceso de materiales consumidos, +Excess Transfer,Exceso de transferencia, +Exchange Gain Or Loss,Ganancias o pĆ©rdidas por cambio de divisas, +Exchange Gain/Loss amount has been booked through {0},El importe de las ganancias/pĆ©rdidas de cambio se ha contabilizado a travĆ©s de {0}., +Exchange Rate Revaluation Settings,Configuración de revaluación del tipo de cambio, +Excluded DocTypes,DocTipes excluidos, +Exempt Supplies,Suministros exentos, +Expected,Esperado, +Expected Balance Qty,Balance esperado, +Expected End Date should be less than or equal to parent task's Expected End Date {0}.,La fecha de finalización esperada debe ser menor o igual a la fecha de finalización esperada de la tarea principal {0}., +Expected Stock Value,Valor esperado de stock, +Expected Time Required (In Mins),Tiempo previsto necesario (en minutos), +Expiry,Expiración, +Export Data,Exportar Datos, +Export Errored Rows,Exportar filas con errores, +Export Import Log,Exportar registro de importación, +Extra Consumed Qty,Cantidad extra consumida, +Extra Job Card Quantity,Cantidad de tarjetas de trabajo adicionales, +FIFO Queue vs Qty After Transaction Comparison,Comparación entre cola FIFO y cantidad despuĆ©s de la transacción, +"FIFO Stock Queue (qty, rate)","Cola de existencias FIFO (cantidad, tasa)", +FIFO/LIFO Queue,Cola FIFO/LIFO, +Failed Entries,Entradas fallidas, +"Failed to erase demo data, please delete the demo company manually.","Fallo al borrar los datos de demostración, por favor borre la empresa de demostración manualmente.", +Failed to post depreciation entries,Fallo al contabilizar las entradas de depreciación, +Failed to setup defaults for country {0}. Please contact support.,Fallo al configurar los valores predeterminados para el paĆ­s {0}. Póngase en contacto con el servicio de asistencia., +Failure,Fracaso, +Failure Description,Descripción del fallo, +Fetch Based On,Obtener Basado en, +Fetch Overdue Payments,Recuperar pagos atrasados, +Fetch Timesheet,Obtener Hoja de Tiempo, +Fetch Value From,Obtener valor de, +Fetching exchange rates ...,Obteniendo tipos de cambio..., +Filter by Reference Date,Filtrar por Fecha de Referencia, +Filter on Invoice,Filtrar en factura, +Filter on Payment,Filtrar en el pago, +Filters missing,Faltan filtros, +Final Product,Producto final, +Financial Ratios,ƍndices financieros, +Financial Reports,Informes Financieros, +Financial Year Begins On,El aƱo fiscal comienza el, +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) ,Los informes financieros se generarĆ”n utilizando los doctypes de entrada GL (debe activarse si el Comprobante de Cierre de PerĆ­odo no se contabiliza para todos los aƱos secuencialmente o faltantes) , +Finished Good BOM,Lista de materiales de productos terminados, +Finished Good Item,ArtĆ­culo de Producto Terminado, +Finished Good Item Qty,Producto acabado Cantidad, +Finished Good Item Quantity,Producto acabado Cantidad, +Finished Good Item is not specified for service item {0},ArtĆ­culo de producto terminado no especificado para artĆ­culo de servicio {0}, +Finished Good Item {0} Qty can not be zero,Producto terminado ArtĆ­culo {0} La cantidad no puede ser cero, +Finished Good Item {0} must be a sub-contracted item,El artĆ­culo terminado {0} debe ser un artĆ­culo subcontratado, +Finished Good Qty,Cantidad de producto acabado, +Finished Good Quantity ,Cantidad de producto acabado , +Finished Good UOM,Producto acabado UdM, +Finished Good {0} does not have a default BOM.,El Producto acabado {0} no tiene una lista de materiales predeterminada., +Finished Good {0} is disabled.,El Producto acabado {0} estĆ” deshabilitado., +Finished Good {0} must be a stock item.,El Producto acabado {0} debe ser un artĆ­culo de stock., +Finished Good {0} must be a sub-contracted item.,El producto terminado {0} debe ser un artĆ­culo subcontratado., +Finished Goods Based Operating Cost,Coste de explotación basado en los productos acabados, +Finished Goods Item,Producto acabado, +Finished Goods Reference,Productos acabados Referencia, +Finished Goods Value,Valor de los productos acabados, +Finished Goods based Operating Cost,Coste de explotación basado en los productos acabados, +Finished Item {0} does not match with Work Order {1},ArtĆ­culo terminado {0} no coincide con la orden de trabajo {1}, +First Response Due,Primera respuesta pendiente, +First Response SLA Failed by {},El primer acuerdo de nivel de servicio de respuesta falló por {}, +Fixed Asset Defaults,Cuenta de activo fijo predeterminada, +Fixed Time,Tiempo fijo, +Floor,Piso, +Floor Name,Nombre del Piso, +For Item,Para artĆ­culo, +For Item {0} cannot be received more than {1} qty against the {2} {3},Para el artĆ­culo {0} no se puede recibir mĆ”s de {1} cantidad contra {2} {3}, +For Job Card,Para tarjeta de trabajo, +For Operation,Para operaciones, +"For Return Invoices with Stock effect, '0' qty Items are not allowed. Following rows are affected: {0}","Para las Facturas de Devolución con efecto de Stock, no se permiten artĆ­culos de cant. '0'. Se ven afectadas las siguientes lĆ­neas: {0}", +For Work Order,Para Orden de Trabajo, +For dunning fee and interest,Por gastos de reclamación e intereses, +"For item {0}, rate must be a positive number. To Allow negative rates, enable {1} in {2}","Para el producto {0}, el precio debe ser un nĆŗmero positivo. Para permitir precios negativos, habilite {1} en {2}", +For quantity {0} should not be greater than allowed quantity {1},Para la cantidad {0} no debe ser mayor que la cantidad permitida {1}, +"For the item {0}, the quantity should be {1} according to the BOM {2}.","Para el artĆ­culo {0}, la cantidad debe ser {1} segĆŗn la lista de materiales {2}.", +"For the {0}, no stock is available for the return in the warehouse {1}.","Para la {0}, no hay existencias disponibles para la devolución en el almacĆ©n {1}.", +"For the {0}, the quantity is required to make the return entry","Para el {0}, se requiere la cantidad para realizar la entrada de devolución", +Force-Fetch Subscription Updates,Actualizaciones forzadas de suscripciones, +Forecasting,Previsión, +Formula Based Criteria,Criterios basados en fórmulas, +Free Item Rate,Tarifa de artĆ­culo libre, +From Corrective Job Card,De la ficha de trabajo correctiva, +From Date and To Date are mandatory,Desde la fecha y hasta la fecha son obligatorios, +From Date is mandatory,Desde Fecha es obligatorio, +From Date: {0} cannot be greater than To date: {1},Desde la fecha: {0} no puede ser mayor que Hasta la fecha: {1}, +From Delivery Date,Desde la fecha de entrega, +From Doctype,Desde DocType, +From Due Date,Desde la fecha de vencimiento, +From Opportunity,Desde Oportunidad, +From Payment Date,Desde la fecha de pago, +From Prospect,Desde Prospecto, +From Reference Date,Desde la fecha de referencia, +From Voucher Detail No,Desde el nĆŗmero de detalle del comprobante, +From Voucher No,Del nĆŗmero de comprobante, +From Voucher Type,Desde el tipo de comprobante, +From and To dates are required,Las fechas desde y hasta son obligatorias, +Full and Final Statement,Declaración completa y definitiva, +GL Balance,Balance GL, +GL Entry Processing Status,Estado de procesamiento de la entrada GL, +GL reposting index,ƍndice de traspaso GL, +Gain/Loss accumulated in foreign currency account. Accounts with '0' balance in either Base or Account currency,Ganancia/pĆ©rdida acumulada en cuenta en moneda extranjera. Cuentas con saldo ā€œ0ā€ en moneda base o de cuenta, +Gain/Loss already booked,Ganancias/pĆ©rdidas ya contabilizadas, +Gain/Loss from Revaluation,Ganancias/pĆ©rdidas por revalorización, +General Ledger,Balance general,Warehouse +General and Payment Ledger Comparison,Comparación de los libros mayor y de pagos, +General and Payment Ledger mismatch,Desajuste entre el Libro Mayor y el Libro de Pagos, +Generate Closing Stock Balance,Generar balance de cierre de stock, +Generate Demo Data for Exploration,Generar datos de demostración para la exploración, +Generate E-Invoice,Generar E-Factura, +Generate Invoice At,Generar factura el, +Generated,Generado, +Generating Preview,Generando vista previa, +Get Allocations,Obtener Asignaciones, +Get Customer Group Details,Obtener Detalles del Grupo de Clientes, +Get Finished Goods for Manufacture,Obtener productos acabados para su fabricación, +Get Outstanding Orders,Obtener pedidos pendientes, +Get Raw Materials Cost from Consumption Entry,Obtenga el coste de las materias primas a partir de la entrada de consumo, +Get Raw Materials for Purchase,Obtener materias primas para comprar, +Get Raw Materials for Transfer,Obtener materias primas para el traslado, +Get Scrap Items,Obtener artĆ­culos de desecho, +Get Stock,Obtener existencias, +Get Sub Assembly Items,Obtener artĆ­culos de subensamblaje, +Get Supplier Group Details,Obtener detalles del grupo de proveedores, +Get Timesheets,Obtener Hojas de Tiempo, +Get stops from,Obtener paradas de, +Getting Scrap Items,Obtener artĆ­culos de desecho, +Give free item for every N quantity,Regale un artĆ­culo gratis por cada cantidad N, +Go back,Volver, +Go to {0} List,Ir a la lista {0}, +Goals,Objetivos, +Goods,MercancĆ­as, +Grant Commission,Conceder Comisión, +Greeting Message,Mensaje de saludo, +Gross Profit Percent,Porcentaje de beneficio bruto, +Gross Purchase Amount Too Low: {0} cannot be depreciated over {1} cycles with a frequency of {2} depreciations.,Importe bruto de compra demasiado bajo: {0} no puede amortizarse a lo largo de {1} ciclos con una frecuencia de {2} amortizaciones., +Gross Purchase Amount should be equal to purchase amount of one single Asset.,El monto bruto de compra debe ser igual a al monto de compra de un solo activo., +Group Same Items,Agrupar mismos artĆ­culos, +Growth View,Vista de Crecimiento, +Half-yearly,Medio aƱo, +Handle Employee Advances,Gestionar los anticipos de los empleados, +Has Alternative Item,Tiene ƍtem Alternativo, +Has Item Scanned,Tiene artĆ­culos escaneados, +Has Priority,Tiene prioridad, +Have Default Naming Series for Batch ID?,ĀæDispone de series de nombres por defecto para el ID de lote?, +Heatmap,Mapa de calor, +Height (cm),Altura (cm), +"Hello,","Hola,", +Helps you distribute the Budget/Target across months if you have seasonality in your business.,Le ayuda a distribuir el Presupuesto/Objetivo a lo largo de los meses si tiene estacionalidad en su negocio., +Here are the error logs for the aforementioned failed depreciation entries: {0},A continuación se muestran los registros de errores de las entradas de depreciación fallidas mencionadas anteriormente: {0}, +Here are the options to proceed:,Estas son las opciones para proceder:, +"Here, you can select a senior of this Employee. Based on this, Organization Chart will be populated.","AquĆ­ puede seleccionar un superior de este Empleado. BasĆ”ndose en esto, se rellenarĆ” el Organigrama.", +"Here, your weekly offs are pre-populated based on the previous selections. You can add more rows to also add public and national holidays individually.","AquĆ­, los dĆ­as libres semanales se rellenan previamente en función de las selecciones anteriores. Puede agregar mĆ”s filas para agregar tambiĆ©n dĆ­as festivos pĆŗblicos y nacionales de forma individual.", +"Hi,","Hola,", +Hide Images,Ocultar ImĆ”genes, +Holiday Date {0} added multiple times,Fecha de vacaciones {0} aƱadida varias veces, +Hours Spent,Horas Dedicadas, +How often should Project be updated of Total Purchase Cost ?,ĀæCon quĆ© frecuencia debe actualizarse el proyecto del coste total de compra?, +Idle,Inactivo, +"If Enabled - Reconciliation happens on the Advance Payment posting date
+If Disabled - Reconciliation happens on oldest of 2 Dates: Invoice Date or the Advance Payment posting date
+","Si estÔ habilitado : la conciliación se realiza en la fecha de contabilización del pago por adelantado
+Si estÔ deshabilitado : la conciliación se realiza en la fecha mÔs antigua de las 2 fechas: fecha de factura o la fecha de contabilización del pago por adelantado
+", +"If an operation is divided into sub operations, they can be added here.","Si una operación se divide en suboperaciones, Ć©stas pueden aƱadirse aquĆ­.", +"If checked, Rejected Quantity will be included while making Purchase Invoice from Purchase Receipt.","Si se marca, la Cantidad Rechazada se incluirĆ” al hacer la Factura de Compra a partir del Recibo de Compra.", +"If checked, Stock will be reserved on Submit","Si estĆ” marcado, se reservarĆ”n las existencias al Validar", +"If checked, picked qty won't automatically be fulfilled on submit of pick list.","Si se marca, la cantidad seleccionada no se completarĆ” automĆ”ticamente al validar la lista de selección.", +"If checked, the tax amount will be considered as already included in the Paid Amount in Payment Entry","Si estĆ” marcada, el importe del impuesto se considerarĆ” ya incluido en el Importe pagado en la Entrada de pago", +"If checked, we will create demo data for you to explore the system. This demo data can be erased later.","Si estĆ” marcada, crearemos datos de demostración para que explore el sistema. Estos datos de demostración pueden borrarse posteriormente.", +If enabled then system will manufacture Sub-assembly against the Job Card (operation).,"Si se activa, el sistema fabricarĆ” el subconjunto segĆŗn la tarjeta de trabajo (operación).", +If enabled then system won't apply the pricing rule on the delivery note which will be create from the pick list,"Si se activa, el sistema no aplicarĆ” la regla de precios en el albarĆ”n que se crearĆ” a partir de la lista de selección.", +If enabled then system won't override the picked qty / batches / serial numbers.,"Si se activa, el sistema no anularĆ” las cantidades / lotes / nĆŗmeros de serie elegidos.", +"If enabled, a print of this document will be attached to each email","Si estĆ” habilitado, se adjuntarĆ” una impresión de este documento a cada correo electrónico", +"If enabled, additional ledger entries will be made for discounts in a separate Discount Account","Si estĆ” habilitado, se realizarĆ”n entradas contables adicionales para descuentos en una cuenta de descuento separada", +"If enabled, all files attached to this document will be attached to each email","Si estĆ” habilitado, todos los archivos adjuntos a este documento se adjuntarĆ”n a cada correo electrónico.", +"If enabled, do not update serial / batch values in the stock transactions on creation of auto Serial + / Batch Bundle. ","Si estĆ” habilitado, no actualice los valores de serie/lote en las transacciones de stock al crear automĆ”ticamente el paquete de serie + /lote. ", +"If enabled, ledger entries will be posted for change amount in POS transactions","Si se activa, se contabilizarĆ”n las entradas en el libro mayor para el importe de cambio en las transacciones de TPV", +"If enabled, the consolidated invoices will have rounded total disabled","Si se activa, las facturas consolidadas tendrĆ”n el total redondeado desactivado", +"If enabled, the item rate won't adjust to the valuation rate during internal transfers, but accounting will still use the valuation rate.","Si estĆ” activada, la tasa del artĆ­culo no se ajustarĆ” a la tasa de valoración durante las transferencias internas, pero la contabilidad seguirĆ” utilizando la tasa de valoración.", +"If enabled, the system will create material requests even if the stock exists in the 'Raw Materials Warehouse'.","Si se activa, el sistema crearĆ” solicitudes de material aunque haya existencias en el ""AlmacĆ©n de materias primas"".", +"If enabled, the system will use the moving average valuation method to calculate the valuation rate for the batched items and will not consider the individual batch-wise incoming rate.","Si se activa, el sistema utilizarĆ” el mĆ©todo de valoración media móvil para calcular la tasa de valoración de los artĆ­culos por lotes y no tendrĆ” en cuenta la tasa de entrada individual por lotes.", +"If enabled, then system will only validate the pricing rule and not apply automatically. User has to manually set the discount percentage / margin / free items to validate the pricing rule","Si estĆ” activada, el sistema sólo validarĆ” la regla de precios y no la aplicarĆ” automĆ”ticamente. El usuario debe establecer manualmente el porcentaje de descuento / margen / artĆ­culos gratuitos para validar la regla de precios.", +"If mentioned, the system will allow only the users with this Role to create or modify any stock transaction earlier than the latest stock transaction for a specific item and warehouse. If set as blank, it allows all users to create/edit back-dated transactions.","Si se menciona, el sistema permitirĆ” sólo a los usuarios con este Rol crear o modificar cualquier transacción de existencias anterior a la Ćŗltima transacción de existencias para un artĆ­culo y almacĆ©n especĆ­ficos. Si se establece en blanco, permite a todos los usuarios crear/editar transacciones con fecha anterior.", +"If not, you can Cancel / Submit this entry","En caso contrario, puedes Cancelar/Validar esta entrada", +"If rate is zero then item will be treated as ""Free Item""","Si la tarifa es cero, el artĆ­culo se tratarĆ” como ""ArtĆ­culo gratuito"".", +"If the BOM results in Scrap material, the Scrap Warehouse needs to be selected.","Si la lista de materiales arroja como resultado material de desecho, se debe seleccionar el almacĆ©n de desecho.", +"If the selected BOM has Operations mentioned in it, the system will fetch all Operations from BOM, these values can be changed.","Si la lista de materiales seleccionada tiene Operaciones mencionadas en ella, el sistema obtendrĆ” todas las Operaciones de la lista de materiales, estos valores pueden modificarse.", +"If this checkbox is enabled, then the system won’t run the MRP for the available sub-assembly items.","Si esta casilla de verificación estĆ” activada, el sistema no ejecutarĆ” la planificación de necesidades para los artĆ­culos de subensamblaje disponibles.", +If this is undesirable please cancel the corresponding Payment Entry.,"Si no lo desea, anule el asiento de pago correspondiente.", +"If yes, then this warehouse will be used to store rejected materials","En caso afirmativo, este almacĆ©n se utilizarĆ” para almacenar los materiales rechazados", +"If you are maintaining stock of this Item in your Inventory, ERPNext will make a stock ledger entry for each transaction of this item.","Si mantiene existencias de este artĆ­culo en su inventario, ERPNext realizarĆ” una entrada en el libro de existencias para cada transacción de este artĆ­culo.", +"If you need to reconcile particular transactions against each other, then please select accordingly. If not, all the transactions will be allocated in FIFO order.","Si necesita conciliar transacciones especĆ­ficas entre sĆ­, seleccione la opción correspondiente. De lo contrario, todas las transacciones se asignarĆ”n en orden FIFO.", +"If you still want to proceed, please disable 'Skip Available Sub Assembly Items' checkbox.","Si aĆŗn asĆ­ desea continuar, desactive la casilla 'Omitir elementos de subensamblaje disponibles'.", +"If you still want to proceed, please enable {0}.","Si aĆŗn desea continuar, habilite {0}.", +"If your CSV uses a different delimiter, add that character here, ensuring no spaces or additional characters are included.","Si su CSV utiliza un delimitador diferente, aƱada ese carĆ”cter aquĆ­, asegurĆ”ndose de no incluir espacios ni caracteres adicionales.", +Ignore Account Closing Balance,Ignorar el saldo de cierre de la cuenta, +Ignore Available Stock,Ignorar stock disponible, +Ignore Closing Balance,Ignorar el saldo de cierre, +Ignore Default Payment Terms Template,Ignorar Plantilla de TĆ©rminos de Pago Predeterminado, +Ignore Empty Stock,Ignorar Stock VacĆ­o, +Ignore Exchange Rate Revaluation Journals,Ignorar los Diarios de Revaluación del Tipo de Cambio, +Ignore Pricing Rule is enabled. Cannot apply coupon code.,La opción Ignorar regla de precios estĆ” habilitada. No se puede aplicar el código de cupón., +Ignore System Generated Credit / Debit Notes,Ignorar las notas de crĆ©dito / dĆ©bito generadas por el sistema, +Ignore Voucher Type filter and Select Vouchers Manually,Ignorar el filtro de tipo de cupón y seleccionar cupones manualmente, +Impairment,Deteriorado, +Import File,Importar archivo, +Import File Errors and Warnings,Importar errores y advertencias de archivos, +Import Log Preview,Vista previa de registro de importación, +Import Preview,Vista previa de importación, +Import Progress,Progreso de importación, +Import Type,Tipo de importación, +Import Using CSV file,Importación mediante archivo CSV, +Import Warnings,Advertencias de importación, +Import from Google Sheets,Importar desde Google Sheets, +"Importing {0} of {1}, {2}","Importar {0} de {1}, {2}", +In House,En casa, +In Minutes,En Minutos, +In Party Currency,En moneda del individuo, +In Transit Transfer,Transferencia en trĆ”nsito, +In Transit Warehouse,AlmacĆ©n en TrĆ”nsito, +In mins,En minutos, +"In row {0} of Appointment Booking Slots: ""To Time"" must be later than ""From Time"".","En la fila {0} de las franjas horarias de reserva de citas: ""Hora de llegada"" debe ser posterior a ""Hora de salida"".", +"In the case of 'Use Multi-Level BOM' in a work order, if the user wishes to add sub-assembly costs to Finished Goods items without using a job card as well the scrap items, then this option needs to be enable.","En el caso de ""Utilizar lista de materiales multi-nivel"" en una orden de trabajo, si el usuario desea aƱadir costes de subconjuntos a los artĆ­culos de productos acabados sin utilizar una ficha de trabajo, asĆ­ como a los artĆ­culos de desecho, deberĆ” activar esta opción.", +"In this section, you can define Company-wide transaction-related defaults for this Item. Eg. Default Warehouse, Default Price List, Supplier, etc.","En esta sección, puede definir los valores predeterminados relacionados con las transacciones de toda la empresa para este ArtĆ­culo. Por ejemplo, AlmacĆ©n por defecto, Lista de precios por defecto, Proveedor, etc.", +Inactive Status,Estado inactivo, +Include Account Currency,Incluir la divisa de la cuenta, +Include Closed Orders,Incluye Pedidos Cerrados, +Include Default FB Assets,Incluir activos FB por defecto, +Include Disabled,Incluye Deshabilitado, +Include Expired Batches,Incluir lotes caducados, +Include Safety Stock in Required Qty Calculation,Incluir stock de seguridad en el cĆ”lculo de cantidad requerida, +Include Timesheets in Draft Status,Incluir hojas de horas en estado borrador, +Include Zero Stock Items,Incluir Productos sin existencias, +Incoming Call Handling Schedule,Programa de gestión de llamadas entrantes, +Incoming Call Settings,Configuración de llamadas entrantes, +Incoming Rate (Costing),Tarifa de entrada (costo), +Incorrect Balance Qty After Transaction,Cantidad de saldo incorrecta tras la transacción, +Incorrect Batch Consumed,Lote incorrecto consumido, +Incorrect Check in (group) Warehouse for Reorder,Comprobación incorrecta en (grupo) AlmacĆ©n para Reordenar, +Incorrect Component Quantity,Cantidad incorrecta de componentes, +Incorrect Invoice,Factura incorrecta, +Incorrect Movement Purpose,Movimiento incorrecto Propósito, +Incorrect Payment Type,Tipo de pago incorrecto, +Incorrect Reference Document (Purchase Receipt Item),Documento de referencia incorrecto (partida de recibo de compra), +Incorrect Serial No Valuation,Valoración incorrecta del nĆŗmero de serie, +Incorrect Serial Number Consumed,NĆŗmero de serie incorrecto Consumido, +Incorrect Stock Value Report,Informe incorrecto sobre el valor de las existencias, +Incorrect Type of Transaction,Tipo de transacción incorrecto, +Increase In Asset Life(Months),Aumento de la vida Ćŗtil del activo (meses), +Indent,SangrĆ­a, +Indirect Expense,Gastos Indirectos, +Individual GL Entry cannot be cancelled.,La inscripción GL individual no puede cancelarse., +Individual Stock Ledger Entry cannot be cancelled.,La entrada individual en el Libro Mayor no puede cancelarse., +Initialize Summary Table,Inicializar tabla resumen, +Insert New Records,Insertar nuevos registros, +Inspection Rejected,Inspección Rechazada, +Inspection Submission,Presentación de la inspección, +Instruction,Instrucción, +Insufficient Capacity,Capacidad Insuficiente, +Insufficient Stock for Batch,Stock insuficiente para el lote, +Inter Transfer Reference,Referencia de inter transferencia, +Interest and/or dunning fee,Intereses y/o gastos de reclamación, +Internal,Interno, +Internal Customer,Cliente Interno, +Internal Customer for company {0} already exists,Cliente Interno para empresa {0} ya existe, +Internal Sale or Delivery Reference missing.,Falta referencia de venta o entrega interna., +Internal Sales Reference Missing,Falta la referencia de ventas internas, +Internal Supplier,Proveedor Interno, +Internal Supplier for company {0} already exists,El Proveedor Interno de la compaƱƭa {0} ya existe, +Internal Transfer Reference Missing,Falta referencia de transferencia interna, +Internal Transfers,Transferencias Internas, +Internal transfers can only be done in company's default currency,Las transferencias internas solo se pueden realizar en la moneda predeterminada de la empresa., +Invalid,InvĆ”lido, +Invalid Allocated Amount,Importe asignado no vĆ”lido, +Invalid Amount,Importe no vĆ”lido, +Invalid Auto Repeat Date,Fecha de repetición automĆ”tica invĆ”lida, +Invalid Cost Center,Centro de Costo InvĆ”lido, +Invalid Delivery Date,Fecha de Entrega InvĆ”lida, +Invalid Document,Documento invĆ”lido, +Invalid Document Type,Tipo de Documento InvĆ”lido, +Invalid Formula,Fórmula InvĆ”lida, +Invalid Group By,Agrupar por no vĆ”lido, +Invalid Item Defaults,ArtĆ­culos por defecto no vĆ”lidos, +Invalid Ledger Entries,Entradas no vĆ”lidas en el libro mayor, +Invalid Primary Role,Función principal no vĆ”lida, +Invalid Priority,Prioridad invĆ”lida, +Invalid Process Loss Configuration,Configuración de pĆ©rdida de proceso no vĆ”lida, +Invalid Purchase Invoice,Factura de Compra no vĆ”lida, +Invalid Qty,Cant. invĆ”lida, +Invalid Schedule,Programación no vĆ”lida, +Invalid Serial and Batch Bundle,Paquete de serie y lote no vĆ”lidos, +Invalid Warehouse,AlmacĆ©n invĆ”lido, +Invalid result key. Response:,Clave de resultado no vĆ”lida. Respuesta:, +Invalid value {0} for {1} against account {2},Valor no vĆ”lido {0} para {1} contra la cuenta {2}, +Inventory Dimension,Dimensión del inventario, +Inventory Dimension Negative Stock,Dimensión del inventario Existencias negativas, +Inventory Settings,Configuración de Inventario, +Invoice Cancellation,Cancelación de facturas, +Invoice Limit,LĆ­mite de facturación, +Invoice Portion (%),Porción de Factura (%), +Invoice and Billing,Factura y facturación, +Invoiced Qty,Cant. Facturada, +Invoices and Payments have been Fetched and Allocated,Se han obtenido y asignado facturas y pagos, +Invoicing Features,CaracterĆ­sticas de Facturación, +Is Adjustment Entry,Es entrada de ajuste, +Is Alternative,Es Alternativo, +Is Cash or Non Trade Discount,Es Efectivo o Descuento no Comercial, +Is Composite Asset,Es Activo compuesto, +Is Corrective Job Card,Es Tarjeta de Trabajo Correctiva, +Is Corrective Operation,Es Operación Correctiva, +Is Expandable,Es expansible, +Is Finished Item,Es un artĆ­culo acabado, +Is Fully Depreciated,EstĆ” completamente Depreciado, +Is Group Warehouse,Es AlmacĆ©n de Grupo, +Is Old Subcontracting Flow,Es un antiguo flujo de subcontratación, +Is Outward,Es hacia afuera, +Is Period Closing Voucher Entry,Es la entrada de comprobante de cierre de perĆ­odo, +Is Rate Adjustment Entry (Debit Note),Es Entrada de Ajuste de Tarifa (Nota de DĆ©bito), +Is Recursive,Es recursivo, +Is Rejected,Rechazado, +Is Rejected Warehouse,Es AlmacĆ©n de Rechazados, +Is Scrap Item,Es artĆ­culo de desecho, +Is Short Year,Es AƱo corto, +Is Standard,Es estĆ”ndar, +Is Stock Item,Es artĆ­culo de stock, +Is System Generated,Es generado por el sistema, +Is Tax Withholding Account,Es una cuenta de retención de impuestos, +Is Template,Es Plantilla, +Issue Analytics,AnĆ”lisis de Incidencias, +Issue Summary,Resumen de Incidencias, +Issue a debit note with 0 qty against an existing Sales Invoice,Emitir una Nota de DĆ©bito con cantidad 0 contra una Factura de Venta existente, +Issuing cannot be done to a location. Please enter employee to issue the Asset {0} to,No se puede emitir a una ubicación. Ingrese el empleado al que se emitirĆ” el activo {0}, +It can take upto few hours for accurate stock values to be visible after merging items.,Pueden pasar algunas horas hasta que los valores de stock precisos sean visibles despuĆ©s de fusionar los elementos., +"It's not possible to distribute charges equally when total amount is zero, please set 'Distribute Charges Based On' as 'Quantity'","No es posible distribuir los cargos equitativamente cuando el importe total es cero, por favor configure 'Distribuir cargos en base a' como 'Cantidad'.", +Item Code (Final Product),Código de artĆ­culo (producto final), +Item Group wise Discount,Descuento por grupo de artĆ­culos, +Item Price Settings,Configuración del precio del artĆ­culo, +"Item Price appears multiple times based on Price List, Supplier/Customer, Currency, Item, Batch, UOM, Qty, and Dates.","El precio del producto aparece varias veces segĆŗn la lista de precios, proveedor/cliente, moneda, producto, lote, unidad de medida, cantidad y fechas.", +Item Reference,Referencia del artĆ­culo, +Item Warehouse based reposting has been enabled.,Se ha habilitado el traspaso basado en el almacĆ©n de artĆ­culos., +Item and Warehouse,Producto y AlmacĆ©n, +Item is removed since no serial / batch no selected.,El artĆ­culo se elimina al no haberse seleccionado ningĆŗn nĆŗmero de serie / lote., +Item qty can not be updated as raw materials are already processed.,La cantidad de artĆ­culos no puede actualizarse porque las materias primas ya estĆ”n procesadas., +Item rate has been updated to zero as Allow Zero Valuation Rate is checked for item {0},La tasa del artĆ­culo se ha actualizado a cero ya que la opción Permitir tasa de valoración cero estĆ” marcada para el artĆ­culo {0}, +Item valuation reposting in progress. Report might show incorrect item valuation.,Traspaso de valoración de artĆ­culos en curso. El informe podrĆ­a mostrar una valoración de artĆ­culos incorrecta., +Item {0} cannot be added as a sub-assembly of itself,El artĆ­culo {0} no puede aƱadirse como subconjunto de sĆ­ mismo, +Item {0} cannot be ordered more than {1} against Blanket Order {2}.,El artĆ­culo {0} no se puede pedir mĆ”s de {1} contra el pedido general {2}., +Item {0} does not exist.,El artĆ­culo {0} no existe., +Item {0} entered multiple times.,Producto {0} ingresado varias veces., +Item {0} is already reserved/delivered against Sales Order {1}.,El artĆ­culo {0} ya estĆ” reservado/entregado contra el pedido de venta {1}., +Item {0} must be a Non-Stock Item,El artĆ­culo {0} debe ser un artĆ­culo que no se encuentra en stock, +Item {0} not found in 'Raw Materials Supplied' table in {1} {2},El artĆ­culo {0} no se encontró en la tabla 'Materias primas suministradas' en {1} {2}, +Item {0} not found.,ArtĆ­culo {0} no encontrado., +Item {} does not exist.,Producto {0} no existe., +Items & Pricing,Productos y Precios, +Items Catalogue,CatĆ”logo de Productos, +Items cannot be updated as Subcontracting Order is created against the Purchase Order {0}.,Los artĆ­culos no se pueden actualizar ya que la orden de subcontratación se crea contra la orden de compra {0}., +Items rate has been updated to zero as Allow Zero Valuation Rate is checked for the following items: {0},"La tasa de artĆ­culos se ha actualizado a cero, ya que la opción Permitir tasa de valoración cero estĆ” marcada para los siguientes artĆ­culos: {0}", +Items to Be Repost,ArtĆ­culos a reenviar, +Items to Order and Receive,Productos para Ordenar y Recibir, +Items to Reserve,ArtĆ­culos para reservar, +Items {0} do not exist in the Item master.,Los artĆ­culos {0} no existen en el maestro de artĆ­culos., +Job Capacity,Capacidad de Trabajo, +Job Card Operation,Ficha de trabajo Operación, +Job Card Scheduled Time,Ficha de trabajo Hora programada, +Job Card Scrap Item,Ficha de trabajo ArtĆ­culo de desecho, +Job Card and Capacity Planning,Tarjeta de trabajo y planificación de capacidad, +Job Cards,Tarjetas de Trabajo, +Job Paused,Trabajo en pausa, +Job Worker,Trabajador, +Job Worker Address,Dirección del trabajador, +Job Worker Address Details,Datos de la dirección del trabajador, +Job Worker Contact,Contacto del trabajador, +Job Worker Delivery Note,AlbarĆ”n del trabajador, +Job Worker Name,Nombre del trabajador, +Job Worker Warehouse,Trabajador de almacĆ©n, +Job: {0} has been triggered for processing failed transactions,Trabajo: {0} se ha activado para procesar transacciones fallidas, +Joining,UniĆ©ndose, +Journal Entries,Entradas de diario, +Journal Entry for Asset scrapping cannot be cancelled. Please restore the Asset.,No se puede cancelar la entrada del diario correspondiente al desguace de activos. Restaure el activo., +Journal Entry type should be set as Depreciation Entry for asset depreciation,El tipo de entrada de diario debe configurarse como Entrada de depreciación para la depreciación de activos., +Journal entries have been created,Se han creado entradas de diario, +Journals,Diarios, +Key,Clave, +Kindly cancel the Manufacturing Entries first against the work order {0}.,Por favor cancele primero las entradas de fabricación contra la orden de trabajo {0}., +"Last Name, Email or Phone/Mobile of the user are mandatory to continue.","Apellido, Email o TelĆ©fono/Móvil del usuario son obligatorios para continuar.", +Lead -> Prospect,Cliente potencial -> Prospecto, +Lead Conversion Time,Tiempo de conversión de clientes potenciales, +Lead Owner cannot be same as the Lead Email Address,El propietario del cliente potencial no puede ser el mismo que la dirección de correo electrónico del cliente potencial, +Lead {0} has been added to prospect {1}.,El cliente potencial {0} se ha agregado al prospecto {1}., +Leaderboard,Tabla de lĆ­deres, +Leads,Clientes potenciales, +Learn Accounting,Aprender Contabilidad, +Learn Inventory Management,Aprende la Gestión del Inventario, +Learn Manufacturing,Aprender Manufactura, +Learn Procurement,Aprender Compras, +Learn Project Management,Aprender Gestión de Proyecto, +Learn Sales Management,Aprender Gestión de Ventas, +"Learn about Common Party","Conozca mĆ”s sobre Partes comĆŗnes", +"Leave blank for home. +This is relative to site URL, for example ""about"" will redirect to ""https://yoursitename.com/about""","DĆ©jelo en blanco para la pĆ”gina de inicio. +Esto es relativo a la URL del sitio, por ejemplo ""acerca de"" redirigirĆ” a ""https://yoursitename.com/about""", +Ledger Health,Estado del libro mayor, +Ledger Health Monitor,Monitor de estado del libro mayor, +Ledger Health Monitor Company,Monitor de estado del libro mayor Empresa, +Ledger Merge,Fusión de libro mayor, +Ledger Merge Accounts,Cuentas de fusión de libro mayor, +Ledgers,Libros mayores, +Left Child,Secundario izquierdo, +Legend,Leyenda, +Length,Largo, +Length (cm),Longitud (cm), +Less than 12 months.,Menos de 12 meses., +Level (BOM),Nivel (lista de materiales), +Limit timeslot for Stock Reposting,Limitar el tiempo disponible para la reubicación de existencias, +Limits don't apply on,Los lĆ­mites no se aplican en, +Link a new bank account,Vincular una nueva cuenta bancaria, +Link with Customer,Enlace con el cliente, +Link with Supplier,Enlace con el proveedor, +Linked with submitted documents,Vinculado con los documentos validados, +Linking Failed,Enlace fallido, +Linking to Customer Failed. Please try again.,Error al vincular al cliente. IntĆ©ntalo de nuevo., +Linking to Supplier Failed. Please try again.,Error al vincular al proveedor. IntĆ©ntalo nuevamente., +Links,Enlaces, +Loading import file...,Cargando archivo de importación..., +Locked,Bloqueado, +Log Entries,Entradas de registro, +Log the selling and buying rate of an Item,Registra la tasa de venta y compra de un artĆ­culo, +Lost Quotations,Cotizaciones perdidas, +Lost Quotations %,Cotizaciones perdidas %, +Lost Reasons are required in case opportunity is Lost.,Se requieren motivos de pĆ©rdida en caso de que se pierda la oportunidad., +Lost Value,Valor perdido, +Lost Value %,% de valor perdido, +Machine Type,Tipo de MĆ”quina, +Main Cost Center,Centro de Costo principal, +Main Cost Center {0} cannot be entered in the child table,El centro de costo principal {0} no se puede ingresar en la tabla secundaria, +Maintain Asset,Mantener activos, +Maintenance Details,Detalles de mantenimiento, +Make ,Crear , +Make Asset Movement,Realizar movimiento de activos, +Make Quotation,Hacer Cotización, +Make Return Entry,Hacer Entrada de Devolución, +Make Serial No / Batch from Work Order,Crear nĆŗmero de serie/lote a partir de la orden de trabajo, +Make {0} Variant,Hacer {0} variante, +Make {0} Variants,Hacer {0} variantes, +Making Journal Entries against advance accounts: {0} is not recommended. These Journals won't be available for Reconciliation.,No se recomienda realizar asientos contables contra cuentas anticipadas: {0} . Estos asientos contables no estarĆ”n disponibles para la conciliación., +Manage,Gestionar, +Mandatory Accounting Dimension,Dimensión contable obligatoria, +Mandatory Depends On,Obligatorio depende de, +Mandatory Field,Campo obligatorio, +Mandatory Section,Sección obligatoria, +Manual Inspection,Inspección Manual, +Manufacture Sub-assembly in Operation,Subensamblaje de Manufactura en Operación, +Manufacturing Type,Tipo de fabricación, +Mapping Purchase Receipt ...,Mapeando Recibo de Compra..., +Mapping Subcontracting Order ...,Mapeando órdenes de subcontratación..., +Mapping {0} ...,Mapeando {0} ..., +Margin View,Vista de Margen, +Mark As Closed,Marcar como cerrado, +Material Returned from WIP,Material devuelto de WIP, +Material Transfer (In Transit),Transferencia de material (en trĆ”nsito), +Materials are already received against the {0} {1},Los materiales ya se recibieron contra el {0} {1}, +Materials needs to be transferred to the work in progress warehouse for the job card {0},Es necesario transferir los materiales al almacĆ©n de trabajos en curso para la ficha de trabajo {0}, +Max Qty (As Per Stock UOM),Cantidad mĆ”xima (segĆŗn unidad de medida en existencia), +Maximum Net Rate,Tasa Neta MĆ”xima, +Maximum Payment Amount,Importe mĆ”ximo del pago, +Maximum Value,Valor MĆ”ximo, +Maximum quantity scanned for item {0}.,Cantidad mĆ”xima escaneada para el artĆ­culo {0}., +Meeting,Reunión, +Mention if non-standard Receivable account,Indique si no es Cuenta por Cobrar estĆ”ndar, +Merge Invoices Based On,Fusionar facturas en función de, +Merge Progress,Progreso de fusión, +Merge Similar Account Heads,Fusionar cuentas similares, +Merge taxes from multiple documents,Fusionar impuestos de varios documentos, +Merged,Combinado, +"Merging is only possible if following properties are same in both records. Is Group, Root Type, Company and Account Currency","La fusión solo es posible si las siguientes propiedades son las mismas en ambos registros: grupo, tipo de raĆ­z, empresa y moneda de la cuenta.", +Merging {0} of {1},Fusionando {0} de {1}, +Min Qty (As Per Stock UOM),Cant. mĆ­n. (segĆŗn UdM en almacĆ©n), +Min Qty should be greater than Recurse Over Qty,La cantidad mĆ­nima debe ser mayor que la cantidad recursiva, +Minimum Net Rate,Tasa Neta MĆ­nima, +Minimum Payment Amount,Importe mĆ­nimo de pago, +Minimum Value,Valor mĆ­nimo, +Mismatch,Desajuste, +Missing,Faltante, +Missing Asset,Activo faltante, +Missing Cost Center,Centro de costos faltante, +Missing Finance Book,Falta el libro de finanzas, +Missing Finished Good,Falta Acabado Bueno, +Missing Formula,Falta fórmula, +Missing Items,Faltan artĆ­culos, +Missing Payments App,Aplicación de pagos faltantes, +Missing Serial No Bundle,Falta el nĆŗmero de serie del paquete, +Missing value,Valor faltante, +Modified By,Modificado por, +Modified On,Modificado el, +Module Settings,Configuración de Módulos, +Monitor for Last 'X' days,"Monitorización de los Ćŗltimos ""X"" dĆ­as", +Move Stock,Mover Stock, +Move to Cart,Mover al carrito, +Movement,Movimiento, +Moving up in tree ...,Subiendo en Ć”rbol ..., +Multi-level BOM Creator,Creador de listas de materiales multinivel, +Multiple Loyalty Programs found for Customer {}. Please select manually.,Se encontraron varios programas de fidelización para el cliente {}. Seleccione manualmente., +Multiple Warehouse Accounts,MĆŗltiples cuentas de almacĆ©n, +Multiple items cannot be marked as finished item,No se pueden marcar varios artĆ­culos como artĆ­culo terminado, +Must be a publicly accessible Google Sheets URL and adding Bank Account column is necessary for importing via Google Sheets,Debe ser una URL de Hojas de cĆ”lculo de Google de acceso pĆŗblico y es necesario agregar la columna Cuenta bancaria para importar a travĆ©s de Hojas de cĆ”lculo de Google., +Named Place,Lugar nombrado, +Naming Series and Price Defaults,Series de Nombres y Precios por Defecto, +Net total calculation precision loss,PĆ©rdida neta total de precisión de cĆ”lculo, +New Balance In Account Currency,Nuevo saldo en la moneda de la cuenta, +New Event,Nuevo Evento, +New Note,Nueva Nota, +New Task,Nueva Tarea, +New Version,Nueva versión, +Newsletter,BoletĆ­n de noticias, +No Answer,Sin respuesta, +No Customers found with selected options.,No se encontraron clientes con las opciones seleccionadas., +No Items selected for transfer.,No hay artĆ­culos seleccionados para transferir., +No Matching Bank Transactions Found,No se han encontrado transacciones bancarias coincidentes, +No Notes,Sin notas, +No Outstanding Invoices found for this party,No se encontraron facturas pendientes para esta parte, +No POS Profile found. Please create a New POS Profile first,"No se ha encontrado ningĆŗn Perfil de PV. Por favor, cree primero un Nuevo Perfil PV", +No Records for these settings.,No hay registros para estas configuraciones., +No Serial / Batches are available for return,No hay nĆŗmeros de serie ni lotes disponibles para devolución, +No Stock Available Currently,No hay existencias disponibles actualmente, +No Summary,Sin resumen, +No Tax Withholding data found for the current posting date.,No se han encontrado datos de retenciones fiscales para la fecha de contabilización actual., +No Terms,Sin tĆ©rminos, +No Unreconciled Invoices and Payments found for this party and account,No se encontraron facturas ni pagos sin conciliar para esta parte y cuenta, +No Unreconciled Payments found for this party,No se encontraron pagos no conciliados para esta parte, +No Work Orders were created,No se crearon órdenes de trabajo, +No additional fields available,No hay campos adicionales disponibles, +No billing email found for customer: {0},No se encontró ningĆŗn correo electrónico de facturación para el cliente: {0}, +No data found. Seems like you uploaded a blank file,No se encontraron datos. Parece que has subido un archivo en blanco., +No employee was scheduled for call popup,No se programó ningĆŗn empleado para la ventana emergente de llamada, +No failed logs,No hay registros fallidos, +No item available for transfer.,No hay ningĆŗn artĆ­culo disponible para transferencia., +No items are available in sales orders {0} for production,No hay artĆ­culos disponibles en los pedidos de venta {0} para producción, +No items are available in the sales order {0} for production,No hay artĆ­culos disponibles en la orden de venta {0} para producción, +No items in cart,No hay artĆ­culos en el carrito, +No matches occurred via auto reconciliation,No se produjeron coincidencias mediante la conciliación automĆ”tica, +No more children on Left,No mĆ”s secundarios en la izquierda, +No more children on Right,No mĆ”s secundarios en la derecha, +No of Docs,NĀŗ de documentos, +No of Employees,NĆŗm. de Empleados, +No of Months (Expense),NĀŗ de meses (gastos), +No of Months (Revenue),NĀŗ de meses (Ingresos), +No open event,NingĆŗn evento abierto, +No open task,Sin tareas abiertas, +No outstanding {0} found for the {1} {2} which qualify the filters you have specified.,No se encontraron {0} pendientes para los {1} {2} que califican para los filtros que ha especificado., +No primary email found for customer: {0},No se encontró ningĆŗn correo electrónico principal para el cliente: {0}, +No records found in Allocation table,No se encontraron registros en la tabla de asignación, +No records found in the Invoices table,No se encontraron registros en la tabla Facturas, +No records found in the Payments table,No se encontraron registros en la tabla Pagos, +No stock transactions can be created or modified before this date.,No se podrĆ”n crear ni modificar transacciones de stock antes de esta fecha., +No {0} Accounts found for this company.,No se han encontrado cuentas en {0} para esta empresa., +No.,NĀŗ, +No. of Employees,NĆŗm. de Empleados, +No. of parallel job cards which can be allowed on this workstation. Example: 2 would mean this workstation can process production for two Work Orders at a time.,NĀŗ de tarjetas de trabajo paralelas que se pueden permitir en esta estación de trabajo. Ejemplo: 2 significarĆ­a que esta estación de trabajo puede procesar la producción de dos Ɠrdenes de Trabajo a la vez., +Note: Automatic log deletion only applies to logs of type Update Cost,Nota: El borrado automĆ”tico de registros sólo se aplica a los registros de tipo Coste de actualización, +"Note: To merge the items, create a separate Stock Reconciliation for the old item {0}","Nota: Para fusionar los artĆ­culos, cree una reconciliación de existencias separada para el antiguo artĆ­culo {0}.", +Notes HTML,Notas HTML, +Notification,Notificación, +Notification Settings,Configuración de las notificaciones, +Notify Reposting Error to Role,Notificar error de reenvĆ­o al rol, +Number of Days,NĆŗmero de dĆ­as, +Numeric,NumĆ©rico, +Numeric Inspection,Inspección numĆ©rica, +Off,Apagado, +Offsetting Account,Cuenta de compensación, +Offsetting for Accounting Dimension,Compensación de la dimensión contable, +On Paid Amount,Sobre el importe pagado, +On This Date,En esta fecha, +On Track,En marcha, +On enabling this cancellation entries will be posted on the actual cancellation date and reports will consider cancelled entries as well,Al habilitar esta cancelación las entradas se contabilizarĆ”n en la fecha real de cancelación y los informes tambiĆ©n tendrĆ”n en cuenta las entradas canceladas, +"On expanding a row in the Items to Manufacture table, you'll see an option to 'Include Exploded Items'. Ticking this includes raw materials of the sub-assembly items in the production process.","Al expandir una fila en la tabla de ArtĆ­culos para fabricar, verĆ” una opción para ""Incluir artĆ­culos despiezados"". Al marcar esta opción, se incluyen las materias primas de los artĆ­culos del subconjunto en el proceso de producción.", +"On submission of the stock transaction, system will auto create the Serial and Batch Bundle based on the Serial No / Batch fields.","En la validación la transacción de existencias, el sistema crearĆ” automĆ”ticamente el lote de series y lotes basĆ”ndose en los campos NĆŗmero de serie / Lote.", +Once the Work Order is Closed. It can't be resumed.,Una vez cerrada la Orden de Trabajo. No se puede reanudar., +Only 'Payment Entries' made against this advance account are supported.,Sólo se admiten 'Entradas de pago' realizadas contra esta cuenta de anticipo., +Only CSV and Excel files can be used to for importing data. Please check the file format you are trying to upload,Solo se pueden utilizar archivos CSV y Excel para importar datos. Verifique el formato del archivo que intenta cargar, +Only Deduct Tax On Excess Amount ,Deducir impuestos solo sobre el importe excedente , +Only Include Allocated Payments,Incluir sólo los pagos asignados, +Only Parent can be of type {0},Sólo el principal puede ser del tipo {0}, +Only existing assets,Solo activos existentes, +"Only one Subcontracting Order can be created against a Purchase Order, cancel the existing Subcontracting Order to create a new one.",Solo se puede crear una orden de subcontratación contra una orden de compra; cancele la orden de subcontratación existente para crear una nueva., +Only one {0} entry can be created against the Work Order {1},Solo se puede crear una entrada {0} contra la orden de trabajo {1}, +"Only values between [0,1) are allowed. Like {0.00, 0.04, 0.09, ...} +Ex: If allowance is set at 0.07, accounts that have balance of 0.07 in either of the currencies will be considered as zero balance account","Solo se permiten valores entre [0,1). Como {0,00, 0,04, 0,09, ...} +Ejemplo: si la asignación se establece en 0,07, las cuentas que tengan un saldo de 0,07 en cualquiera de las monedas se considerarĆ”n cuentas con saldo cero.", +Only {0} are supported,Solo se admiten {0}, +Open Activities HTML,Abrir actividades HTML, +Open Call Log,Abrir registro de llamadas, +Open Event,Abrir Evento, +Open Events,Eventos abiertos, +Open Sales Orders,Ɠrdenes de venta abiertas, +Open Task,Abrir tarea, +Open Tasks,Tareas abiertas, +Open Work Order {0},Orden de trabajo abierta {0}, +Opening & Closing,Apertura y cierre, +Opening Accumulated Depreciation must be less than or equal to {0},La depreciación acumulada de apertura debe ser inferior o igual a {0}., +Opening Entry can not be created after Period Closing Voucher is created.,El asiento de apertura no puede crearse despuĆ©s de haber creado el comprobante de cierre del perĆ­odo., +Opening Number of Booked Depreciations,NĆŗmero de apertura de depreciaciones registradas, +Opening Purchase Invoices have been created.,Se han creado facturas de compra de apertura., +Opening Sales Invoices have been created.,Se han creado facturas de venta de apertura., +Operating Cost Per BOM Quantity,Coste operativo por cantidad de la lista de materiales, +Operation time does not depend on quantity to produce,El tiempo de operación no depende de la cantidad a producir, +Opportunity Amount (Company Currency),Valor de la oportunidad (moneda de la empresa), +Opportunity Owner,Propietario de oportunidad, +Opportunity Source,Fuente de oportunidad, +Opportunity Summary by Sales Stage,Resumen de oportunidades por etapa de venta, +Opportunity Summary by Sales Stage ,Resumen de oportunidades por etapa de venta , +Opportunity Value,Valor de oportunidad, +Order Status,Estado del Pedido, +Other Info,Información adicional, +Out of stock,Agotado, +Over Picking Allowance,Tolerancia por exceso de recogida, +Over Receipt,Sobre recibo, +Over Receipt/Delivery of {0} {1} ignored for item {2} because you have {3} role.,Se ignora la recepción/entrega excesiva de {0} {1} para el artĆ­culo {2} porque tiene el rol {3} ., +Over Transfer Allowance,Exceso de asignación por transferencia, +Overbilling of {0} {1} ignored for item {2} because you have {3} role.,Sobrefacturación de {0} {1} ignorada para el artĆ­culo {2} porque tiene el rol {3} ., +Overbilling of {} ignored because you have {} role.,Se ignora la sobrefacturación de {} porque tiene el rol {}., +Overdue Payment,Pago vencido, +Overdue Payments,Pagos vencidos, +Overdue Tasks,Tareas atrasadas, +Overview,Visión general, +PDF Name,Nombre del PDF, +POS Closing Failed,Cierre de TPV fallido, +POS Closing failed while running in a background process. You can resolve the {0} and retry the process again.,El Cierre del TPV falló mientras se ejecutaba un proceso en segundo plano. Puede resolver el {0} y reintentar el proceso de nuevo., +POS Invoice is already consolidated,La factura del TPV ya estĆ” consolidada, +POS Invoice is not submitted,La factura del TPV no se presenta, +POS Invoice should have the field {0} checked.,La factura del TPV debe tener el campo {} marcado., +POS Invoices will be consolidated in a background process,Las facturas de TPV se consolidarĆ”n en un proceso en segundo plano, +POS Invoices will be unconsolidated in a background process,Las facturas de TPV no se consolidarĆ”n en un proceso en segundo plano, +POS Profile doesn't match {},El perfil de TPV no coincide con {}, +POS Profile {} contains Mode of Payment {}. Please remove them to disable this mode.,"El Perfil de TPV {} contiene el Modo de Pago {}. Por favor, elimĆ­nelos para desactivar este modo.", +POS Search Fields,Campos de bĆŗsqueda en el TPV, +POS Setting,Ajuste del TPV, +Package No(s) already in use. Try from Package No {0},Los nĆŗmeros de paquete ya estĆ”n en uso. Pruebe desde el nĆŗmero de paquete {0}, +Packaging Slip From Delivery Note,Recibo de embalaje de la nota de entrega, +Packed Items cannot be transferred internally,Los artĆ­culos empaquetados no se pueden transferir internamente, +Packed Qty,Cantidad empaquetada, +Page Break After Each SoA,Salto de pĆ”gina despuĆ©s de cada SoA, +Paid Amount After Tax,Importe pagado despuĆ©s de impuestos, +Paid Amount After Tax (Company Currency),Importe pagado despuĆ©s de impuestos (moneda de la empresa), +Paid From Account Type,Pagado desde tipo de cuenta, +Paid To Account Type,Tipo de cuenta pagada, +Pallets,PalĆ©s, +Parameter Group,Grupo de parĆ”metros, +Parameter Group Name,Nombre del grupo de parĆ”metros, +Parcel Template,Plantilla de paquete, +Parcel Template Name,Nombre de la plantilla de parcela, +Parcel weight cannot be 0,El peso del paquete no puede ser 0, +Parcels,Paquetes, +Parent Account Missing,Falta la cuenta principal, +Parent Document,Documento Principal, +Parent Item {0} must not be a Fixed Asset,El artĆ­culo principal {0} no debe ser un activo fijo, +Parent Row No,NĆŗmero de fila principal, +Parent Task {0} is not a Template Task,La tarea principal {0} no es una tarea de plantilla, +Partial Material Transferred,Material parcial transferido, +Partial Stock Reservation,Reserva parcial de stock, +Partial Success,Ɖxito Parcial, +"Partial stock can be reserved. For example, If you have a Sales Order of 100 units and the Available Stock is 90 units then a Stock Reservation Entry will be created for 90 units. ","Se puede reservar un stock parcial. Por ejemplo, si tiene un pedido de venta de 100 unidades y el stock disponible es de 90 unidades, se crearĆ” una entrada de reserva de stock para 90 unidades. ", +Partially Delivered,Entregado parcialmente, +Partially Reconciled,Parcialmente reconciliado, +Partially Reserved,Parcialmente reservado, +Partly Paid,Pagado parcialmente, +Partly Paid and Discounted,Parcialmente pagado y descontado, +Partnership,Asociación, +Party Account No. (Bank Statement),NĆŗmero de cuenta de la Entidad(extracto bancario), +Party Account {0} currency ({1}) and document currency ({2}) should be same,La moneda de la cuenta de la Entidad {0} ({1}) y la moneda del documento ({2}) deben ser las mismas, +Party IBAN (Bank Statement),IBAN del destinatario (extracto bancario), +Party Item Code,Código de artĆ­culo de la Entidad, +Party Link,Enlace de la Entidad, +Party Name/Account Holder (Bank Statement),Nombre de la Entidad/titular de la cuenta (extracto bancario), +Party Specific Item,Producto especĆ­fico de la Parte, +Party Type and Party is required for Receivable / Payable account {0},Se requiere el tipo de Entidad y la Entidad para la cuenta por cobrar/pagar {0}, +Party can only be one of {0},La Entidad solo puede ser una de {0}, +Passport Details,Datos del pasaporte, +Pause Job,Pausar trabajo, +Paused,Pausado, +Payment Amount (Company Currency),Importe del pago (moneda de la empresa), +"Payment Entry {0} is linked against Order {1}, check if it should be pulled as advance in this invoice.","La entrada de pago {0} estĆ” vinculada al pedido {1}, verifique si debe extraerse como anticipo en esta factura.", +Payment Ledger,Libro de pagos, +Payment Ledger Balance,Saldo del libro mayor de pagos, +Payment Ledger Entry,Entrada de libro mayor de pagos, +Payment Limit,LĆ­mite de pago, +Payment Reconciliation Allocation,Asignación de conciliación de pagos, +Payment Reconciliation Job: {0} is running for this party. Can't reconcile now.,Trabajo de conciliación de pagos: {0} se estĆ” ejecutando para esta parte. No se puede conciliar ahora., +Payment Reconciliations,Conciliaciones de pagos, +Payment Request Outstanding,Solicitud de pago pendiente, +Payment Request created from Sales Order or Purchase Order will be in Draft status. When disabled document will be in unsaved state.,"La solicitud de pago creada a partir de una orden de venta o una orden de compra estarĆ” en estado de borrador. Si estĆ” deshabilitada, el documento estarĆ” en estado de no guardado.", +Payment Request is already created,La solicitud de pago ya estĆ” creada, +Payment Request took too long to respond. Please try requesting for payment again.,La solicitud de pago tardó demasiado en responder. Intente solicitar el pago nuevamente., +Payment Requests cannot be created against: {0},No se pueden crear solicitudes de pago contra: {0}, +Payment Term Outstanding,Plazo de pago pendiente, +Payment Terms Status for Sales Order,Estado de las condiciones de pago de la orden de venta, +Payment Terms from orders will be fetched into the invoices as is,Las condiciones de pago de los pedidos se reflejarĆ”n en las facturas tal como estĆ”n., +Payment Unlink Error,Error al desvincular el pago, +Payment of {0} received successfully.,Pago de {0} recibido exitosamente., +Payment of {0} received successfully. Waiting for other requests to complete...,Pago de {0} recibido con Ć©xito. Esperando que se completen otras solicitudes..., +Payment request failed,Solicitud de pago fallida, +Payment term {0} not used in {1},TĆ©rmino de pago {0} no utilizado en {1}, +Pending processing,Pendiente de procesamiento, +Per Received,Por recibido, +Percentage (%),Porcentaje (%), +Percentage you are allowed to order beyond the Blanket Order quantity.,Porcentaje que se le permite pedir mĆ”s allĆ” de la cantidad del pedido general., +Percentage you are allowed to sell beyond the Blanket Order quantity.,Porcentaje que se le permite vender mĆ”s allĆ” de la cantidad del pedido general., +Period Closed,PerĆ­odo cerrado, +Period Closing Entry For Current Period,Asiento de cierre de perĆ­odo para el perĆ­odo actual, +Period Closing Settings,Configuración de cierre de perĆ­odo, +Period Details,Detalles del periodo, +Period To Date,PerĆ­odo hasta la fecha, +Period_from_date,Periodo_desde_fecha, +Phone Ext.,Extensión telefónica, +Pick List Incomplete,Lista de selección incompleta, +Pick Manually,Seleccionar manualmente, +Pick Serial / Batch Based On,Selección de serie / lote basada en, +Pick Serial / Batch No,Seleccione el nĆŗmero de serie/lote, +Picked Qty (in Stock UOM),Cantidad seleccionada (en stock UdM), +Pickup,Recogida, +Pickup Contact Person,Persona de contacto para la recogida, +Pickup Date,Fecha de recogida, +Pickup Date cannot be before this day,La fecha de recogida no puede ser anterior a este dĆ­a., +Pickup From,Recoger de, +Pickup To time should be greater than Pickup From time,La hora de recogida hasta debe ser mayor que la hora de recogida desde, +Pickup Type,Tipo de recogida, +Pickup from,Recoger de, +Pickup to,Recogida a, +Pipeline By,Fuente de información por, +Plaid Link Failed,Vinculación a Plaid fallido, +Plaid Link Refresh Required,Se requiere actualización de la vinculación con Plaid, +Plaid Link Updated,Enlace de Plaid actualizado, +Plan to Request Qty,Plan para solicitar cantidad, +Plant Dashboard,Panel de control de la planta, +Plant Floor,Planta, +Please Set Priority,"Por favor, establezca la prioridad", +Please Specify Account,Por favor especifique la cuenta, +Please add 'Supplier' role to user {0}.,"Por favor, aƱada el rol 'Proveedor' al usuario {0}.", +Please add Request for Quotation to the sidebar in Portal Settings.,"Por favor, aƱada la Solicitud de Presupuesto a la barra lateral en los Ajustes del Portal.", +Please add Root Account for - {0},"Por favor, aƱada una cuenta raĆ­z para - {0}", +Please add atleast one Serial No / Batch No,"Por favor, aƱada al menos un nĀŗ de serie / nĀŗ de lote", +Please add the Bank Account column,"Por favor, aƱada la columna Cuenta bancaria", +Please add the account to root level Company - {0},"Por favor, aƱada la cuenta al nivel raĆ­z Empresa - {0}", +Please add {1} role to user {0}.,"Por favor, aƱada el rol {1} al usuario {0}.", +Please adjust the qty or edit {0} to proceed.,Ajuste la cantidad o edite {0} para continuar., +Please attach CSV file,Adjunte el archivo CSV, +Please cancel and amend the Payment Entry,"Por favor, cancele y modifique la Entrada de Pago", +Please cancel payment entry manually first,"Por favor, cancele primero la entrada del pago manualmente", +Please cancel related transaction.,"Por favor, cancele la transacción relacionada.", +Please check Process Deferred Accounting {0} and submit manually after resolving errors.,"Por favor, marque Procesar contabilidad diferida {0} y valĆ­delo manualmente despuĆ©s de resolver los errores.", +Please check either with operations or FG Based Operating Cost.,Consulte con operaciones o con el costo operativo basado en FG., +Please check the error message and take necessary actions to fix the error and then restart the reposting again.,"Por favor, compruebe el mensaje de error y tome las medidas necesarias para solucionar el error y luego reinicie el reenvĆ­o de nuevo.", +Please check your email to confirm the appointment,"Por favor, compruebe su correo electrónico para confirmar la cita", +Please contact any of the following users to extend the credit limits for {0}: {1},ComunĆ­quese con cualquiera de los siguientes usuarios para ampliar los lĆ­mites de crĆ©dito para {0}: {1}, +Please contact any of the following users to {} this transaction.,"Por favor, póngase en contacto con cualquiera de los siguientes usuarios para {} esta transacción.", +Please contact your administrator to extend the credit limits for {0}.,Póngase en contacto con su administrador para ampliar los lĆ­mites de crĆ©dito de {0}., +Please create Landed Cost Vouchers against Invoices that have 'Update Stock' enabled.,"Cree comprobantes de costo de destino contra facturas que tengan habilitada la opción ""Actualizar stock"".", +Please create a new Accounting Dimension if required.,"Por favor, cree una nueva Dimensión Contable si es necesario.", +Please create purchase from internal sale or delivery document itself,"Por favor, cree la compra a partir de la venta interna o del propio documento de entrega", +"Please delete Product Bundle {0}, before merging {1} into {2}","Por favor, elimine el paquete de productos {0}, antes de fusionar {1} en {2}", +Please do not book expense of multiple assets against one single Asset.,"Por favor, no contabilice gastos de mĆŗltiples activos contra un Ćŗnico Activo.", +Please enable Use Old Serial / Batch Fields to make_bundle,"Por favor, active Usar campos de serie / lote antiguos en make_bundle", +Please enable only if the understand the effects of enabling this.,HabilĆ­telo solo si comprende los efectos de habilitar esto., +Please enable {0} in the {1}.,"Por favor, habilite {0} en la pĆ”gina {1}.", +Please enable {} in {} to allow same item in multiple rows,"Por favor, active {} en {} para permitir el mismo elemento en varias filas", +Please ensure that the {0} account is a Balance Sheet account. You can change the parent account to a Balance Sheet account or select a different account.,AsegĆŗrese de que la cuenta {0} es una cuenta de Balance. Puede cambiar la cuenta padre a una cuenta de Balance o seleccionar una cuenta diferente., +Please ensure that the {0} account {1} is a Payable account. You can change the account type to Payable or select a different account.,AsegĆŗrese de que la cuenta {0} {1} sea una cuenta de pago. Puede cambiar el tipo de cuenta a pago o seleccionar una cuenta diferente., +Please ensure {} account is a Balance Sheet account.,AsegĆŗrese de que la cuenta {} sea una cuenta de balance general., +Please ensure {} account {} is a Receivable account.,AsegĆŗrese de que {} cuenta {} sea una cuenta por cobrar., +Please enter Root Type for account- {0},"Por favor, introduzca el tipo de cuenta- {0}", +Please enter Serial Nos,"Por favor, introduzca Serial Nos", +Please enter Shipment Parcel information,"Por favor, introduzca la información del paquete de envĆ­o", +Please enter Stock Items consumed during the Repair.,Ingrese los artĆ­culos en stock consumidos durante la reparación., +Please enter mobile number first.,"Por favor, introduzca primero el nĆŗmero de móvil.", +Please enter quantity for item {0},Ingrese la cantidad del artĆ­culo {0}, +Please enter serial nos,"Por favor, introduzca los nĆŗmeros de serie", +"Please first set Last Name, Email and Phone for the user","Primero configure el apellido, el correo electrónico y el telĆ©fono del usuario.", +Please fix overlapping time slots for {0},"Por favor, arreglen los espacios de tiempo superpuestos para {0}", +Please fix overlapping time slots for {0}.,"Por favor, arregle los espacios de tiempo superpuestos para {0}.", +Please import accounts against parent company or enable {} in company master.,"Por favor, importe las cuentas contra la empresa principal o habilite {} en el maestro de empresas.", +"Please keep one Applicable Charges, when 'Distribute Charges Based On' is 'Distribute Manually'. For more charges, please create another Landed Cost Voucher.","Conserve un solo Cargo aplicable cuando la opción ""Distribuir cargos segĆŗn"" estĆ© configurada como ""Distribuir manualmente"". Para mĆ”s cargos, cree otro Comprobante de costo de destino.", +Please make sure the file you are using has 'Parent Account' column present in the header.,AsegĆŗrese de que el archivo que estĆ” utilizando tenga la columna 'Cuenta principal' presente en el encabezado., +Please mention 'Weight UOM' along with Weight.,Mencione 'Peso UdM' junto con el Peso., +Please mention the Current and New BOM for replacement.,"Por favor, mencione la lista de materiales actual y la nueva para la sustitución.", +Please rectify and try again.,"Por favor, corrija y vuelva a intentarlo.", +Please refresh or reset the Plaid linking of the Bank {}.,"Por favor, actualice o reinicie la vinculación del Banco con Plaid {}.", +Please save before proceeding.,"Por favor, guarde antes de continuar.", +Please select Bank Account,"Por favor, seleccione Cuenta Bancaria", +Please select Finished Good Item for Service Item {0},"Por favor, seleccione ArtĆ­culo de producto terminado para el artĆ­culo de servicio {0}", +Please select Serial/Batch Nos to reserve or change Reservation Based On to Qty.,Seleccione los nĆŗmeros de serie/lote para reservar o cambie Reserva basada en a Cantidad., +Please select Subcontracting Order instead of Purchase Order {0},Por favor seleccione Orden de Subcontratación en lugar de Orden de Compra {0}, +Please select Unrealized Profit / Loss account or add default Unrealized Profit / Loss account account for company {0},Seleccione la cuenta de ganancias/pĆ©rdidas no realizadas o agregue la cuenta de ganancias/pĆ©rdidas no realizadas predeterminada para la empresa {0}, +Please select a Subcontracting Purchase Order.,Seleccione una orden de compra de subcontratación., +Please select a Warehouse,Por favor seleccione un almacĆ©n, +Please select a Work Order first.,Seleccione primero una orden de trabajo., +Please select a country,"Por favor, selecciona un paĆ­s", +Please select a customer for fetching payments.,Seleccione un cliente para obtener los pagos., +Please select a date,"Por favor, seleccione una fecha", +Please select a date and time,Por favor seleccione una fecha y hora, +Please select a row to create a Reposting Entry,Seleccione una fila para crear una entrada de reenvĆ­o, +Please select a supplier for fetching payments.,"Por favor, seleccione un proveedor para obtener los pagos.", +Please select a valid Purchase Order that has Service Items.,"Por favor, seleccione una Orden de Compra vĆ”lida que tenga ArtĆ­culos de Servicio.", +Please select a valid Purchase Order that is configured for Subcontracting.,"Por favor, seleccione un Pedido vĆ”lido que estĆ© configurado para Subcontratación.", +Please select an item code before setting the warehouse.,Seleccione un código de artĆ­culo antes de configurar el almacĆ©n., +Please select either the Item or Warehouse or Warehouse Type filter to generate the report.,Seleccione el filtro ArtĆ­culo o AlmacĆ©n o Tipo de almacĆ©n para generar el informe., +Please select items,Por favor seleccione artĆ­culos, +Please select items to reserve.,Por favor seleccione los artĆ­culos que desea reservar., +Please select items to unreserve.,Seleccione los artĆ­culos que desea cancelar la reserva., +Please select only one row to create a Reposting Entry,Seleccione solo una fila para crear una entrada de reenvĆ­o, +Please select rows to create Reposting Entries,Seleccione filas para crear entradas de reenvĆ­o, +Please select the required filters,Por favor seleccione los filtros requeridos, +Please select valid document type.,Seleccione un tipo de documento vĆ”lido., +Please set Account,"Por favor, establezca una cuenta", +Please set Accounting Dimension {} in {},"Por favor, establezca la dimensión contable {} en {}", +Please set Email/Phone for the contact,"Por favor, establezca Email/TelĆ©fono para el contacto", +Please set Fiscal Code for the customer '%s',"Por favor, establezca el código fiscal para el cliente '%s'", +Please set Fiscal Code for the public administration '%s',"Por favor, establezca el código fiscal para la administración pĆŗblica '%s'", +Please set Fixed Asset Account in {} against {}.,Establezca la cuenta de activos fijos en {} frente a {}., +Please set Opening Number of Booked Depreciations,"Por favor, establezca el nĆŗmero de apertura de las depreciaciones registradas", +Please set Parent Row No for item {0},Establezca el nĆŗmero de fila principal para el artĆ­culo {0}, +Please set Root Type,"Por favor, configure el tipo de raĆ­z", +Please set Tax ID for the customer '%s',"Por favor, establezca el nĆŗmero de identificación fiscal para el cliente '%s'", +Please set VAT Accounts in {0},Establezca las cuentas de IVA en {0}, +"Please set Vat Accounts for Company: ""{0}"" in UAE VAT Settings","Establezca las cuentas de IVA para la empresa: ""{0}"" en la configuración de IVA de los EAU", +Please set a Cost Center for the Asset or set an Asset Depreciation Cost Center for the Company {},Establezca un centro de costo para el activo o establezca un centro de costo de depreciación de activos para la empresa {}, +Please set a default Holiday List for Company {0},Establezca una lista de dĆ­as festivos predeterminada para la empresa {0}, +Please set an Address on the Company '%s',"Por favor, establezca una dirección en la empresa '%s'", +Please set an Expense Account in the Items table,Establezca una cuenta de gastos en la tabla de artĆ­culos, +Please set default Exchange Gain/Loss Account in Company {},Establezca la cuenta de ganancias/pĆ©rdidas de cambio predeterminada en la empresa {}, +Please set default Expense Account in Company {0},Establezca la cuenta de gastos predeterminada en la empresa {0}, +Please set default cost of goods sold account in company {0} for booking rounding gain and loss during stock transfer,Establezca la cuenta de costo de bienes vendidos predeterminada en la empresa {0} para registrar las ganancias y pĆ©rdidas por redondeo durante la transferencia de existencias, +Please set either the Tax ID or Fiscal Code on Company '%s',Establezca el ID fiscal o el código fiscal en la empresa '%s', +Please set filters,"Por favor, defina los filtros", +Please set one of the following:,Establezca una de las siguientes opciones:, +Please set the cost center field in {0} or setup a default Cost Center for the Company.,Establezca el campo del centro de costo en {0} o configure un centro de costo predeterminado para la empresa., +Please set {0} in BOM Creator {1},Establezca {0} en LdM Creator {1}, +Please setup and enable a group account with the Account Type - {0} for the company {1},"Por favor, configura y habilita una cuenta de grupo con el tipo de cuenta - {0} para la empresa {1}.", +Please share this email with your support team so that they can find and fix the issue.,Comparta este correo electrónico con su equipo de soporte para que puedan encontrar y solucionar el problema., +Please specify a {0},Por favor especifique un {0}, +Please try again in an hour.,Vuelve a intentarlo en 1 hora., +Please update Repair Status.,"Por favor, actualice el estado de la reparación.", +Portal User,Usuario del portal, +Portal Users,Usuario del Portal, +Posting Datetime,Fecha y hora de publicación, +Powered by {0},Desarrollado por {0}, +"Previous Year is not closed, please close it first","El aƱo anterior no estĆ” cerrado, por favor ciĆ©rrelo primero", +Price ({0}),Precio ({0}), +Price List Defaults,Lista de precios por defecto, +Price Per Unit ({0}),Precio por Unidad ({0}), +Primary Address and Contact,Dirección principal y Contacto, +Primary Contact,Contacto Principal, +Primary Party,Entidad Primaria, +Primary Role,Rol principal, +Print Format Builder,DiseƱador de formatos de impresión, +Print Style,Estilo de Impresión, +Printing,Impresión, +Priority cannot be lesser than 1.,La prioridad no puede ser menor a 1., +Priority is mandatory,La prioridad es obligatoria, +Probability,Probabilidad, +Process Loss,PĆ©rdida por Proceso, +Process Loss Percentage cannot be greater than 100,El porcentaje de pĆ©rdida de proceso no puede ser mayor que 100, +Process Loss Qty,Cantidad de pĆ©rdida de proceso, +Process Loss Report,Informe de pĆ©rdidas de proceso, +Process Loss Value,Valor de pĆ©rdida del proceso, +Process Payment Reconciliation,Proceso de conciliación de pagos, +Process Payment Reconciliation Log,Registro de conciliación de procesos de pago, +Process Payment Reconciliation Log Allocations,Asignaciones del registro de conciliación de pagos del proceso, +Process Subscription,Proceso de suscripción, +Process in Single Transaction,Proceso en Transacción Única, +Processed BOMs,Listas de materiales procesadas, +Processing Sales! Please Wait...,Ā”Procesando ventas! Por favor espere..., +Produced / Received Qty,Cantidad producida/recibida, +Product Price ID,ID del Precio del producto, +Production Plan Already Submitted,Plan de producción ya validado, +Production Plan Item Reference,Plan de producción Articulo de referencia, +Production Plan Qty,Plan de producción Cantidad, +Production Plan Sub Assembly Item,Plan de producción Elemento de subensamblaje, +Production Plan Sub-assembly Item,Plan de producción Subconjunto ArtĆ­culo, +Production Plan Summary,Resumen del plan de producción, +Profile,Perfil, +Profit and Loss Summary,Resumen de pĆ©rdidas y ganancias, +Progress,Progreso, +Progress (%),Progreso (%), +Project Progress:,Progreso del proyecto:, +Prompt Qty,Cantidad inmediata, +Prospect,Prospecto, +Prospect Lead,Prospecto de cliente potencial, +Prospect Opportunity,Oportunidad prospecto, +Prospect Owner,Propietario del prospecto, +Prospect {0} already exists,El prospecto {0} ya existe, +Provisional Account,Cuenta provisional, +Provisional Expense Account,Cuenta de Gastos Provisionales, +Purchase Order Item reference is missing in Subcontracting Receipt {0},Falta la referencia del artĆ­culo de la orden de compra en el recibo de subcontratación {0}, +Purchase Orders {0} are un-linked,Las órdenes de compra {0} no estĆ”n vinculadas, +Purchase Receipt (Draft) will be auto-created on submission of Subcontracting Receipt.,El recibo de compra (borrador) se crearĆ” automĆ”ticamente al validar el recibo de subcontratación., +Purchase Receipt {0} created.,Recibo de compra {0} creado., +Purchase Value,Valor de compra, +Purchases,Compras, +Purposes Required,Propósitos requeridos, +Putaway Rule,Regla de almacenamiento, +Putaway Rule already exists for Item {0} in Warehouse {1}.,La regla de almacenamiento ya existe para el artĆ­culo {0} en el almacĆ©n {1}., +Qty ,Cant. , +Qty After Transaction,Cantidad despuĆ©s de la transacción, +Qty As Per BOM,Cantidad segĆŗn lista de materiales, +Qty Change,Cantidad Cambio, +Qty In Stock,Cant en existencia, +Qty Per Unit,Cantidad por unidad, +"Qty To Manufacture ({0}) cannot be a fraction for the UOM {2}. To allow this, disable '{1}' in the UOM {2}.","La cantidad a fabricar ({0}) no puede ser una fracción para la unidad de medida {2}. Para permitir esto, deshabilite '{1}' en la unidad de medida {2}.", +Qty To Produce,Cantidad a producir, +Qty and Rate,Cantidad y Tarifa, +Qty as Per Stock UOM,Cantidad segĆŗn stock UdM, +Qty for which recursion isn't applicable.,Cantidad para la que no es aplicable la recursividad., +Qty in Stock UOM,Cantidad en stock UdM, +Qty of Finished Goods Item should be greater than 0.,La cantidad de productos acabados debe ser superior a 0., +Qty to Be Consumed,Cantidad para ser consumida, +Qty to Build,Cantidad a construir, +Qty to Fetch,Cantidad a buscar, +Qty to Produce,Cantidad a producir, +Qualification Status,Estado de la cualificación, +Qualified,Cualificado, +Qualified By,Calificado por, +Qualified on,Calificado el, +Quality Inspection Parameter,ParĆ”metros de inspección de calidad, +Quality Inspection Parameter Group,Grupo de parĆ”metros de inspección de calidad, +Quality Inspection Settings,Configuración de inspección de calidad, +Quality Inspection(s),Inspección(es) de calidad, +Quantity is required,Se requiere cantidad, +"Quantity must be greater than zero, and less or equal to {0}",La cantidad debe ser mayor que cero y menor o igual a {0}, +Quantity to Produce should be greater than zero.,La cantidad a producir debe ser mayor que cero., +Quantity to Scan,Cantidad a escanear, +Quarter {0} {1},Cuatrimestre {0} {1}, +Quotation Number,NĆŗmero de Cotización, +Quoted Amount,Importe Cotizado, +Rate Difference with Purchase Invoice,Diferencia de tarifa con la factura de compra, +Rate Section,Sección de tarifas, +Rate of Stock UOM,Tasa de stock UdM, +Raw Material Cost Per Qty,Coste de la materia prima por cant., +Raw Material Item,ArtĆ­culo de materia prima, +Raw Material Value,Valor de la materia prima, +Raw Materials Actions,Acciones de Materias Primas, +Raw Materials Consumption ,Consumo de materias primas , +Raw Materials Warehouse,AlmacĆ©n de materias primas, +Reached Root,RaĆ­z alcanzada, +Reading Value,Valor de lectura, +Reason for hold:,Motivo de la retención:, +Rebuild Tree,Reconstruir el Ć”rbol, +Rebuilding BTree for period ...,Reconstruyendo BTree para el perĆ­odo..., +Recalculate Incoming/Outgoing Rate,Recalcular la tasa de entrada/salida, +Recalculating Purchase Cost against this Project...,Recalcular el coste de compra contra este proyecto..., +Receivable/Payable Account,Cuenta por cobrar/por pagar, +Receivable/Payable Account: {0} doesn't belong to company {1},Cuenta por cobrar/por pagar: {0} no pertenece a la empresa {1}, +Received Amount After Tax,Importe recibido despuĆ©s de impuestos, +Received Amount After Tax (Company Currency),Importe recibido despuĆ©s de impuestos (moneda de la empresa), +Received Amount cannot be greater than Paid Amount,El importe recibido no puede ser mayor que el importe pagado, +Received Qty in Stock UOM,Cantidad recibida en stock UdM, +Recent Orders,Pedidos recientes, +Recent Transactions,Transacciones recientes, +Reconcile All Serial Nos / Batches,Concilie todos los nĆŗmeros de serie / lotes, +Reconcile on Advance Payment Date,Conciliar en fecha de pago anticipado, +Reconcile the Bank Transaction,Conciliar la transacción bancaria, +Reconciled Entries,Entradas conciliadas, +Reconciliation Error Log,Registro de errores de conciliación, +Reconciliation Logs,Registros de conciliación, +Reconciliation Progress,Progreso de la reconciliación, +Recording HTML,Grabación HTML, +Recoverable Standard Rated expenses should not be set when Reverse Charge Applicable is Y,Los gastos recuperables con tasa estĆ”ndar no se deben establecer cuando la inversión de cargo aplicable es Y, +Recurse Every (As Per Transaction UOM),Recursiva cada (segĆŗn la unidad de medida de la transacción), +Recurse Over Qty cannot be less than 0,El recursivo sobre cantidad no puede ser menor que 0, +Recursive Discounts with Mixed condition is not supported by the system,El sistema no admite descuentos recursivos con condiciones mixtas, +Reference Date for Early Payment Discount,Fecha de referencia para el descuento por pronto pago, +Reference Detail,Detalle de referencia, +Reference DocType,DocType de referencia, +Reference Exchange Rate,Tipo de cambio de referencia, +Reference No,NĀŗ de referencia, +Reference number of the invoice from the previous system,NĆŗmero de referencia de la factura del sistema anterior, +References to Sales Invoices are Incomplete,Las referencias a las facturas de venta estĆ”n incompletas, +References to Sales Orders are Incomplete,Las referencias a los pedidos de venta estĆ”n incompletas, +References {0} of type {1} had no outstanding amount left before submitting the Payment Entry. Now they have a negative outstanding amount.,Las referencias {0} del tipo {1} no tenĆ­an ningĆŗn importe pendiente antes de enviar la Entrada de pago. Ahora tienen un importe pendiente negativo., +Refresh Google Sheet,Actualizar hoja de Google, +Refresh Plaid Link,Actualizar enlace de Plaid, +Regenerate Closing Stock Balance,Regenerar saldo de stock de cierre, +Rejected Serial and Batch Bundle,Lote y serie rechazados, +Rejected Warehouse and Accepted Warehouse cannot be same.,AlmacĆ©n Rechazado y AlmacĆ©n Aceptado no pueden ser el mismo., +Remarks Column Length,Observaciones Longitud de la columna, +Remove Parent Row No in Items Table,Eliminar el nĆŗmero de fila principal en la tabla de elementos, +Repair,Reparar, +Repair Asset,Reparar activos, +Repair Details,Detalles de la reparación, +"Replace a particular BOM in all other BOMs where it is used. It will replace the old BOM link, update cost and regenerate ""BOM Explosion Item"" table as per new BOM. +It also updates latest price in all the BOMs.","Sustituye una determinada lista de materiales en todas las demĆ”s listas de materiales en las que se utilice. ReemplazarĆ” el enlace de la lista de materiales antigua, actualizarĆ” el coste y regenerarĆ” la tabla ""Elemento de explosión de la lista de materiales"" segĆŗn la nueva lista de materiales. +TambiĆ©n actualiza el Ćŗltimo precio en todas las listas de materiales.", +Report Error,Reportar Error, +Report Filters,Filtros de informe, +Report View,Vista de Reporte, +Repost Accounting Ledger,Traspaso libro de contabilidad, +Repost Accounting Ledger Items,Traspaso de partidas del libro mayor de contabilidad, +Repost Accounting Ledger Settings,Traspasar configuración del libro mayor de contabilidad, +Repost Allowed Types,Tipos de Traspasos permitidos, +Repost Error Log,Traspaso registro de errores, +Repost Item Valuation,Traspaso Valoración de artĆ­culos, +Repost Payment Ledger,Traspaso del Libro de Pagos, +Repost Payment Ledger Items,Traspaso de partidas del libro de pagos, +Repost Status,Estado del Traspaso, +Repost has started in the background,El traspaso ha comenzado en segundo plano., +Repost in background,Traspasar en segundo plano, +Repost started in the background,Traspaso iniciado en segundo plano, +Reposting Completed {0}%,Traspaso completado {0}%, +Reposting Data File,Traspaso del archivo de datos, +Reposting Info,Información de Traspaso , +Reposting Progress,Traspasar el progreso, +Reposting entries created: {0},Traspaso de entradas creadas: {0}, +Reposting has been started in the background.,Se ha iniciado un traspaso en segundo plano., +Reposting in the background.,Traspasando en segundo plano., +Represents a Financial Year. All accounting entries and other major transactions are tracked against the Fiscal Year.,Representa un aƱo fiscal. Todos los asientos contables y otras transacciones importantes se registran en relación con el aƱo fiscal., +Request Parameters,ParĆ”metros de la solicitud, +Request Timeout,Tiempo de espera superado, +Reservation Based On,Reserva basada en, +Reserve,Reservar, +Reserve Stock,Reservar stock, +"Reserved Qty ({0}) cannot be a fraction. To allow this, disable '{1}' in UOM {3}.","La cantidad reservada ({0}) no puede ser una fracción. Para permitirlo, deshabilite '{1}' en la unidad de medida {3}.", +Reserved Qty for Production Plan,Cantidad reservada para el plan de producción, +Reserved Qty for Subcontract,Cantidad reservada para subcontrato, +Reserved Qty should be greater than Delivered Qty.,La cantidad reservada debe ser mayor que la cantidad entregada., +Reserved Serial No.,NĆŗmero de serie reservado., +Reserved Stock,Existencias Reservadas, +Reserved Stock for Batch,Stock reservado para lote, +Reserved for POS Transactions,Reservado para transacciones TPV, +Reserved for Production,Reservado para producción, +Reserved for Production Plan,Reservado para el plan de producción, +Reserved for Sub Contracting,Reservado para subcontratación, +Reserving Stock...,Reservando de stock..., +Reset Company Default Values,Restablecer valores predeterminados de la empresa, +Reset Plaid Link,Restablecer vinculación con Plaid, +Reset Raw Materials Table,Restablecer tabla de materias primas, +Resolution Due,Resolución pendiente, +Response and Resolution,Respuesta y resolución, +Restart,Reiniciar, +Restore Asset,Restaurar activo, +Restrict,Restringir, +Restrict Items Based On,Restringir Pruductos segĆŗn, +Result Key,Clave de resultados, +Resume Job,Reanudar Trabajo, +Retried,Reintentado, +Retry,Reintentar, +Retry Failed Transactions,Reintentar transacciones fallidas, +Return Against,Devolución contra, +Return Against Subcontracting Receipt,Devolución contra recibo de subcontratación, +Return Components,Componentes de retorno, +Return Issued,Devolución emitida, +Return Qty,Cantidad devuelta, +Return Qty from Rejected Warehouse,Cantidad devuelta del almacĆ©n rechazado, +Return of Components,Devolución de componentes, +Returned,Devuelto, +Returned Against,Devuelto contra, +Returned Qty ,Cant devuelta , +Returned Qty in Stock UOM,Cantidad devuelta en stock UdM, +Returned exchange rate is neither integer not float.,El tipo de cambio devuelto no es ni entero ni flotante., +Revaluation Journals,Diarios de revalorización, +Revaluation Surplus,SuperĆ”vit de revalorización, +Revenue,Ganancia, +Reversal Of,Reversión de, +Right Child,Secundario correcto, +Role Allowed to Create/Edit Back-dated Transactions,Rol permitido para crear o editar transacciones retroactivas, +Role Allowed to Over Bill ,Rol Permitido para Facturar en Exceso , +Role Allowed to Over Deliver/Receive,Rol que permite entregar/recibir mĆ”s de lo debido, +Role Allowed to Override Stop Action,Rol autorizado para anular la acción de parada, +Role allowed to bypass Credit Limit,Rol permitido para eludir el lĆ­mite de crĆ©dito, +Root,RaĆ­z, +"Root Type for {0} must be one of the Asset, Liability, Income, Expense and Equity","El tipo de raĆ­z para {0} debe ser uno de los siguientes: Activo, Pasivo, Ingreso, Gasto y Patrimonio", +Round Free Qty,Redondear cantidad gratis, +Round Off Tax Amount,Redondear el importe del impuesto, +Round Tax Amount Row-wise,Redondear el importe del impuesto por filas, +Rounding Loss Allowance,Redondeo de la indemnización por pĆ©rdidas, +Rounding Loss Allowance should be between 0 and 1,El margen de pĆ©rdida por redondeo debe estar entre 0 y 1, +Rounding gain/loss Entry for Stock Transfer,Redondeo de ganancias/pĆ©rdidas Entrada para traslado de existencias, +Row #,Fila #, +Row # {0}:,Fila # {0}:, +Row #{0}: A reorder entry already exists for warehouse {1} with reorder type {2}.,Fila #{0}: Ya existe una entrada de reorden para el almacĆ©n {1} con el tipo de reorden {2}., +Row #{0}: Acceptance Criteria Formula is incorrect.,Fila #{0}: La fórmula de los criterios de aceptación es incorrecta., +Row #{0}: Acceptance Criteria Formula is required.,Fila #{0}: Se requiere la fórmula de criterios de aceptación., +Row #{0}: Accepted Warehouse and Rejected Warehouse cannot be same,Fila #{0}: AlmacĆ©n Aceptado y AlmacĆ©n Rechazado no puede ser el mismo, +Row #{0}: Accepted Warehouse is mandatory for the accepted Item {1},Fila #{0}: El almacĆ©n aceptado es obligatorio para el artĆ­culo aceptado {1}, +Row #{0}: Allocated Amount cannot be greater than Outstanding Amount of Payment Request {1},Fila #{0}: El Importe Asignado no puede ser mayor que el Importe Pendiente de la Solicitud de Pago {1}, +Row #{0}: Allocated amount:{1} is greater than outstanding amount:{2} for Payment Term {3},Fila #{0}: Importe asignado:{1} es superior al importe pendiente:{2} para el plazo de pago {3}, +Row #{0}: Amount must be a positive number,Fila #{0}: El importe debe ser un nĆŗmero positivo, +Row #{0}: BOM is not specified for subcontracting item {0},Fila #{0}: La lista de materiales no estĆ” especificada para el artĆ­culo de subcontratación {0}, +Row #{0}: Batch No {1} is already selected.,Fila #{0}: El lote nĀŗ {1} ya estĆ” seleccionado., +Row #{0}: Cannot allocate more than {1} against payment term {2},Fila #{0}: No se puede asignar mĆ”s de {1} contra la condición de pago {2}, +Row #{0}: Cannot transfer more than Required Qty {1} for Item {2} against Job Card {3},Fila #{0}: No se puede transferir mĆ”s de la cantidad requerida {1} para el artĆ­culo {2} contra la tarjeta de trabajo {3}, +Row #{0}: Consumed Asset {1} cannot be Draft,Fila #{0}: El activo consumido {1} no puede ser borrador, +Row #{0}: Consumed Asset {1} cannot be cancelled,Fila #{0}: El activo consumido {1} no puede estar cancelado, +Row #{0}: Consumed Asset {1} cannot be the same as the Target Asset,Fila #{0}: El activo consumido {1} no puede ser el mismo que el activo de destino, +Row #{0}: Consumed Asset {1} cannot be {2},Fila #{0}: El activo consumido {1} no puede ser {2}, +Row #{0}: Consumed Asset {1} does not belong to company {2},Fila #{0}: El activo consumido {1} no pertenece a la empresa {2}, +Row #{0}: Cumulative threshold cannot be less than Single Transaction threshold,Fila #{0}: El umbral acumulativo no puede ser menor que el umbral de transacción Ćŗnica, +Row #{0}: Dates overlapping with other row,Fila #{0}: Fechas superpuestas con otras filas, +Row #{0}: Default BOM not found for FG Item {1},Fila #{0}: No se encontró la lista de materiales predeterminada para el artĆ­culo FG {1}, +Row #{0}: Expense Account not set for the Item {1}. {2},Fila #{0}: Cuenta de gastos no configurada para el artĆ­culo {1}. {2}, +Row #{0}: Finished Good Item Qty can not be zero,Fila #{0}: La cantidad de artĆ­culos terminados no puede ser cero, +Row #{0}: Finished Good Item is not specified for service item {1},Fila #{0}: No se especifica el artĆ­culo bueno acabado para el artĆ­culo de servicio {1}, +Row #{0}: Finished Good Item {1} must be a sub-contracted item,Fila #{0}: ArtĆ­culo terminado. El artĆ­culo {1} debe ser un artĆ­culo subcontratado., +Row #{0}: Finished Good reference is mandatory for Scrap Item {1}.,Fila #{0}: La referencia de producto terminado es obligatoria para el artĆ­culo de desecho {1}., +Row #{0}: For {1} Clearance date {2} cannot be before Cheque Date {3},Fila #{0}: Para {1} La fecha de liquidación {2} no puede ser anterior a la fecha del cheque {3}, +"Row #{0}: For {1}, you can select reference document only if account gets credited","Fila #{0}: Para {1}, puede seleccionar el documento de referencia solo si se acredita la cuenta", +"Row #{0}: For {1}, you can select reference document only if account gets debited","Fila #{0}: Para {1}, puede seleccionar el documento de referencia solo si se debita la cuenta", +Row #{0}: From Date cannot be before To Date,Fila #{0}: La fecha de inicio no puede ser anterior a la fecha de finalización, +Row #{0}: Item {1} does not exist,Fila #{0}: El artĆ­culo {1} no existe, +"Row #{0}: Item {1} has been picked, please reserve stock from the Pick List.","Fila #{0}: El artĆ­culo {1} ha sido recogido, por favor reserve existencias de la Lista de Recogida.", +Row #{0}: Item {1} is not a service item,Fila #{0}: El artĆ­culo {1} no es un artĆ­culo de servicio, +Row #{0}: Item {1} is not a stock item,Fila #{0}: El artĆ­culo {1} no es un artĆ­culo de stock, +Row #{0}: Only {1} available to reserve for the Item {2},Fila #{0}: Solo {1} disponible para reservar para el artĆ­culo {2}, +Row #{0}: Please select Item Code in Assembly Items,"Fila #{0}: Por favor, seleccione el código del artĆ­culo en ArtĆ­culos de ensamblaje", +Row #{0}: Please select the BOM No in Assembly Items,"Fila #{0}: Por favor, seleccione el nĀŗ de lista de materiales en ArtĆ­culos de ensamblaje", +Row #{0}: Please select the Sub Assembly Warehouse,"Fila #{0}: Por favor, seleccione el AlmacĆ©n de Sub-montaje", +Row #{0}: Please update deferred revenue/expense account in item row or default account in company master,"Fila #{0}: Por favor, actualice la cuenta de ingresos/gastos diferidos en la fila de artĆ­culos o la cuenta por defecto en el maestro de empresas", +Row #{0}: Qty increased by {1},Fila #{0}: Cantidad aumentada en {1}, +Row #{0}: Qty must be a positive number,Fila #{0}: La cantidad debe ser un nĆŗmero positivo, +Row #{0}: Qty should be less than or equal to Available Qty to Reserve (Actual Qty - Reserved Qty) {1} for Iem {2} against Batch {3} in Warehouse {4}.,Fila #{0}: La cantidad debe ser menor o igual a la cantidad disponible para reservar (cantidad real - cantidad reservada) {1} para ArtĆ­culo {2} contra el lote {3} en el almacĆ©n {4}., +Row #{0}: Quantity to reserve for the Item {1} should be greater than 0.,Fila #{0}: La cantidad a reservar para el artĆ­culo {1} debe ser superior a 0., +Row #{0}: Rate must be same as {1}: {2} ({3} / {4}),Fila #{0}: La tasa debe ser la misma que {1}: {2} ({3} / {4}), +Row #{0}: Received Qty must be equal to Accepted + Rejected Qty for Item {1},Fila #{0}: La cantidad recibida debe ser igual a la cantidad aceptada + rechazada para el artĆ­culo {1}, +Row #{0}: Rejected Qty cannot be set for Scrap Item {1}.,Fila #{0}: No se puede establecer la cantidad rechazada para el artĆ­culo de rechazo {1}., +Row #{0}: Rejected Warehouse is mandatory for the rejected Item {1},Fila #{0}: El almacĆ©n rechazado es obligatorio para el artĆ­culo rechazado {1}, +Row #{0}: Scrap Item Qty cannot be zero,Fila #{0}: La cantidad de artĆ­culos desechados no puede ser cero, +"Row #{0}: Selling rate for item {1} is lower than its {2}. + Selling {3} should be atleast {4}.

Alternatively, + you can disable selling price validation in {5} to bypass + this validation.","Fila #{0}: El precio de venta del artĆ­culo {1} es inferior a su {2}. + Venta {3} debe ser al menos {4}.

Alternativamente, + puede desactivar la validación del precio de venta en {5} para saltarse + esta validación.", +Row #{0}: Serial No {1} for Item {2} is not available in {3} {4} or might be reserved in another {5}.,Fila #{0}: El nĆŗmero de serie {1} del artĆ­culo {2} no estĆ” disponible en {3} {4} o podrĆ­a estar reservado en otro {5}., +Row #{0}: Serial No {1} is already selected.,Fila #{0}: El nĆŗmero de serie {1} ya estĆ” seleccionado., +Row #{0}: Start Time and End Time are required,Fila #{0}: Se requiere hora de inicio y hora de fin, +Row #{0}: Start Time must be before End Time,Fila #{0}: La hora de inicio debe ser antes del fin, +Row #{0}: Status is mandatory,Fila #{0}: El estado es obligatorio, +Row #{0}: Stock cannot be reserved for Item {1} against a disabled Batch {2}.,Fila #{0}: No se puede reservar stock para el artĆ­culo {1} contra un lote deshabilitado {2}., +Row #{0}: Stock cannot be reserved for a non-stock Item {1},Fila #{0}: No se puede reservar stock para un artĆ­culo que no es de stock {1}, +Row #{0}: Stock cannot be reserved in group warehouse {1}.,Fila #{0}: No se pueden reservar existencias en el almacĆ©n de grupo {1}., +Row #{0}: Stock is already reserved for the Item {1}.,Fila #{0}: Ya hay stock reservado para el artĆ­culo {1}., +Row #{0}: Stock is reserved for item {1} in warehouse {2}.,Fila #{0}: Hay stock reservado para el artĆ­culo {1} en el almacĆ©n {2}., +Row #{0}: Stock not available to reserve for Item {1} against Batch {2} in Warehouse {3}.,Fila #{0}: Stock no disponible para reservar para el artĆ­culo {1} contra el lote {2} en el almacĆ©n {3}., +Row #{0}: Stock not available to reserve for the Item {1} in Warehouse {2}.,Fila #{0}: Stock no disponible para reservar para el artĆ­culo {1} en el almacĆ©n {2}., +Row #{0}: The warehouse {1} is not a child warehouse of a group warehouse {2},Fila #{0}: El almacĆ©n {1} no es un almacĆ©n secundario de un almacĆ©n de grupo {2}, +Row #{0}: You cannot use the inventory dimension '{1}' in Stock Reconciliation to modify the quantity or valuation rate. Stock reconciliation with inventory dimensions is intended solely for performing opening entries.,Fila #{0}: No se puede utilizar la dimensión de inventario '{1}' en la conciliación de stock para modificar la cantidad o la tasa de valoración. La conciliación de stock con las dimensiones de inventario estĆ” destinada Ćŗnicamente a realizar asientos de apertura., +Row #{0}: You must select an Asset for Item {1}.,Fila #{0}: Debe seleccionar un activo para el artĆ­culo {1}., +Row #{0}: {1} is not a valid reading field. Please refer to the field description.,Fila #{0}: {1} no es un campo de lectura vĆ”lido. Consulte la descripción del campo., +Row #{0}: {1} of {2} should be {3}. Please update the {1} or select a different account.,"Fila #{0}: {1} de {2} deberĆ­a ser {3}. Por favor, actualice {1} o seleccione una cuenta diferente.", +Row #{1}: Warehouse is mandatory for stock Item {0},Fila #{1}: El AlmacĆ©n es obligatorio para el producto en stock {0}, +Row #{}: Finance Book should not be empty since you're using multiple.,"Fila #{}: Libro de Finanzas no debe estar vacĆ­o, ya que estĆ” utilizando mĆŗltiples.", +Row #{}: Please use a different Finance Book.,"Fila #{}: Por favor, utilice un Libro de Finanzas diferente.", +Row #{}: The original Invoice {} of return invoice {} is not consolidated.,Fila #{}: La factura original {} de la factura de devolución {} no estĆ” consolidada., +Row #{}: item {} has been picked already.,Fila #{}: el artĆ­culo {} ya ha sido seleccionado., +Row #{}: {} {} doesn't belong to Company {}. Please select valid {}.,"Fila #{}: {} {} no pertenece a la empresa {}. Por favor, seleccione una {} vĆ”lida.", +Row No {0}: Warehouse is required. Please set a Default Warehouse for Item {1} and Company {2},Fila n.° {0}: Se requiere almacĆ©n. Establezca un almacĆ©n predeterminado para el artĆ­culo {1} y la empresa {2}, +Row Number,NĆŗmero de fila, +Row {0},Fila {0}, +"Row {0} picked quantity is less than the required quantity, additional {1} {2} required.","Fila {0} la cantidad recogida es inferior a la requerida, se requiere {1} {2} adicional.", +Row {0}# Item {1} cannot be transferred more than {2} against {3} {4},Fila {0}# El artĆ­culo {1} no puede transferirse mĆ”s que {2} contra {3} {4}, +Row {0}# Item {1} not found in 'Raw Materials Supplied' table in {2} {3},Fila {0}# El artĆ­culo {1} no se encontró en la tabla 'Materias primas suministradas' en {2} {3}, +Row {0}: Accepted Qty and Rejected Qty can't be zero at the same time.,Fila {0}: La cantidad aceptada y la cantidad rechazada no pueden ser cero al mismo tiempo., +Row {0}: Account {1} and Party Type {2} have different account types,Fila {0}: La cuenta {1} y el tipo de entidad {2} tienen diferentes tipos de cuenta, +Row {0}: Allocated amount {1} must be less than or equal to invoice outstanding amount {2},Fila {0}: El importe asignado {1} debe ser inferior o igual al importe pendiente de la factura {2}, +Row {0}: Allocated amount {1} must be less than or equal to remaining payment amount {2},Fila {0}: El importe asignado {1} debe ser inferior o igual al importe de pago restante {2}, +"Row {0}: As {1} is enabled, raw materials cannot be added to {2} entry. Use {3} entry to consume raw materials.","Fila {0}: Como {1} estĆ” activada, no se pueden aƱadir materias primas a la entrada {2} . Utilice la entrada {3} para consumir materias primas.", +Row {0}: Both Debit and Credit values cannot be zero,Fila {0}: Tanto el Debe como el Haber no pueden ser cero, +Row {0}: Cost Center {1} does not belong to Company {2},Fila {0}: El centro de costes {1} no pertenece a la empresa {2}, +Row {0}: Either Delivery Note Item or Packed Item reference is mandatory.,Fila {0}: La referencia del artĆ­culo de la nota de entrega o del artĆ­culo empaquetado es obligatoria., +Row {0}: Expense Head changed to {1} as no Purchase Receipt is created against Item {2}.,Fila {0}: el encabezado de gasto cambió a {1} ya que no se crea ningĆŗn recibo de compra para el artĆ­culo {2}., +Row {0}: Expense Head changed to {1} because account {2} is not linked to warehouse {3} or it is not the default inventory account,Fila {0}: Cabecera de Gasto cambiada a {1} porque la cuenta {2} no estĆ” vinculada al almacĆ©n {3} o no es la cuenta de inventario por defecto, +Row {0}: Expense Head changed to {1} because expense is booked against this account in Purchase Receipt {2},Fila {0}: Cabecera de Gasto cambiada a {1} porque el gasto se contabiliza contra esta cuenta en el Recibo de Compra {2}, +Row {0}: From Warehouse is mandatory for internal transfers,Fila {0}: Desde el almacĆ©n es obligatorio para transferencias internas, +Row {0}: Item Tax template updated as per validity and rate applied,Fila {0}: Plantilla de impuesto del artĆ­culo actualizada segĆŗn la validez y la tasa aplicada, +Row {0}: Item rate has been updated as per valuation rate since its an internal stock transfer,"Fila {0}: La tarifa del artĆ­culo se ha actualizado segĆŗn la tarifa de valoración, ya que se trata de una transferencia de stock interna", +Row {0}: Item {1} must be a stock item.,Fila {0}: El artĆ­culo {1} debe ser un artĆ­culo de stock., +Row {0}: Item {1} must be a subcontracted item.,Fila {0}: El artĆ­culo {1} debe ser un artĆ­culo subcontratado., +Row {0}: Packed Qty must be equal to {1} Qty.,Fila {0}: La cantidad embalada debe ser igual a la cantidad {1} ., +Row {0}: Packing Slip is already created for Item {1}.,Fila {0}: Ya se creó el albarĆ”n para el artĆ­culo {1}., +Row {0}: Payment Term is mandatory,Fila {0}: El plazo de pago es obligatorio, +Row {0}: Please provide a valid Delivery Note Item or Packed Item reference.,Fila {0}: proporcione una referencia de artĆ­culo de nota de entrega o artĆ­culo empaquetado vĆ”lida., +Row {0}: Please select a BOM for Item {1}.,"Fila {0}: Por favor, seleccione una lista de materiales para el artĆ­culo {1}.", +Row {0}: Please select an active BOM for Item {1}.,"Fila {0}: Por favor, seleccione una lista de materiales activa para el artĆ­culo {1}.", +Row {0}: Please select an valid BOM for Item {1}.,"Fila {0}: Por favor, seleccione una lista de materiales vĆ”lida para el artĆ­culo {1}.", +Row {0}: Project must be same as the one set in the Timesheet: {1}.,Fila {0}: El proyecto debe ser el mismo que el establecido en la hoja de horas: {1}., +Row {0}: Purchase Invoice {1} has no stock impact.,Fila {0}: La factura de compra {1} no tiene impacto en el stock., +Row {0}: Qty cannot be greater than {1} for the Item {2}.,Fila {0}: La cantidad no puede ser mayor que {1} para el artĆ­culo {2}., +Row {0}: Qty in Stock UOM can not be zero.,Fila {0}: La UdM de cantidad en stock no puede ser cero., +Row {0}: Qty must be greater than 0.,Fila {0}: La cantidad debe ser mayor que 0., +Row {0}: Shift cannot be changed since the depreciation has already been processed,Fila {0}: No se puede cambiar el turno porque ya se ha procesado la amortización, +Row {0}: Target Warehouse is mandatory for internal transfers,Fila {0}: El almacĆ©n de destino es obligatorio para las transferencias internas, +"Row {0}: To set {1} periodicity, difference between from and to date must be greater than or equal to {2}","Fila {0}: Para establecer la periodicidad {1} , la diferencia entre la fecha de inicio y la de finalización debe ser mayor o igual a {2}", +Row {0}: Total Number of Depreciations cannot be less than or equal to Opening Number of Booked Depreciations,Fila {0}: El nĆŗmero total de amortizaciones no puede ser inferior o igual al nĆŗmero inicial de amortizaciones contabilizadas, +Row {0}: {1} account already applied for Accounting Dimension {2},Fila {0}: {1} cuenta ya aplicada para la Dimensión Contable {2}, +Row {0}: {1} {2} cannot be same as {3} (Party Account) {4},Fila {0}: {1} {2} no puede ser la misma que {3} (Cuenta de la Entidad) {4}, +Row {0}: {2} Item {1} does not exist in {2} {3},Fila {0}: {2} El elemento {1} no existe en {2} {3}, +Row({0}): Outstanding Amount cannot be greater than actual Outstanding Amount {1} in {2},Fila({0}): El importe pendiente no puede ser mayor que el importe pendiente real {1} en {2}, +Rows with Same Account heads will be merged on Ledger,Las lĆ­neas con los mismos encabezamientos de cuenta se fusionarĆ”n en el Libro Mayor, +Rows: {0} have 'Payment Entry' as reference_type. This should not be set manually.,Filas: {0} tienen 'Entrada de pago' como reference_type. No debe establecerse manualmente., +Rows: {0} in {1} section are Invalid. Reference Name should point to a valid Payment Entry or Journal Entry.,Las filas {0} en la sección {1} no son vĆ”lidas. El nombre de referencia debe apuntar a una entrada de pago o de diario vĆ”lida., +Run parallel job cards in a workstation,Ejecutar tarjetas de trabajo en paralelo en una estación de trabajo, +Running,Ejecutando, +SCO Supplied Item,ArtĆ­culo suministrado por SCO, +SLA Fulfilled On,SLA completado el, +SLA Fulfilled On Status,SLA cumplido en estado, +SLA Paused On,SLA en pausa, +SLA will be applied if {1} is set as {2}{3},SLA se aplicarĆ” si {1} se establece como {2}{3}, +SLA will be applied on every {0},El SLA se aplicarĆ” en cada {0}, +SMS Settings,Ajustes de SMS, +SO Total Qty,SO Cantidad total, +Salary Currency,Divisa de salario, +Sales Incoming Rate,Tasa de entrada de ventas, +Sales Invoice {0} must be deleted before cancelling this Sales Order,La factura de venta {0} debe eliminarse antes de cancelar esta orden de venta, +Sales Order Packed Item,ArtĆ­culo empaquetado de la orden de venta, +Sales Order Reference,Referencia del pedido de venta, +Sales Order Status,Estado del pedido de venta, +"Sales Order {0} already exists against Customer's Purchase Order {1}. To allow multiple Sales Orders, Enable {2} in {3}","El Pedido de Venta {0} ya existe contra el Pedido de Compra del Cliente {1}. Para permitir mĆŗltiples Pedidos de Venta, habilite {2} en {3}.", +Sales Partner ,Socio de ventas , +Sales Partner Item,ArtĆ­culo de socio de ventas, +Sales Partner Target Variance Based On Item Group,Variación de objetivos del socio de ventas segĆŗn el grupo de artĆ­culos, +Sales Pipeline Analytics,AnĆ”lisis del flujo de ventas, +Sales Update Frequency in Company and Project,Frecuencia de actualización de ventas en empresa y proyecto, +Sales Value,Valor de las ventas, +Salvage Value Percentage,Porcentaje de Valor de Recuperación, +Same item and warehouse combination already entered.,Ya se ha introducido la misma combinación de artĆ­culo y almacĆ©n., +Savings,Ahorros, +Scan Batch No,Escanear Lote No, +Scan Mode,Modo de escaneo, +Scan Serial No,Escanear nĆŗmero de serie, +Scan barcode for item {0},Escanee el código de barras del artĆ­culo {0}, +"Scan mode enabled, existing quantity will not be fetched.","Modo de escaneo habilitado, la cantidad existente no se obtendrĆ”.", +Scanned Quantity,Cantidad escaneada, +Scheduled Time Logs,Registros de tiempo programado, +Scheduler is Inactive. Can't trigger job now.,El planificador estĆ” inactivo. No se puede activar el trabajo ahora., +Scheduler is Inactive. Can't trigger jobs now.,El planificador estĆ” inactivo. No se pueden activar los trabajos ahora., +Scheduler is inactive. Cannot enqueue job.,El planificador estĆ” inactivo. No se puede poner en cola el trabajo., +Scheduler is inactive. Cannot merge accounts.,El planificador estĆ” inactivo. No se pueden combinar cuentas., +Scheduling,Planificación, +Select Alternative Items for Sales Order,Seleccionar Ć­tems alternativos para Orden de Venta, +Select View,Seleccione Vista, +Select an item from each set to be used in the Sales Order.,Seleccione un Ć­tem de cada conjunto para usarlo en la Orden de Venta., +Send Attached Files,Enviar Archivos Adjuntos, +Send Document Print,Enviar Impresión de Documento, +Service Expenses,Gastos de servicio, +Service Item,ArtĆ­culo de servicio, +Set Default Supplier,Establecer Proveedor Predeterminado, +Set Quantity,Establecer cantidad, +Set Warehouse,Establecer AlmacĆ©n, +Shipment,EnvĆ­o, +Shipment Amount,Monto del envĆ­o, +Shipment Delivery Note,Nota de Entrega de envĆ­o, +Shipment ID,ID de EnvĆ­o, +Shipment Information,Información del EnvĆ­o, +Shipment Type,Tipo de EnvĆ­o, +Shipment details,Detalles del envĆ­o, +Show Failed Logs,Mostrar registros fallidos, +Show Preview,Mostrar Vista Previa, +Show Remarks,Mostrar Observaciones, +Skipped,Omitido, +"Skipping {0} of {1}, {2}","Saltando {0} de {1}, {2}", +Sold by,Vendido por, +Spacer,Espaciador, +Start Deletion,Iniciar eliminación, +Start Import,Comience a Importar, +Sub Operation,Sub operación, +Sub Operations,Sub operaciones, +Subcontracting Settings,Configuración de Subcontratación, +Subdivision,Subdivisión, +Submit After Import,Validar despuĆ©s de la importación, +Successfully imported {0} record.,Importado correctamente {0} registro., +Successfully imported {0} records.,Importado correctamente {0} registros., +Successfully linked to Customer,Vinculado exitosamente al Cliente, +Successfully linked to Supplier,Vinculado exitosamente al Proveedor, +Successfully merged {0} out of {1}.,Fusionado satisfactoriamente {0} de {1}., +Successfully updated {0},Actualizado exitosamente {0}, +"Successfully updated {0} record out of {1}. Click on Export Errored Rows, fix the errors and import again.","Registro {0} actualizado correctamente de {1}. Haga clic en Exportar filas con errores, corrija los errores e importe nuevamente.", +Successfully updated {0} record.,Registro {0} actualizado correctamente., +"Successfully updated {0} records out of {1}. Click on Export Errored Rows, fix the errors and import again.","Actualizado correctamente los registros {0} de {1}. Haga clic en Exportar filas Erroradas, corrija los errores e importe de nuevo.", +Successfully updated {0} records.,Registros {0} actualizados correctamente., +Supplied Item,Producto suministrado, +Supplier Address Details,Detalles de Dirección del Proveedor, +Supplier Info,Info. Proveedor, +Supplier Invoice,Factura de Proveedor, +Supplier Item,Producto del Proveedor, +Supplier Portal Users,Usuarios del Portal del Proveedor, +Supplier Primary Address,Dirección principal del Proveedor, +Supplier Primary Contact,Contacto principal del Proveedor, +Sync Now,Sincronizar ahora, +Sync Started,Sincronización Iniciada, +System Settings,Configuración del Sistema, +Tax Amount,Importe de Impuestos, +Tax Masters,Maestros Fiscales, +Tax Settings,Configuración de Impuestos, +Team,Equipo, +Template Options,Opciones de plantilla, +Template Warnings,Advertencias de plantilla, +Terms & Conditions,TĆ©rminos y Condiciones, +Terms Template,Plantilla de TĆ©rminos, +Territory Wise Sales,Ventas por territorios, +The Condition '{0}' is invalid,La Condición '{0}' no es vĆ”lida, +"The sync has started in the background, please check the {0} list for new records.","La sincronización se ha iniciado en segundo plano, por favor compruebe en la lista {0} si hay nuevos registros.", +The task has been enqueued as a background job.,La tarea se ha puesto en cola como trabajo en segundo plano., +There was an error creating Bank Account while linking with Plaid.,Se ha producido un error al crear la cuenta bancaria mientras se vinculaba con Plaid., +There was an error syncing transactions.,Se ha producido un error al sincronizar las transacciones., +There was an error updating Bank Account {} while linking with Plaid.,Se ha producido un error al actualizar la cuenta bancaria {} mientras se vinculaba con Plaid., +There was an issue connecting to Plaid's authentication server. Check browser console for more information,Se ha producido un problema al conectar con el servidor de autenticación de Plaid. Compruebe la consola del navegador para obtener mĆ”s información, +Time in mins,Tiempo en min, +Time in mins.,Tiempo en minutos., +Time slot is not available,La franja horaria no estĆ” disponible, +To Doctype,A Doctype, +Total Incoming Value (Receipt),Valor Total Entrante (Recepción), +Total Issues,Total de Incidencias, +Total Outgoing Value (Consumption),Valor Total Saliente (Consumo), +Total Supplied Qty,Cant. Total suministrada, +Total Time (in Mins),Tiempo total (en minutos), +Total Value,Valor Total, +Total Value Difference (Incoming - Outgoing),Diferencia de valor total (entrante - saliente), +Total Views,Total de visualizaciones, +Total Warehouses,Total Almacenes, +Tracking Status,Estado de seguimiento, +Tracking Status Info,Información de estado de seguimiento, +Tracking URL,URL de Seguimiento, +Transaction Settings,Configuración de Transacciones, +Transaction currency: {0} cannot be different from Bank Account({1}) currency: {2},Moneda de la transacción: {0} no puede ser diferente de la moneda de la cuenta bancaria ({1}): {2}, +Transit,TrĆ”nsito, +Transit Entry,Entrada de TrĆ”nsito, +Type of Transaction,Tipo de Transacción, +UAE VAT 201,IVA EAU 201, +Unable to find variable:,No se puede encontrar la variable:, +Unassigned Qty,Cant. Sin asignar, +Unit of Measure (UOM),Unidad de Medida (UdM), +Unlinked,Desvincular, +Unqualified,No calificada, +Unrealized Profit / Loss Account,Cuenta de PĆ©rdidas/Ganancias no realizada, +Up,Arriba, +Update Existing Records,Actualizar registros existentes, +Update Rate as per Last Purchase,Actualizar tasa segĆŗn la Ćŗltima compra, +Update Total Purchase Cost,Actualizar Costo Total de Compra, +Updating Work Order status,Actualizando estado de la Orden de Trabajo, +"Updating {0} of {1}, {2}","Actualización {0} de {1}, {2}", +Upload Bank Statement,Cargar extracto bancario, +Use Company Default Round Off Cost Center,Utilizar el Centro de Costes de redondeo por defecto de la CompaƱƭa, +Use Company default Cost Center for Round off,Utilizar el Centro de Costos por defecto de la compaƱƭa para el redondeo, +Use HTTP Protocol,Usar protocolo HTTP, +Use Transaction Date Exchange Rate,Usar el tipo de cambio de fecha de la transacción, +User {0}: Removed Employee Self Service role as there is no mapped employee.,"Usuario {0}: Eliminado el rol de Autoservicio del Empleado, ya que no hay ningĆŗn empleado mapeado.", +User {0}: Removed Employee role as there is no mapped employee.,"Usuario {0}: Se eliminó el rol de Empleado, ya que no hay ningĆŗn empleado asignado.", +VAT Accounts,Cuentas de IVA, +VAT Amount (AED),Importe del IVA (AED), +VAT Audit Report,Informe de auditorĆ­a del IVA, +Valuation Rate (In / Out),Tasa de Valoración (Entrada/Salida), +Value Change,Cambio de Valor, +Voucher,Comprobante, +"WARNING: Exotel app has been separated from ERPNext, please install the app to continue using Exotel integration.",ADVERTENCIA: La aplicación Exotel se ha separado de ERPNext; instale la aplicación para continuar usando la integración de Exotel., +Waiting for payment...,Esperando Pago..., +Warehouse Capacity for Item '{0}' must be greater than the existing stock level of {1} {2}.,Capacidad del AlmacĆ©n para el Producto '{0}' debe ser mayor que el nivel de stock existente de {1} {2}., +Warehouse Details,Detalle del AlmacĆ©n, +Warehouse Disabled?,ĀæAlmacĆ©n deshabilitado?, +Warehouse Settings,Configuración del AlmacĆ©n, +Warehouse Wise Stock Balance,Saldo de existencias en almacĆ©n, +Warehouse wise Stock Value,Valor de las existencias en función del almacĆ©n, +Warehouse {0} does not belong to Company {1}.,AlmacĆ©n {0} no pertenece a la CompaƱƭa {1}., +Warning!,Ā”Advertencia!, +Watch Video,Ver video, +Website Script,Script del Sitio Web, +Website Theme,Tema del Sitio Web, +Week {0} {1},Semana {0} {1}, +Weight (kg),Peso (kg), +Width (cm),Ancho (cm), +Workflow,Flujos de Trabajo, +Workflow Action,Acciones de flujos de trabajo, +Workflow State,Estados de flujos de trabajo, +Workstation Status,Estado de la estación de trabajo, +Workstation Type,Tipo de estación de trabajo, +Workstations,Estación de trabajo, +Wrong Company,CompaƱƭa incorrecta, +You haven't created a {0} yet,AĆŗn no ha creado un {0}, +Your Name (required),Su nombre (requerido), +`Allow Negative rates for Items`,`Permitir precios Negativos para los Productos`, +description,descripción, +fieldname,nombre del campo, +variance,variación, +{0} and {1},{0} y {1}, +{0} is already running for {1},{0} ya se estĆ” ejecutando por {1}, +{0} {1} Manually,{0} {1} Manualmente, +{0} {1} Partially Reconciled,{0} {1} Parcialmente reconciliado, +{0} {1}: Account {2} is a Group Account and group accounts cannot be used in transactions,{0} {1}: La cuenta {2} es una Cuenta de Grupo y las Cuentas de Grupo no pueden utilizarse en transacciones, +{0} {1}: Cost Center {2} is a group cost center and group cost centers cannot be used in transactions,{0} {1}: El Centro de Costos {2} es un Centro de Costos de Grupo y los Centros de Costos de Grupo no pueden utilizarse en transacciones, +{0}% of total invoice value will be given as discount.,{0}% del valor total de la factura se otorgarĆ” como descuento., +{} Available,{} Disponible, +{} Assigned,{} Asignado, +{} Available,{} Disponible, +{} Open,{} Abierto, +{} {} is already linked with another {},{} {} ya estĆ” vinculado con otro {}, +{} {} is already linked with {} {},{} {} ya estĆ” vinculado con {} {}, diff --git a/erpnext/translations/fa.csv b/erpnext/translations/fa.csv index 7c2e8544c9c..f65226bf761 100644 --- a/erpnext/translations/fa.csv +++ b/erpnext/translations/fa.csv @@ -8743,3 +8743,2535 @@ WhatsApp,واتس اپ, Make a call,ŲŖŁ…Ų§Ų³ بگیر, Approve,تایید, Reject,Ų±ŲÆ کردن, + Address, Ų¢ŲÆŲ±Ų³, + Amount, Ł…ŪŒŲ²Ų§Ł†, + Is Child Table, Ų¬ŲÆŁˆŁ„ فرزند Ų§Ų³ŲŖ, + Name, نام, + Rate, نرخ, + Summary, خلاصه, +"""SN-01::10"" for ""SN-01"" to ""SN-10""","""SN-01::10"" برای ""SN-01"" ŲŖŲ§ ""SN-10""", +# In Stock,# ŲÆŲ± Ł…ŁˆŲ¬ŁˆŲÆŪŒ, +# Req'd Items,# Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ŪŒ درخواست ؓده, +% Finished Item Quantity,% مقدار Ų¢ŪŒŲŖŁ… ŲŖŁ…Ų§Ł… ؓده, +% Occupied,ŁŖ Ł…Ų“ŲŗŁˆŁ„, +% Picked,% انتخاب ؓده, +% Process Loss,% Ų§Ų² ŲÆŲ³ŲŖ دادن ŁŲ±Ų¢ŪŒŁ†ŲÆ, +% Returned,% برگردانده ؓده, +'Account' in the Accounting section of Customer {0},Ų­Ų³Ų§ŲØ ŲÆŲ± ŲØŲ®Ų“ حسابداری Ł…Ų“ŲŖŲ±ŪŒ {0}, +'Allow Multiple Sales Orders Against a Customer's Purchase Order',اجازه دادن Ų³ŁŲ§Ų±Ų“ā€ŒŁ‡Ų§ŪŒ فروؓ چندگانه ŲÆŲ± ŲØŲ±Ų§ŲØŲ± سفارؓ خرید Ł…Ų“ŲŖŲ±ŪŒ, +'Default {0} Account' in Company {1},«حساب Ł¾ŪŒŲ“ā€ŒŁŲ±Ų¶ {0}Ā» ŲÆŲ± ؓرکت {1}, +'To Package No.' cannot be less than 'From Package No.',"'به ؓماره بسته.' Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł†ŲÆ کمتر Ų§Ų² ""Ų§Ų² ؓماره بسته"" ŲØŲ§Ų“ŲÆ.", +'{0}' account is already used by {1}. Use another account.,Ų­Ų³Ų§ŲØ '{0}' قبلاً توسط {1} استفاده ؓده Ų§Ų³ŲŖ. Ų§Ų² Ų­Ų³Ų§ŲØ دیگری استفاده Ś©Ł†ŪŒŲÆ., +'{0}' should be in company currency {1}.,"""{0}"" باید به Ų§Ų±Ų² ؓرکت {1} ŲØŲ§Ų“ŲÆ.", +(A) Qty After Transaction,(A) ŲŖŲ¹ŲÆŲ§ŲÆ پس Ų§Ų² تراکنؓ, +(B) Expected Qty After Transaction,(ŲØ) ŲŖŲ¹ŲÆŲ§ŲÆ Ł…ŁˆŲ±ŲÆ انتظار پس Ų§Ų² تراکنؓ, +(C) Total Qty in Queue,(C) ŲŖŲ¹ŲÆŲ§ŲÆ کل ŲÆŲ± صف, +(C) Total qty in queue,(C) ŲŖŲ¹ŲÆŲ§ŲÆ کل ŲÆŲ± صف, +(D) Balance Stock Value,(D) Ų§Ų±Ų²Ų“ Ł…ŁˆŲ¬ŁˆŲÆŪŒ, +(E) Balance Stock Value in Queue,(E) Ł…ŁˆŲ¬ŁˆŲÆŪŒ Ų§Ų±Ų²Ų“ Ł…ŁˆŲ¬ŁˆŲÆŪŒ ŲÆŲ± صف, +(F) Change in Stock Value,(F) تغییر ŲÆŲ± Ų§Ų±Ų²Ų“ Ł…ŁˆŲ¬ŁˆŲÆŪŒ, +(G) Sum of Change in Stock Value,(Ų²) Ł…Ų¬Ł…ŁˆŲ¹ تغییر ŲÆŲ± Ų§Ų±Ų²Ų“ Ł…ŁˆŲ¬ŁˆŲÆŪŒ, +(H) Change in Stock Value (FIFO Queue),(H) تغییر ŲÆŲ± Ų§Ų±Ų²Ų“ Ł…ŁˆŲ¬ŁˆŲÆŪŒ (صف FIFO), +(H) Valuation Rate,(H) نرخ Ų§Ų±Ų²Ų“ گذاری, +(I) Valuation Rate,(I) نرخ Ų§Ų±Ų²Ų“ گذاری, +(J) Valuation Rate as per FIFO,(J) نرخ ارزیابی Ł…Ų·Ų§ŲØŁ‚ ŲØŲ§ FIFO, +(K) Valuation = Value (D) Ć· Qty (A),(K) Ų§Ų±Ų²Ų“ = Ų§Ų±Ų²Ų“ (D) Ć· ŲŖŲ¹ŲÆŲ§ŲÆ (A), +", with the inventory {0}: {1}",، ŲØŲ§ Ł…ŁˆŲ¬ŁˆŲÆŪŒ {0}: {1}, +0-30 Days,0-30 روز, +3 Yearly,3 سالانه, +30-60 Days,30-60 روز, +60-90 Days,60-90 روز, +90 Above,90 بالا, +"
Other Details
","
جزئیات دیگر
", +"
No Matching Bank Transactions Found
","
Ł‡ŪŒŚ† تراکنؓ ŲØŲ§Ł†Ś©ŪŒ Ł…Ł†Ų·ŲØŁ‚ŪŒ پیدا نؓد
", +"
+

All dimensions in centimeter only

+
","
+

همه Ų§ŲØŲ¹Ų§ŲÆ فقط به Ų³Ų§Ł†ŲŖŪŒā€ŒŁ…ŲŖŲ±

+
", +"

About Product Bundle

+ +

Aggregate group of Items into another Item. This is useful if you are bundling a certain Items into a package and you maintain stock of the packed Items and not the aggregate Item.

+

The package Item will have Is Stock Item as No and Is Sales Item as Yes.

+

Example:

+

If you are selling Laptops and Backpacks separately and have a special price if the customer buys both, then the Laptop + Backpack will be a new Product Bundle Item.

","

درباره باندل Ł…Ų­ŲµŁˆŁ„

+ +

ŚÆŲ±ŁˆŁ‡ŪŒ Ų§Ų² Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ Ų±Ų§ ŲÆŲ± Ų¢ŪŒŲŖŁ… دیگری جمع آوری Ś©Ł†ŪŒŲÆ. Ų§ŚÆŲ± Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ŪŒ خاصی Ų±Ų§ ŲÆŲ± یک باندل قرار ŲÆŁ‡ŪŒŲÆ و Ł…ŁˆŲ¬ŁˆŲÆŪŒ Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ŪŒ بسته ŲØŁ†ŲÆŪŒ ؓده Ų±Ų§ حفظ Ś©Ł†ŪŒŲÆ و نه Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ŪŒ جمع آوری Ų“ŲÆŁ‡ŲŒ Ł…ŁŪŒŲÆ Ų§Ų³ŲŖ.

+

بسته Ų¢ŪŒŲŖŁ… دارای Ų¢ŪŒŲŖŁ… Ł…ŁˆŲ¬ŁˆŲÆŪŒ Ų§Ų³ŲŖ به Ų¹Ł†ŁˆŲ§Ł† خیر و Ų¢ŪŒŲŖŁ… فروؓ Ų§Ų³ŲŖ به Ų¹Ł†ŁˆŲ§Ł† بله Ų®ŁˆŲ§Ł‡ŲÆ بود. .

+

Ł…Ų«Ų§Ł„:

+

Ų§ŚÆŲ± Ł„Ł¾ā€ŒŲŖŲ§Ł¾ و Ś©ŁˆŁ„Ł‡ā€ŒŁ¾Ų“ŲŖŪŒ Ų±Ų§ جداگانه Ł…ŪŒā€ŒŁŲ±ŁˆŲ“ŪŒŲÆ و Ų§ŚÆŲ± Ł…Ų“ŲŖŲ±ŪŒ هر دو Ų±Ų§ بخرد، Ł‚ŪŒŁ…ŲŖ ŁˆŪŒŚ˜Ł‡ā€ŒŲ§ŪŒ دارید، Ł„Ł¾ā€ŒŲŖŲ§Ł¾ + Ś©ŁˆŁ„Ł‡ā€ŒŁ¾Ų“ŲŖŪŒ یک Ś©Ų§Ł„Ų§ŪŒ باندل Ł…Ų­ŲµŁˆŁ„ جدید Ų®ŁˆŲ§Ł‡ŲÆ بود.

", +"
Or
","
یا
", +"","", +"","", +"","", +"Masters & Reports","مستندات و ŚÆŲ²Ų§Ų±Ų“ ها", +"Quick Access","دسترسی سریع", +"Reports & Masters","ŚÆŲ²Ų§Ų±Ų“ ها و مستندات", +"Reports & Masters","ŚÆŲ²Ų§Ų±Ų“ ها و مستندات", +"Settings","ŲŖŁ†ŲøŪŒŁ…Ų§ŲŖ", +"Shortcuts","Ł…ŪŒŲ§Ł†ŲØŲ±Ł‡Ų§", +"Your Shortcuts + + + + + + ","Ł…ŪŒŲ§Ł†ŲØŲ±Ł‡Ų§ŪŒ Ų“Ł…Ų§ + + + + + + ", +"Your Shortcuts","Ł…ŪŒŲ§Ł†ŲØŲ±Ł‡Ų§ŪŒ Ų“Ł…Ų§", +Grand Total: {0},جمع کل: {0}, +Outstanding Amount: {0},مبلغ Ł…Ų¹ŁˆŁ‚: {0}, +A - B,الف - ŲØ, +A - C,الف - Ų¬, +A Holiday List can be added to exclude counting these days for the Workstation.,فهرست ŲŖŲ¹Ų·ŪŒŁ„Ų§ŲŖ Ų±Ų§ Ł…ŪŒ ŲŖŁˆŲ§Ł† اضافه کرد ŲŖŲ§ Ų“Ł…Ų§Ų±Ų“ Ų§ŪŒŁ† Ų±ŁˆŲ²Ł‡Ų§ برای Ų§ŪŒŲ³ŲŖŚÆŲ§Ł‡ کاری حذف ؓود., +A Packing Slip can only be created for Draft Delivery Note.,یک برگه بسته ŲØŁ†ŲÆŪŒ فقط Ł…ŪŒ ŲŖŁˆŲ§Ł†ŲÆ برای پیؓ Ł†ŁˆŪŒŲ³ یادداؓت ŲŖŲ­ŁˆŪŒŁ„ ایجاد ؓود., +"A Price List is a collection of Item Prices either Selling, Buying, or both",Ł„ŪŒŲ³ŲŖ Ł‚ŪŒŁ…ŲŖ Ł…Ų¬Ł…ŁˆŲ¹Ł‡ ای Ų§Ų² Ł‚ŪŒŁ…ŲŖ Ł‡Ų§ŪŒ Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ŪŒ فروؓ، خرید یا هر دو Ų§Ų³ŲŖ, +A Reconciliation Job {0} is running for the same filters. Cannot reconcile now,یک کار ŲŖŲ·ŲØŪŒŁ‚ {0} برای Ł‡Ł…ŪŒŁ† ŁŪŒŁ„ŲŖŲ±Ł‡Ų§ ŲÆŲ± Ų­Ų§Ł„ Ų§Ų¬Ų±Ų§ Ų§Ų³ŲŖ. الان Ł†Ł…ŪŒŲ“Ł‡ ŲŖŲ·ŲØŪŒŁ‚ کرد, +A Transaction Deletion Document: {0} is triggered for {0},یک سند حذف تراکنؓ: {0} برای {0} فعال Ł…ŪŒ ؓود, +A customer must have primary contact email.,Ł…Ų“ŲŖŲ±ŪŒ باید Ų§ŪŒŁ…ŪŒŁ„ ŲŖŁ…Ų§Ų³ Ų§ŲµŁ„ŪŒ داؓته ŲØŲ§Ų“ŲÆ., +A driver must be set to submit.,یک راننده باید برای Ų§Ų±Ų³Ų§Ł„ ŲŖŁ†ŲøŪŒŁ… ؓود., +A template with tax category {0} already exists. Only one template is allowed with each tax category,Ų§Ł„ŚÆŁˆŪŒŪŒ ŲØŲ§ دسته Ł…Ų§Ł„ŪŒŲ§ŲŖŪŒ {0} Ų§Ų² قبل وجود ŲÆŲ§Ų±ŲÆ. فقط یک Ų§Ł„ŚÆŁˆ ŲØŲ§ هر دسته Ł…Ų§Ł„ŪŒŲ§ŲŖ Ł…Ų¬Ų§Ų² Ų§Ų³ŲŖ, +API Details,جزئیات API, +AWB Number,ؓماره AWB, +Abbreviation: {0} must appear only once,مخفف: {0} باید فقط یک ŲØŲ§Ų± ظاهر ؓود, +About Us Settings,ŲŖŁ†ŲøŪŒŁ…Ų§ŲŖ درباره Ł…Ų§, +About {0} minute remaining,حدود {0} ŲÆŁ‚ŪŒŁ‚Ł‡ ŲØŲ§Ł‚ŪŒ مانده Ų§Ų³ŲŖ, +About {0} minutes remaining,حدود {0} ŲÆŁ‚ŪŒŁ‚Ł‡ ŲØŲ§Ł‚ŪŒ مانده Ų§Ų³ŲŖ, +About {0} seconds remaining,حدود {0} Ų«Ų§Ł†ŪŒŁ‡ ŲØŲ§Ł‚ŪŒ مانده Ų§Ų³ŲŖ, +Acceptance Criteria Formula,ŁŲ±Ł…ŁˆŁ„ Ł…Ų¹ŪŒŲ§Ų±Ł‡Ų§ŪŒ پذیرؓ, +Acceptance Criteria Value,مقدار Ł…Ų¹ŪŒŲ§Ų±Ł‡Ų§ŪŒ پذیرؓ, +Accepted Qty in Stock UOM,ŲŖŲ¹ŲÆŲ§ŲÆ Ł¾Ų°ŪŒŲ±ŁŲŖŁ‡ ؓده ŲÆŲ± انبار UOM, +Access Key,Ś©Ł„ŪŒŲÆ دسترسی, +Access Key is required for Service Provider: {0},Ś©Ł„ŪŒŲÆ دسترسی برای Ų§Ų±Ų§Ų¦Ł‡ā€ŒŲÆŁ‡Ł†ŲÆŁ‡ Ų®ŲÆŁ…Ų§ŲŖ لازم Ų§Ų³ŲŖ: {0}, +Account Balance (From),ŲŖŲ±Ų§Ų² Ų­Ų³Ų§ŲØ (Ų§Ų²), +Account Balance (To),ŲŖŲ±Ų§Ų² Ų­Ų³Ų§ŲØ (به), +Account Closing Balance,ŲŖŲ±Ų§Ų² Ų§Ų®ŲŖŲŖŲ§Ł…ŪŒŁ‡ Ų­Ų³Ų§ŲØ, +Account Currency (From),Ų§Ų±Ų² Ų­Ų³Ų§ŲØ (Ų§Ų²), +Account Currency (To),Ų§Ų±Ų² Ų­Ų³Ų§ŲØ (به), +Account Opening Balance,ŲŖŲ±Ų§Ų² Ų§ŁŲŖŲŖŲ§Ų­ŪŒŁ‡ Ų­Ų³Ų§ŲØ, +Account not Found,Ų­Ų³Ų§ŲØ پیدا نؓد, +Account {0} added multiple times,Ų­Ų³Ų§ŲØ {0} Ś†Ł†ŲÆŪŒŁ† ŲØŲ§Ų± اضافه Ų“ŲÆ, +Accounting Dimension Filter,ŁŪŒŁ„ŲŖŲ± Ų§ŲØŲ¹Ų§ŲÆ حسابداری, +Accounting Dimensions Filter,ŁŪŒŁ„ŲŖŲ± Ų§ŲØŲ¹Ų§ŲÆ حسابداری, +Accounting Entry for {0},Ų«ŲØŲŖ حسابداری برای {0}, +Accounts Closing,بسته ؓدن Ų­Ų³Ų§ŲØ ها, +Accounts Missing Error,خطای ŚÆŁ… ؓدن Ų­Ų³Ų§ŲØ ها, +Accounts Receivable/Payable,Ų­Ų³Ų§ŲØ Ł‡Ų§ŪŒ ŲÆŲ±ŪŒŲ§ŁŲŖŁ†ŪŒ / Ł¾Ų±ŲÆŲ§Ų®ŲŖŁ†ŪŒ, +Accounts to Merge,Ų­Ų³Ų§ŲØ ها برای Ų§ŲÆŲŗŲ§Ł…, +Action If Quality Inspection Is Rejected,اقدام Ų§ŚÆŲ± بازرسی کیفیت Ų±ŲÆ ؓود, +Action If Same Rate is Not Maintained,Ų§ŚÆŲ± همان نرخ حفظ Ł†Ų“ŁˆŲÆŲŒ اقدام Ś©Ł†ŪŒŲÆ, +Action if Same Rate is Not Maintained Throughout Sales Cycle,Ų§ŚÆŲ± نرخ ŪŒŚ©Ų³Ų§Ł†ŪŒ ŲÆŲ± Ų·ŁˆŁ„ چرخه فروؓ حفظ Ł†Ų“ŁˆŲÆŲŒ اقدام Ś©Ł†ŪŒŲÆ, +Active Status,وضعیت فعال, +Actual Balance Qty,مقدار ŲŖŲ±Ų§Ų² ŁˆŲ§Ł‚Ų¹ŪŒ, +Actual Expense,Ł‡Ų²ŪŒŁ†Ł‡ ŁˆŲ§Ł‚Ų¹ŪŒ, +Actual Posting,Ų§Ų±Ų³Ų§Ł„ ŁˆŲ§Ł‚Ų¹ŪŒ, +Actual Qty in Warehouse,مقدار ŁˆŲ§Ł‚Ų¹ŪŒ ŲÆŲ± انبار, +Actual Time,زمان ŁˆŲ§Ł‚Ų¹ŪŒ, +Add Columns in Transaction Currency,اضافه کردن Ų³ŲŖŁˆŁ† به Ų§Ų±Ų² تراکنؓ, +Add Corrective Operation Cost in Finished Good Valuation,اضافه کردن Ł‡Ų²ŪŒŁ†Ł‡ Ų¹Ł…Ł„ŪŒŲ§ŲŖ Ų§ŲµŁ„Ų§Ų­ŪŒ ŲÆŲ± Ų§Ų±Ų²Ų“ گذاری Ś©Ų§Ł„Ų§ŪŒ ŲŖŁ…Ų§Ł… ؓده, +Add Discount,Ų§ŁŲ²ŁˆŲÆŁ† تخفیف, +Add Items in the Purpose Table,Ų§ŁŲ²ŁˆŲÆŁ† Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ ŲÆŲ± Ų¬ŲÆŁˆŁ„ هدف, +Add Lead to Prospect,Ł„ŪŒŲÆ Ų±Ų§ به Prospect اضافه Ś©Ł†ŪŒŲÆ, +Add Local Holidays,ŲŖŲ¹Ų·ŪŒŁ„Ų§ŲŖ Ł…Ų­Ł„ŪŒ Ų±Ų§ اضافه Ś©Ł†ŪŒŲÆ, +Add Manually,Ų§ŁŲ²ŁˆŲÆŁ† دستی, +Add Or Deduct,Ų§ŁŲ²ŁˆŲÆŁ† یا کسر, +Add Serial / Batch Bundle,Ų§ŁŲ²ŁˆŲÆŁ† باندل Ų³Ų±ŪŒŲ§Ł„ / دسته, +Add Serial / Batch No,اضافه کردن Ų³Ų±ŪŒŲ§Ł„ / ؓماره دسته, +Add Serial / Batch No (Rejected Qty),اضافه کردن Ų³Ų±ŪŒŲ§Ł„ / ؓماره دسته (ŲŖŲ¹ŲÆŲ§ŲÆ Ų±ŲÆ ؓده), +Add Stock,Ų§ŁŲ²ŁˆŲÆŁ† Ł…ŁˆŲ¬ŁˆŲÆŪŒ, +Add Sub Assembly,Ų§ŁŲ²ŁˆŲÆŁ† زیر Ł…ŁˆŁ†ŲŖŲ§Ś˜, +Add Template,اضافه کردن Ų§Ł„ŚÆŁˆ, +Add a Note,یک یادداؓت اضافه Ś©Ł†ŪŒŲÆ, +Add details,جزئیات Ų±Ų§ اضافه Ś©Ł†ŪŒŲÆ, +Add to Prospect,به Prospect اضافه Ś©Ł†ŪŒŲÆ, +Added By,اضافه ؓده توسط, +Added On,اضافه ؓده ŲÆŲ±, +Added Supplier Role to User {0}.,نقؓ ŲŖŲ§Ł…ŪŒŁ† کننده به کاربر {0} اضافه Ų“ŲÆ., +Added {1} Role to User {0}.,نقؓ {1} به کاربر {0} اضافه Ų“ŲÆ., +Adding Lead to Prospect...,Ų§ŁŲ²ŁˆŲÆŁ† سرنخ به Ł…Ų“ŲŖŲ±ŪŒ ŲØŲ§Ł„Ł‚ŁˆŁ‡..., +Additional,اضافی, +Additional Asset Cost,Ł‡Ų²ŪŒŁ†Ł‡ دارایی اضافی, +Additional Cost Per Qty,Ł‡Ų²ŪŒŁ†Ł‡ اضافی ŲÆŲ± هر ŲŖŲ¹ŲÆŲ§ŲÆ, +Additional Info,اطلاعات اضافی, +Address And Contacts,Ų¢ŲÆŲ±Ų³ و Ł…Ų®Ų§Ų·ŲØŪŒŁ†, +Adjust Asset Value,Ų§Ų±Ų²Ų“ دارایی Ų±Ų§ ŲŖŁ†ŲøŪŒŁ… Ś©Ł†ŪŒŲÆ, +Adjustment Against,ŲŖŲ¹ŲÆŪŒŁ„ ŲÆŲ± مقابل, +Adjustment based on Purchase Invoice rate,ŲŖŲ¹ŲÆŪŒŁ„ ŲØŲ± Ų§Ų³Ų§Ų³ نرخ فاکتور خرید, +Advance Payment,پیؓ پرداخت, +Advance Tax,پیؓ پرداخت Ł…Ų§Ł„ŪŒŲ§ŲŖ, +Advance Taxes and Charges,پیؓ پرداخت Ł…Ų§Ł„ŪŒŲ§ŲŖ و Ł‡Ų²ŪŒŁ†Ł‡ ها, +Advance paid against {0} {1} cannot be greater than Grand Total {2},پیؓ پرداخت ŲÆŲ± مقابل {0} {1} Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł†ŲÆ بیؓتر Ų§Ų² کل کل {2} ŲØŲ§Ų“ŲÆ, +Advance payments allocated against orders will only be fetched,پیؓ پرداخت Ł‡Ų§ŪŒ تخصیص ŪŒŲ§ŁŲŖŁ‡ ŲÆŲ± ŲØŲ±Ų§ŲØŲ± سفارؓ ها فقط واکؓی Ł…ŪŒ ؓود, +Affected Transactions,معاملات ŲŖŲ­ŲŖ تأثیر, +Against Customer Order {0},ŲÆŲ± مقابل سفارؓ Ł…Ų“ŲŖŲ±ŪŒ {0}, +Against Supplier Invoice {0},ŲÆŲ± مقابل فاکتور ŲŖŲ§Ł…ŪŒŁ† کننده {0}, +Against Voucher No,ŲÆŲ± مقابل Ś©ŁˆŁ¾Ł† ؓماره, +Age ({0}),سن ({0}), +Ageing Range,Ł…Ų­ŲÆŁˆŲÆŁ‡ Ų³Ų§Ł„Ų®ŁˆŲ±ŲÆŚÆŪŒ, +Agent Busy Message,Ł¾ŪŒŲ§Ł… Ł…Ų§Ł…ŁˆŲ± Ł…Ų“ŲŗŁˆŁ„, +Agent Group,ŚÆŲ±ŁˆŁ‡ Ų¹Ų§Ł…Ł„, +Agent Unavailable Message,Ł¾ŪŒŲ§Ł… Ų¹Ų§Ł…Ł„ ŲÆŲ± ŲÆŲ³ŲŖŲ±Ų³ Ł†ŪŒŲ³ŲŖ, +Aggregate a group of Items into another Item. This is useful if you are maintaining the stock of the packed items and not the bundled item,ŚÆŲ±ŁˆŁ‡ŪŒ Ų§Ų² Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ Ų±Ų§ ŲÆŲ± یک Ų¢ŪŒŲŖŁ… دیگر جمع Ś©Ł†ŪŒŲÆ. Ų§ŚÆŲ± Ł…ŁˆŲ¬ŁˆŲÆŪŒ Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ŪŒ بسته ŲØŁ†ŲÆŪŒ ؓده Ų±Ų§ Ł†ŚÆŁ‡ŲÆŲ§Ų±ŪŒ Ł…ŪŒ Ś©Ł†ŪŒŲÆ و نه Ų¢ŪŒŲŖŁ… باندل Ų“ŲÆŁ‡ŲŒ Ł…ŁŪŒŲÆ Ų§Ų³ŲŖ, +Algorithm,Ų§Ł„ŚÆŁˆŲ±ŪŒŲŖŁ…, +All Activities,ŲŖŁ…Ų§Ł… ŁŲ¹Ų§Ł„ŪŒŲŖ ها, +All Activities HTML,ŲŖŁ…Ų§Ł… ŁŲ¹Ų§Ł„ŪŒŲŖ ها HTML, +All Items,همه Ų¢ŪŒŲŖŁ… ها, +All allocations have been successfully reconciled,همه تخصیص ها ŲØŲ§ Ł…ŁˆŁŁ‚ŪŒŲŖ ŲŖŲ·ŲØŪŒŁ‚ داده ؓده Ų§Ų³ŲŖ, +All items have already been received,همه Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ قبلاً دریافت ؓده Ų§Ų³ŲŖ, +All items in this document already have a linked Quality Inspection.,همه Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ ŲÆŲ± Ų§ŪŒŁ† سند قبلاً دارای یک بازرسی کیفیت Ł…Ų±ŲŖŲØŲ· هستند., +All the Comments and Emails will be copied from one document to another newly created document(Lead -> Opportunity -> Quotation) throughout the CRM documents.,ŲŖŁ…Ų§Ł… نظرات و Ų§ŪŒŁ…ŪŒŁ„ ها Ų§Ų² یک سند به سند جدید ایجاد ؓده دیگر (سرنخ -> فرصت -> پیؓ فاکتور) ŲÆŲ± Ų³Ų±Ų§Ų³Ų± اسناد CRM کپی Ł…ŪŒ Ų“ŁˆŁ†ŲÆ., +"All the required items (raw materials) will be fetched from BOM and populated in this table. Here you can also change the Source Warehouse for any item. And during the production, you can track transferred raw materials from this table.",ŲŖŁ…Ų§Ł… Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ŪŒ Ł…ŁˆŲ±ŲÆ Ł†ŪŒŲ§Ų² (Ł…ŁˆŲ§ŲÆ Ų§ŁˆŁ„ŪŒŁ‡) Ų§Ų² BOM واکؓی ؓده و ŲÆŲ± Ų§ŪŒŁ† Ų¬ŲÆŁˆŁ„ پر Ł…ŪŒ ؓود. ŲÆŲ± Ų§ŪŒŁ†Ų¬Ų§ Ų“Ł…Ų§ Ł‡Ł…Ś†Ł†ŪŒŁ† Ł…ŪŒ ŲŖŁˆŲ§Ł†ŪŒŲÆ انبار منبع Ų±Ų§ برای هر Ų¢ŪŒŲŖŁ… تغییر ŲÆŁ‡ŪŒŲÆ. و ŲÆŲ± Ų­ŪŒŁ† ŲŖŁˆŁ„ŪŒŲÆ Ł…ŪŒ ŲŖŁˆŲ§Ł†ŪŒŲÆ Ł…ŁˆŲ§ŲÆ Ų§ŁˆŁ„ŪŒŁ‡ انتقال ŪŒŲ§ŁŲŖŁ‡ Ų±Ų§ Ų§Ų² Ų§ŪŒŁ† Ų¬ŲÆŁˆŁ„ ردیابی Ś©Ł†ŪŒŲÆ., +Allocate Payment Request,تخصیص درخواست پرداخت, +Allocated Entries,ورودی Ł‡Ų§ŪŒ Ų§Ų®ŲŖŲµŲ§Ųµ داده ؓده, +Allocated To:,Ų§Ų®ŲŖŲµŲ§Ųµ داده ؓده به:, +Allocations,تخصیص ها, +Allow,اجازه, +Allow Alternative Item must be checked on Item {},Ų¢ŪŒŲŖŁ… Ų¬Ų§ŪŒŚÆŲ²ŪŒŁ† Ł…Ų¬Ų§Ų² Ų§Ų³ŲŖ باید برای Ų¢ŪŒŲŖŁ… {} علامت زده ؓود, +Allow Continuous Material Consumption,اجازه مصرف Ł…ŲÆŲ§ŁˆŁ… Ł…ŁˆŲ§ŲÆ, +Allow Excess Material Transfer,اجازه انتقال Ł…ŁˆŲ§ŲÆ اضافی Ų±Ų§ ŲØŲÆŁ‡ŪŒŲÆ, +Allow Item to be Added Multiple Times in a Transaction,اجازه ŲÆŁ‡ŪŒŲÆ Ų¢ŪŒŲŖŁ… Ś†Ł†ŲÆŪŒŁ† ŲØŲ§Ų± ŲÆŲ± یک تراکنؓ اضافه ؓود, +Allow Lead Duplication based on Emails,اجازه تکرار سرنخ ŲØŲ± Ų§Ų³Ų§Ų³ Ų§ŪŒŁ…ŪŒŁ„, +Allow Negative rates for Items,نرخ Ł‡Ų§ŪŒ Ł…Ł†ŁŪŒ برای Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ Ł…Ų¬Ų§Ų² Ų§Ų³ŲŖ, +Allow Or Restrict Dimension,اجازه یا Ł…Ų­ŲÆŁˆŲÆ کردن Ų§ŲØŲ¹Ų§ŲÆ, +Allow Partial Reservation,اجازه رزرو جزئی, +Allow Purchase,اجازه خرید, +Allow Sales,اجازه فروؓ, +Allow Sales Order Creation For Expired Quotation,اجازه ایجاد سفارؓ فروؓ برای Ł‚ŪŒŁ…ŲŖ Ł…Ł†Ł‚Ų¶ŪŒ ؓده, +Allow User to Edit Discount,به کاربر اجازه ویرایؓ تخفیف Ų±Ų§ ŲØŲÆŁ‡ŪŒŲÆ, +Allow User to Edit Rate,به کاربر اجازه ویرایؓ نرخ ŲØŲÆŁ‡ŪŒŲÆ, +Allow Zero Rate,اجازه نرخ صفر, +Allow material consumptions without immediately manufacturing finished goods against a Work Order,اجازه مصرف Ł…ŁˆŲ§ŲÆ Ų±Ų§ ŲØŲÆŁˆŁ† ŲŖŁˆŁ„ŪŒŲÆ فوری Ś©Ų§Ł„Ų§Ł‡Ų§ŪŒ ŲŖŁ…Ų§Ł… ؓده ŲÆŲ± ŲØŲ±Ų§ŲØŲ± دستور کار ŲØŲÆŁ‡ŪŒŲÆ, +Allow multi-currency invoices against single party account , ŲµŁˆŲ±ŲŖŲ­Ų³Ų§ŲØā€ŒŁ‡Ų§ŪŒ چند ارزی Ų±Ų§ ŲÆŲ± ŲØŲ±Ų§ŲØŲ± Ų­Ų³Ų§ŲØ یک طرف Ł…Ų¬Ų§Ų² Ś©Ł†ŪŒŲÆ, +Allow to Edit Stock UOM Qty for Purchase Documents,اجازه ویرایؓ Ł…ŁˆŲ¬ŁˆŲÆŪŒ UOM Qty برای اسناد خرید, +Allow to Edit Stock UOM Qty for Sales Documents,امکان ویرایؓ Ł…ŁˆŲ¬ŁˆŲÆŪŒ UOM Qty برای اسناد فروؓ, +Allow transferring raw materials even after the Required Quantity is fulfilled,امکان انتقال Ł…ŁˆŲ§ŲÆ Ų®Ų§Ł… حتی پس Ų§Ų² ŲØŲ±Ų¢ŁˆŲ±ŲÆŁ‡ ؓدن مقدار Ł…ŁˆŲ±ŲÆ Ł†ŪŒŲ§Ų², +Allowed,Ł…Ų¬Ų§Ų², +Allowed Dimension,Ų§ŲØŲ¹Ų§ŲÆ Ł…Ų¬Ų§Ų², +Allowed Doctypes,Doctypes Ł…Ų¬Ų§Ų², +Allowed Items,Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ŪŒ Ł…Ų¬Ų§Ų², +Allowed primary roles are 'Customer' and 'Supplier'. Please select one of these roles only.,Ł†Ł‚Ų“ā€ŒŁ‡Ų§ŪŒ Ų§ŲµŁ„ŪŒ Ł…Ų¬Ų§Ų² عبارتند Ų§Ų² Ā«Ł…Ų“ŲŖŲ±ŪŒĀ» و Ā«ŲŖŲ§Ł…ŪŒŁ†ā€ŒŚ©Ł†Ł†ŲÆŁ‡Ā». لطفا فقط یکی Ų§Ų² Ų§ŪŒŁ† نقؓ ها Ų±Ų§ انتخاب Ś©Ł†ŪŒŲÆ., +Allows to keep aside a specific quantity of inventory for a particular order.,اجازه Ł…ŪŒ دهد ŲŖŲ§ مقدار Ł…Ų“Ų®ŲµŪŒ Ų§Ų² Ł…ŁˆŲ¬ŁˆŲÆŪŒ Ų±Ų§ برای یک سفارؓ Ų®Ų§Ųµ کنار بگذارید., +Already Picked,قبلاً انتخاب ؓده Ų§Ų³ŲŖ, +Alternative Items,Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ŪŒ Ų¬Ų§ŪŒŚÆŲ²ŪŒŁ†, +"Alternatively, you can download the template and fill your data in.",Ł‡Ł…Ś†Ł†ŪŒŁ† Ł…ŪŒ ŲŖŁˆŲ§Ł†ŪŒŲÆ Ų§Ł„ŚÆŁˆ Ų±Ų§ ŲÆŲ§Ł†Ł„ŁˆŲÆ کرده و داده Ł‡Ų§ŪŒ خود Ų±Ų§ پر Ś©Ł†ŪŒŲÆ., +Amount (AED),مقدار (AED), +Amount Eligible for Commission,مبلغ واجد ؓرایط برای Ś©Ł…ŪŒŲ³ŪŒŁˆŁ†, +Amount in Account Currency,مبلغ به Ų§Ų±Ų² Ų­Ų³Ų§ŲØ, +Amount in party's bank account currency,مبلغ به Ų§Ų±Ų² Ų­Ų³Ų§ŲØ ŲØŲ§Ł†Ś©ŪŒ طرف, +Amount in transaction currency,مبلغ به Ų§Ų±Ų² تراکنؓ, +An Item Group is a way to classify items based on types.,ŚÆŲ±ŁˆŁ‡ Ų¢ŪŒŲŖŁ… Ų±Ų§Ł‡ŪŒ برای دسته ŲØŁ†ŲÆŪŒ Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ ŲØŲ± Ų§Ų³Ų§Ų³ Ų§Ł†ŁˆŲ§Ų¹ Ų§Ų³ŲŖ., +An error has been appeared while reposting item valuation via {0},هنگام Ų§Ų±Ų³Ų§Ł„ Ł…Ų¬ŲÆŲÆ ارزیابی Ł…ŁˆŲ±ŲÆ Ų§Ų² Ų·Ų±ŪŒŁ‚ {0} خطایی ظاهر Ų“ŲÆ, +An error has occurred during {0}. Check {1} for more details,خطایی ŲÆŲ± Ų·ŁˆŁ„ {0} Ų±Ų® داده Ų§Ų³ŲŖ. برای جزئیات بیؓتر {1} Ų±Ų§ بررسی Ś©Ł†ŪŒŲÆ,Error Log +Annual Revenue,درآمد سالانه, +"Another Cost Center Allocation record {0} applicable from {1}, hence this allocation will be applicable upto {2}",یکی دیگر Ų§Ų² Ų±Ś©ŁˆŲ±ŲÆŁ‡Ų§ŪŒ تخصیص مرکز Ł‡Ų²ŪŒŁ†Ł‡ {0} قابل اعمال Ų§Ų² {1}، ŲØŁ†Ų§ŲØŲ±Ų§ŪŒŁ† Ų§ŪŒŁ† تخصیص ŲŖŲ§ {2} قابل اعمال Ų®ŁˆŲ§Ł‡ŲÆ بود., +"Any one of following filters required: warehouse, Item Code, Item Group",هر یک Ų§Ų² ŁŪŒŁ„ŲŖŲ±Ł‡Ų§ŪŒ زیر Ł…ŁˆŲ±ŲÆ Ł†ŪŒŲ§Ų² Ų§Ų³ŲŖ: Ų§Ł†ŲØŲ§Ų±ŲŒ کد Ų¢ŪŒŲŖŁ…ŲŒ ŚÆŲ±ŁˆŁ‡ Ų¢ŪŒŲŖŁ…, +Applicable Dimension,Ų§ŲØŲ¹Ų§ŲÆ قابل Ų§Ų¬Ų±Ų§, +Applicable On Account,قابل Ų§Ų¬Ų±Ų§ ŲÆŲ± Ų­Ų³Ų§ŲØ, +Applied on each reading.,ŲÆŲ± هر Ų®ŁˆŲ§Ł†ŲÆŁ† اعمال Ł…ŪŒ ؓود., +Applied putaway rules.,اعمال Ł‚ŁˆŲ§Ł†ŪŒŁ† حذف, +Apply Putaway Rule,اعمال Ł‚Ų§Ł†ŁˆŁ† Putaway, +Apply Recursion Over (As Per Transaction UOM),اعمال بازگؓت بیؓ Ų§Ų² (ŲØŲ± Ų§Ų³Ų§Ų³ UOM تراکنؓ), +Apply SLA for Resolution Time,اعمال SLA برای زمان Ų­Ł„ و فصل, +Apply TDS,TDS Ų±Ų§ اعمال Ś©Ł†ŪŒŲÆ, +Apply Tax Withholding Amount , اعمال مبلغ کسر Ł…Ų§Ł„ŪŒŲ§ŲŖ, +Apply restriction on dimension values,اعمال Ł…Ų­ŲÆŁˆŲÆŪŒŲŖ ŲÆŲ± Ł…Ł‚Ų§ŲÆŪŒŲ± Ų§ŲØŲ¹Ų§ŲÆ, +Apply to All Inventory Documents,برای همه اسناد Ł…ŁˆŲ¬ŁˆŲÆŪŒ اعمال ؓود, +Apply to Document,درخواست برای سند, +Appointment Created Successfully,قرار ملاقات ŲØŲ§ Ł…ŁˆŁŁ‚ŪŒŲŖ ایجاد Ų“ŲÆ, +Appointment Scheduling Disabled,Ų²Ł…Ų§Ł†ā€ŒŲØŁ†ŲÆŪŒ قرار ŲŗŪŒŲ±ŁŲ¹Ų§Ł„ Ų§Ų³ŲŖ, +Appointment Scheduling has been disabled for this site,Ų²Ł…Ų§Ł†ā€ŒŲØŁ†ŲÆŪŒ قرار برای Ų§ŪŒŁ† سایت ŲŗŪŒŲ±ŁŲ¹Ų§Ł„ ؓده Ų§Ų³ŲŖ, +Appointment was created. But no lead was found. Please check the email to confirm,قرار ملاقات ایجاد Ų“ŲÆ. Ų§Ł…Ų§ Ų³Ų±Ł†Ų®ŪŒ پیدا نؓد. لطفا برای تایید Ų§ŪŒŁ…ŪŒŁ„ Ų±Ų§ بررسی Ś©Ł†ŪŒŲÆ, +Approximately match the description/party name against parties,ŲŖŁ‚Ų±ŪŒŲØŲ§Ł‹ توصیف/نام طرف Ų±Ų§ ŲØŲ§ Ų·Ų±Łā€ŒŁ‡Ų§ مطابقت ŲÆŁ‡ŪŒŲÆ, +Are you sure you want to clear all demo data?,آیا مطمئن Ł‡Ų³ŲŖŪŒŲÆ که Ł…ŪŒ Ų®ŁˆŲ§Ł‡ŪŒŲÆ ŲŖŁ…Ų§Ł… داده Ł‡Ų§ŪŒ Ł†Ł…Ų§ŪŒŲ“ŪŒ Ų±Ų§ پاک Ś©Ł†ŪŒŲÆŲŸ, +Are you sure you want to delete this Item?,آیا مطمئن Ł‡Ų³ŲŖŪŒŲÆ که Ł…ŪŒŲ®ŁˆŲ§Ł‡ŪŒŲÆ Ų§ŪŒŁ† Ų¢ŪŒŲŖŁ… Ų±Ų§ حذف Ś©Ł†ŪŒŲÆŲŸ, +Are you sure you want to restart this subscription?,آیا مطمئن Ł‡Ų³ŲŖŪŒŲÆ که Ł…ŪŒ Ų®ŁˆŲ§Ł‡ŪŒŲÆ Ų§ŪŒŁ† Ų§Ų“ŲŖŲ±Ų§Ś© Ų±Ų§ Ł…Ų¬ŲÆŲÆŲ§Ł‹ Ų±Ų§Ł‡ā€ŒŲ§Ł†ŲÆŲ§Ų²ŪŒ Ś©Ł†ŪŒŲÆŲŸ, +As on Date,Ł‡Ł…Ų§Ł†Ų·ŁˆŲ± که ŲÆŲ± تاریخ, +"As there are existing submitted transactions against item {0}, you can not change the value of {1}.",Ų§Ų² Ų¢Ł†Ų¬Ų§ŪŒŪŒ که ŲŖŲ±Ų§Ś©Ł†Ų“ā€ŒŁ‡Ų§ŪŒ Ų§Ų±Ų³Ų§Ł„ŪŒ Ł…ŁˆŲ¬ŁˆŲÆ ŲÆŲ± مقابل Ų¢ŪŒŲŖŁ… {0} وجود دارد، Ł†Ł…ŪŒā€ŒŲŖŁˆŲ§Ł†ŪŒŲÆ مقدار {1} Ų±Ų§ تغییر ŲÆŁ‡ŪŒŲÆ., +"As there are negative stock, you can not enable {0}.",Ų§Ų² Ų¢Ł†Ų¬Ų§ŪŒŪŒ که Ł…ŁˆŲ¬ŁˆŲÆŪŒ Ł…Ł†ŁŪŒ وجود دارد، Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł†ŪŒŲÆ {0} Ų±Ų§ فعال Ś©Ł†ŪŒŲÆ., +"As there are reserved stock, you cannot disable {0}.",Ų§Ų² Ų¢Ł†Ų¬Ų§ŪŒŪŒ که Ł…ŁˆŲ¬ŁˆŲÆŪŒ رزرو ؓده وجود دارد، Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł†ŪŒŲÆ {0} Ų±Ų§ ŲŗŪŒŲ±ŁŲ¹Ų§Ł„ Ś©Ł†ŪŒŲÆ., +"As there are sufficient Sub Assembly Items, Work Order is not required for Warehouse {0}.",Ų§Ų² Ų¢Ł†Ų¬Ų§ŪŒŪŒ که Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ŪŒ زیر Ł…ŁˆŁ†ŲŖŲ§Ś˜ کافی وجود دارد، برای انبار {0} Ł†ŪŒŲ§Ų²ŪŒ به دستور کار Ł†ŪŒŲ³ŲŖ., +"As {0} is enabled, you can not enable {1}.",Ų§Ų² Ų¢Ł†Ų¬Ų§ŪŒŪŒ که {0} فعال است، Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł†ŪŒŲÆ {1} Ų±Ų§ فعال Ś©Ł†ŪŒŲÆ., +Assembly Items,Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ŪŒ Ł…ŁˆŁ†ŲŖŲ§Ś˜, +Asset Activity,ŁŲ¹Ų§Ł„ŪŒŲŖ دارایی, +Asset Capitalization,Ų³Ų±Ł…Ų§ŪŒŁ‡ گذاری دارایی ها, +Asset Capitalization Asset Item,دارایی دارایی ŲØŲ§ Ų³Ų±Ł…Ų§ŪŒŁ‡, +Asset Capitalization Service Item,Ų¢ŪŒŲŖŁ… Ų®ŲÆŁ…Ų§ŲŖ Ų³Ų±Ł…Ų§ŪŒŁ‡ گذاری دارایی, +Asset Capitalization Stock Item,Ų¢ŪŒŲŖŁ… Ł…ŁˆŲ¬ŁˆŲÆŪŒ Ų³Ų±Ł…Ų§ŪŒŁ‡ گذاری دارایی, +Asset Depreciation Details,جزئیات استهلاک دارایی, +Asset Depreciation Schedule,Ų¬ŲÆŁˆŁ„ استهلاک دارایی ها, +Asset Depreciation Schedule for Asset {0} and Finance Book {1} is not using shift based depreciation,برنامه استهلاک دارایی برای دارایی {0} و کتاب Ł…Ų§Ł„ŪŒ {1} Ų§Ų² استهلاک Ł…ŲØŲŖŁ†ŪŒ ŲØŲ± ؓیفت استفاده Ł†Ł…ŪŒ کند, +Asset Depreciation Schedule not found for Asset {0} and Finance Book {1},برنامه استهلاک دارایی برای دارایی {0} و کتاب Ł…Ų§Ł„ŪŒ {1} یافت نؓد, +Asset Depreciation Schedule {0} for Asset {1} already exists.,برنامه استهلاک دارایی {0} برای دارایی {1} Ų§Ų² قبل وجود ŲÆŲ§Ų±ŲÆ., +Asset Depreciation Schedule {0} for Asset {1} and Finance Book {2} already exists.,برنامه استهلاک دارایی {0} برای دارایی {1} و کتاب Ł…Ų§Ł„ŪŒ {2} Ų§Ų² قبل وجود ŲÆŲ§Ų±ŲÆ., +"Asset Depreciation Schedules created:
{0}

Please check, edit if needed, and submit the Asset.",ŲØŲ±Ł†Ų§Ł…Ł‡ā€ŒŁ‡Ų§ŪŒ استهلاک دارایی ایجاد ؓده:
{0}

لطفاً بررسی Ś©Ł†ŪŒŲÆŲŒ ŲÆŲ± صورت Ł†ŪŒŲ§Ų² ویرایؓ Ś©Ł†ŪŒŲÆ و دارایی Ų±Ų§ Ų§Ų±Ų³Ų§Ł„ Ś©Ł†ŪŒŲÆ., +Asset ID,ؓناسه دارایی, +Asset Quantity,مقدار دارایی, +Asset Repair Consumed Item,Ś©Ų§Ł„Ų§ŪŒ Ł…ŲµŲ±ŁŪŒ ŲŖŲ¹Ł…ŪŒŲ± دارایی, +Asset Settings,ŲŖŁ†ŲøŪŒŁ…Ų§ŲŖ دارایی, +Asset Shift Allocation,تخصیص تغییر دارایی, +Asset Shift Factor,Ų¹Ų§Ł…Ł„ تغییر دارایی, +Asset Shift Factor {0} is set as default currently. Please change it first.,Ų¹Ų§Ł…Ł„ تغییر دارایی {0} ŲÆŲ± Ų­Ų§Ł„ Ų­Ų§Ų¶Ų± به Ų¹Ł†ŁˆŲ§Ł† پیؓ فرض ŲŖŁ†ŲøŪŒŁ… ؓده Ų§Ų³ŲŖ. لطفا Ų§ŲØŲŖŲÆŲ§ آن Ų±Ų§ تغییر ŲÆŁ‡ŪŒŲÆ., +Asset cancelled,دارایی Ł„ŲŗŁˆ Ų“ŲÆ, +Asset capitalized after Asset Capitalization {0} was submitted,پس Ų§Ų² Ų§Ų±Ų³Ų§Ł„ دارایی ŲØŲ§ حروف بزرگ {0} دارایی ŲØŲ§ حروف بزرگ Ł†ŁˆŲ“ŲŖŁ‡ Ų“ŲÆ, +Asset created,دارایی ایجاد Ų“ŲÆ, +Asset created after Asset Capitalization {0} was submitted,دارایی ایجاد ؓده پس Ų§Ų² Ų§Ų±Ų³Ų§Ł„ ŲØŲ§ حروف بزرگ دارایی {0}, +Asset created after being split from Asset {0},دارایی پس Ų§Ų² Ų¬ŲÆŲ§ ؓدن Ų§Ų² دارایی {0} ایجاد Ų“ŲÆ, +Asset decapitalized after Asset Capitalization {0} was submitted,پس Ų§Ų² Ų§Ų±Ų³Ų§Ł„ دارایی ŲØŲ§ Ų³Ų±Ł…Ų§ŪŒŁ‡ {0}، دارایی Ų§Ų² Ų³Ų±Ł…Ų§ŪŒŁ‡ Ų®Ų§Ų±Ų¬ Ų“ŲÆ, +Asset deleted,دارایی حذف Ų“ŲÆ, +Asset issued to Employee {0},دارایی برای کارمند {0} Ų­ŁˆŲ§Ł„Ł‡ ؓده, +Asset out of order due to Asset Repair {0},دارایی Ų§Ų² کار افتاده به ŲÆŁ„ŪŒŁ„ ŲŖŲ¹Ł…ŪŒŲ± دارایی {0}, +Asset received at Location {0} and issued to Employee {1},دارایی ŲÆŲ± مکان {0} دریافت و برای کارمند {1} Ų­ŁˆŲ§Ł„Ł‡ Ų“ŲÆ, +Asset restored,دارایی بازیابی Ų“ŲÆ, +Asset restored after Asset Capitalization {0} was cancelled,دارایی پس Ų§Ų² Ł„ŲŗŁˆ حروف بزرگ دارایی {0} بازیابی Ų“ŲÆ, +Asset returned,دارایی برگردانده Ų“ŲÆ, +Asset scrapped,دارایی Ų§Ų² ŲØŪŒŁ† رفته Ų§Ų³ŲŖ, +Asset sold,دارایی ŁŲ±ŁˆŲ®ŲŖŁ‡ ؓده, +Asset submitted,دارایی Ų§Ų±Ų³Ų§Ł„ Ų“ŲÆ, +Asset transferred to Location {0},دارایی به مکان {0} منتقل Ų“ŲÆ, +Asset updated after being split into Asset {0},دارایی پس Ų§Ų² ŲŖŁ‚Ų³ŪŒŁ… به دارایی {0} به روز Ų“ŲÆ, +Asset updated after cancellation of Asset Repair {0},دارایی پس Ų§Ų² Ł„ŲŗŁˆ ŲŖŲ¹Ł…ŪŒŲ± دارایی {0} به روز Ų“ŲÆ, +Asset updated after completion of Asset Repair {0},دارایی پس Ų§Ų² Ų§ŲŖŁ…Ų§Ł… ŲŖŲ¹Ł…ŪŒŲ± دارایی به روز Ų“ŲÆ {0}, +Asset {0} cannot be received at a location and given to an employee in a single movement,دارایی {0} Ų±Ų§ Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł† ŲÆŲ± یک مکان دریافت کرد و ŲÆŲ± یک حرکت به کارمند ŲÆŲ§ŲÆ, +Asset {0} does not belong to Item {1},دارایی {0} به Ų¢ŪŒŲŖŁ… {1} تعلق ندارد, +Asset {0} does not exist,دارایی {0} وجود ندارد, +Asset {0} has been created. Please set the depreciation details if any and submit it.,دارایی {0} ایجاد ؓده Ų§Ų³ŲŖ. لطفاً جزئیات استهلاک Ų±Ų§ ŲÆŲ± صورت وجود ŲŖŁ†ŲøŪŒŁ… و Ų§Ų±Ų³Ų§Ł„ Ś©Ł†ŪŒŲÆ., +Asset {0} has been updated. Please set the depreciation details if any and submit it.,دارایی {0} به روز ؓده Ų§Ų³ŲŖ. لطفاً جزئیات استهلاک Ų±Ų§ ŲÆŲ± صورت وجود ŲŖŁ†ŲøŪŒŁ… و Ų§Ų±Ų³Ų§Ł„ Ś©Ł†ŪŒŲÆ., +Asset's depreciation schedule updated after Asset Shift Allocation {0},برنامه استهلاک دارایی پس Ų§Ų² تخصیص تغییر دارایی {0} به روز Ų“ŲÆ, +Asset's value adjusted after cancellation of Asset Value Adjustment {0},Ų§Ų±Ų²Ų“ دارایی پس Ų§Ų² Ł„ŲŗŁˆ ŲŖŲ¹ŲÆŪŒŁ„ Ų§Ų±Ų²Ų“ دارایی ŲŖŁ†ŲøŪŒŁ… Ų“ŲÆ {0}, +Asset's value adjusted after submission of Asset Value Adjustment {0},Ų§Ų±Ų²Ų“ دارایی پس Ų§Ų² Ų§Ų±Ų³Ų§Ł„ ŲŖŲ¹ŲÆŪŒŁ„ Ų§Ų±Ų²Ų“ دارایی ŲŖŁ†ŲøŪŒŁ… Ų“ŲÆ {0}, +Assign Job to Employee,کار Ų±Ų§ به کارمند واگذار Ś©Ł†ŪŒŲÆ, +Assignment,تخصیص, +Assignment Conditions,ؓرایط تخصیص, +At Row #{0}: The picked quantity {1} for the item {2} is greater than available stock {3} for the batch {4} in the warehouse {5}.,ŲÆŲ± ردیف #{0}: مقدار انتخاب ؓده {1} برای Ų¢ŪŒŲŖŁ… {2} بیؓتر Ų§Ų² Ł…ŁˆŲ¬ŁˆŲÆŪŒ ŲÆŲ± ŲÆŲ³ŲŖŲ±Ų³ {3} برای دسته {4} ŲÆŲ± انبار {5} Ų§Ų³ŲŖ., +At Row #{0}: The picked quantity {1} for the item {2} is greater than available stock {3} in the warehouse {4}.,ŲÆŲ± ردیف #{0}: مقدار انتخاب ؓده {1} برای Ų¢ŪŒŲŖŁ… {2} بیؓتر Ų§Ų² Ł…ŁˆŲ¬ŁˆŲÆŪŒ ŲÆŲ± ŲÆŲ³ŲŖŲ±Ų³ {3} ŲÆŲ± انبار {4} Ų§Ų³ŲŖ., +At row {0}: Batch No is mandatory for Item {1},ŲÆŲ± ردیف {0}: ؓماره دسته برای Ł…ŁˆŲ±ŲÆ {1} اجباری Ų§Ų³ŲŖ, +At row {0}: Parent Row No cannot be set for item {1},ŲÆŲ± ردیف {0}: ردیف ŁˆŲ§Ł„ŲÆ برای Ų¢ŪŒŲŖŁ… {1} قابل ŲŖŁ†ŲøŪŒŁ… Ł†ŪŒŲ³ŲŖ, +At row {0}: Qty is mandatory for the batch {1},ŲÆŲ± ردیف {0}: مقدار برای دسته {1} اجباری Ų§Ų³ŲŖ, +At row {0}: Serial No is mandatory for Item {1},ŲÆŲ± ردیف {0}: ؓماره Ų³Ų±ŪŒŲ§Ł„ برای Ų¢ŪŒŲŖŁ… {1} اجباری Ų§Ų³ŲŖ, +At row {0}: Serial and Batch Bundle {1} has already created. Please remove the values from the serial no or batch no fields.,ŲÆŲ± ردیف {0}: باندل Ų³Ų±ŪŒŲ§Ł„ و دسته {1} قبلا ایجاد ؓده Ų§Ų³ŲŖ. لطفاً Ł…Ł‚Ų§ŲÆŪŒŲ± Ų±Ų§ Ų§Ų² ŁŪŒŁ„ŲÆŁ‡Ų§ŪŒ ؓماره Ų³Ų±ŪŒŲ§Ł„ یا ؓماره دسته حذف Ś©Ł†ŪŒŲÆ., +At row {0}: set Parent Row No for item {1},ŲÆŲ± ردیف {0}: ŲŖŁ†ŲøŪŒŁ… ؓماره ردیف ŁˆŲ§Ł„ŲÆ برای Ų¢ŪŒŲŖŁ… {1}, +Attach CSV File,پیوست ŁŲ§ŪŒŁ„ CSV, +Attendance & Leaves,حضور و غیاب و Ł…Ų±Ų®ŲµŪŒ, +Attribute value: {0} must appear only once,مقدار مؓخصه: {0} باید فقط یک ŲØŲ§Ų± ظاهر ؓود, +Auto Create Exchange Rate Revaluation,ایجاد خودکار تجدید ارزیابی نرخ Ų§Ų±Ų², +Auto Create Purchase Receipt,ایجاد خودکار رسید خرید, +Auto Create Serial and Batch Bundle For Outward,ایجاد خودکار باندل Ų³Ų±ŪŒŲ§Ł„ و دسته برای ŲØŪŒŲ±ŁˆŁ†, +Auto Create Subcontracting Order,ایجاد خودکار سفارؓ قرارداد فرعی, +Auto Created Serial and Batch Bundle,باندل Ų³Ų±ŪŒŲ§Ł„ و دسته ایجاد ؓده به صورت خودکار, +Auto Creation of Contact,ایجاد خودکار Ł…Ų®Ų§Ų·ŲØ, +Auto Email Report,ŚÆŲ²Ų§Ų±Ų“ خودکار Ų§ŪŒŁ…ŪŒŁ„, +Auto Insert Item Price If Missing,ŲÆŲ±Ų¬ خودکار Ł‚ŪŒŁ…ŲŖ Ų¢ŪŒŲŖŁ… ŲÆŲ± صورت ŁŲ±Ų§Ł…ŁˆŲ“ ؓدن, +Auto Name,نام خودکار, +Auto Reconcile,ŲŖŲ·ŲØŪŒŁ‚ خودکار, +Auto Reconcile Payments,ŲŖŲ·ŲØŪŒŁ‚ خودکار پرداخت ها, +Auto Reconciliation,ŲŖŲ·ŲØŪŒŁ‚ خودکار, +Auto Reconciliation of Payments has been disabled. Enable it through {0},ŲŖŲ·ŲØŪŒŁ‚ خودکار Ł¾Ų±ŲÆŲ§Ų®ŲŖā€ŒŁ‡Ų§ ŲŗŪŒŲ±ŁŲ¹Ų§Ł„ ؓده Ų§Ų³ŲŖ. آن Ų±Ų§ Ų§Ų² Ų·Ų±ŪŒŁ‚ {0} فعال Ś©Ł†ŪŒŲÆ, +Auto Reserve Serial and Batch Nos,ؓماره Ų³Ų±ŪŒŲ§Ł„ و دسته رزرو خودکار, +Auto Reserve Stock for Sales Order on Purchase,Ų°Ų®ŪŒŲ±Ł‡ خودکار Ł…ŁˆŲ¬ŁˆŲÆŪŒ برای سفارؓ فروؓ ŲÆŲ± هنگام خرید, +Auto close Opportunity Replied after the no. of days mentioned above,فرصت بسته ؓدن خودکار پس Ų§Ų² خیر پاسخ داده Ų“ŲÆ. Ų§Ų² Ų±ŁˆŲ²Ł‡Ų§ŪŒ ذکر ؓده ŲÆŲ± بالا, +Auto match and set the Party in Bank Transactions,مطابقت خودکار و ŲŖŁ†ŲøŪŒŁ… طرف ŲÆŲ± معاملات ŲØŲ§Ł†Ś©ŪŒ, +Auto write off precision loss while consolidation,حذف خودکار Ų§Ų² ŲÆŲ³ŲŖ دادن دقت ŲÆŲ± هنگام ŲŖŁ„ŁŪŒŁ‚, +Automatically Add Filtered Item To Cart,Ų§ŁŲ²ŁˆŲÆŁ† خودکار Ų¢ŪŒŲŖŁ… ŁŪŒŁ„ŲŖŲ± ؓده به Ų³ŲØŲÆ خرید, +Automatically Fetch Payment Terms from Order,واکؓی خودکار ؓرایط پرداخت Ų§Ų² سفارؓ, +Automatically post balancing accounting entry,Ų«ŲØŲŖ حسابداری ŲŖŲ±Ų§Ų² به طور خودکار, +Available Qty For Consumption,ŲŖŲ¹ŲÆŲ§ŲÆ Ł…ŁˆŲ¬ŁˆŲÆ برای مصرف, +Available Qty at Company,ŲŖŲ¹ŲÆŲ§ŲÆ Ł…ŁˆŲ¬ŁˆŲÆ ŲÆŲ± ؓرکت, +Available Qty at Target Warehouse,ŲŖŲ¹ŲÆŲ§ŲÆ Ł…ŁˆŲ¬ŁˆŲÆ ŲÆŲ± انبار هدف, +Available Qty to Reserve,ŲŖŲ¹ŲÆŲ§ŲÆ برای رزرو Ł…ŁˆŲ¬ŁˆŲÆ Ų§Ų³ŲŖ, +Average Completion,Ł…ŪŒŲ§Ł†ŚÆŪŒŁ† ŲŖŚ©Ł…ŪŒŁ„, +Avg Rate,Ł…ŪŒŲ§Ł†ŚÆŪŒŁ† نرخ, +Avg Rate (Balance Stock),Ł…ŪŒŲ§Ł†ŚÆŪŒŁ† نرخ (ŲŖŲ±Ų§Ų² Ł…ŁˆŲ¬ŁˆŲÆŪŒ), +BOM Created,BOM ایجاد Ų“ŲÆ, +BOM Creator,ایجاد کننده BOM, +BOM Creator Item,Ų¢ŪŒŲŖŁ… ایجاد کننده BOM, +BOM Info,اطلاعات BOM, +BOM Level,Ų³Ų·Ų­ BOM, +BOM Tree,ŲÆŲ±Ų®ŲŖ BOM, +BOM UoM,واحد اندازه گیری BOM, +BOM Update Batch,دسته به روز Ų±Ų³Ų§Ł†ŪŒ BOM, +BOM Update Initiated,به روز Ų±Ų³Ų§Ł†ŪŒ BOM Ų¢ŲŗŲ§Ų² Ų“ŲÆ, +BOM Update Log,لاگ به روز Ų±Ų³Ų§Ł†ŪŒ BOM, +BOM Updation already in progress. Please wait until {0} is complete.,ŲØŁ‡ā€ŒŲ±ŁˆŲ²Ų±Ų³Ų§Ł†ŪŒ BOM ŲÆŲ± Ų­Ų§Ł„ انجام Ų§Ų³ŲŖ. لطفاً ŲµŲØŲ± Ś©Ł†ŪŒŲÆ ŲŖŲ§ {0} کامل ؓود., +BOM Updation is queued and may take a few minutes. Check {0} for progress.,به روز Ų±Ų³Ų§Ł†ŪŒ BOM ŲÆŲ± صف Ų§Ų³ŲŖ و ممکن Ų§Ų³ŲŖ چند ŲÆŁ‚ŪŒŁ‚Ł‡ Ų·ŁˆŁ„ بکؓد. برای پیؓرفت، {0} Ų±Ų§ بررسی Ś©Ł†ŪŒŲÆ., +BOM and Production,BOM و ŲŖŁˆŁ„ŪŒŲÆ, +BOM recursion: {1} cannot be parent or child of {0},بازگؓت BOM: {1} Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł†ŲÆ ŁˆŲ§Ł„ŲÆ یا فرزند {0} ŲØŲ§Ų“ŲÆ, +BOMs Updated,BOM ها به روز ؓدند, +BOMs created successfully,BOM ŲØŲ§ Ł…ŁˆŁŁ‚ŪŒŲŖ ایجاد Ų“ŲÆ, +BOMs creation failed,ایجاد BOM Ł†Ų§Ł…ŁˆŁŁ‚ بود, +"BOMs creation has been enqueued, kindly check the status after some time",ایجاد BOM ŲÆŲ± Ł†ŁˆŲØŲŖ قرار گرفته است، لطفاً وضعیت Ų±Ų§ پس Ų§Ų² Ł…ŲÆŲŖŪŒ بررسی Ś©Ł†ŪŒŲÆ, +Balance Qty (Stock),مقدار ŲŖŲ±Ų§Ų² (Ł…ŁˆŲ¬ŁˆŲÆŪŒ), +Balance Sheet Summary,خلاصه ترازنامه, +Balance Stock Value,ŲŖŲ±Ų§Ų² Ų§Ų±Ų²Ų“ Ł…ŁˆŲ¬ŁˆŲÆŪŒ, +Bank Reconciliation Tool,Ų§ŲØŲ²Ų§Ų± ŲŖŲ·ŲØŪŒŁ‚ ŲØŲ§Ł†Ś©ŪŒ, +Bank Statement Import,ŲÆŲ±ŁˆŁ†ā€ŒŲØŁŲ±ŲÆ صورتحساب ŲØŲ§Ł†Ś©ŪŒ, +Bank Transaction {0} Matched,تراکنؓ ŲØŲ§Ł†Ś©ŪŒ {0} مطابقت ŲÆŲ§Ų±ŲÆ, +Bank Transaction {0} added as Journal Entry,تراکنؓ ŲØŲ§Ł†Ś©ŪŒ {0} به Ų¹Ł†ŁˆŲ§Ł† Ų«ŲØŲŖ دفتر Ų±ŁˆŲ²Ł†Ų§Ł…Ł‡ اضافه Ų“ŲÆ, +Bank Transaction {0} added as Payment Entry,تراکنؓ ŲØŲ§Ł†Ś©ŪŒ {0} به Ų¹Ł†ŁˆŲ§Ł† Ų«ŲØŲŖ پرداخت اضافه Ų“ŲÆ, +Bank Transaction {0} is already fully reconciled,تراکنؓ ŲØŲ§Ł†Ś©ŪŒ {0} ŲÆŲ± Ų­Ų§Ł„ Ų­Ų§Ų¶Ų± به طور کامل ŲŖŲ·ŲØŪŒŁ‚ ؓده Ų§Ų³ŲŖ, +Bank Transaction {0} updated,تراکنؓ ŲØŲ§Ł†Ś©ŪŒ {0} به روز Ų“ŲÆ, +Bank/Cash Account,بانک / Ų­Ų³Ų§ŲØ Ł†Ł‚ŲÆŪŒ, +Bank/Cash Account {0} doesn't belong to company {1},Ų­Ų³Ų§ŲØ ŲØŲ§Ł†Ś©ŪŒ/Ł†Ł‚ŲÆŪŒ {0} به ؓرکت {1} تعلق ندارد, +Base Amount,مقدار Ł¾Ų§ŪŒŁ‡, +Base Cost Per Unit,Ł‡Ų²ŪŒŁ†Ł‡ Ł¾Ų§ŪŒŁ‡ به ازای هر واحد, +Base Rate,نرخ Ł¾Ų§ŪŒŁ‡, +Base Tax Withholding Net Total,کل خالص کسر Ł…Ų§Ł„ŪŒŲ§ŲŖ Ł¾Ų§ŪŒŁ‡, +Base Total,Ł…Ų¬Ł…ŁˆŲ¹ Ł¾Ų§ŪŒŁ‡, +Base Total Billable Amount,مبنا کل مبلغ قابل پرداخت, +Base Total Billed Amount,مبنا کل مبلغ صورتحساب, +Base Total Costing Amount,مبنا کل ŲØŁ‡Ų§ŪŒ ŲŖŁ…Ų§Ł… ؓده, +Based On Value,ŲØŲ± Ų§Ų³Ų§Ų³ Ų§Ų±Ų²Ų“, +"Based on your HR Policy, select your leave allocation period's end date",ŲØŲ± Ų§Ų³Ų§Ų³ Ų®Ų· Ł…Ų“ŪŒ منابع Ų§Ł†Ų³Ų§Ł†ŪŒ خود، تاریخ Ł¾Ų§ŪŒŲ§Ł† ŲÆŁˆŲ±Ł‡ تخصیص Ł…Ų±Ų®ŲµŪŒ خود Ų±Ų§ انتخاب Ś©Ł†ŪŒŲÆ, +"Based on your HR Policy, select your leave allocation period's start date",ŲØŲ± Ų§Ų³Ų§Ų³ Ų®Ų· Ł…Ų“ŪŒ منابع Ų§Ł†Ų³Ų§Ł†ŪŒ خود، تاریخ ؓروع ŲÆŁˆŲ±Ł‡ تخصیص Ł…Ų±Ų®ŲµŪŒ خود Ų±Ų§ انتخاب Ś©Ł†ŪŒŲÆ, +Batch No is mandatory,ؓماره دسته اجباری Ų§Ų³ŲŖ, +Batch No {0} does not exists,ؓماره دسته {0} وجود ندارد, +Batch No {0} is linked with Item {1} which has serial no. Please scan serial no instead.,ؓماره دسته {0} ŲØŲ§ Ų¢ŪŒŲŖŁ… {1} که دارای ؓماره Ų³Ų±ŪŒŲ§Ł„ Ų§Ų³ŲŖ Ł¾ŪŒŁˆŁ†ŲÆ داده ؓده Ų§Ų³ŲŖ. لطفاً ؓماره Ų³Ų±ŪŒŲ§Ł„ Ų±Ų§ اسکن Ś©Ł†ŪŒŲÆ., +Batch No.,ؓماره دسته, +Batch Nos,ؓماره Ł‡Ų§ŪŒ دسته, +Batch Nos are created successfully,ؓماره Ł‡Ų§ŪŒ دسته ŲØŲ§ Ł…ŁˆŁŁ‚ŪŒŲŖ ایجاد Ų“ŲÆ, +Batch Qty,مقدار دسته, +Batch and Serial No,ؓماره دسته و Ų³Ų±ŪŒŲ§Ł„, +Batch not created for item {} since it does not have a batch series.,دسته ای برای Ų¢ŪŒŲŖŁ… {} ایجاد نؓده Ų§Ų³ŲŖ زیرا سری دسته ای ندارد., +Batch {0} and Warehouse,دسته {0} و انبار, +Batch {0} is not available in warehouse {1},دسته {0} ŲÆŲ± انبار {1} Ł…ŁˆŲ¬ŁˆŲÆ Ł†ŪŒŲ³ŲŖ, +Batchwise Valuation,Ų§Ų±Ų²Ų“ گذاری دسته ای, +Beginning of the current subscription period,ؓروع ŲÆŁˆŲ±Ł‡ Ų§Ų“ŲŖŲ±Ų§Ś© ŁŲ¹Ł„ŪŒ, +Bill for Rejected Quantity in Purchase Invoice,صورتحساب مقدار Ų±ŲÆ ؓده ŲÆŲ± فاکتور خرید, +Billed Items To Be Received,Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ŪŒ صورتحساب ؓده برای دریافت, +"Billed, Received & Returned",صورتحساب، دریافت و برگردانده Ų“ŲÆ, +Billing Address Details,جزئیات Ų¢ŲÆŲ±Ų³ صورتحساب, +Billing Interval in Subscription Plan must be Month to follow calendar months,فاصله صورتحساب ŲÆŲ± Ų·Ų±Ų­ Ų§Ų“ŲŖŲ±Ų§Ś© باید ماه ŲØŲ§Ų“ŲÆ ŲŖŲ§ Ł…Ų§Ł‡ā€ŒŁ‡Ų§ŪŒ ŲŖŁ‚ŁˆŪŒŁ…ŪŒ Ų±Ų§ دنبال Ś©Ł†ŪŒŲÆ, +Blanket Order Allowance (%),سفارؓ Ś©Ł„ŪŒ Ł…Ų¬Ų§Ų² (%), +Bom No,ؓماره BOM, +Book Advance Payments as Liability option is chosen. Paid From account changed from {0} to {1}.,ŚÆŲ²ŪŒŁ†Ł‡ رزرو پیؓ پرداخت به Ų¹Ł†ŁˆŲ§Ł† ŲØŲÆŁ‡ŪŒ انتخاب ؓده Ų§Ų³ŲŖ. Ų­Ų³Ų§ŲØ Paid From Ų§Ų² {0} به {1} تغییر کرد., +Book Advance Payments in Separate Party Account,پیؓ پرداخت Ų±Ų§ ŲÆŲ± Ų­Ų³Ų§ŲØ طرف جداگانه رزرو Ś©Ł†ŪŒŲÆ, +Book Tax Loss on Early Payment Discount,Ų§Ų² ŲÆŲ³ŲŖ دادن Ł…Ų§Ł„ŪŒŲ§ŲŖ ŲÆŲ± تخفیف پرداخت Ų²ŁˆŲÆŁ‡Ł†ŚÆŲ§Ł… رزرو Ś©Ł†ŪŒŲÆ, +Book an appointment,یک قرار ملاقات رزرو Ś©Ł†ŪŒŲÆ, +Booking stock value across multiple accounts will make it harder to track stock and account value.,رزرو Ų§Ų±Ų²Ų“ Ł…ŁˆŲ¬ŁˆŲÆŪŒ ŲÆŲ± Ś†Ł†ŲÆŪŒŁ† حساب، ردیابی Ł…ŁˆŲ¬ŁˆŲÆŪŒ و Ų§Ų±Ų²Ų“ Ų­Ų³Ų§ŲØ Ų±Ų§ دؓوارتر Ł…ŪŒ کند., +Books have been closed till the period ending on {0},Ś©ŲŖŲ§ŲØā€ŒŁ‡Ų§ ŲŖŲ§ Ł¾Ų§ŪŒŲ§Ł† ŲÆŁˆŲ±Ł‡ {0} بسته Ų“ŲÆŁ‡ā€ŒŲ§Ł†ŲÆ, +Budget Exceeded,ŲØŁˆŲÆŲ¬Ł‡ بیؓ Ų§Ų² Ų­ŲÆ, +Build All?,Ų³Ų§Ų®ŲŖ Ł‡Ł…Ł‡ŲŸ, +Buildable Qty,ŲŖŲ¹ŲÆŲ§ŲÆ قابل Ų³Ų§Ų®ŲŖ, +Bulk Transaction Log,لاگ تراکنؓ Ł‡Ų§ŪŒ Ų§Ł†ŲØŁˆŁ‡, +Bulk Transaction Log Detail,جزئیات لاگ تراکنؓ Ł‡Ų§ŪŒ Ų§Ł†ŲØŁˆŁ‡, +Bulk Update,به روز Ų±Ų³Ų§Ł†ŪŒ Ų§Ł†ŲØŁˆŁ‡, +Bundle Items,Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ŪŒ باندل, +Buying & Selling Settings,ŲŖŁ†ŲøŪŒŁ…Ų§ŲŖ خرید و فروؓ, +Buying and Selling,خرید و فروؓ, +"By default, the Supplier Name is set as per the Supplier Name entered. If you want Suppliers to be named by a Naming Series choose the 'Naming Series' option.","ŲØŁ‡ā€ŒŲ·ŁˆŲ± Ł¾ŪŒŲ“ā€ŒŁŲ±Ų¶ŲŒ نام ŲŖŲ§Ł…ŪŒŁ†ā€ŒŚ©Ł†Ł†ŲÆŁ‡ Ł…Ų·Ų§ŲØŁ‚ ŲØŲ§ نام ŲŖŲ§Ł…ŪŒŁ†ā€ŒŚ©Ł†Ł†ŲÆŁ‡ وارد ؓده ŲŖŁ†ŲøŪŒŁ… Ł…ŪŒā€ŒŲ“ŁˆŲÆ. Ų§ŚÆŲ± Ł…ŪŒā€ŒŲ®ŁˆŲ§Ł‡ŪŒŲÆ ŲŖŲ§Ł…ŪŒŁ†ā€ŒŚ©Ł†Ł†ŲÆŚÆŲ§Ł† ŲØŲ§ سری Ł†Ų§Ł…ā€ŒŚÆŲ°Ų§Ų±ŪŒ Ł†Ų§Ł…ā€ŒŚÆŲ°Ų§Ų±ŪŒ Ų“ŁˆŁ†ŲÆ. ŚÆŲ²ŪŒŁ†Ł‡ ""Naming Series"" Ų±Ų§ انتخاب Ś©Ł†ŪŒŲÆ.", +Bypass credit check at Sales Order,دور زدن بررسی Ų§Ų¹ŲŖŲØŲ§Ų± ŲÆŲ± سفارؓ فروؓ, +COGS By Item Group,COGS ŲØŲ± Ų§Ų³Ų§Ų³ ŚÆŲ±ŁˆŁ‡ Ų¢ŪŒŲŖŁ…, +CRM Note,یادداؓت CRM, +CRM Settings,ŲŖŁ†ŲøŪŒŁ…Ų§ŲŖ CRM, +Calculate Product Bundle Price based on Child Items' Rates,Ł‚ŪŒŁ…ŲŖ باندل Ł…Ų­ŲµŁˆŁ„ Ų±Ų§ ŲØŲ± Ų§Ų³Ų§Ų³ نرخ Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ŪŒ فرزند محاسبه Ś©Ł†ŪŒŲÆ, +Call Again,ŲÆŁˆŲØŲ§Ų±Ł‡ ŲŖŁ…Ų§Ų³ بگیر, +Call Ended,مکالمه ŲŖŁ„ŁŁ†ŪŒ ŲŖŁ…Ų§Ł… Ų“ŲÆ, +Call Handling Schedule,برنامه رسیدگی به ŲŖŁ…Ų§Ų³ ها, +Call Received By,ŲŖŁ…Ų§Ų³ دریافت ؓده توسط, +Call Receiving Device,دستگاه دریافت ŲŖŁ…Ų§Ų³, +Call Routing,Ł…Ų³ŪŒŲ±ŪŒŲ§ŲØŪŒ ŲŖŁ…Ų§Ų³, +Call Schedule Row {0}: To time slot should always be ahead of From time slot.,ردیف Ų²Ł…Ų§Ł†ā€ŒŲØŁ†ŲÆŪŒ ŲŖŁ…Ų§Ų³ {0}: بازه Ų²Ł…Ų§Ł†ŪŒ To Ł‡Ł…ŪŒŲ“Ł‡ باید Ų¬Ł„ŁˆŲŖŲ± Ų§Ų² بازه Ų²Ł…Ų§Ł†ŪŒ Ų§Ų² زمان ŲØŲ§Ų“ŲÆ., +Call Type,Ł†ŁˆŲ¹ ŲŖŁ…Ų§Ų³, +Callback,پاسخ به ŲŖŁ…Ų§Ų³, +Campaign Item,Ų¢ŪŒŲŖŁ… Ś©Ł…Ł¾ŪŒŁ†, +Can not close Work Order. Since {0} Job Cards are in Work In Progress state.,Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł† دستور کار Ų±Ų§ ŲØŲ³ŲŖ. Ų§Ų² Ų¢Ł†Ų¬Ų§ŪŒŪŒ که کارت Ś©Ų§Ų±Ł‡Ų§ŪŒ {0} ŲÆŲ± حالت کار ŲÆŲ± Ų­Ų§Ł„ انجام هستند., +"Can not filter based on Child Account, if grouped by Account",Ų§ŚÆŲ± ŲØŲ±Ų§Ų³Ų§Ų³ Ų­Ų³Ų§ŲØ ŚÆŲ±ŁˆŁ‡ā€ŒŲØŁ†ŲÆŪŒ ؓود، Ł†Ł…ŪŒā€ŒŲŖŁˆŲ§Ł† ŲØŲ± Ų§Ų³Ų§Ų³ Ų­Ų³Ų§ŲØ فرزند ŁŪŒŁ„ŲŖŲ± کرد, +"Can't change the valuation method, as there are transactions against some items which do not have its own valuation method",Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł† روؓ Ų§Ų±Ų²Ų“ گذاری Ų±Ų§ تغییر داد، زیرا Ł…Ų¹Ų§Ł…Ł„Ų§ŲŖŪŒ ŲÆŲ± ŲØŲ±Ų§ŲØŲ± برخی اقلام وجود ŲÆŲ§Ų±ŲÆ که روؓ Ų§Ų±Ų²Ų“ گذاری Ų®Ų§Ųµ خود Ų±Ų§ ندارند., +Cannot Merge,Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł† Ų§ŲÆŲŗŲ§Ł… کرد, +Cannot Resubmit Ledger entries for vouchers in Closed fiscal year.,Ł†Ł…ŪŒā€ŒŲŖŁˆŲ§Ł† ŁˆŲ±ŁˆŲÆŪŒā€ŒŁ‡Ų§ŪŒ دفتر کل Ų±Ų§ برای Ś©ŁˆŁ¾Ł†ā€ŒŁ‡Ų§ ŲÆŲ± Ų³Ų§Ł„ Ł…Ų§Ł„ŪŒ بسته ŲÆŁˆŲØŲ§Ų±Ł‡ Ų§Ų±Ų³Ų§Ł„ کرد., +"Cannot amend {0} {1}, please create a new one instead.",Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł† {0} {1} Ų±Ų§ اصلاح کرد، لطفاً ŲÆŲ± عوض یک Ł…ŁˆŲ±ŲÆ جدید ایجاد Ś©Ł†ŪŒŲÆ., +Cannot apply TDS against multiple parties in one entry,Ł†Ł…ŪŒā€ŒŲŖŁˆŲ§Ł† TDS Ų±Ų§ ŲÆŲ± یک ورودی ŲÆŲ± مقابل Ś†Ł†ŲÆŪŒŁ† طرف اعمال کرد, +Cannot cancel the transaction. Reposting of item valuation on submission is not completed yet.,Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł† تراکنؓ Ų±Ų§ Ł„ŲŗŁˆ کرد. Ų§Ų±Ų³Ų§Ł„ Ł…Ų¬ŲÆŲÆ ارزیابی اقلام هنگام Ų§Ų±Ų³Ų§Ł„ Ł‡Ł†ŁˆŲ² ŲŖŚ©Ł…ŪŒŁ„ نؓده Ų§Ų³ŲŖ., +Cannot change Reference Document Type.,Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł† Ł†ŁˆŲ¹ سند Ł…Ų±Ų¬Ų¹ Ų±Ų§ تغییر ŲÆŲ§ŲÆ., +Cannot complete task {0} as its dependant task {1} are not completed / cancelled.,Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł† کار {0} Ų±Ų§ ŲŖŚ©Ł…ŪŒŁ„ کرد زیرا ŁˆŲøŪŒŁŁ‡ ŁˆŲ§ŲØŲ³ŲŖŁ‡ آن {1} ŲŖŚ©Ł…ŪŒŁ„ نؓده / Ł„ŲŗŁˆ Ų“ŲÆ., +Cannot convert Task to non-group because the following child Tasks exist: {0}.,Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł† ŁˆŲøŪŒŁŁ‡ Ų±Ų§ به غیر ŚÆŲ±ŁˆŁ‡ŪŒ ŲŖŲØŲÆŪŒŁ„ کرد زیرا وظایف فرزند زیر وجود ŲÆŲ§Ų±ŲÆ: {0}., +Cannot convert to Group because Account Type is selected.,Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł† به ŚÆŲ±ŁˆŁ‡ ŲŖŲØŲÆŪŒŁ„ کرد زیرا Ł†ŁˆŲ¹ Ų­Ų³Ų§ŲØ انتخاب ؓده Ų§Ų³ŲŖ., +Cannot create Stock Reservation Entries for future dated Purchase Receipts.,Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł† ورودی Ł‡Ų§ŪŒ رزرو Ł…ŁˆŲ¬ŁˆŲÆŪŒ Ų±Ų§ برای Ų±Ų³ŪŒŲÆŁ‡Ų§ŪŒ خرید ŲØŲ§ تاریخ Ų¢ŪŒŁ†ŲÆŁ‡ ایجاد کرد., +Cannot create a pick list for Sales Order {0} because it has reserved stock. Please unreserve the stock in order to create a pick list.,Ł†Ł…ŪŒā€ŒŲŖŁˆŲ§Ł† فهرست Ų§Ł†ŲŖŲ®Ų§ŲØŪŒ برای سفارؓ فروؓ {0} ایجاد کرد زیرا Ł…ŁˆŲ¬ŁˆŲÆŪŒ رزرو کرده Ų§Ų³ŲŖ. لطفاً برای ایجاد Ł„ŪŒŲ³ŲŖ Ų§Ł†ŲŖŲ®Ų§ŲØŲŒ Ł…ŁˆŲ¬ŁˆŲÆŪŒ Ų±Ų§ Ł„ŲŗŁˆ رزرو Ś©Ł†ŪŒŲÆ., +Cannot create accounting entries against disabled accounts: {0},Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł† ورودی Ł‡Ų§ŪŒ حسابداری Ų±Ų§ ŲÆŲ± ŲØŲ±Ų§ŲØŲ± Ų­Ų³Ų§ŲØ Ł‡Ų§ŪŒ ŲŗŪŒŲ±ŁŲ¹Ų§Ł„ ایجاد کرد: {0}, +Cannot enqueue multi docs for one company. {0} is already queued/running for company: {1},Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł† چند سند Ų±Ų§ برای یک ؓرکت ŲÆŲ± صف قرار ŲÆŲ§ŲÆ. {0} قبلاً برای ؓرکت: {1} ŲÆŲ± صف/ŲÆŲ± Ų­Ų§Ł„ Ų§Ų¬Ų±Ų§Ų³ŲŖ, +Cannot find a default warehouse for item {0}. Please set one in the Item Master or in Stock Settings.,Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł† یک انبار پیؓ فرض برای Ų¢ŪŒŲŖŁ… {0} پیدا کرد. لطفاً یکی Ų±Ų§ ŲÆŲ± Ł…ŲÆŪŒŲ±ŪŒŲŖ Ų¢ŪŒŲŖŁ… یا ŲÆŲ± ŲŖŁ†ŲøŪŒŁ…Ų§ŲŖ سهام ŲŖŁ†ŲøŪŒŁ… Ś©Ł†ŪŒŲÆ., +Cannot make any transactions until the deletion job is completed,ŲŖŲ§ Ų²Ł…Ų§Ł†ŪŒ که کار حذف کامل Ł†Ų“ŁˆŲÆŲŒ Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł† Ł‡ŪŒŚ† ŲŖŲ±Ų§Ś©Ł†Ų“ŪŒ انجام ŲÆŲ§ŲÆ, +Cannot produce more item for {0},Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł† Ł…ŁˆŲ±ŲÆ بیؓتری برای {0} ŲŖŁˆŁ„ŪŒŲÆ کرد, +Cannot produce more than {0} items for {1},Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł† بیؓ Ų§Ų² {0} Ł…ŁˆŲ±ŲÆ برای {1} ŲŖŁˆŁ„ŪŒŲÆ کرد, +Cannot receive from customer against negative outstanding,Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł† Ų§Ų² Ł…Ų“ŲŖŲ±ŪŒ ŲÆŲ± ŲØŲ±Ų§ŲØŲ± Ł…Ų¹ŁˆŁ‚Ų§ŲŖ Ł…Ł†ŁŪŒ دریافت کرد, +Cannot retrieve link token for update. Check Error Log for more information,Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł† ŲŖŁˆŚ©Ł† Ł¾ŪŒŁˆŁ†ŲÆ Ų±Ų§ برای به روز Ų±Ų³Ų§Ł†ŪŒ بازیابی کرد. برای اطلاعات بیؓتر Log Ų®Ų·Ų§ Ų±Ų§ بررسی Ś©Ł†ŪŒŲÆ, +Cannot retrieve link token. Check Error Log for more information,ŲŖŁˆŚ©Ł† Ł¾ŪŒŁˆŁ†ŲÆ بازیابی Ł†Ł…ŪŒ ؓود. برای اطلاعات بیؓتر Log Ų®Ų·Ų§ Ų±Ų§ بررسی Ś©Ł†ŪŒŲÆ, +Capacity (Stock UOM),ظرفیت (Stock UOM), +Capacity in Stock UOM,ظرفیت Ł…ŁˆŲ¬ŁˆŲÆ ŲÆŲ± انبار UOM, +Capacity must be greater than 0,ظرفیت باید بیؓتر Ų§Ų² 0 ŲØŲ§Ų“ŲÆ, +Capitalization,حروف بزرگ, +Capitalization Method,روؓ حروف بزرگ, +Capitalize Asset,Ų³Ų±Ł…Ų§ŪŒŁ‡ گذاری دارایی, +Capitalize Repair Cost,Ų³Ų±Ł…Ų§ŪŒŁ‡ گذاری ŲÆŲ± Ł‡Ų²ŪŒŁ†Ł‡ ŲŖŲ¹Ł…ŪŒŲ±, +Capitalized,ŲØŲ§ حروف بزرگ, +Carrier,Ų­Ų§Ł…Ł„, +Carrier Service,Ų®ŲÆŁ…Ų§ŲŖ Ų­Ų§Ł…Ł„, +Carry Forward Communication and Comments,انتقال Ų§Ų±ŲŖŲØŲ§Ų·Ų§ŲŖ و نظرات, +Category Details,جزئیات دسته, +Caution: This might alter frozen accounts.,احتیاط: Ų§ŪŒŁ† ممکن Ų§Ų³ŲŖ Ų­Ų³Ų§ŲØ Ł‡Ų§ŪŒ Ł…Ų³ŲÆŁˆŲÆ ؓده Ų±Ų§ تغییر دهد., +Change in Stock Value,تغییر ŲÆŲ± Ų§Ų±Ų²Ų“ Ł…ŁˆŲ¬ŁˆŲÆŪŒ, +Changed customer name to '{}' as '{}' already exists.,"نام Ł…Ų“ŲŖŲ±ŪŒ به ""{}"" به Ų¹Ł†ŁˆŲ§Ł† ""{}"" تغییر کرده Ų§Ų³ŲŖ.", +Changes,تغییرات, +Charge of type 'Actual' in row {0} cannot be included in Item Rate or Paid Amount,"Ł‡Ų²ŪŒŁ†Ł‡ Ų§Ų² Ł†ŁˆŲ¹ ""ŁˆŲ§Ł‚Ų¹ŪŒ"" ŲÆŲ± ردیف {0} Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł†ŲÆ ŲÆŲ± نرخ Ł…ŁˆŲ±ŲÆ یا مبلغ پرداختی لحاظ ؓود", +Chart Of Accounts,Ł†Ł…ŁˆŲÆŲ§Ų± Ų­Ų³Ų§ŲØ, +Checking this will round off the tax amount to the nearest integer,بررسی Ų§ŪŒŁ† مقدار Ł…Ų§Ł„ŪŒŲ§ŲŖ Ų±Ų§ به Ł†Ų²ŲÆŪŒŚ©ŲŖŲ±ŪŒŁ† Ų¹ŲÆŲÆ صحیح ŚÆŲ±ŲÆ Ł…ŪŒ کند, +Choose a WIP composite asset,یک دارایی ترکیبی «کار ŲÆŲ± Ų­Ų§Ł„ انجام» Ų±Ų§ انتخاب Ś©Ł†ŪŒŲÆ, +Clear Demo Data,پاک کردن داده Ł‡Ų§ŪŒ Ł†Ł…Ų§ŪŒŲ“ŪŒ, +Clear Notifications,پاک کردن اعلان ها, +Clearing Demo Data...,ŲÆŲ± Ų­Ų§Ł„ پاک کردن داده Ł‡Ų§ŪŒ Ł†Ł…Ų§ŪŒŲ“ŪŒ..., +Click on 'Get Finished Goods for Manufacture' to fetch the items from the above Sales Orders. Items only for which a BOM is present will be fetched.,"برای دریافت Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ Ų§Ų² Ų³ŁŲ§Ų±Ų“ā€ŒŁ‡Ų§ŪŒ فروؓ ŁŁˆŁ‚ŲŒ روی ""دریافت Ś©Ų§Ł„Ų§Ł‡Ų§ŪŒ ŲŖŁ…Ų§Ł… ؓده برای Ų³Ų§Ų®ŲŖ"" Ś©Ł„ŪŒŚ© Ś©Ł†ŪŒŲÆ. فقط Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ŪŒŪŒ که BOM برای آنها وجود ŲÆŲ§Ų±ŲÆ واکؓی Ł…ŪŒ Ų“ŁˆŁ†ŲÆ.", +Click on Add to Holidays. This will populate the holidays table with all the dates that fall on the selected weekly off. Repeat the process for populating the dates for all your weekly holidays,روی Ų§ŁŲ²ŁˆŲÆŁ† به ŲŖŲ¹Ų·ŪŒŁ„Ų§ŲŖ Ś©Ł„ŪŒŚ© Ś©Ł†ŪŒŲÆ. ŲØŲ§ Ų§ŪŒŁ† کار Ų¬ŲÆŁˆŁ„ ŲŖŲ¹Ų·ŪŒŁ„Ų§ŲŖ ŲØŲ§ ŲŖŁ…Ų§Ł… تاریخ Ł‡Ų§ŪŒŪŒ که ŲÆŲ± ŲŖŲ¹Ų·ŪŒŁ„Ų§ŲŖ Ł‡ŁŲŖŚÆŪŒ انتخاب ؓده قرار Ł…ŪŒ ŚÆŪŒŲ±Ł†ŲÆ پر Ł…ŪŒ کند. ŁŲ±Ų¢ŪŒŁ†ŲÆ پر کردن تاریخ ها Ų±Ų§ برای ŲŖŁ…Ų§Ł… ŲŖŲ¹Ų·ŪŒŁ„Ų§ŲŖ Ł‡ŁŲŖŚÆŪŒ خود تکرار Ś©Ł†ŪŒŲÆ, +Click on Get Sales Orders to fetch sales orders based on the above filters.,برای دریافت Ų³ŁŲ§Ų±Ų“ā€ŒŁ‡Ų§ŪŒ فروؓ ŲØŲ± Ų§Ų³Ų§Ų³ ŁŪŒŁ„ŲŖŲ±Ł‡Ų§ŪŒ ŲØŲ§Ł„Ų§ŲŒ روی دریافت Ų³ŁŲ§Ų±Ų“ā€ŒŁ‡Ų§ŪŒ فروؓ Ś©Ł„ŪŒŚ© Ś©Ł†ŪŒŲÆ., +Click to add email / phone,برای Ų§ŁŲ²ŁˆŲÆŁ† Ų§ŪŒŁ…ŪŒŁ„ / تلفن Ś©Ł„ŪŒŚ© Ś©Ł†ŪŒŲÆ, +Close Replied Opportunity After Days,بستن فرصت پاسخ داده ؓده پس Ų§Ų² چند روز, +Closed Work Order can not be stopped or Re-opened,دستور کار بسته Ų±Ų§ Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł† Ł…ŲŖŁˆŁ‚Ł کرد یا ŲÆŁˆŲØŲ§Ų±Ł‡ ŲØŲ§Ų² کرد, +Closing,بسته ؓدن, +Closing Balance as per Bank Statement,مانده Ł¾Ų§ŪŒŲ§Ł†ŪŒ Ų·ŲØŁ‚ صورتحساب ŲØŲ§Ł†Ś©ŪŒ, +Closing Balance as per ERP,ŲŖŲ±Ų§Ų² Ł¾Ų§ŪŒŲ§Ł†ŪŒ Ų·ŲØŁ‚ ERP, +Closing Stock Balance,ŲŖŲ±Ų§Ų² Ł¾Ų§ŪŒŲ§Ł†ŪŒ Ł…ŁˆŲ¬ŁˆŲÆŪŒ, +Communication Channel,کانال ارتباطی, +Company Address Display,Ł†Ł…Ų§ŪŒŲ“ Ų¢ŲÆŲ±Ų³ ؓرکت, +Company Billing Address,Ų¢ŲÆŲ±Ų³ صورتحساب ؓرکت, +Company Details,جزئیات ؓرکت, +Company Shipping Address,Ų¢ŲÆŲ±Ų³ Ų­Ł…Ł„ و نقل ؓرکت, +Company Tax ID,ؓناسه Ł…Ų§Ł„ŪŒŲ§ŲŖŪŒ ؓرکت, +Company and Posting Date is mandatory,ؓرکت و تاریخ Ų§Ų±Ų³Ų§Ł„ Ų§Ł„Ų²Ų§Ł…ŪŒ Ų§Ų³ŲŖ, +Company is mandatory,ؓرکت Ų§Ł„Ų²Ų§Ł…ŪŒ Ų§Ų³ŲŖ, +Company is mandatory for generating an invoice. Please set a default company in Global Defaults.,ؓرکت برای ŲŖŁ‡ŪŒŁ‡ فاکتور Ų§Ł„Ų²Ų§Ł…ŪŒ Ų§Ų³ŲŖ. لطفاً یک ؓرکت Ł¾ŪŒŲ“ā€ŒŁŲ±Ų¶ Ų±Ų§ ŲÆŲ± Ł¾ŪŒŲ“ā€ŒŁŲ±Ų¶ā€ŒŁ‡Ų§ŪŒ سراسری ŲŖŁ†ŲøŪŒŁ… Ś©Ł†ŪŒŲÆ., +Company which internal customer represents,ؓرکتی که Ł…Ų“ŲŖŲ±ŪŒ ŲÆŲ§Ų®Ł„ŪŒ Ł†Ł…Ų§ŪŒŁ†ŲÆŁ‡ آن Ų§Ų³ŲŖ, +Company which internal customer represents.,ؓرکتی که Ł…Ų“ŲŖŲ±ŪŒ ŲÆŲ§Ų®Ł„ŪŒ Ł†Ł…Ų§ŪŒŁ†ŲÆŁ‡ آن Ų§Ų³ŲŖ., +Company which internal supplier represents,ؓرکتی که ŲŖŲ§Ł…ŪŒŁ† کننده ŲÆŲ§Ų®Ł„ŪŒ آن Ų±Ų§ Ł†Ł…Ų§ŪŒŁ†ŲÆŚÆŪŒ Ł…ŪŒ کند, +Company {0} is added more than once,ؓرکت {0} بیؓ Ų§Ų² یک ŲØŲ§Ų± اضافه ؓده Ų§Ų³ŲŖ, +Company {} does not exist yet. Taxes setup aborted.,ؓرکت {} Ł‡Ł†ŁˆŲ² وجود ندارد. ŲŖŁ†ŲøŪŒŁ… Ł…Ų§Ł„ŪŒŲ§ŲŖ Ł„ŲŗŁˆ Ų“ŲÆ., +Company {} does not match with POS Profile Company {},ؓرکت {} ŲØŲ§ Ł†Ł…Ų§ŪŒŁ‡ POS ؓرکت {} مطابقت ندارد, +Competitor,Ų±Ł‚ŪŒŲØ, +Competitor Detail,جزئیات Ų±Ł‚ŪŒŲØ, +Competitor Name,نام Ų±Ł‚ŪŒŲØ, +Competitors,رقبا, +Complete Job,کار کامل, +Completed On,ŲŖŚ©Ł…ŪŒŁ„ Ų“ŲÆ, +Completed On cannot be greater than Today,ŲŖŚ©Ł…ŪŒŁ„ ؓده ŲÆŲ± تاریخ Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł†ŲÆ بزرگتر Ų§Ų² Ų§Ł…Ų±ŁˆŲ² ŲØŲ§Ų“ŲÆ, +Completed Tasks,وظایف ŲŖŚ©Ł…ŪŒŁ„ ؓده, +Completed Time,زمان ŲŖŚ©Ł…ŪŒŁ„ ؓده, +Conditional Rule,Ł‚Ų§Ł†ŁˆŁ† Ł…Ų“Ų±ŁˆŲ·, +Conditional Rule Examples,Ł…Ų«Ų§Ł„ Ł‡Ų§ŪŒ Ł‚Ų§Ł†ŁˆŁ† ؓرطی, +Configure Product Assembly,Ł¾ŪŒŚ©Ų±ŲØŁ†ŲÆŪŒ Ų§Ų³Ł…ŲØŁ„ŪŒ Ł…Ų­ŲµŁˆŁ„, +Configure the action to stop the transaction or just warn if the same rate is not maintained.,کنؓ Ų±Ų§ طوری Ł¾ŪŒŚ©Ų±ŲØŁ†ŲÆŪŒ Ś©Ł†ŪŒŲÆ که تراکنؓ Ų±Ų§ Ł…ŲŖŁˆŁ‚Ł کند یا ŲÆŲ± صورت Ų¹ŲÆŁ… حفظ همان نرخ فقط هؓدار دهد., +Connections,اتصالات, +Consider Entire Party Ledger Amount,کل مبلغ دفتر کل طرف Ų±Ų§ ŲÆŲ± نظر بگیرید, +Consider Minimum Order Qty,ŲÆŲ± نظر گرفتن حداقل ŲŖŲ¹ŲÆŲ§ŲÆ سفارؓ, +Consider Rejected Warehouses,ŲÆŲ± نظر گرفتن Ų§Ł†ŲØŲ§Ų±Ł‡Ų§ŪŒ Ł…Ų±Ų¬ŁˆŲ¹ŪŒ, +Considered In Paid Amount,به مبلغ پرداختی ŲÆŲ± نظر گرفته ؓده Ų§Ų³ŲŖ, +Consolidate Sales Order Items,ŲŖŁ„ŁŪŒŁ‚ Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ŪŒ سفارؓ فروؓ, +Consolidate Sub Assembly Items,ŲŖŁ„ŁŪŒŁ‚ Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ŪŒ زیر Ł…ŁˆŁ†ŲŖŲ§Ų², +Consumed Asset Items is mandatory for Decapitalization,اقلام دارایی مصرف ؓده برای کاهؓ Ų³Ų±Ł…Ų§ŪŒŁ‡ اجباری Ų§Ų³ŲŖ, +Consumed Asset Total Value,Ų§Ų±Ų²Ų“ کل دارایی مصرف ؓده, +Consumed Assets,دارایی Ł‡Ų§ŪŒ مصرف ؓده, +Consumed Quantity,مقدار مصرف ؓده, +Consumed Stock Items,Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ŪŒ Ł…ŁˆŲ¬ŁˆŲÆŪŒ Ł…ŲµŲ±ŁŪŒ, +Consumed Stock Total Value,Ų§Ų±Ų²Ų“ کل Ł…ŁˆŲ¬ŁˆŲÆŪŒ مصرف ؓده, +Consumption Rate,نرخ مصرف, +Contact Details,اطلاعات ŲŖŁ…Ų§Ų³, +Contact Mobile,Ł…ŁˆŲØŲ§ŪŒŁ„ Ł…Ų®Ų§Ų·ŲØ, +Contact Us Settings,ŲŖŁ†ŲøŪŒŁ…Ų§ŲŖ ŲŖŁ…Ų§Ų³ ŲØŲ§ Ł…Ų§, +Contacts,Ł…Ų®Ų§Ų·ŲØ, +Contract Template Help,Ų±Ų§Ł‡Ł†Ł…Ų§ŪŒ Ų§Ł„ŚÆŁˆŪŒ قرارداد, +Contribution Qty,مقدار مؓارکت, +Control Historical Stock Transactions,معاملات تاریخی Ł…ŁˆŲ¬ŁˆŲÆŪŒ Ų±Ų§ کنترل Ś©Ł†ŪŒŲÆ, +Convert Item Description to Clean HTML in Transactions,توضیحات Ł…ŁˆŲ±ŲÆ Ų±Ų§ به Clean HTML ŲÆŲ± Transactions ŲŖŲØŲÆŪŒŁ„ Ś©Ł†ŪŒŲÆ, +Convert to Group,ŲŖŲØŲÆŪŒŁ„ به ŚÆŲ±ŁˆŁ‡,Warehouse +Convert to Item Based Reposting,ŲŖŲØŲÆŪŒŁ„ به Ų§Ų±Ų³Ų§Ł„ Ł…Ų¬ŲÆŲÆ ŲØŲ± Ų§Ų³Ų§Ų³ Ų¢ŪŒŲŖŁ…, +Convert to Ledger,ŲŖŲØŲÆŪŒŁ„ به دفتر کل,Warehouse +Core,هسته, +Corrective Job Card,کارت کار Ų§ŲµŁ„Ų§Ų­ŪŒ, +Corrective Operation,Ų¹Ł…Ł„ŪŒŲ§ŲŖ Ų§ŲµŁ„Ų§Ų­ŪŒ, +Corrective Operation Cost,Ł‡Ų²ŪŒŁ†Ł‡ Ų¹Ł…Ł„ŪŒŲ§ŲŖ Ų§ŲµŁ„Ų§Ų­ŪŒ, +Cost Center Allocation,تخصیص مرکز Ł‡Ų²ŪŒŁ†Ł‡, +Cost Center Allocation Percentage,ŲÆŲ±ŲµŲÆ تخصیص مرکز Ł‡Ų²ŪŒŁ†Ł‡, +Cost Center Allocation Percentages,ŲÆŲ±ŲµŲÆŁ‡Ų§ŪŒ تخصیص مرکز Ł‡Ų²ŪŒŁ†Ł‡, +Cost Center For Item with Item Code {0} has been Changed to {1},مرکز Ł‡Ų²ŪŒŁ†Ł‡ برای Ł…ŁˆŲ±ŲÆ ŲØŲ§ کد Ł…ŁˆŲ±ŲÆ {0} به {1} تغییر کرده Ų§Ų³ŲŖ, +"Cost Center is a part of Cost Center Allocation, hence cannot be converted to a group",مرکز Ł‡Ų²ŪŒŁ†Ł‡ بخؓی Ų§Ų² تخصیص مرکز Ł‡Ų²ŪŒŁ†Ł‡ است، ŲØŁ†Ų§ŲØŲ±Ų§ŪŒŁ† Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł† آن Ų±Ų§ به یک ŚÆŲ±ŁˆŁ‡ ŲŖŲØŲÆŪŒŁ„ کرد, +Cost Center with Allocation records can not be converted to a group,مرکز Ł‡Ų²ŪŒŁ†Ł‡ ŲØŲ§ Ų±Ś©ŁˆŲ±ŲÆŁ‡Ų§ŪŒ تخصیص Ų±Ų§ Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł† به ŚÆŲ±ŁˆŁ‡ ŲŖŲØŲÆŪŒŁ„ کرد, +Cost Center {0} cannot be used for allocation as it is used as main cost center in other allocation record.,مرکز Ł‡Ų²ŪŒŁ†Ł‡ {0} Ų±Ų§ Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł† برای تخصیص استفاده کرد زیرا به Ų¹Ł†ŁˆŲ§Ł† مرکز Ł‡Ų²ŪŒŁ†Ł‡ Ų§ŲµŁ„ŪŒ ŲÆŲ± سایر Ų±Ś©ŁˆŲ±ŲÆŁ‡Ų§ŪŒ تخصیص استفاده Ł…ŪŒ ؓود., +Cost Center {} doesn't belong to Company {},مرکز Ł‡Ų²ŪŒŁ†Ł‡ {} متعلق به ؓرکت {} Ł†ŪŒŲ³ŲŖ, +Cost Center {} is a group cost center and group cost centers cannot be used in transactions,مرکز Ł‡Ų²ŪŒŁ†Ł‡ {} یک مرکز Ł‡Ų²ŪŒŁ†Ł‡ ŚÆŲ±ŁˆŁ‡ŪŒ Ų§Ų³ŲŖ و مراکز Ł‡Ų²ŪŒŁ†Ł‡ ŚÆŲ±ŁˆŁ‡ŪŒ Ų±Ų§ Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł† ŲÆŲ± تراکنؓ ها استفاده کرد, +Cost Configuration,Ł¾ŪŒŚ©Ų±ŲØŁ†ŲÆŪŒ Ł‡Ų²ŪŒŁ†Ł‡, +Cost Per Unit,Ł‡Ų²ŪŒŁ†Ł‡ هر واحد, +Cost of Poor Quality Report,ŚÆŲ²Ų§Ų±Ų“ Ł‡Ų²ŪŒŁ†Ł‡ کیفیت Ł¾Ų§ŪŒŪŒŁ†, +Cost to Company (CTC),Ł‡Ų²ŪŒŁ†Ł‡ ؓرکت (CTC), +Costing Details,جزئیات Ł‡Ų²ŪŒŁ†Ł‡, +Could Not Delete Demo Data,داده Ł‡Ų§ŪŒ نسخه ی Ł†Ł…Ų§ŪŒŲ“ŪŒ حذف نؓد, +Could not auto update shifts. Shift with shift factor {0} needed.,ŲØŁ‡ā€ŒŲ±ŁˆŲ²Ų±Ų³Ų§Ł†ŪŒ خودکار Ų“ŪŒŁŲŖā€ŒŁ‡Ų§ ممکن Ł†ŪŒŲ³ŲŖ. Shift ŲØŲ§ ضریب تغییر {0} Ł…ŁˆŲ±ŲÆ Ł†ŪŒŲ§Ų² Ų§Ų³ŲŖ., +Could not detect the Company for updating Bank Accounts,ؓرکت برای ŲØŁ‡ā€ŒŲ±ŁˆŲ²Ų±Ų³Ų§Ł†ŪŒ Ų­Ų³Ų§ŲØā€ŒŁ‡Ų§ŪŒ ŲØŲ§Ł†Ś©ŪŒ Ų“Ł†Ų§Ų³Ų§ŪŒŪŒ نؓد, +Could not find path for , Ł…Ų³ŪŒŲ±ŪŒ برای پیدا نؓد, +Count,ؓمردن, +Create Depreciation Entry,ایجاد Ų«ŲØŲŖ استهلاک, +Create Employee records.,ایجاد Ų±Ś©ŁˆŲ±ŲÆŁ‡Ų§ŪŒ کارمندان., +Create Grouped Asset,ایجاد دارایی ŚÆŲ±ŁˆŁ‡ŪŒ, +Create Job Card based on Batch Size,ایجاد کارت کار ŲØŲ± Ų§Ų³Ų§Ų³ اندازه دسته ای, +Create Ledger Entries for Change Amount,برای تغییر Ł…Ł‚ŲÆŲ§Ų±ŲŒ ورودی Ł‡Ų§ŪŒ دفتر کل ایجاد Ś©Ł†ŪŒŲÆ, +Create Link,ایجاد Ł„ŪŒŁ†Ś©, +Create Multi-level BOM,BOM چند سطحی ایجاد Ś©Ł†ŪŒŲÆ, +Create New Customer,Ł…Ų“ŲŖŲ±ŪŒ جدید ایجاد Ś©Ł†ŪŒŲÆ, +Create Opportunity,ایجاد فرصت, +Create Prospect,ایجاد Ł…Ų“ŲŖŲ±ŪŒ ŲØŲ§Ł„Ł‚ŁˆŁ‡, +Create Reposting Entries,ورودی Ł‡Ų§ŪŒ Ų§Ų±Ų³Ų§Ł„ Ł…Ų¬ŲÆŲÆ ایجاد Ś©Ł†ŪŒŲÆ, +Create Reposting Entry,ایجاد ورودی Ų§Ų±Ų³Ų§Ł„ Ł…Ų¬ŲÆŲÆ, +Create Stock Entry,ایجاد Ų«ŲØŲŖ Ł…ŁˆŲ¬ŁˆŲÆŪŒ, +Create a new composite asset,یک دارایی ترکیبی جدید ایجاد Ś©Ł†ŪŒŲÆ, +Create {0} {1} ?,{0} {1} ایجاد ؓود؟, +Created On,ایجاد Ų“ŲÆ, +Created {0} scorecards for {1} between:,ایجاد {0} کارت Ų§Ł…ŲŖŪŒŲ§Ų²ŪŒ برای {1} ŲØŪŒŁ†:, +Creating Delivery Note ...,ایجاد یادداؓت ŲŖŲ­ŁˆŪŒŁ„ ..., +Creating Packing Slip ...,ایجاد برگه بسته ŲØŁ†ŲÆŪŒ ..., +Creating Purchase Invoices ...,ایجاد ŁŲ§Ś©ŲŖŁˆŲ±Ł‡Ų§ŪŒ خرید ..., +Creating Purchase Receipt ...,ایجاد رسید خرید ..., +Creating Sales Invoices ...,ایجاد ŁŲ§Ś©ŲŖŁˆŲ±Ł‡Ų§ŪŒ فروؓ ..., +Creating Stock Entry,ایجاد Ų«ŲØŲŖ Ł…ŁˆŲ¬ŁˆŲÆŪŒ, +Creating Subcontracting Order ...,ایجاد سفارؓ Ł¾ŪŒŁ…Ų§Ł†Ś©Ų§Ų±ŪŒ فرعی ..., +Creating Subcontracting Receipt ...,ایجاد رسید Ł¾ŪŒŁ…Ų§Ł†Ś©Ų§Ų±ŪŒ فرعی ..., +Creating User...,ایجاد کاربر..., +Creation,ایجاد, +Creation of {1}(s) successful,ایجاد {1}(ها) ŲØŲ§ Ł…ŁˆŁŁ‚ŪŒŲŖ, +Credit (Transaction),Ų§Ų¹ŲŖŲØŲ§Ų± (تراکنؓ), +Credit Amount in Transaction Currency,مقدار Ų§Ų¹ŲŖŲØŲ§Ų± به Ų§Ų±Ų² تراکنؓ, +Credit Limit Crossed,Ų§Ų² Ų­ŲÆ Ų§Ų¹ŲŖŲØŲ§Ų± عبور کرد, +Credit Limit Settings,ŲŖŁ†ŲøŪŒŁ…Ų§ŲŖ Ł…Ų­ŲÆŁˆŲÆŪŒŲŖ اعتباری, +Currency Exchange Settings Details,جزئیات ŲŖŁ†ŲøŪŒŁ…Ų§ŲŖ ŲŖŲØŲ§ŲÆŁ„ Ų§Ų±Ų², +Currency Exchange Settings Result,Ł†ŲŖŪŒŲ¬Ł‡ ŲŖŁ†ŲøŪŒŁ…Ų§ŲŖ ŲŖŲØŲ§ŲÆŁ„ Ų§Ų±Ų², +Current Asset,دارایی جاری, +Current Index,Ų“Ų§Ų®Ųµ ŁŲ¹Ł„ŪŒ, +Current Level,Ų³Ų·Ų­ ŁŲ¹Ł„ŪŒ, +Current Liability,ŲØŲÆŁ‡ŪŒ جاری, +Current Serial / Batch Bundle,باندل Ų³Ų±ŪŒŲ§Ł„ / دسته ŁŲ¹Ł„ŪŒ, +Custom,سفارؓی, +Customer , Ł…Ų“ŲŖŲ±ŪŒ, +Customer / Item / Item Group,Ł…Ų“ŲŖŲ±ŪŒ / Ų¢ŪŒŲŖŁ… / ŚÆŲ±ŁˆŁ‡ Ų¢ŪŒŲŖŁ…, +Customer Defaults,پیؓ فرض Ł‡Ų§ŪŒ Ł…Ų“ŲŖŲ±ŪŒ, +Customer Group Item,Ł…ŁˆŲ±ŲÆ ŚÆŲ±ŁˆŁ‡ Ł…Ų“ŲŖŲ±ŪŒ, +Customer Group: {0} does not exist,ŚÆŲ±ŁˆŁ‡ Ł…Ų“ŲŖŲ±ŪŒ: {0} وجود ندارد, +Customer Item,Ł…ŁˆŲ±ŲÆ Ł…Ų“ŲŖŲ±ŪŒ, +Customer Name: , نام Ł…Ų“ŲŖŲ±ŪŒ:, +Customer Portal Users,کاربران Ł¾ŁˆŲ±ŲŖŲ§Ł„ Ł…Ų“ŲŖŲ±ŪŒ, +Customer: , Ł…Ų“ŲŖŲ±ŪŒ:, +D - E,ŲÆ - ای, +Daily Time to send,زمان Ų§Ų±Ų³Ų§Ł„ Ų±ŁˆŲ²Ų§Ł†Ł‡, +Dashboard,داؓبورد, +Data Based On,داده ها ŲØŲ± Ų§Ų³Ų§Ų³, +Date , تاریخ, +Date must be between {0} and {1},تاریخ باید ŲØŪŒŁ† {0} و {1} ŲØŲ§Ų“ŲÆ, +Days before the current subscription period,چند روز قبل Ų§Ų² ŲÆŁˆŲ±Ł‡ Ų§Ų“ŲŖŲ±Ų§Ś© ŁŲ¹Ł„ŪŒ, +Deal Owner,ŲµŲ§Ų­ŲØ معامله, +Debit (Transaction),ŲØŲÆŁ‡ŪŒ (تراکنؓ), +Debit Amount in Transaction Currency,مبلغ ŲØŲÆŁ‡ŪŒ به Ų§Ų±Ų² تراکنؓ, +Decapitalization,Ų³Ų±Ł…Ų§ŪŒŁ‡ زدایی, +Decapitalized,ŲØŲÆŁˆŁ† Ų³Ų±Ł…Ų§ŪŒŁ‡, +Default Advance Account,Ų­Ų³Ų§ŲØ پیؓ فرض پیؓ فرض, +Default Advance Paid Account,Ų­Ų³Ų§ŲØ پیؓ فرض پیؓ پرداخت, +Default Advance Received Account,پیؓ فرض پیؓ فرض Ų­Ų³Ų§ŲØ دریافت ؓده, +Default BOM not found for FG Item {0},BOM پیؓ فرض برای Ų¢ŪŒŲŖŁ… Ś©Ų§Ł„Ų§ŪŒ ŲŖŁ…Ų§Ł… ؓده {0} یافت نؓد, +Default Discount Account,Ų­Ų³Ų§ŲØ تخفیف پیؓ فرض, +Default In-Transit Warehouse,انبار پیؓ فرض ŲÆŲ± Ų­Ł…Ł„ و نقل, +Default Payment Discount Account,Ų­Ų³Ų§ŲØ تخفیف Ł¾ŪŒŲ“ā€ŒŁŲ±Ų¶ پرداخت, +Default Provisional Account,Ų­Ų³Ų§ŲØ Ł…ŁˆŁ‚ŲŖ پیؓ فرض, +Default Service Level Agreement for {0} already exists.,ŲŖŁˆŲ§ŁŁ‚Ł†Ų§Ł…Ł‡ Ų³Ų·Ų­ سرویس پیؓ فرض برای {0} Ų§Ų² قبل وجود ŲÆŲ§Ų±ŲÆ., +Default Unit of Measure for Item {0} cannot be changed directly because you have already made some transaction(s) with another UOM. You need to either cancel the linked documents or create a new Item.,واحد اندازه گیری پیؓ فرض برای Ł…ŁˆŲ±ŲÆ {0} Ų±Ų§ Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł† Ł…Ų³ŲŖŁ‚ŪŒŁ…Ų§Ł‹ تغییر ŲÆŲ§ŲÆ زیرا قبلاً تراکنؓ(Ł‡Ų§ŪŒŪŒ) Ų±Ų§ ŲØŲ§ UOM دیگری انجام داده اید. Ų“Ł…Ų§ باید اسناد Ł¾ŪŒŁˆŁ†ŲÆ داده ؓده Ų±Ų§ Ł„ŲŗŁˆ Ś©Ł†ŪŒŲÆ یا یک Ł…ŁˆŲ±ŲÆ جدید ایجاد Ś©Ł†ŪŒŲÆ., +Default settings for your stock-related transactions,ŲŖŁ†ŲøŪŒŁ…Ų§ŲŖ Ł¾ŪŒŲ“ā€ŒŁŲ±Ų¶ برای ŲŖŲ±Ų§Ś©Ł†Ų“ā€ŒŁ‡Ų§ŪŒ Ł…Ų±ŲØŁˆŲ· به Ł…ŁˆŲ¬ŁˆŲÆŪŒ Ų“Ł…Ų§, +"Default tax templates for sales, purchase and items are created.",Ų§Ł„ŚÆŁˆŁ‡Ų§ŪŒ Ł…Ų§Ł„ŪŒŲ§ŲŖŪŒ پیؓ فرض برای فروؓ، خرید و اقلام ایجاد Ł…ŪŒ ؓود., +Deferred Accounting,حسابداری Ł…Ų¹ŁˆŁ‚, +Deferred Accounting Defaults,Ł¾ŪŒŲ“ā€ŒŁŲ±Ų¶ā€ŒŁ‡Ų§ŪŒ حسابداری Ł…Ų¹ŁˆŁ‚, +Deferred Revenue and Expense,درآمد و Ł‡Ų²ŪŒŁ†Ł‡ Ł…Ų¹ŁˆŁ‚, +Deferred accounting failed for some invoices:,حسابداری Ł…Ų¹ŁˆŁ‚ برای برخی Ų§Ų² ŁŲ§Ś©ŲŖŁˆŲ±Ł‡Ų§ Ł†Ų§Ł…ŁˆŁŁ‚ بود:, +Delay (In Days),تاخیر (ŲÆŲ± چند روز), +Delayed,ŲØŲ§ تاخیر, +Delayed Tasks Summary,خلاصه وظایف تاخیری, +Delete Accounting and Stock Ledger Entries on deletion of Transaction,حذف ورودی Ł‡Ų§ŪŒ حسابداری و دفتر کل Ł…ŁˆŲ¬ŁˆŲÆŪŒ ŲÆŲ± حذف تراکنؓ, +Delete Cancelled Ledger Entries,ورودی Ł‡Ų§ŪŒ Ł„ŲŗŁˆ ؓده ŲÆŲ± دفتر کل Ų±Ų§ حذف Ś©Ł†ŪŒŲÆ, +Delete Dimension,حذف Ų§ŲØŲ¹Ų§ŲÆ, +Delete Leads and Addresses,سرنخ ها و Ų¢ŲÆŲ±Ų³ ها Ų±Ų§ حذف Ś©Ł†ŪŒŲÆ, +Delete Transactions,حذف تراکنؓ ها, +Deleted Documents,اسناد حذف ؓده, +Deletion in Progress!,حذف ŲÆŲ± Ų­Ų§Ł„ انجام Ų§Ų³ŲŖ!, +Delimiter options,ŚÆŲ²ŪŒŁ†Ł‡ Ł‡Ų§ŪŒ جداکننده, +Delivery Manager,Ł…ŲÆŪŒŲ± ŲŖŲ­ŁˆŪŒŁ„, +Delivery Note Packed Item,Ś©Ų§Ł„Ų§ŪŒ بسته ŲØŁ†ŲÆŪŒ ؓده یادداؓت ŲŖŲ­ŁˆŪŒŁ„, +Delivery Note(s) created for the Pick List,یادداؓت(Ł‡Ų§ŪŒ) ŲŖŲ­ŁˆŪŒŁ„ برای Ł„ŪŒŲ³ŲŖ انتخاب ایجاد ؓده Ų§Ų³ŲŖ, +Delivery User,کاربر ŲŖŲ­ŁˆŪŒŁ„, +Delivery to,ŲŖŲ­ŁˆŪŒŁ„ به, +Demo Company,ؓرکت ŲÆŁ…Łˆ, +Demo data cleared,ŲÆŲ§ŲÆŁ‡ā€ŒŁ‡Ų§ŪŒ Ł†Ł…Ų§ŪŒŲ“ŪŒ پاک Ų“ŲÆ, +Dependant SLE Voucher Detail No,ؓماره جزئیات Ś©ŁˆŁ¾Ł† SLE ŁˆŲ§ŲØŲ³ŲŖŁ‡, +Dependent Task {0} is not a Template Task,ŁˆŲøŪŒŁŁ‡ ŁˆŲ§ŲØŲ³ŲŖŁ‡ {0} یک کار Ų§Ł„ŚÆŁˆ Ł†ŪŒŲ³ŲŖ, +Deposit,سپرده, +Depreciate based on daily pro-rata,استهلاک ŲØŲ± Ų§Ų³Ų§Ų³ تناسب Ų±ŁˆŲ²Ų§Ł†Ł‡, +Depreciate based on shifts,استهلاک ŲØŲ± Ų§Ų³Ų§Ų³ Ł†ŁˆŲØŲŖ, +Depreciation Details,جزئیات استهلاک, +Depreciation Entry Posting Status,وضعیت Ų«ŲØŲŖ استهلاک, +Depreciation Expense Account should be an Income or Expense Account.,Ų­Ų³Ų§ŲØ Ł‡Ų²ŪŒŁ†Ł‡ استهلاک باید یک Ų­Ų³Ų§ŲØ درآمد یا Ł‡Ų²ŪŒŁ†Ł‡ ŲØŲ§Ų“ŲÆ., +Depreciation Schedule View,مؓاهده برنامه Ų²Ł…Ų§Ł†ŲØŁ†ŲÆŪŒ استهلاک, +Depreciation cannot be calculated for fully depreciated assets,استهلاک برای دارایی Ł‡Ų§ŪŒ کاملا مستهلک ؓده قابل محاسبه Ł†ŪŒŲ³ŲŖ, +Description of Content,Ų“Ų±Ų­ مطالب, +Desk User,کاربر Ł…ŪŒŲ², +Difference In,تفاوت ŲÆŲ±, +Difference Posting Date,تفاوت تاریخ Ų§Ų±Ų³Ų§Ł„, +Difference Qty,تفاوت ŲŖŲ¹ŲÆŲ§ŲÆ, +Different 'Source Warehouse' and 'Target Warehouse' can be set for each row.,برای هر ردیف Ł…ŪŒ ŲŖŁˆŲ§Ł† «انبار منبع» و «انبار هدف» Ł…ŲŖŁŲ§ŁˆŲŖŪŒ Ų±Ų§ ŲŖŁ†ŲøŪŒŁ… کرد., +Dimension Details,جزئیات Ų§ŲØŲ¹Ų§ŲÆ, +Dimension Filter Help,Ų±Ų§Ł‡Ł†Ł…Ų§ŪŒ ŁŪŒŁ„ŲŖŲ± Ų§ŲØŲ¹Ų§ŲÆ, +Dimension-wise Accounts Balance Report,ŚÆŲ²Ų§Ų±Ų“ ŲŖŲ±Ų§Ų² حسابها Ų§Ų² نظر Ų§ŲØŲ¹Ų§ŲÆ, +Direct Expense,Ł‡Ų²ŪŒŁ†Ł‡ Ł…Ų³ŲŖŁ‚ŪŒŁ…, +Disable Last Purchase Rate,نرخ Ų¢Ų®Ų±ŪŒŁ† خرید Ų±Ų§ ŲŗŪŒŲ±ŁŲ¹Ų§Ł„ Ś©Ł†ŪŒŲÆ, +Disable Serial No And Batch Selector,ŲŗŪŒŲ±ŁŲ¹Ų§Ł„ کردن Ų³Ų±ŪŒŲ§Ł„ No And Batch Selector, +Disabled Account Selected,Ų­Ų³Ų§ŲØ ŲŗŪŒŲ±ŁŲ¹Ų§Ł„ انتخاب Ų“ŲÆ, +Disabled Warehouse {0} cannot be used for this transaction.,Ų§Ų² انبار ŲŗŪŒŲ±ŁŲ¹Ų§Ł„ ؓده {0} Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł† برای Ų§ŪŒŁ† تراکنؓ استفاده کرد., +Disabled pricing rules since this {} is an internal transfer,Ł‚ŁˆŲ§Ł†ŪŒŁ† Ł‚ŪŒŁ…ŲŖ گذاری ŲŗŪŒŲ±ŁŲ¹Ų§Ł„ ؓده Ų§Ų³ŲŖ زیرا Ų§ŪŒŁ† {} یک انتقال ŲÆŲ§Ų®Ł„ŪŒ Ų§Ų³ŲŖ, +Disabled tax included prices since this {} is an internal transfer,Ł…Ų§Ł„ŪŒŲ§ŲŖ ŲŗŪŒŲ±ŁŲ¹Ų§Ł„ Ų“Ų§Ł…Ł„ Ł‚ŪŒŁ…ŲŖā€ŒŁ‡Ų§ Ł…ŪŒā€ŒŲ“ŁˆŲÆ زیرا Ų§ŪŒŁ† {} یک انتقال ŲÆŲ§Ų®Ł„ŪŒ Ų§Ų³ŲŖ, +Disables auto-fetching of existing quantity,واکؓی خودکار مقدار Ł…ŁˆŲ¬ŁˆŲÆ Ų±Ų§ ŲŗŪŒŲ±ŁŲ¹Ų§Ł„ Ł…ŪŒ کند, +Disassemble,Ł…Ų¬Ų²Ų§ کردن (دیس اسمبل), +Discount Account,Ų­Ų³Ų§ŲØ تخفیف, +Discount Date,تاریخ تخفیف, +Discount Settings,ŲŖŁ†ŲøŪŒŁ…Ų§ŲŖ تخفیف, +Discount Validity,Ų§Ų¹ŲŖŲØŲ§Ų± تخفیف, +Discount Validity Based On,Ų§Ų¹ŲŖŲØŲ§Ų± تخفیف ŲØŲ± Ų§Ų³Ų§Ų³, +Discount of {} applied as per Payment Term,تخفیف {} Ų·ŲØŁ‚ ؓرایط پرداخت اعمال Ų“ŲÆ, +Discounted Amount,مبلغ ŲØŲ§ تخفیف, +"Discounts to be applied in sequential ranges like buy 1 get 1, buy 2 get 2, buy 3 get 3 and so on",تخفیف Ł‡Ų§ŪŒŪŒ که ŲÆŲ± Ł…Ų­ŲÆŁˆŲÆŁ‡ Ł‡Ų§ŪŒ Ł…ŲŖŁˆŲ§Ł„ŪŒ اعمال Ł…ŪŒ Ų“ŁˆŁ†ŲÆ مانند خرید 1 دریافت 1، خرید 2 دریافت 2، خرید 3 دریافت 3 و ŲŗŪŒŲ±Ł‡, +Discrepancy between General and Payment Ledger,اختلاف ŲØŪŒŁ† دفتر کل و دفتر پرداخت, +Dispatch Address,Ų¢ŲÆŲ±Ų³ Ų§Ų¹Ų²Ų§Ł…, +Dispatch Address Name,نام Ų¢ŲÆŲ±Ų³ Ų§Ų¹Ų²Ų§Ł…, +Distinct Item and Warehouse,کالا و انبار Ł…ŲŖŁ…Ų§ŪŒŲ², +Distribute Additional Costs Based On , توزیع Ł‡Ų²ŪŒŁ†Ł‡ Ł‡Ų§ŪŒ اضافی ŲØŲ± Ų§Ų³Ų§Ų³, +Distribute Manually,توزیع دستی, +Do Not Explode,گسترده Ł†Ś©Ł†ŪŒŲÆ, +DocTypes should not be added manually to the 'Excluded DocTypes' table. You are only allowed to remove entries from it.,DocTypes Ł†ŲØŲ§ŪŒŲÆ به صورت دستی به Ų¬ŲÆŁˆŁ„ 'Excluded DocTypes' اضافه ؓود. Ų“Ł…Ų§ فقط Ł…Ų¬Ų§Ų² به حذف ورودی ها Ų§Ų² آن Ł‡Ų³ŲŖŪŒŲÆ., +Document Type already used as a dimension,Ł†ŁˆŲ¹ سند قبلاً به Ų¹Ł†ŁˆŲ§Ł† ŲØŲ¹ŲÆ استفاده ؓده Ų§Ų³ŲŖ, +Documents,اسناد, +Documents: {0} have deferred revenue/expense enabled for them. Cannot repost.,اسناد: {0} درآمد/Ł‡Ų²ŪŒŁ†Ł‡ Ł…Ų¹ŁˆŁ‚ Ų±Ų§ برای آنها فعال کرده Ų§Ų³ŲŖ. امکان Ų§Ų±Ų³Ų§Ł„ Ł…Ų¬ŲÆŲÆ وجود ندارد., +Domain Settings,ŲŖŁ†ŲøŪŒŁ…Ų§ŲŖ دامنه, +Don't Reserve Sales Order Qty on Sales Return,ŲŖŲ¹ŲÆŲ§ŲÆ سفارؓ فروؓ Ų±Ų§ ŲÆŲ± بازگؓت فروؓ رزرو Ł†Ś©Ł†ŪŒŲÆ, +Don't Send Emails,Ų§ŪŒŁ…ŪŒŁ„ Ų§Ų±Ų³Ų§Ł„ Ł†Ś©Ł†ŪŒŲÆ, +Dont Recompute tax,Ł…Ų§Ł„ŪŒŲ§ŲŖ Ų±Ų§ ŲÆŁˆŲØŲ§Ų±Ł‡ محاسبه Ł†Ś©Ł†ŪŒŲÆ, +Download Backups,ŲÆŲ§Ł†Ł„ŁˆŲÆ نسخه Ł¾Ų“ŲŖŪŒŲØŲ§Ł†, +Download CSV Template,ŲÆŲ§Ł†Ł„ŁˆŲÆ قالب CSV, +Download Materials Request Plan,ŲÆŲ§Ł†Ł„ŁˆŲÆ Ų·Ų±Ų­ درخواست Ł…ŁˆŲ§ŲÆ, +Download Materials Request Plan Section,ŲÆŲ§Ł†Ł„ŁˆŲÆ ŲØŲ®Ų“ Ų·Ų±Ų­ درخواست Ł…ŁˆŲ§ŲÆ, +Dunning Amount (Company Currency),مقدار Dunning (Ų§Ų±Ų² ؓرکت), +Dunning Level,Ų³Ų·Ų­ ŲÆŲ§Ł†ŪŒŁ†ŚÆ, +Duplicate Closing Stock Balance,تکرار کردن ŲŖŲ±Ų§Ų² Ł…ŁˆŲ¬ŁˆŲÆŪŒ Ų§Ų®ŲŖŲŖŲ§Ł…ŪŒŁ‡, +Duplicate Customer Group,ŚÆŲ±ŁˆŁ‡ Ł…Ų“ŲŖŲ±ŪŒŲ§Ł† تکراری, +Duplicate Finance Book,کتاب Ł…Ų§Ł„ŪŒ تکراری, +Duplicate Item Group,ŚÆŲ±ŁˆŁ‡ Ų¢ŪŒŲŖŁ… تکراری, +Duplicate POS Invoices found,ŁŲ§Ś©ŲŖŁˆŲ±Ł‡Ų§ŪŒ POS تکراری پیدا Ų“ŲÆ, +Edit Capacity,ویرایؓ ظرفیت, +Edit Full Form,ویرایؓ فرم کامل, +Edit Note,ویرایؓ یادداؓت, +Editing {0} is not allowed as per POS Profile settings,ویرایؓ {0} Ų·ŲØŁ‚ ŲŖŁ†ŲøŪŒŁ…Ų§ŲŖ Ł†Ł…Ų§ŪŒŁ‡ POS Ł…Ų¬Ų§Ų² Ł†ŪŒŲ³ŲŖ, +Either 'Selling' or 'Buying' must be selected,«فروؓ» یا «خرید» باید انتخاب ؓود, +Email / Notifications,Ų§ŪŒŁ…ŪŒŁ„ / اعلان ها, +Email Address (required),Ų¢ŲÆŲ±Ų³ Ų§ŪŒŁ…ŪŒŁ„ (Ų§Ł„Ų²Ų§Ł…ŪŒ), +"Email Address must be unique, it is already used in {0}",Ų¢ŲÆŲ±Ų³ Ų§ŪŒŁ…ŪŒŁ„ باید منحصر به فرد باؓد، Ų§Ų² قبل ŲÆŲ± {0} استفاده ؓده Ų§Ų³ŲŖ, +Email Digest Recipient,دریافت کننده خلاصه Ų§ŪŒŁ…ŪŒŁ„, +Email Digest: {0},خلاصه Ų§ŪŒŁ…ŪŒŁ„: {0}, +Email Domain,دامنه Ų§ŪŒŁ…ŪŒŁ„, +Email or Phone/Mobile of the Contact are mandatory to continue.,برای ادامه Ų§ŪŒŁ…ŪŒŁ„ یا تلفن/Ł…ŁˆŲØŲ§ŪŒŁ„ Ł…Ų®Ų§Ų·ŲØ Ų§Ł„Ų²Ų§Ł…ŪŒ Ų§Ų³ŲŖ., +Email verification failed.,تأیید Ų§ŪŒŁ…ŪŒŁ„ انجام نؓد., +Employee User Id,ؓناسه کاربر کارمند, +Enable Allow Partial Reservation in the Stock Settings to reserve partial stock.,برای رزرو Ł…ŁˆŲ¬ŁˆŲÆŪŒ جزئی، Allow Partial Reservation Ų±Ų§ ŲÆŲ± ŲŖŁ†ŲøŪŒŁ…Ų§ŲŖ Ł…ŁˆŲ¬ŁˆŲÆŪŒ فعال Ś©Ł†ŪŒŲÆ., +Enable Automatic Party Matching,ŲŖŁ†ŲøŪŒŁ… خودکار طرف Ų±Ų§ فعال Ś©Ł†ŪŒŲÆ, +Enable Common Party Accounting,حسابداری طرف مؓترک Ų±Ų§ فعال Ś©Ł†ŪŒŲÆ, +Enable Discount Accounting for Selling,فعال کردن حسابداری تخفیف برای فروؓ, +Enable Fuzzy Matching,Fuzzy Matching Ų±Ų§ فعال Ś©Ł†ŪŒŲÆ, +Enable Provisional Accounting For Non Stock Items,فعال کردن حسابداری Ł…ŁˆŁ‚ŲŖ برای Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ غیر Ł…ŁˆŲ¬ŁˆŲÆŪŒ, +Enable Stock Reservation,فعال کردن رزرو Ł…ŁˆŲ¬ŁˆŲÆŪŒ, +Enable it if users want to consider rejected materials to dispatch.,Ų§ŚÆŲ± کاربران Ł…ŪŒ Ų®ŁˆŲ§Ł‡Ł†ŲÆ Ł…ŁˆŲ§ŲÆ Ų±ŲÆ ؓده Ų±Ų§ برای Ų§Ų±Ų³Ų§Ł„ ŲÆŲ± نظر ŲØŚÆŪŒŲ±Ł†ŲÆŲŒ آن Ų±Ų§ فعال Ś©Ł†ŪŒŲÆ., +Enable to apply SLA on every {0},فعال کردن اعمال SLA ŲÆŲ± هر {0}, +Enabling this option will allow you to record -

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

2. Advances Paid in an Asset Account instead of the Liability Account,فعال کردن Ų§ŪŒŁ† ŚÆŲ²ŪŒŁ†Ł‡ به Ų“Ł…Ų§ امکان Ł…ŪŒ دهد Ų«ŲØŲŖ Ś©Ł†ŪŒŲÆ -

1. پیؓ پرداخت Ł‡Ų§ŪŒ دریافت ؓده ŲÆŲ± Ų­Ų³Ų§ŲØ ŲØŲÆŁ‡ŪŒ به جای Ų­Ų³Ų§ŲØ دارایی

2. پیؓ پرداخت Ł‡Ų§ŪŒ پرداخت ؓده ŲÆŲ± Ų­Ų³Ų§ŲØ دارایی به جای Ų­Ų³Ų§ŲØ ŲØŲÆŁ‡ŪŒ, +Enabling this will allow creation of multi-currency invoices against single party account in company currency,فعال کردن Ų§ŪŒŁ† امکان ایجاد ŲµŁˆŲ±ŲŖŲ­Ų³Ų§ŲØā€ŒŁ‡Ų§ŪŒ چند ارزی Ų±Ų§ ŲÆŲ± ŲØŲ±Ų§ŲØŲ± Ų­Ų³Ų§ŲØ یک طرف به واحد Ł¾ŁˆŁ„ ؓرکت فراهم Ł…ŪŒā€ŒŚ©Ł†ŲÆ, +End Transit,Ł¾Ų§ŪŒŲ§Ł† Ų­Ł…Ł„ و نقل, +End of the current subscription period,Ł¾Ų§ŪŒŲ§Ł† ŲÆŁˆŲ±Ł‡ Ų§Ų“ŲŖŲ±Ų§Ś© ŁŲ¹Ł„ŪŒ, +"Enter First and Last name of Employee, based on Which Full Name will be updated. IN transactions, it will be Full Name which will be fetched.",نام و نام Ų®Ų§Ł†ŁˆŲ§ŲÆŚÆŪŒ کارمند Ų±Ų§ که ŲØŲ± Ų§Ų³Ų§Ų³ نام کامل به روز Ł…ŪŒ ؓود وارد Ś©Ł†ŪŒŲÆ. ŲÆŲ± Ł…Ų¹Ų§Ł…Ł„Ų§ŲŖŲŒ نام کامل Ų®ŁˆŲ§Ł‡ŲÆ بود که واکؓی Ł…ŪŒ ؓود., +Enter Manually,ورود دستی, +Enter Serial Nos,ؓماره Ł‡Ų§ŪŒ Ų³Ų±ŪŒŲ§Ł„ Ų±Ų§ وارد Ś©Ł†ŪŒŲÆ, +Enter Visit Details,جزئیات بازدید Ų±Ų§ وارد Ś©Ł†ŪŒŲÆ, +Enter a name for Routing.,یک نام برای Ł…Ų³ŪŒŲ±ŪŒŲ§ŲØŪŒ وارد Ś©Ł†ŪŒŲÆ., +"Enter a name for the Operation, for example, Cutting.",یک نام برای Ų¹Ł…Ł„ŪŒŲ§ŲŖ وارد Ś©Ł†ŪŒŲÆŲŒ به Ų¹Ł†ŁˆŲ§Ł† Ł…Ų«Ų§Ł„ŲŒ ŲØŲ±Ų“., +Enter a name for this Holiday List.,یک نام برای Ų§ŪŒŁ† Ł„ŪŒŲ³ŲŖ ŲŖŲ¹Ų·ŪŒŁ„Ų§ŲŖ وارد Ś©Ł†ŪŒŲÆ., +"Enter an Item Code, the name will be auto-filled the same as Item Code on clicking inside the Item Name field.",یک کد Ł…ŁˆŲ±ŲÆ Ų±Ų§ وارد Ś©Ł†ŪŒŲÆŲŒ نام ŲØŲ§ Ś©Ł„ŪŒŚ© کردن ŲÆŲ± داخل قسمت نام Ł…ŁˆŲ±ŲÆŲŒ به طور خودکار مانند کد Ł…ŁˆŲ±ŲÆ پر Ł…ŪŒ ؓود., +Enter each serial no in a new line,هر ؓماره Ų³Ų±ŪŒŲ§Ł„ Ų±Ų§ ŲÆŲ± یک Ų®Ų· جدید وارد Ś©Ł†ŪŒŲÆ, +Enter the opening stock units.,ŁˆŲ§Ų­ŲÆŁ‡Ų§ŪŒ Ł…ŁˆŲ¬ŁˆŲÆŪŒ افتتاحی Ų±Ų§ وارد Ś©Ł†ŪŒŲÆ., +Enter the quantity of the Item that will be manufactured from this Bill of Materials.,مقدار Ų¢ŪŒŲŖŁ…ŪŒ Ų±Ų§ که Ų§Ų² Ų§ŪŒŁ† صورتحساب Ł…ŁˆŲ§ŲÆ ŲŖŁˆŁ„ŪŒŲÆ Ł…ŪŒ ؓود وارد Ś©Ł†ŪŒŲÆ., +Enter the quantity to manufacture. Raw material Items will be fetched only when this is set.,مقدار ŲŖŁˆŁ„ŪŒŲÆ Ų±Ų§ وارد Ś©Ł†ŪŒŲÆ. Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ŪŒ Ł…ŁˆŲ§ŲÆ Ų®Ų§Ł… فقط Ų²Ł…Ų§Ł†ŪŒ واکؓی Ł…ŪŒ Ų“ŁˆŁ†ŲÆ که Ų§ŪŒŁ† ŲŖŁ†ŲøŪŒŁ… ؓود., +Error during caller information update,Ų®Ų·Ų§ ŲÆŲ± Ų­ŪŒŁ† به روز Ų±Ų³Ų§Ł†ŪŒ اطلاعات ŲŖŁ…Ų§Ų³ ŚÆŪŒŲ±Ł†ŲÆŁ‡, +Error while posting depreciation entries,Ų®Ų·Ų§ هنگام Ų§Ų±Ų³Ų§Ł„ ورودی Ł‡Ų§ŪŒ استهلاک, +Error while processing deferred accounting for {0},Ų®Ų·Ų§ هنگام پردازؓ حسابداری Ł…Ų¹ŁˆŁ‚ برای {0}, +Error while reposting item valuation,Ų®Ų·Ų§ هنگام Ų§Ų±Ų³Ų§Ł„ Ł…Ų¬ŲÆŲÆ ارزیابی Ł…ŁˆŲ±ŲÆ, +Errors Notification,اعلان خطاها, +Even invoices with apply tax withholding unchecked will be considered for checking cumulative threshold breach,حتی ŁŲ§Ś©ŲŖŁˆŲ±Ł‡Ų§ŪŒŪŒ ŲØŲ§ اعمال کسر Ł…Ų§Ł„ŪŒŲ§ŲŖ ŲØŲÆŁˆŁ† بررسی برای بررسی نقض آستانه ŲŖŲ¬Ł…Ų¹ŪŒ ŲÆŲ± نظر گرفته Ł…ŪŒā€ŒŲ“ŁˆŁ†ŲÆ., +Example URL,URL Ł…Ų«Ų§Ł„, +Example of a linked document: {0},Ł†Ł…ŁˆŁ†Ł‡ ای Ų§Ų² یک سند Ł¾ŪŒŁˆŁ†ŲÆŪŒ: {0}, +Example: Serial No {0} reserved in {1}.,Ł…Ų«Ų§Ł„: ؓماره Ų³Ų±ŪŒŲ§Ł„ {0} ŲÆŲ± {1} رزرو ؓده Ų§Ų³ŲŖ., +Excess Materials Consumed,Ł…ŁˆŲ§ŲÆ اضافی مصرف ؓده, +Excess Transfer,انتقال Ł…Ų§Ų²Ų§ŲÆ, +Exchange Gain Or Loss,سود یا Ų¶Ų±Ų± مبادله, +Exchange Gain/Loss amount has been booked through {0},مبلغ سود/Ų²ŪŒŲ§Ł† مبادله Ų§Ų² Ų·Ų±ŪŒŁ‚ {0} رزرو ؓده Ų§Ų³ŲŖ, +Exchange Rate Revaluation Settings,ŲŖŁ†ŲøŪŒŁ…Ų§ŲŖ تجدید ارزیابی نرخ Ų§Ų±Ų², +Excluded DocTypes,DocType Ł‡Ų§ŪŒ حذف ؓده, +Exempt Supplies,Ł„ŁˆŲ§Ų²Ł… معاف, +Expected,انتظار Ł…ŪŒ رود, +Expected Balance Qty,مقدار ŲŖŲ±Ų§Ų² Ł…ŁˆŲ±ŲÆ انتظار, +Expected End Date should be less than or equal to parent task's Expected End Date {0}.,تاریخ Ł¾Ų§ŪŒŲ§Ł† Ł…ŁˆŲ±ŲÆ انتظار باید کمتر یا Ł…Ų³Ų§ŁˆŪŒ ŲØŲ§ تاریخ Ł¾Ų§ŪŒŲ§Ł† Ł…ŁˆŲ±ŲÆ انتظار ŁˆŲøŪŒŁŁ‡ ŁˆŲ§Ł„ŲÆ {0} ŲØŲ§Ų“ŲÆ., +Expected Stock Value,Ų§Ų±Ų²Ų“ Ł…ŁˆŲ¬ŁˆŲÆŪŒ Ł…ŁˆŲ±ŲÆ انتظار, +Expected Time Required (In Mins),زمان Ł…ŁˆŲ±ŲÆ Ł†ŪŒŲ§Ų² Ł…ŁˆŲ±ŲÆ انتظار (به ŲÆŁ‚ŪŒŁ‚Ł‡), +Expiry,انقضا, +Export Data,ŲØŲ±ŁˆŁ†ā€ŒŲØŁŲ±ŲÆ داده ها, +Export Errored Rows,ŲµŲ§ŲÆŲ± کردن ردیف Ł‡Ų§ŪŒ Ų®Ų·Ų§, +Export Import Log,لاگ ŲÆŲ±ŁˆŁ†ā€ŒŲØŁŲ±ŲÆ ŲØŲ±ŁˆŁ†ā€ŒŲØŁŲ±ŲÆ, +Extra Consumed Qty,مقدار مصرف اضافی, +Extra Job Card Quantity,مقدار کارت کار اضافی, +FIFO Queue vs Qty After Transaction Comparison,صف FIFO ŲÆŲ± مقابل ŲŖŲ¹ŲÆŲ§ŲÆ پس Ų§Ų² Ł…Ł‚Ų§ŪŒŲ³Ł‡ تراکنؓ, +"FIFO Stock Queue (qty, rate)",صف Ł…ŁˆŲ¬ŁˆŲÆŪŒ FIFO (تعداد، نرخ), +FIFO/LIFO Queue,صف FIFO/LIFO, +Failed Entries,ورودی Ł‡Ų§ŪŒ Ł†Ų§Ł…ŁˆŁŁ‚, +"Failed to erase demo data, please delete the demo company manually.",ŲÆŲ§ŲÆŁ‡ā€ŒŁ‡Ų§ŪŒ Ł†Ł…Ų§ŪŒŲ“ŪŒ پاک Ł†Ų“ŲÆŲŒ لطفاً ؓرکت Ł†Ł…Ų§ŪŒŲ“ŪŒ Ų±Ų§ به صورت دستی حذف Ś©Ł†ŪŒŲÆ., +Failed to setup defaults for country {0}. Please contact support.,ŲŖŁ†ŲøŪŒŁ… پیؓ فرض Ł‡Ų§ŪŒ کؓور {0} انجام نؓد. لطفا ŲØŲ§ Ł¾Ų“ŲŖŪŒŲØŲ§Ł†ŪŒ ŲŖŁ…Ų§Ų³ بگیرید., +Failure,ؓکست, +Failure Description,Ų“Ų±Ų­ ؓکست, +Fetch Based On,واکؓی ŲØŲ± Ų§Ų³Ų§Ų³, +Fetch Overdue Payments,واکؓی پرداخت Ł‡Ų§ŪŒ Ł…Ų¹ŁˆŁ‚, +Fetch Timesheet,واکؓی Ų¬ŲÆŁˆŁ„ Ų²Ł…Ų§Ł†ŪŒ, +Fetch Value From,واکؓی Ų§Ų±Ų²Ų“ Ų§Ų², +Fetching exchange rates ...,واکؓی نرخ Ų§Ų±Ų² ..., +Filter by Reference Date,ŁŪŒŁ„ŲŖŲ± ŲØŲ± Ų§Ų³Ų§Ų³ تاریخ Ł…Ų±Ų¬Ų¹, +Filter on Invoice,ŁŪŒŁ„ŲŖŲ± روی فاکتور, +Filter on Payment,ŁŪŒŁ„ŲŖŲ± ŲÆŲ± پرداخت, +Filters missing,ŁŪŒŁ„ŲŖŲ±Ł‡Ų§ یافت نؓدند, +Final Product,Ł…Ų­ŲµŁˆŁ„ Ł†Ł‡Ų§ŪŒŪŒ, +Financial Ratios,نسبت Ł‡Ų§ŪŒ Ł…Ų§Ł„ŪŒ, +Financial Reports,ŚÆŲ²Ų§Ų±Ų“Ł‡Ų§ŪŒ Ł…Ų§Ł„ŪŒ, +Financial Year Begins On,Ų³Ų§Ł„ Ł…Ų§Ł„ŪŒ ؓروع Ł…ŪŒ ؓود, +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) ,ŚÆŲ²Ų§Ų±Ų“ā€ŒŁ‡Ų§ŪŒ Ł…Ų§Ł„ŪŒ ŲØŲ§ استفاده Ų§Ų² اسناد Ų«ŲØŲŖ دفتر کل ایجاد Ł…ŪŒā€ŒŲ“ŁˆŁ†ŲÆ (Ų§ŚÆŲ± Ś©ŁˆŁ¾Ł† Ł¾Ų§ŪŒŲ§Ł† ŲÆŁˆŲ±Ł‡ برای همه Ų³Ų§Ł„ā€ŒŁ‡Ų§ ŲØŁ‡ā€ŒŲ·ŁˆŲ± Ł…ŲŖŁˆŲ§Ł„ŪŒ پست نؓده ŲØŲ§Ų“ŲÆ یا Ł…ŁŁ‚ŁˆŲÆ ؓده باؓد، باید فعال ؓود) , +Finished Good BOM,BOM Ś©Ų§Ł„Ų§ŪŒ ŲŖŁ…Ų§Ł… ؓده, +Finished Good Item,Ų¢ŪŒŲŖŁ… Ś©Ų§Ł„Ų§ŪŒ ŲŖŁ…Ų§Ł… ؓده, +Finished Good Item Qty,ŲŖŲ¹ŲÆŲ§ŲÆ Ų¢ŪŒŲŖŁ… Ś©Ų§Ł„Ų§ŪŒ ŲŖŁ…Ų§Ł… ؓده, +Finished Good Item Quantity,ŲŖŲ¹ŲÆŲ§ŲÆ Ų¢ŪŒŲŖŁ… Ś©Ų§Ł„Ų§ŪŒ ŲŖŁ…Ų§Ł… ؓده, +Finished Good Item is not specified for service item {0},Ų¢ŪŒŲŖŁ… Ś©Ų§Ł„Ų§ŪŒ ŲŖŁ…Ų§Ł… ؓده برای Ų¢ŪŒŲŖŁ… سرویس Ł…Ų“Ų®Ųµ نؓده Ų§Ų³ŲŖ {0}, +Finished Good Item {0} Qty can not be zero,مقدار Ų¢ŪŒŲŖŁ… Ś©Ų§Ł„Ų§ŪŒ ŲŖŁ…Ų§Ł… ؓده {0} ŲŖŲ¹ŲÆŲ§ŲÆ Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł†ŲÆ صفر ŲØŲ§Ų“ŲÆ, +Finished Good Item {0} must be a sub-contracted item,Ų¢ŪŒŲŖŁ… Ś©Ų§Ł„Ų§ŪŒ ŲŖŁ…Ų§Ł… ؓده {0} باید یک Ų¢ŪŒŲŖŁ… قرارداد فرعی ŲØŲ§Ų“ŲÆ, +Finished Good Qty,مقدار Ś©Ų§Ł„Ų§ŪŒ ŲŖŁ…Ų§Ł… ؓده, +Finished Good Quantity ,مقدار Ś©Ų§Ł„Ų§ŪŒ ŲŖŁ…Ų§Ł… ؓده , +Finished Good UOM,UOM خوب به Ł¾Ų§ŪŒŲ§Ł† رسید, +Finished Good {0} does not have a default BOM.,Finished Good {0} BOM پیؓ فرض ندارد., +Finished Good {0} is disabled.,Finished Good {0} ŲŗŪŒŲ±ŁŲ¹Ų§Ł„ Ų§Ų³ŲŖ., +Finished Good {0} must be a stock item.,Ś©Ų§Ł„Ų§ŪŒ ŲŖŁ…Ų§Ł… ؓده {0} باید یک Ų¢ŪŒŲŖŁ… Ł…ŁˆŲ¬ŁˆŲÆŪŒ ŲØŲ§Ų“ŲÆ., +Finished Good {0} must be a sub-contracted item.,Ś©Ų§Ł„Ų§ŪŒ ŲŖŁ…Ų§Ł… ؓده {0} باید یک Ų¢ŪŒŲŖŁ… قرارداد فرعی ŲØŲ§Ų“ŲÆ., +Finished Goods Based Operating Cost,Ł‡Ų²ŪŒŁ†Ł‡ Ų¹Ł…Ł„ŪŒŲ§ŲŖŪŒ ŲØŲ± Ų§Ų³Ų§Ų³ Ś©Ų§Ł„Ų§Ł‡Ų§ŪŒ ŲŖŁ…Ų§Ł… ؓده, +Finished Goods Item,Ų¢ŪŒŲŖŁ… Ś©Ų§Ł„Ų§Ł‡Ų§ŪŒ ŲŖŁ…Ų§Ł… ؓده, +Finished Goods Reference,Ł…Ų±Ų¬Ų¹ Ś©Ų§Ł„Ų§Ł‡Ų§ŪŒ ŲŖŁ…Ų§Ł… ؓده, +Finished Goods Value,Ų§Ų±Ų²Ų“ Ś©Ų§Ł„Ų§Ł‡Ų§ŪŒ ŲŖŁ…Ų§Ł… ؓده, +Finished Goods based Operating Cost,Ł‡Ų²ŪŒŁ†Ł‡ Ų¹Ł…Ł„ŪŒŲ§ŲŖŪŒ ŲØŲ± Ų§Ų³Ų§Ų³ Ś©Ų§Ł„Ų§Ł‡Ų§ŪŒ ŲŖŁ…Ų§Ł… ؓده, +Finished Item {0} does not match with Work Order {1},Ł…ŁˆŲ±ŲÆ ŲŖŁ…Ų§Ł… ؓده {0} ŲØŲ§ دستور کار {1} مطابقت ندارد, +First Response Due,Ų§ŁˆŁ„ŪŒŁ† پاسخ به علت, +First Response SLA Failed by {},Ų§ŁˆŁ„ŪŒŁ† پاسخ SLA توسط {} انجام نؓد, +Fixed Asset Defaults,پیؓ فرض دارایی Ł‡Ų§ŪŒ Ų«Ų§ŲØŲŖ, +Fixed Time,زمان Ų«Ų§ŲØŲŖ, +For Item,برای Ų¢ŪŒŲŖŁ…, +For Job Card,برای کارت کار, +For Operation,برای Ų¹Ł…Ł„ŪŒŲ§ŲŖ, +For Work Order,برای دستور کار, +For dunning fee and interest,برای Ł‡Ų²ŪŒŁ†Ł‡ و سود, +"For item {0}, rate must be a positive number. To Allow negative rates, enable {1} in {2}",برای Ł…ŁˆŲ±ŲÆ {0}، نرخ باید یک Ų¹ŲÆŲÆ Ł…Ų«ŲØŲŖ ŲØŲ§Ų“ŲÆ. برای Ł…Ų¬Ų§Ų² کردن Ł†Ų±Ų®ā€ŒŁ‡Ų§ŪŒ Ł…Ł†ŁŪŒŲŒ {1} Ų±Ų§ ŲÆŲ± {2} فعال Ś©Ł†ŪŒŲÆ, +For quantity {0} should not be greater than allowed quantity {1},برای مقدار {0} Ł†ŲØŲ§ŪŒŲÆ بیؓتر Ų§Ų² مقدار Ł…Ų¬Ų§Ų² {1} ŲØŲ§Ų“ŲÆ, +Forecasting,پیؓ ŲØŪŒŁ†ŪŒ, +Formula Based Criteria,Ł…Ų¹ŪŒŲ§Ų±Ł‡Ų§ŪŒ Ł…ŲØŲŖŁ†ŪŒ ŲØŲ± ŁŲ±Ł…ŁˆŁ„, +Free Item Rate,نرخ Ų¢ŪŒŲŖŁ… Ų±Ų§ŪŒŚÆŲ§Ł†, +From Corrective Job Card,Ų§Ų² کارت کار Ų§ŲµŁ„Ų§Ų­ŪŒ, +From Date and To Date are mandatory,Ų§Ų² تاریخ و ŲŖŲ§ تاریخ اجباری Ų§Ų³ŲŖ, +From Delivery Date,Ų§Ų² تاریخ ŲŖŲ­ŁˆŪŒŁ„, +From Doctype,Ų§Ų² Doctype, +From Due Date,Ų§Ų² تاریخ سررسید, +From Opportunity,Ų§Ų² فرصت, +From Payment Date,Ų§Ų² تاریخ پرداخت, +From Reference Date,Ų§Ų² تاریخ Ł…Ų±Ų¬Ų¹, +From Voucher Detail No,Ų§Ų² جزئیات Ś©ŁˆŁ¾Ł† ؓماره, +From Voucher No,Ų§Ų² Ś©ŁˆŁ¾Ł† ؓماره, +From Voucher Type,Ų§Ų² Ł†ŁˆŲ¹ Ś©ŁˆŁ¾Ł†, +From and To dates are required,تاریخ Ł‡Ų§ŪŒ Ų§Ų² و ŲŖŲ§ تاریخ لازم Ų§Ų³ŲŖ, +Full and Final Statement,صورت کامل و Ł†Ł‡Ų§ŪŒŪŒ, +GL Balance,ŲŖŲ±Ų§Ų² دفتر کل, +GL Entry Processing Status,وضعیت پردازؓ Ų«ŲØŲŖ دفتر کل, +GL reposting index,فهرست Ų§Ų±Ų³Ų§Ł„ Ł…Ų¬ŲÆŲÆ دفتر کل, +Gain/Loss accumulated in foreign currency account. Accounts with '0' balance in either Base or Account currency,سود/Ų²ŪŒŲ§Ł† انباؓته ؓده ŲÆŲ± Ų­Ų³Ų§ŲØ ارزی. Ų­Ų³Ų§ŲØā€ŒŁ‡Ų§ŪŒŪŒ ŲØŲ§ Ł…ŁˆŲ¬ŁˆŲÆŪŒ Ā«0Ā» به Ų§Ų±Ų² Ł¾Ų§ŪŒŁ‡ یا Ų­Ų³Ų§ŲØ, +Gain/Loss already booked,سود/ŲØŲ§Ų®ŲŖ قبلا رزرو ؓده Ų§Ų³ŲŖ, +Gain/Loss from Revaluation,سود/Ų²ŪŒŲ§Ł† Ł†Ų§Ų“ŪŒ Ų§Ų² تجدید ارزیابی, +General Ledger,دفتر کل Ł…Ų±Ś©Ų²ŪŒ,Warehouse +General and Payment Ledger Comparison,Ł…Ł‚Ų§ŪŒŲ³Ł‡ دفتر کل Ł…Ų±Ś©Ų²ŪŒ و پرداخت, +General and Payment Ledger mismatch,Ų¹ŲÆŁ… ŲŖŲ·Ų§ŲØŁ‚ دفتر کل و دفتر پرداخت, +Generate Closing Stock Balance,ایجاد ŲŖŲ±Ų§Ų² Ł…ŁˆŲ¬ŁˆŲÆŪŒ Ų§Ų®ŲŖŲŖŲ§Ł…ŪŒŁ‡, +Generate Demo Data for Exploration,داده Ł‡Ų§ŪŒ نسخه ی Ł†Ł…Ų§ŪŒŲ“ŪŒ Ų±Ų§ برای کاوؓ ایجاد Ś©Ł†ŪŒŲÆ, +Generate E-Invoice,ایجاد فاکتور Ų§Ł„Ś©ŲŖŲ±ŁˆŁ†ŪŒŚ©ŪŒ, +Generate Invoice At,ایجاد فاکتور ŲÆŲ±, +Generating Preview,ایجاد پیؓ Ł†Ł…Ų§ŪŒŲ“, +Get Allocations,تخصیص ها Ų±Ų§ دریافت Ś©Ł†ŪŒŲÆ, +Get Customer Group Details,دریافت جزئیات ŚÆŲ±ŁˆŁ‡ Ł…Ų“ŲŖŲ±ŪŒ, +Get Finished Goods for Manufacture,دریافت Ś©Ų§Ł„Ų§Ł‡Ų§ŪŒ ŲŖŁ…Ų§Ł… ؓده برای ŲŖŁˆŁ„ŪŒŲÆ, +Get Outstanding Orders,دریافت Ų³ŁŲ§Ų±Ų“ā€ŒŁ‡Ų§ŪŒ Ł…Ų¹ŁˆŁ‚, +Get Raw Materials for Purchase,دریافت Ł…ŁˆŲ§ŲÆ Ų§ŁˆŁ„ŪŒŁ‡ برای خرید, +Get Raw Materials for Transfer,دریافت Ł…ŁˆŲ§ŲÆ Ų§ŁˆŁ„ŪŒŁ‡ برای انتقال, +Get Scrap Items,Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ŪŒ ضایعات Ų±Ų§ دریافت Ś©Ł†ŪŒŲÆ, +Get Stock,Ł…ŁˆŲ¬ŁˆŲÆŪŒ دریافت Ś©Ł†ŪŒŲÆ, +Get Sub Assembly Items,دریافت Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ŪŒ زیر Ł…ŁˆŁ†ŲŖŲ§Ś˜, +Get Supplier Group Details,جزئیات ŚÆŲ±ŁˆŁ‡ ŲŖŲ§Ł…ŪŒŁ† کننده Ų±Ų§ دریافت Ś©Ł†ŪŒŲÆ, +Get Timesheets,برگه Ł‡Ų§ŪŒ Ų²Ł…Ų§Ł†ŪŒ Ų±Ų§ دریافت Ś©Ł†ŪŒŲÆ, +Get stops from,ŲŖŁˆŁ‚Ł Ų§Ų², +Getting Scrap Items,دریافت Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ŪŒ ضایعات, +Give free item for every N quantity,برای هر N مقدار Ų¢ŪŒŲŖŁ… Ų±Ų§ŪŒŚÆŲ§Ł† ŲØŲÆŁ‡ŪŒŲÆ, +Go back,برگرد, +Go to {0} List,به فهرست {0} بروید, +Goals,اهداف, +Goods,کالاها, +Grant Commission,Ų§Ų¹Ų·Ų§Ų” Ś©Ł…ŪŒŲ³ŪŒŁˆŁ†, +Greeting Message,Ł¾ŪŒŲ§Ł… تبریک, +Gross Profit Percent,ŲÆŲ±ŲµŲÆ سود ناخالص, +Gross Purchase Amount should be equal to purchase amount of one single Asset.,مقدار خرید ناخالص باید ŲØŲ±Ų§ŲØŲ± برای خرید یک دارایی واحد ŲØŲ§Ų“ŲÆ., +Group Same Items,ŚÆŲ±ŁˆŁ‡ ŲØŁ†ŲÆŪŒ Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ŪŒ مؓابه, +Growth View,Ł†Ł…Ų§ŪŒ Ų±Ų“ŲÆ, +Half-yearly,Ł†ŪŒŁ… Ų³Ų§Ł„, +Has Alternative Item,دارای Ų¢ŪŒŲŖŁ… Ų¬Ų§ŪŒŚÆŲ²ŪŒŁ†, +Has Item Scanned,Ų¢ŪŒŲŖŁ… اسکن ؓده Ų§Ų³ŲŖ, +Have Default Naming Series for Batch ID?,آیا سری Ł†Ų§Ł…ā€ŒŚÆŲ°Ų§Ų±ŪŒ Ł¾ŪŒŲ“ā€ŒŁŲ±Ų¶ برای Batch ID دارید؟, +Heatmap,نقؓه Ų­Ų±Ų§Ų±ŲŖ, +Height (cm),ارتفاع (Ų³Ų§Ł†ŲŖŪŒ Ł…ŲŖŲ±), +"Hello,",Ų³Ł„Ų§Ł…ŲŒ, +Here are the error logs for the aforementioned failed depreciation entries: {0},ŲÆŲ± Ų§ŪŒŁ†Ų¬Ų§ ŚÆŲ²Ų§Ų±Ų“ Ł‡Ų§ŪŒ Ų®Ų·Ų§ برای ورودی Ł‡Ų§ŪŒ استهلاک Ł†Ų§Ł…ŁˆŁŁ‚ ŁŁˆŁ‚ الذکر آمده Ų§Ų³ŲŖ: {0}, +Here are the options to proceed:,ŲÆŲ± Ų§ŪŒŁ†Ų¬Ų§ ŚÆŲ²ŪŒŁ†Ł‡ Ł‡Ų§ŪŒŪŒ برای ادامه وجود ŲÆŲ§Ų±ŲÆ:, +"Here, you can select a senior of this Employee. Based on this, Organization Chart will be populated.",ŲÆŲ± Ų§ŪŒŁ†Ų¬Ų§ŲŒ Ł…ŪŒ ŲŖŁˆŲ§Ł†ŪŒŲÆ یک Ų§Ų±Ų“ŲÆ Ų§ŪŒŁ† کارمند Ų±Ų§ انتخاب Ś©Ł†ŪŒŲÆ. ŲØŲ± Ų§ŪŒŁ† Ų§Ų³Ų§Ų³ Ł†Ł…ŁˆŲÆŲ§Ų± Ų³Ų§Ų²Ł…Ų§Ł†ŪŒ پر Ł…ŪŒ ؓود., +"Here, your weekly offs are pre-populated based on the previous selections. You can add more rows to also add public and national holidays individually.",ŲÆŲ± Ų§ŪŒŁ†Ų¬Ų§ŲŒ ŲŖŲ®ŁŪŒŁā€ŒŁ‡Ų§ŪŒ Ł‡ŁŲŖŚÆŪŒ Ų“Ł…Ų§ ŲØŲ± Ų§Ų³Ų§Ų³ Ų§Ł†ŲŖŲ®Ų§ŲØā€ŒŁ‡Ų§ŪŒ Ł‚ŲØŁ„ŪŒ Ų§Ų² قبل پر ؓده Ų§Ų³ŲŖ. Ł…ŪŒā€ŒŲŖŁˆŲ§Ł†ŪŒŲÆ Ų±ŲÆŪŒŁā€ŒŁ‡Ų§ŪŒ بیؓتری اضافه Ś©Ł†ŪŒŲÆ ŲŖŲ§ ŲŖŲ¹Ų·ŪŒŁ„Ų§ŲŖ Ų¹Ł…ŁˆŁ…ŪŒ و Ł…Ł„ŪŒ Ų±Ų§ ŲØŁ‡ā€ŒŲµŁˆŲ±ŲŖ جداگانه اضافه Ś©Ł†ŪŒŲÆ., +"Hi,",Ų³Ł„Ų§Ł…ŲŒ, +Hide Images,Ł…Ų®ŁŪŒ کردن تصاویر, +Holiday Date {0} added multiple times,تاریخ ŲŖŲ¹Ų·ŪŒŁ„Ų§ŲŖ {0} Ś†Ł†ŲÆŪŒŁ† ŲØŲ§Ų± اضافه Ų“ŲÆ, +Hours Spent,Ų³Ų§Ų¹ŲŖ Ł‡Ų§ŪŒ صرف ؓده, +How often should Project be updated of Total Purchase Cost ?,هر چند ŁˆŁ‚ŲŖ یکبار Ł¾Ų±ŁˆŚ˜Ł‡ باید Ų§Ų² Ł‡Ų²ŪŒŁ†Ł‡ کل خرید به روز ؓود؟, +I - J,من - جی, +I - K,من - Ś©, +Idle,بیکار (Idle), +"If an operation is divided into sub operations, they can be added here.",Ų§ŚÆŲ± یک Ų¹Ł…Ł„ŪŒŲ§ŲŖ به Ų¹Ł…Ł„ŪŒŲ§ŲŖ فرعی ŲŖŁ‚Ų³ŪŒŁ… ؓود، Ł…ŪŒ ŲŖŁˆŲ§Ł† آنها Ų±Ų§ ŲÆŲ± Ų§ŪŒŁ†Ų¬Ų§ اضافه کرد., +"If checked, Rejected Quantity will be included while making Purchase Invoice from Purchase Receipt.",ŲÆŲ± صورت علامت Ų²ŲÆŁ†ŲŒ مقدار Ų±ŲÆ ؓده هنگام ŲŖŁ‡ŪŒŁ‡ فاکتور خرید Ų§Ų² رسید خرید لحاظ Ł…ŪŒ ؓود., +"If checked, Stock will be reserved on Submit",ŲÆŲ± صورت علامت Ų²ŲÆŁ†ŲŒ Ł…ŁˆŲ¬ŁˆŲÆŪŒ ŲÆŲ± Ų§Ų±Ų³Ų§Ł„ رزرو Ų®ŁˆŲ§Ł‡ŲÆ Ų“ŲÆ, +"If checked, picked qty won't automatically be fulfilled on submit of pick list.",Ų§ŚÆŲ± علامت زده ؓود، ŲŖŲ¹ŲÆŲ§ŲÆ انتخاب ؓده به طور خودکار ŲÆŲ± Ų§Ų±Ų³Ų§Ł„ Ł„ŪŒŲ³ŲŖ انتخاب انجام Ł†Ł…ŪŒ ؓود., +"If checked, the tax amount will be considered as already included in the Paid Amount in Payment Entry",ŲÆŲ± صورت بررسی، مبلغ Ł…Ų§Ł„ŪŒŲ§ŲŖ به Ų¹Ł†ŁˆŲ§Ł† مبلغ پرداخت ؓده ŲÆŲ± Ų«ŲØŲŖ پرداخت ŲÆŲ± نظر گرفته Ł…ŪŒ ؓود, +"If checked, we will create demo data for you to explore the system. This demo data can be erased later.",ŲÆŲ± صورت علامت Ų²ŲÆŁ†ŲŒ ŲÆŲ§ŲÆŁ‡ā€ŒŁ‡Ų§ŪŒ Ł†Ł…Ų§ŪŒŲ“ŪŒ Ų±Ų§ برای Ų“Ł…Ų§ ایجاد Ł…ŪŒā€ŒŚ©Ł†ŪŒŁ… ŲŖŲ§ Ų³ŪŒŲ³ŲŖŁ… Ų±Ų§ کاوؓ Ś©Ł†ŪŒŲÆ. Ų§ŪŒŁ† داده Ł‡Ų§ŪŒ Ł†Ł…Ų§ŪŒŲ“ŪŒ Ų±Ų§ Ł…ŪŒ ŲŖŁˆŲ§Ł† ŲØŲ¹ŲÆŲ§Ł‹ پاک کرد., +If enabled then system won't override the picked qty / batches / serial numbers.,Ų§ŚÆŲ± فعال باؓد، Ų³ŪŒŲ³ŲŖŁ… ŲŖŲ¹ŲÆŲ§ŲÆ / دسته / ؓماره Ų³Ų±ŪŒŲ§Ł„ انتخاب ؓده Ų±Ų§ ŲØŲ§Ų²Ł†ŁˆŪŒŲ³ŪŒ Ł†Ł…ŪŒ کند., +"If enabled, a print of this document will be attached to each email",ŲÆŲ± صورت فعال ŲØŁˆŲÆŁ†ŲŒ Ś†Ų§Ł¾ŪŒ Ų§Ų² Ų§ŪŒŁ† سند به هر Ų§ŪŒŁ…ŪŒŁ„ پیوست Ł…ŪŒ ؓود, +"If enabled, additional ledger entries will be made for discounts in a separate Discount Account",ŲÆŲ± صورت فعال ŲØŁˆŲÆŁ†ŲŒ ورودی Ł‡Ų§ŪŒ دفتر کل اضافی برای تخفیف ŲÆŲ± یک Ų­Ų³Ų§ŲØ تخفیف جداگانه ایجاد Ł…ŪŒ ؓود, +"If enabled, all files attached to this document will be attached to each email",ŲÆŲ± صورت فعال ŲØŁˆŲÆŁ†ŲŒ ŲŖŁ…Ų§Ł… ŁŲ§ŪŒŁ„ Ł‡Ų§ŪŒ پیوست ؓده به Ų§ŪŒŁ† سند به هر Ų§ŪŒŁ…ŪŒŁ„ پیوست Ł…ŪŒ ؓود, +"If enabled, ledger entries will be posted for change amount in POS transactions",ŲÆŲ± صورت فعال ŲØŁˆŲÆŁ†ŲŒ ŁˆŲ±ŁˆŲÆŪŒā€ŒŁ‡Ų§ŪŒ دفتر کل برای مبلغ تغییر ŲÆŲ± ŲŖŲ±Ų§Ś©Ł†Ų“ā€ŒŁ‡Ų§ŪŒ POS پست Ł…ŪŒā€ŒŲ“ŁˆŁ†ŲÆ, +"If enabled, the consolidated invoices will have rounded total disabled",ŲÆŲ± صورت فعال ŲØŁˆŲÆŁ†ŲŒ ŲµŁˆŲ±ŲŖŲ­Ų³Ų§ŲØā€ŒŁ‡Ų§ŪŒ ŲŖŁ„ŁŪŒŁ‚ŪŒŲŒ کل ŲŗŪŒŲ±ŁŲ¹Ų§Ł„ Ł…ŪŒā€ŒŲ“ŁˆŁ†ŲÆ, +"If enabled, the system will create material requests even if the stock exists in the 'Raw Materials Warehouse'.","Ų§ŚÆŲ± فعال باؓد، Ų³ŪŒŲ³ŲŖŁ… حتی Ų§ŚÆŲ± Ł…ŁˆŲ¬ŁˆŲÆŪŒ ŲÆŲ± ""انبار Ł…ŁˆŲ§ŲÆ Ų®Ų§Ł…"" وجود داؓته باؓد، درخواست Ł‡Ų§ŪŒ Ł…ŁˆŲ§ŲÆ ایجاد Ł…ŪŒ کند.", +"If mentioned, the system will allow only the users with this Role to create or modify any stock transaction earlier than the latest stock transaction for a specific item and warehouse. If set as blank, it allows all users to create/edit back-dated transactions.",ŲÆŲ± صورت ذکر Ų“ŲÆŁ‡ŲŒ Ų§ŪŒŁ† Ų³ŪŒŲ³ŲŖŁ… فقط به کاربران دارای Ų§ŪŒŁ† نقؓ اجازه Ł…ŪŒā€ŒŲÆŁ‡ŲÆ ŲŖŲ§ هر تراکنؓ Ł…ŁˆŲ¬ŁˆŲÆŪŒ Ų±Ų§ زودتر Ų§Ų² Ų¢Ų®Ų±ŪŒŁ† تراکنؓ Ł…ŁˆŲ¬ŁˆŲÆŪŒ برای یک کالا و انبار Ų®Ų§Ųµ ایجاد یا اصلاح کنند. Ų§ŚÆŲ± به صورت Ų®Ų§Ł„ŪŒ ŲŖŁ†ŲøŪŒŁ… ؓود، به همه کاربران اجازه Ł…ŪŒ دهد ŲŖŲ§ تراکنؓ Ł‡Ų§ŪŒ Ł‚ŲÆŪŒŁ…ŪŒ Ų±Ų§ ایجاد/ویرایؓ کنند., +"If not, you can Cancel / Submit this entry",Ų§ŚÆŲ± Ł†Ł‡ŲŒ Ł…ŪŒ ŲŖŁˆŲ§Ł†ŪŒŲÆ Ų§ŪŒŁ† Ų«ŲØŲŖ Ų±Ų§ Ł„ŲŗŁˆ / Ų§Ų±Ų³Ų§Ł„ Ś©Ł†ŪŒŲÆ, +"If rate is zero then item will be treated as ""Free Item""","Ų§ŚÆŲ± نرخ صفر باؓد، Ų¢ŪŒŲŖŁ… به Ų¹Ł†ŁˆŲ§Ł† ""Ų¢ŪŒŲŖŁ… Ų±Ų§ŪŒŚÆŲ§Ł†"" ŲŖŁ„Ł‚ŪŒ Ł…ŪŒ ؓود", +"If the BOM results in Scrap material, the Scrap Warehouse needs to be selected.",Ų§ŚÆŲ± BOM منجر به Ł…ŁˆŲ§ŲÆ قراضه ؓود، انبار ضایعات باید انتخاب ؓود., +"If the selected BOM has Operations mentioned in it, the system will fetch all Operations from BOM, these values can be changed.",Ų§ŚÆŲ± BOM انتخاب ؓده دارای Ų¹Ł…Ł„ŪŒŲ§ŲŖ ذکر ؓده ŲÆŲ± آن باؓد، Ų³ŪŒŲ³ŲŖŁ… ŲŖŁ…Ų§Ł… Ų¹Ł…Ł„ŪŒŲ§ŲŖ Ų±Ų§ Ų§Ų² BOM واکؓی Ł…ŪŒ Ś©Ł†ŲÆŲŒ Ų§ŪŒŁ† Ł…Ł‚Ų§ŲÆŪŒŲ± Ų±Ų§ Ł…ŪŒ ŲŖŁˆŲ§Ł† تغییر ŲÆŲ§ŲÆ., +"If this checkbox is enabled, then the system won’t run the MRP for the available sub-assembly items.",Ų§ŚÆŲ± Ų§ŪŒŁ† چک باکس فعال باؓد، Ų³ŪŒŲ³ŲŖŁ… MRP Ų±Ų§ برای Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ŪŒ زیر Ł…ŁˆŁ†ŲŖŲ§Ś˜ Ł…ŁˆŲ¬ŁˆŲÆ Ų§Ų¬Ų±Ų§ Ł†Ł…ŪŒ کند., +If this is undesirable please cancel the corresponding Payment Entry.,Ų§ŚÆŲ± Ų§ŪŒŁ† Ų§Ł…Ų± Ł†Ų§Ł…Ų·Ł„ŁˆŲØ است، لطفاً Ų«ŲØŲŖ پرداخت Ł…Ų±ŲØŁˆŲ·Ł‡ Ų±Ų§ Ł„ŲŗŁˆ Ś©Ł†ŪŒŲÆ., +"If yes, then this warehouse will be used to store rejected materials",Ų§ŚÆŲ± ŲØŁ„Ł‡ŲŒ پس Ų§Ų² Ų§ŪŒŁ† انبار برای Ł†ŚÆŁ‡ŲÆŲ§Ų±ŪŒ Ł…ŁˆŲ§ŲÆ Ų±ŲÆ ؓده استفاده Ł…ŪŒ ؓود, +"If you are maintaining stock of this Item in your Inventory, ERPNext will make a stock ledger entry for each transaction of this item.",Ų§ŚÆŲ± Ł…ŁˆŲ¬ŁˆŲÆŪŒ Ų§ŪŒŁ† کالا Ų±Ų§ ŲÆŲ± Ł…ŁˆŲ¬ŁˆŲÆŪŒ خود Ł†ŚÆŁ‡ŲÆŲ§Ų±ŪŒ Ł…ŪŒ Ś©Ł†ŪŒŲÆŲŒ ERPNext برای هر تراکنؓ Ų§ŪŒŁ† کالا یک Ų«ŲØŲŖ ŲÆŲ± دفتر کل Ł…ŁˆŲ¬ŁˆŲÆŪŒ ایجاد Ł…ŪŒ کند., +"If you need to reconcile particular transactions against each other, then please select accordingly. If not, all the transactions will be allocated in FIFO order.",Ų§ŚÆŲ± Ł†ŪŒŲ§Ų² به ŲŖŲ·ŲØŪŒŁ‚ معاملات خاصی ŲØŲ§ یکدیگر دارید، لطفاً Ł…Ų·Ų§ŲØŁ‚ آن Ų±Ų§ انتخاب Ś©Ł†ŪŒŲÆ. ŲÆŲ± غیر Ų§ŪŒŁ† صورت، ŲŖŁ…Ų§Ł… تراکنؓ ها به ترتیب FIFO تخصیص Ł…ŪŒ یابد., +"If you still want to proceed, please disable 'Skip Available Sub Assembly Items' checkbox.",Ų§ŚÆŲ± همچنان Ł…ŪŒā€ŒŲ®ŁˆŲ§Ł‡ŪŒŲÆ ادامه ŲÆŁ‡ŪŒŲÆŲŒ لطفاً کادر انتخاب «صرف نظر Ų§Ų² Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ŪŒ زیر Ł…ŁˆŁ†ŲŖŲ§Ś˜ Ł…ŁˆŲ¬ŁˆŲÆĀ» Ų±Ų§ ŲŗŪŒŲ±ŁŲ¹Ų§Ł„ Ś©Ł†ŪŒŲÆ., +"If you still want to proceed, please enable {0}.",Ų§ŚÆŲ± همچنان Ł…ŪŒā€ŒŲ®ŁˆŲ§Ł‡ŪŒŲÆ ادامه ŲÆŁ‡ŪŒŲÆŲŒ لطفاً {0} Ų±Ų§ فعال Ś©Ł†ŪŒŲÆ., +"If your CSV uses a different delimiter, add that character here, ensuring no spaces or additional characters are included.",Ų§ŚÆŲ± CSV Ų“Ł…Ų§ Ų§Ų² جداکننده دیگری استفاده Ł…ŪŒā€ŒŚ©Ł†ŲÆŲŒ آن کاراکتر Ų±Ų§ ŲÆŲ± Ų§ŪŒŁ†Ų¬Ų§ اضافه Ś©Ł†ŪŒŲÆŲŒ مطمئن ؓوید که Ł‡ŪŒŚ† فاصله یا Ł†ŁˆŪŒŲ³Ł‡ اضافی ŲÆŲ± آن وجود ندارد., +Ignore Account Closing Balance,Ł†Ų§ŲÆŪŒŲÆŁ‡ گرفتن Ł…ŁˆŲ¬ŁˆŲÆŪŒ بسته ؓدن Ų­Ų³Ų§ŲØ, +Ignore Available Stock,Ł†Ų§ŲÆŪŒŲÆŁ‡ گرفتن Ł…ŁˆŲ¬ŁˆŲÆŪŒ ŲÆŲ± ŲÆŲ³ŲŖŲ±Ų³, +Ignore Closing Balance,Ł†Ų§ŲÆŪŒŲÆŁ‡ گرفتن ŲŖŲ±Ų§Ų² Ų§Ų®ŲŖŲŖŲ§Ł…ŪŒŁ‡, +Ignore Default Payment Terms Template,Ų§Ł„ŚÆŁˆŪŒ ؓرایط پرداخت Ł¾ŪŒŲ“ā€ŒŁŲ±Ų¶ Ų±Ų§ Ł†Ų§ŲÆŪŒŲÆŁ‡ بگیرید, +Ignore Empty Stock,Ł…ŁˆŲ¬ŁˆŲÆŪŒ Ų®Ų§Ł„ŪŒ Ų±Ų§ Ł†Ų§ŲÆŪŒŲÆŁ‡ بگیرید, +Ignore Exchange Rate Revaluation Journals,دفتر Ų±ŁˆŲ²Ł†Ų§Ł…Ł‡ Ł‡Ų§ŪŒ تجدید ارزیابی نرخ Ų§Ų±Ų² Ų±Ų§ Ł†Ų§ŲÆŪŒŲÆŁ‡ بگیرید, +Ignore Pricing Rule is enabled. Cannot apply coupon code.,Ł†Ų§ŲÆŪŒŲÆŁ‡ گرفتن Ł‚Ų§Ł†ŁˆŁ† Ł‚ŪŒŁ…ŲŖ گذاری فعال Ų§Ų³ŲŖ. Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł† کد تخفیف Ų±Ų§ اعمال کرد., +Ignore Voucher Type filter and Select Vouchers Manually,ŁŪŒŁ„ŲŖŲ± Ł†ŁˆŲ¹ Ś©ŁˆŁ¾Ł† Ų±Ų§ Ł†Ų§ŲÆŪŒŲÆŁ‡ بگیرید و Ś©ŁˆŁ¾Ł† ها Ų±Ų§ به صورت دستی انتخاب Ś©Ł†ŪŒŲÆ, +Import File,ŲÆŲ±ŁˆŁ†ā€ŒŲØŁŲ±ŲÆ ŁŲ§ŪŒŁ„, +Import File Errors and Warnings,خطاها و Ł‡Ų“ŲÆŲ§Ų±Ł‡Ų§ŪŒ ŲÆŲ±ŁˆŁ†ā€ŒŲØŁŲ±ŲÆ ŁŲ§ŪŒŁ„, +Import Log Preview,پیؓ Ł†Ł…Ų§ŪŒŲ“ لاگ ŲÆŲ±ŁˆŁ†ā€ŒŲØŁŲ±ŲÆ, +Import Preview,پیؓ Ł†Ł…Ų§ŪŒŲ“ ŲÆŲ±ŁˆŁ†ā€ŒŲØŁŲ±ŲÆ, +Import Progress,پیؓرفت ŲÆŲ±ŁˆŁ†ā€ŒŲØŁŲ±ŲÆ, +Import Type,Ł†ŁˆŲ¹ ŲÆŲ±ŁˆŁ†ā€ŒŲØŁŲ±ŲÆ, +Import Using CSV file,ŲÆŲ±ŁˆŁ†ā€ŒŲØŁŲ±ŲÆ ŲØŲ§ استفاده Ų§Ų² ŁŲ§ŪŒŁ„ CSV, +Import Warnings,Ł‡Ų“ŲÆŲ§Ų±Ł‡Ų§ŪŒ ŲÆŲ±ŁˆŁ†ā€ŒŲØŁŲ±ŲÆ, +Import from Google Sheets,ŲÆŲ±ŁˆŁ†ā€ŒŲØŁŲ±ŲÆ Ų§Ų² Google Sheets, +"Importing {0} of {1}, {2}",ŲÆŲ± Ų­Ų§Ł„ ŲÆŲ±ŁˆŁ†ā€ŒŲØŁŲ±ŲÆ {0} Ų§Ų² {1}، {2}, +In House,ŲÆŲ± خانه, +In Minutes,به ŲÆŁ‚ŪŒŁ‚Ł‡, +In Party Currency,به Ų§Ų±Ų² طرف, +In Transit Transfer,ŲÆŲ± انتقال ŲŖŲ±Ų§Ł†Ų²ŪŒŲŖ, +In Transit Warehouse,ŲÆŲ± انبار ŲŖŲ±Ų§Ł†Ų²ŪŒŲŖ, +In mins,به ŲÆŁ‚ŪŒŁ‚Ł‡, +"In row {0} of Appointment Booking Slots: ""To Time"" must be later than ""From Time"".",ŲÆŲ± ردیف {0} Ł‚Ų³Ł…ŲŖā€ŒŁ‡Ų§ŪŒ رزرو قرار ملاقات: «تا زمان» باید دیرتر Ų§Ų² «از زمان» ŲØŲ§Ų“ŲÆ., +"In the case of 'Use Multi-Level BOM' in a work order, if the user wishes to add sub-assembly costs to Finished Goods items without using a job card as well the scrap items, then this option needs to be enable.",ŲÆŲ± Ł…ŁˆŲ±ŲÆ «استفاده Ų§Ų² BOM چند سطحی» ŲÆŲ± یک دستور کار، Ų§ŚÆŲ± کاربر ŲØŲ®ŁˆŲ§Ł‡ŲÆ Ł‡Ų²ŪŒŁ†Ł‡ā€ŒŁ‡Ų§ŪŒ زیر Ł…ŁˆŁ†ŲŖŲ§Ś˜ Ų±Ų§ ŲØŲÆŁˆŁ† استفاده Ų§Ų² کارت کار و Ł‡Ł…Ś†Ł†ŪŒŁ† Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ŪŒ ضایعات به Ś©Ų§Ł„Ų§Ł‡Ų§ŪŒ Ł†Ł‡Ų§ŪŒŪŒ اضافه Ś©Ł†ŲÆŲŒ Ų§ŪŒŁ† ŚÆŲ²ŪŒŁ†Ł‡ باید فعال ؓود., +"In this section, you can define Company-wide transaction-related defaults for this Item. Eg. Default Warehouse, Default Price List, Supplier, etc.",ŲÆŲ± Ų§ŪŒŁ† ŲØŲ®Ų“ Ł…ŪŒā€ŒŲŖŁˆŲ§Ł†ŪŒŲÆ Ł¾ŪŒŲ“ā€ŒŁŲ±Ų¶ā€ŒŁ‡Ų§ŪŒ Ł…Ų±ŲØŁˆŲ· به ŲŖŲ±Ų§Ś©Ł†Ų“ā€ŒŁ‡Ų§ŪŒ کل ؓرکت Ų±Ų§ برای Ų§ŪŒŁ† Ų¢ŪŒŲŖŁ… تعریف Ś©Ł†ŪŒŲÆ. به Ų¹Ł†ŁˆŲ§Ł† Ł…Ų«Ų§Ł„. انبار پیؓ فرض، Ł„ŪŒŲ³ŲŖ Ł‚ŪŒŁ…ŲŖ پیؓ فرض، ŲŖŲ§Ł…ŪŒŁ† کننده و ŲŗŪŒŲ±Ł‡, +Inactive Status,وضعیت غیر فعال, +Include Account Currency,Ų“Ų§Ł…Ł„ Ų§Ų±Ų² Ų­Ų³Ų§ŲØ, +Include Default FB Assets,دارایی Ł‡Ų§ŪŒ پیؓ فرض FB Ų±Ų§ Ų“Ų§Ł…Ł„ ؓود, +Include Disabled,Ų“Ų§Ł…Ł„ افراد غیر فعال, +Include Safety Stock in Required Qty Calculation,لحاظ کردن Ł…ŁˆŲ¬ŁˆŲÆŪŒ Ų§ŪŒŁ…Ł†ŪŒ ŲÆŲ± محاسبه مقدار Ł…ŁˆŲ±ŲÆ Ł†ŪŒŲ§Ų², +Include Timesheets in Draft Status,Ų“Ų§Ł…Ł„ Ų¬ŲÆŁˆŁ„ Ų²Ł…Ų§Ł†ŪŒ ŲÆŲ± وضعیت پیؓ Ł†ŁˆŪŒŲ³, +Include Zero Stock Items,Ų“Ų§Ł…Ł„ Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ŪŒ Ł…ŁˆŲ¬ŁˆŲÆŪŒ صفر, +Incoming Call Handling Schedule,برنامه رسیدگی به ŲŖŁ…Ų§Ų³ Ł‡Ų§ŪŒ ورودی, +Incoming Call Settings,ŲŖŁ†ŲøŪŒŁ…Ų§ŲŖ ŲŖŁ…Ų§Ų³ ورودی, +Incoming Rate (Costing),نرخ ورودی (Ł‡Ų²ŪŒŁ†Ł‡ā€ŒŪŒŲ§ŲØŪŒ), +Incorrect Balance Qty After Transaction,ŲŖŲ¹ŲÆŲ§ŲÆ Ł…ŁˆŲ¬ŁˆŲÆŪŒ نادرست پس Ų§Ų² تراکنؓ, +Incorrect Batch Consumed,دسته نادرست مصرف ؓده Ų§Ų³ŲŖ, +Incorrect Invoice,فاکتور نادرست, +Incorrect Movement Purpose,هدف جابجایی نادرست, +Incorrect Payment Type,Ł†ŁˆŲ¹ پرداخت نادرست, +Incorrect Serial No Valuation,Ų§Ų±Ų²Ų“ گذاری ؓماره Ų³Ų±ŪŒŲ§Ł„ نادرست Ų§Ų³ŲŖ, +Incorrect Serial Number Consumed,ؓماره Ų³Ų±ŪŒŲ§Ł„ نادرست مصرف ؓده Ų§Ų³ŲŖ, +Incorrect Stock Value Report,ŚÆŲ²Ų§Ų±Ų“ Ų§Ų±Ų²Ų“ Ł…ŁˆŲ¬ŁˆŲÆŪŒ نادرست Ų§Ų³ŲŖ, +Incorrect Type of Transaction,Ł†ŁˆŲ¹ تراکنؓ نادرست, +Incoterm,Ų§ŪŒŁ†Ś©ŁˆŲŖŲ±Ł…, +Increase In Asset Life(Months),افزایؓ عمر دارایی (ماه), +Indent,تورفتگی, +Indirect Expense,Ł‡Ų²ŪŒŁ†Ł‡ غیر Ł…Ų³ŲŖŁ‚ŪŒŁ…, +Individual GL Entry cannot be cancelled.,Ų«ŲØŲŖ Ų§Ł†ŁŲ±Ų§ŲÆŪŒ دفتر کل Ų±Ų§ Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł† Ł„ŲŗŁˆ کرد., +Individual Stock Ledger Entry cannot be cancelled.,ورود فردی به دفتر Ł…ŁˆŲ¬ŁˆŲÆŪŒ Ų±Ų§ Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł† Ł„ŲŗŁˆ کرد., +Insert New Records,ŲÆŲ±Ų¬ Ų±Ś©ŁˆŲ±ŲÆŁ‡Ų§ŪŒ جدید, +Inspection Rejected,بازرسی Ų±ŲÆ Ų“ŲÆ, +Inspection Submission,Ų§Ų±Ų³Ų§Ł„ بازرسی, +Instruction,ŲÆŲ³ŲŖŁˆŲ±Ų§Ł„Ų¹Ł…Ł„, +Insufficient Capacity,ظرفیت Ł†Ų§Ś©Ų§ŁŪŒ, +Insufficient Stock for Batch,Ł…ŁˆŲ¬ŁˆŲÆŪŒ Ł†Ų§Ś©Ų§ŁŪŒ برای دسته, +Inter Transfer Reference,Ł…Ų±Ų¬Ų¹ انتقال ŲÆŲ§Ų®Ł„ŪŒ, +Interest and/or dunning fee,بهره و/یا Ł‡Ų²ŪŒŁ†Ł‡ اجناس, +Internal,ŲÆŲ±ŁˆŁ†ŪŒŲ› ŲÆŲ§Ų®Ł„ŪŒ, +Internal Customer,Ł…Ų“ŲŖŲ±ŪŒ ŲÆŲ§Ų®Ł„ŪŒ, +Internal Customer for company {0} already exists,Ł…Ų“ŲŖŲ±ŪŒ ŲÆŲ§Ų®Ł„ŪŒ برای ؓرکت {0} Ų§Ų² قبل وجود ŲÆŲ§Ų±ŲÆ, +Internal Sale or Delivery Reference missing.,Ł…Ų±Ų¬Ų¹ فروؓ ŲÆŲ§Ų®Ł„ŪŒ یا ŲŖŲ­ŁˆŪŒŁ„ Ł…ŁˆŲ¬ŁˆŲÆ Ł†ŪŒŲ³ŲŖ., +Internal Sales Reference Missing,Ł…Ų±Ų¬Ų¹ فروؓ ŲÆŲ§Ų®Ł„ŪŒ وجود ندارد, +Internal Supplier,ŲŖŲ§Ł…ŪŒŁ† کننده ŲÆŲ§Ų®Ł„ŪŒ, +Internal Supplier for company {0} already exists,ŲŖŲ§Ł…ŪŒŁ† کننده ŲÆŲ§Ų®Ł„ŪŒ برای ؓرکت {0} Ų§Ų² قبل وجود ŲÆŲ§Ų±ŲÆ, +Internal Transfer Reference Missing,Ł…Ų±Ų¬Ų¹ انتقال ŲÆŲ§Ų®Ł„ŪŒ وجود ندارد, +Internal Transfers,نقل و انتقالات ŲÆŲ§Ų®Ł„ŪŒ, +Internal transfers can only be done in company's default currency,نقل و انتقالات ŲÆŲ§Ų®Ł„ŪŒ فقط ŲØŲ§ Ų§Ų±Ų² پیؓ فرض ؓرکت قابل انجام Ų§Ų³ŲŖ, +Invalid,بی Ų§Ų¹ŲŖŲØŲ§Ų±, +Invalid Auto Repeat Date,تاریخ تکرار خودکار نامعتبر Ų§Ų³ŲŖ, +Invalid Cost Center,مرکز Ł‡Ų²ŪŒŁ†Ł‡ نامعتبر Ų§Ų³ŲŖ, +Invalid Delivery Date,تاریخ ŲŖŲ­ŁˆŪŒŁ„ نامعتبر Ų§Ų³ŲŖ, +Invalid Document,سند نامعتبر, +Invalid Document Type,Ł†ŁˆŲ¹ سند نامعتبر Ų§Ų³ŲŖ, +Invalid Formula,ŁŲ±Ł…ŁˆŁ„ نامعتبر Ų§Ų³ŲŖ, +Invalid Group By,ŚÆŲ±ŁˆŁ‡ نامعتبر توسط, +Invalid Item Defaults,پیؓ فرض Ł‡Ų§ŪŒ Ų¢ŪŒŲŖŁ… نامعتبر, +Invalid Ledger Entries,ورودی Ł‡Ų§ŪŒ دفتر نامعتبر, +Invalid Primary Role,نقؓ Ų§ŲµŁ„ŪŒ نامعتبر Ų§Ų³ŲŖ, +Invalid Priority,Ų§ŁˆŁ„ŁˆŪŒŲŖ نامعتبر Ų§Ų³ŲŖ, +Invalid Process Loss Configuration,Ł¾ŪŒŚ©Ų±ŲØŁ†ŲÆŪŒ Ų§Ų² ŲÆŲ³ŲŖ دادن ŁŲ±Ų¢ŪŒŁ†ŲÆ نامعتبر Ų§Ų³ŲŖ, +Invalid Purchase Invoice,فاکتور خرید نامعتبر, +Invalid Qty,ŲŖŲ¹ŲÆŲ§ŲÆ نامعتبر Ų§Ų³ŲŖ, +Invalid Schedule,Ų²Ł…Ų§Ł†ŲØŁ†ŲÆŪŒ نامعتبر Ų§Ų³ŲŖ, +Invalid Warehouse,انبار نامعتبر, +Invalid result key. Response:,Ś©Ł„ŪŒŲÆ Ł†ŲŖŪŒŲ¬Ł‡ نامعتبر Ų§Ų³ŲŖ. ŁˆŲ§Ś©Ł†Ų“:, +Invalid value {0} for {1} against account {2},مقدار {0} برای {1} ŲÆŲ± ŲØŲ±Ų§ŲØŲ± Ų­Ų³Ų§ŲØ {2} نامعتبر Ų§Ų³ŲŖ, +Inventory Dimension,Ų§ŲØŲ¹Ų§ŲÆ Ł…ŁˆŲ¬ŁˆŲÆŪŒ, +Inventory Dimension Negative Stock,Ų§ŲØŲ¹Ų§ŲÆ Ł…ŁˆŲ¬ŁˆŲÆŪŒ Ł…Ł†ŁŪŒ, +Inventory Settings,ŲŖŁ†ŲøŪŒŁ…Ų§ŲŖ Ł…ŁˆŲ¬ŁˆŲÆŪŒ, +Invoice Cancellation,Ł„ŲŗŁˆ فاکتور, +Invoice Limit,Ų­ŲÆ فاکتور, +Invoice Portion (%),سهم فاکتور (%), +Invoice and Billing,فاکتور و صورتحساب, +Invoiced Qty,ŲŖŲ¹ŲÆŲ§ŲÆ فاکتور, +Invoices and Payments have been Fetched and Allocated,ŁŲ§Ś©ŲŖŁˆŲ±Ł‡Ų§ و پرداخت ها واکؓی و تخصیص داده ؓده Ų§Ų³ŲŖ, +Invoicing Features,ویژگی Ł‡Ų§ŪŒ صورتحساب, +Is Adjustment Entry,Ų«ŲØŲŖ ŲŖŲ¹ŲÆŪŒŁ„ Ų§Ų³ŲŖ, +Is Alternative,Ų¬Ų§ŪŒŚÆŲ²ŪŒŁ† Ų§Ų³ŲŖ, +Is Cash or Non Trade Discount,تخفیف Ł†Ł‚ŲÆŪŒ یا غیرتجاری Ų§Ų³ŲŖ, +Is Composite Asset,دارایی مرکب Ų§Ų³ŲŖ, +Is Corrective Job Card,کارت کار Ų§ŲµŁ„Ų§Ų­ŪŒ Ų§Ų³ŲŖ, +Is Corrective Operation,Ų¹Ł…Ł„ŪŒŲ§ŲŖ Ų§ŲµŁ„Ų§Ų­ŪŒ Ų§Ų³ŲŖ, +Is Expandable,قابل ŚÆŲ³ŲŖŲ±Ų“ Ų§Ų³ŲŖ, +Is Finished Item,Ł…ŁˆŲ±ŲÆ ŲŖŁ…Ų§Ł… ؓده Ų§Ų³ŲŖ, +Is Fully Depreciated,کاملا مستهلک ؓده, +Is Group Warehouse,انبار ŚÆŲ±ŁˆŁ‡ Ų§Ų³ŲŖ, +Is Old Subcontracting Flow,Ų¬Ų±ŪŒŲ§Ł† Ł¾ŪŒŁ…Ų§Ł†Ś©Ų§Ų±ŪŒ فرعی Ł‚ŲÆŪŒŁ…ŪŒ Ų§Ų³ŲŖ, +Is Outward,ŲØŪŒŲ±ŁˆŁ†ŪŒ Ų§Ų³ŲŖ, +Is Period Closing Voucher Entry,Ų«ŲØŲŖ Ś©ŁˆŁ¾Ł† Ł¾Ų§ŪŒŲ§Ł† ŲÆŁˆŲ±Ł‡ Ų§Ų³ŲŖ, +Is Rate Adjustment Entry (Debit Note),Ų«ŲØŲŖ ŲŖŲ¹ŲÆŪŒŁ„ نرخ Ų§Ų³ŲŖ (یادداؓت ŲØŲÆŁ‡ŪŒ), +Is Recursive,بازگؓتی Ų§Ų³ŲŖ, +Is Rejected,Ų±ŲÆ ؓده Ų§Ų³ŲŖ, +Is Rejected Warehouse,انبار Ł…Ų±Ų¬ŁˆŲ¹ŪŒ Ų§Ų³ŲŖ, +Is Scrap Item,Ų¢ŪŒŲŖŁ… ضایعات Ų§Ų³ŲŖ, +Is Short Year,Ų³Ų§Ł„ Ś©ŁˆŲŖŲ§Ł‡ Ų§Ų³ŲŖ, +Is Standard,استاندارد Ų§Ų³ŲŖ, +Is Stock Item,Ų¢ŪŒŲŖŁ… Ł…ŁˆŲ¬ŁˆŲÆŪŒ Ų§Ų³ŲŖ, +Is System Generated,Ų³ŪŒŲ³ŲŖŁ… ŲŖŁˆŁ„ŪŒŲÆ ؓده Ų§Ų³ŲŖ, +Is Template,قالب Ų§Ų³ŲŖ, +Issue Analytics,ŲŖŲ¬Ų²ŪŒŁ‡ و ŲŖŲ­Ł„ŪŒŁ„ مؓکل, +Issue Summary,خلاصه مؓکل, +Issue a debit note with 0 qty against an existing Sales Invoice,ŲÆŲ± مقابل فاکتور فروؓ Ł…ŁˆŲ¬ŁˆŲÆŲŒ یک برگه ŲØŲÆŁ‡ŪŒ ŲØŲ§ مقدار 0 ŲµŲ§ŲÆŲ± Ś©Ł†ŪŒŲÆ, +Issuing cannot be done to a location. Please enter employee to issue the Asset {0} to,صدور Ų±Ų§ Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł† به یک مکان انجام ŲÆŲ§ŲÆ. لطفاً کارمند Ų±Ų§ وارد Ś©Ł†ŪŒŲÆ ŲŖŲ§ دارایی {0} Ų±Ų§ ŲµŲ§ŲÆŲ± کند, +It can take upto few hours for accurate stock values to be visible after merging items.,ممکن Ų§Ų³ŲŖ چند Ų³Ų§Ų¹ŲŖ Ų·ŁˆŁ„ بکؓد ŲŖŲ§ Ų§Ų±Ų²Ų“ Ł…ŁˆŲ¬ŁˆŲÆŪŒ ŲÆŁ‚ŪŒŁ‚ پس Ų§Ų² Ų§ŲÆŲŗŲ§Ł… اقلام قابل مؓاهده ŲØŲ§Ų“ŲÆ., +"It's not possible to distribute charges equally when total amount is zero, please set 'Distribute Charges Based On' as 'Quantity'",ŁˆŁ‚ŲŖŪŒ مبلغ کل صفر است، Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł† Ł‡Ų²ŪŒŁ†Ł‡ ها Ų±Ų§ به طور Ł…Ų³Ų§ŁˆŪŒ ŲŖŁ‚Ų³ŪŒŁ… کرد، لطفاً «توزیع Ł‡Ų²ŪŒŁ†Ł‡ ها ŲØŲ± اساس» Ų±Ų§ به Ų¹Ł†ŁˆŲ§Ł† «مقدار» ŲŖŁ†ŲøŪŒŁ… Ś©Ł†ŪŒŲÆ, +Item Code (Final Product),کد Ų¢ŪŒŲŖŁ… (Ł…Ų­ŲµŁˆŁ„ Ł†Ł‡Ų§ŪŒŪŒ), +Item Group wise Discount,تخفیف ŲØŲ± Ų§Ų³Ų§Ų³ ŚÆŲ±ŁˆŁ‡ Ų¢ŪŒŲŖŁ…, +Item Price Settings,ŲŖŁ†ŲøŪŒŁ…Ų§ŲŖ Ł‚ŪŒŁ…ŲŖ Ų¢ŪŒŲŖŁ…, +"Item Price appears multiple times based on Price List, Supplier/Customer, Currency, Item, Batch, UOM, Qty, and Dates.",Ł‚ŪŒŁ…ŲŖ Ų¢ŪŒŲŖŁ… Ś†Ł†ŲÆŪŒŁ† ŲØŲ§Ų± ŲØŲ± Ų§Ų³Ų§Ų³ Ł„ŪŒŲ³ŲŖ Ł‚ŪŒŁ…ŲŖŲŒ ŲŖŲ§Ł…ŪŒŁ† کننده/Ł…Ų“ŲŖŲ±ŪŒŲŒ ارز، Ų¢ŪŒŲŖŁ…ŲŒ ŲÆŲ³ŲŖŁ‡ŲŒ UOM، مقدار و تاریخ ها ظاهر Ł…ŪŒ ؓود., +Item Reference,Ł…Ų±Ų¬Ų¹ Ų¢ŪŒŲŖŁ…, +Item Warehouse based reposting has been enabled.,Ų§Ų±Ų³Ų§Ł„ Ł…Ų¬ŲÆŲÆ ŲØŲ± Ų§Ų³Ų§Ų³ انبار Ł…ŁˆŲ±ŲÆ فعال ؓده Ų§Ų³ŲŖ., +Item and Warehouse,Ų¢ŪŒŲŖŁ… و انبار, +Item is removed since no serial / batch no selected.,Ł…ŁˆŲ±ŲÆ حذف ؓده Ų§Ų³ŲŖ زیرا Ł‡ŪŒŚ† Ų³Ų±ŪŒŲ§Ł„ / دسته ای انتخاب نؓده Ų§Ų³ŲŖ., +Item qty can not be updated as raw materials are already processed.,ŲŖŲ¹ŲÆŲ§ŲÆ Ł…ŁˆŲ±ŲÆ Ų±Ų§ Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł† به روز کرد زیرا Ł…ŁˆŲ§ŲÆ Ų®Ų§Ł… قبلاً پردازؓ ؓده Ų§Ų³ŲŖ., +Item rate has been updated to zero as Allow Zero Valuation Rate is checked for item {0},نرخ Ł…ŁˆŲ±ŲÆ به صفر ŲØŁ‡ā€ŒŲ±ŁˆŲ²Ų±Ų³Ų§Ł†ŪŒ ؓده Ų§Ų³ŲŖ زیرا نرخ Ų§Ų±Ų²Ų“ گذاری Ł…Ų¬Ų§Ų² صفر برای Ł…ŁˆŲ±ŲÆ {0} بررسی Ł…ŪŒā€ŒŲ“ŁˆŲÆ, +Item valuation reposting in progress. Report might show incorrect item valuation.,Ų§Ų±Ų³Ų§Ł„ Ł…Ų¬ŲÆŲÆ ارزیابی Ų¢ŪŒŲŖŁ… ŲÆŲ± Ų­Ų§Ł„ انجام Ų§Ų³ŲŖ. ŚÆŲ²Ų§Ų±Ų“ ممکن Ų§Ų³ŲŖ Ų§Ų±Ų²Ų“ گذاری اقلام نادرست Ų±Ų§ نؓان دهد., +Item {0} cannot be added as a sub-assembly of itself,Ų¢ŪŒŲŖŁ… {0} Ų±Ų§ Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł† به Ų¹Ł†ŁˆŲ§Ł† یک زیر Ł…ŁˆŁ†ŲŖŲ§Ś˜ Ų§Ų² خودؓ اضافه کرد, +Item {0} cannot be ordered more than {1} against Blanket Order {2}.,Ł…ŁˆŲ±ŲÆ {0} Ų±Ų§ Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł† بیؓ Ų§Ų² {1} ŲÆŲ± مقابل سفارؓ بلانکت {2} سفارؓ ŲÆŲ§ŲÆ., +Item {0} does not exist.,Ų¢ŪŒŲŖŁ… {0} وجود ندارد., +Item {0} entered multiple times.,Ų¢ŪŒŲŖŁ… {0} Ś†Ł†ŲÆŪŒŁ† ŲØŲ§Ų± وارد ؓده Ų§Ų³ŲŖ., +Item {0} is already reserved/delivered against Sales Order {1}.,Ł…ŁˆŲ±ŲÆ {0} قبلاً ŲÆŲ± ŲØŲ±Ų§ŲØŲ± سفارؓ فروؓ {1} رزرو ؓده/ŲŖŲ­ŁˆŪŒŁ„ ؓده Ų§Ų³ŲŖ., +Item {0} must be a Non-Stock Item,Ł…ŁˆŲ±ŲÆ {0} باید یک Ś©Ų§Ł„Ų§ŪŒ غیر Ł…ŁˆŲ¬ŁˆŲÆŪŒ ŲØŲ§Ų“ŲÆ, +Item {0} not found in 'Raw Materials Supplied' table in {1} {2},"Ł…ŁˆŲ±ŲÆ {0} ŲÆŲ± Ų¬ŲÆŁˆŁ„ ""Ł…ŁˆŲ§ŲÆ Ų®Ų§Ł… ŲŖŲ§Ł…ŪŒŁ† ؓده"" ŲÆŲ± {1} {2} یافت نؓد", +Item {0} not found.,Ł…ŁˆŲ±ŲÆ {0} یافت نؓد., +Item {} does not exist.,Ł…ŁˆŲ±ŲÆ {} وجود ندارد., +Items & Pricing,Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ و Ł‚ŪŒŁ…ŲŖ, +Items Catalogue,Ś©Ų§ŲŖŲ§Ł„ŁˆŚÆ Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§, +Items cannot be updated as Subcontracting Order is created against the Purchase Order {0}.,Ł…ŁˆŲ§Ų±ŲÆ Ų±Ų§ Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł† به روز کرد زیرا سفارؓ قرارداد فرعی ŲÆŲ± ŲØŲ±Ų§ŲØŲ± سفارؓ خرید {0} ایجاد ؓده Ų§Ų³ŲŖ., +Items rate has been updated to zero as Allow Zero Valuation Rate is checked for the following items: {0},نرخ اقلام به صفر ŲØŁ‡ā€ŒŲ±ŁˆŲ²Ų±Ų³Ų§Ł†ŪŒ ؓده Ų§Ų³ŲŖ زیرا نرخ Ų§Ų±Ų²Ų“ گذاری Ł…Ų¬Ų§Ų² صفر برای Ł…ŁˆŲ§Ų±ŲÆ زیر بررسی Ł…ŪŒā€ŒŲ“ŁˆŲÆ: {0}, +Items to Be Repost,Ł…ŁˆŲ§Ų±ŲÆŪŒ که باید بازنؓر Ų“ŁˆŁ†ŲÆ, +Items to Order and Receive,Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ŪŒ قابل سفارؓ و دریافت, +Items to Reserve,Ł…ŁˆŲ§Ų±ŲÆ برای رزرو, +Job Capacity,ظرفیت کاری, +Job Card Operation,Ų¹Ł…Ł„ŪŒŲ§ŲŖ کارت کار, +Job Card Scheduled Time,زمان برنامه ریزی ؓده کارت کار, +Job Card Scrap Item,Ų¢ŪŒŲŖŁ… ضایعات کارت کار, +Job Card and Capacity Planning,برنامه ریزی کارت کار و ظرفیت, +Job Paused,کار Ł…ŲŖŁˆŁ‚Ł Ų“ŲÆ, +Job: {0} has been triggered for processing failed transactions,Ų“ŲŗŁ„: {0} برای پردازؓ تراکنؓ Ł‡Ų§ŪŒ Ł†Ų§Ł…ŁˆŁŁ‚ فعال ؓده Ų§Ų³ŲŖ, +Joining,Ł¾ŪŒŁˆŲ³ŲŖŁ†, +Journal Entries,ورودی Ł‡Ų§ŪŒ دفتر Ų±ŁˆŲ²Ł†Ų§Ł…Ł‡, +Journal Entry for Asset scrapping cannot be cancelled. Please restore the Asset.,Ų«ŲØŲŖ دفتر Ų±ŁˆŲ²Ł†Ų§Ł…Ł‡ برای حذف دارایی Ų±Ų§ Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł† Ł„ŲŗŁˆ کرد. لطفا دارایی Ų±Ų§ بازیابی Ś©Ł†ŪŒŲÆ., +Journal Entry type should be set as Depreciation Entry for asset depreciation,Ł†ŁˆŲ¹ Ų«ŲØŲŖ دفتر Ų±ŁˆŲ²Ł†Ų§Ł…Ł‡ باید به Ų¹Ł†ŁˆŲ§Ł† ورودی استهلاک برای استهلاک دارایی ŲŖŁ†ŲøŪŒŁ… ؓود, +Journals,ŲÆŁŲŖŲ±Ł‡Ų§ŪŒ Ų±ŁˆŲ²Ł†Ų§Ł…Ł‡, +Key,Ś©Ł„ŪŒŲÆ, +Kindly cancel the Manufacturing Entries first against the work order {0}.,لطفاً Ų§ŲØŲŖŲÆŲ§ ŁˆŲ±ŁˆŲÆŪŒā€ŒŁ‡Ų§ŪŒ ŲŖŁˆŁ„ŪŒŲÆ Ų±Ų§ ŲÆŲ± ŲØŲ±Ų§ŲØŲ± دستور کار {0} Ł„ŲŗŁˆ Ś©Ł†ŪŒŲÆ., +"Last Name, Email or Phone/Mobile of the user are mandatory to continue.",نام Ų®Ų§Ł†ŁˆŲ§ŲÆŚÆŪŒŲŒ Ų§ŪŒŁ…ŪŒŁ„ یا تلفن / تلفن همراه کاربر برای ادامه اجباری Ų§Ų³ŲŖ., +Lead -> Prospect,سرنخ -> Ł…Ų“ŲŖŲ±ŪŒ ŲØŲ§Ł„Ł‚ŁˆŁ‡, +Lead Conversion Time,زمان ŲŖŲØŲÆŪŒŁ„ سرنخ, +Lead Owner cannot be same as the Lead Email Address,مالک Ų§ŲµŁ„ŪŒ Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł†ŲÆ ŲØŲ§ Ų¢ŲÆŲ±Ų³ Ų§ŪŒŁ…ŪŒŁ„ Ų§ŲµŁ„ŪŒ ŪŒŚ©Ų³Ų§Ł† ŲØŲ§Ų“ŲÆ, +Lead {0} has been added to prospect {1}.,سرنخ {0} به Ł…Ų“ŲŖŲ±ŪŒ ŲØŲ§Ł„Ł‚ŁˆŁ‡ {1} اضافه ؓده Ų§Ų³ŲŖ., +Leaderboard,ŲŖŲ§ŲØŁ„ŁˆŪŒ Ų§Ł…ŲŖŪŒŲ§Ų²Ų§ŲŖ, +Leads,سرنخ ها, +Learn Accounting,حسابداری یاد بگیرید, +Learn Inventory Management,Ł…ŲÆŪŒŲ±ŪŒŲŖ Ł„ŪŒŲ³ŲŖ Ł…ŁˆŲ¬ŁˆŲÆŪŒ Ų±Ų§ یاد بگیرید, +Learn Manufacturing,یادگیری ŲŖŁˆŁ„ŪŒŲÆ, +Learn Procurement,تدارکات Ų±Ų§ ŲØŪŒŲ§Ł…ŁˆŲ²ŪŒŲÆ, +Learn Project Management,Ł…ŲÆŪŒŲ±ŪŒŲŖ Ł¾Ų±ŁˆŚ˜Ł‡ Ų±Ų§ یاد بگیرید, +Learn Sales Management,Ł…ŲÆŪŒŲ±ŪŒŲŖ فروؓ Ų±Ų§ یاد بگیرید, +Ledger Merge,Ų§ŲÆŲŗŲ§Ł… دفتر کل, +Ledger Merge Accounts,Ų­Ų³Ų§ŲØ Ł‡Ų§ŪŒ Ų§ŲÆŲŗŲ§Ł… دفتر کل, +Ledgers,دفتر کل, +Legend,افسانه, +Length,Ų·ŁˆŁ„, +Length (cm),Ų·ŁˆŁ„ (Ų³Ų§Ł†ŲŖŪŒ Ł…ŲŖŲ±), +Less than 12 months.,کمتر Ų§Ų² 12 ماه., +Level (BOM),Ų³Ų·Ų­ (BOM), +Limit timeslot for Stock Reposting,Ł…Ų­ŲÆŁˆŲÆŪŒŲŖ Ų²Ł…Ų§Ł†ŪŒ برای Ų§Ų±Ų³Ų§Ł„ Ł…Ų¬ŲÆŲÆ Ł…ŁˆŲ¬ŁˆŲÆŪŒ, +Limits don't apply on,Ł…Ų­ŲÆŁˆŲÆŪŒŲŖ ها اعمال Ł†Ł…ŪŒ ؓود, +Link a new bank account,Ł¾ŪŒŁˆŁ†ŲÆ یک Ų­Ų³Ų§ŲØ ŲØŲ§Ł†Ś©ŪŒ جدید, +Link with Customer,Ł¾ŪŒŁˆŁ†ŲÆ ŲØŲ§ Ł…Ų“ŲŖŲ±ŪŒ, +Link with Supplier,Ł¾ŪŒŁˆŁ†ŲÆ ŲØŲ§ ŲŖŲ§Ł…ŪŒŁ† کننده, +Linked with submitted documents,Ł…Ų±ŲŖŲØŲ· ŲØŲ§ اسناد Ų§Ų±Ų³Ų§Ł„ŪŒ, +Linking Failed,Ł¾ŪŒŁˆŁ†ŲÆ Ł†Ų§Ł…ŁˆŁŁ‚ بود, +Linking to Customer Failed. Please try again.,Ł¾ŪŒŁˆŁ†ŲÆ به Ł…Ų“ŲŖŲ±ŪŒ انجام نؓد. لطفا ŲÆŁˆŲØŲ§Ų±Ł‡ تلاؓ Ś©Ł†ŪŒŲÆ., +Linking to Supplier Failed. Please try again.,Ł¾ŪŒŁˆŁ†ŲÆ به ŲŖŲ§Ł…ŪŒŁ† کننده انجام نؓد. لطفا ŲÆŁˆŲØŲ§Ų±Ł‡ تلاؓ Ś©Ł†ŪŒŲÆ., +Links,Ł¾ŪŒŁˆŁ†ŲÆŁ‡Ų§, +Loading import file...,ŲÆŲ± Ų­Ų§Ł„ بارگیری ŁŲ§ŪŒŁ„ ŲÆŲ±ŁˆŁ†ā€ŒŲØŁŲ±ŲÆ..., +Locked,قفل ؓده Ų§Ų³ŲŖ, +Log Entries,ورودی Ł‡Ų§ŪŒ لاگ, +Lost Quotations,پیؓ فاکتور Ł‡Ų§ŪŒ Ų§Ų² ŲÆŲ³ŲŖ رفته, +Lost Quotations %,% پیؓ فاکتور Ł‡Ų§ŪŒ Ų§Ų² ŲÆŲ³ŲŖ رفته, +Lost Reasons are required in case opportunity is Lost.,ŲÆŲ± صورت Ų§Ų² ŲÆŲ³ŲŖ رفتن فرصت، ŲÆŁ„Ų§ŪŒŁ„ Ų§Ų² ŲÆŲ³ŲŖ رفتن Ł…ŁˆŲ±ŲÆ Ł†ŪŒŲ§Ų² Ų§Ų³ŲŖ., +Lost Value,Ų§Ų±Ų²Ų“ Ų§Ų² ŲÆŲ³ŲŖ رفته, +Lost Value %,مقدار Ų§Ų² ŲÆŲ³ŲŖ رفته %, +Machine Type,Ł†ŁˆŲ¹ Ł…Ų§Ų“ŪŒŁ†, +Main Cost Center,مرکز Ł‡Ų²ŪŒŁ†Ł‡ Ų§ŲµŁ„ŪŒ, +Main Cost Center {0} cannot be entered in the child table,مرکز Ł‡Ų²ŪŒŁ†Ł‡ Ų§ŲµŁ„ŪŒ {0} Ų±Ų§ Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł† ŲÆŲ± Ų¬ŲÆŁˆŁ„ فرزند وارد کرد, +Maintain Asset,حفظ دارایی, +Maintenance Details,جزئیات ŲŖŲ¹Ł…ŪŒŲ± و Ł†ŚÆŁ‡ŲÆŲ§Ų±ŪŒ, +Make , بسازید, +Make Asset Movement,جابجایی دارایی Ų±Ų§ ایجاد Ś©Ł†ŪŒŲÆ, +Make Quotation,پیؓ فاکتور ایجاد Ś©Ł†ŪŒŲÆ, +Make Return Entry,ایجاد Ų«ŲØŲŖ بازگؓت, +Make Serial No / Batch from Work Order,ؓماره Ų³Ų±ŪŒŲ§Ł„ / دسته Ų§Ų² دستور کار Ų±Ų§ بسازید, +Make {0} Variant,ایجاد {0} ŚÆŁˆŁ†Ł‡, +Make {0} Variants,ایجاد {0} ŚÆŁˆŁ†Ł‡, +Manage,Ł…ŲÆŪŒŲ±ŪŒŲŖ, +Mandatory Accounting Dimension,ŲØŲ¹ŲÆ حسابداری اجباری, +Mandatory Depends On,اجباری بستگی ŲÆŲ§Ų±ŲÆ, +Mandatory Field,ŁŪŒŁ„ŲÆ اجباری, +Mandatory Section,ŲØŲ®Ų“ اجباری, +Manual Inspection,بازرسی دستی, +Manufacture Sub-assembly in Operation,ŲŖŁˆŁ„ŪŒŲÆ زیر Ł…ŁˆŁ†ŲŖŲ§Ś˜ ŲÆŲ± Ų¹Ł…Ł„ŪŒŲ§ŲŖ, +Manufacturing Type,Ł†ŁˆŲ¹ ŲŖŁˆŁ„ŪŒŲÆ, +Mapping Purchase Receipt ...,نگاؓت رسید خرید ..., +Mapping Subcontracting Order ...,نگاؓت سفارؓ Ł¾ŪŒŁ…Ų§Ł†Ś©Ų§Ų±ŪŒ فرعی ..., +Mapping {0} ...,نگاؓت {0}..., +Mark As Closed,علامت گذاری به Ų¹Ł†ŁˆŲ§Ł† بسته ؓده, +Material Returned from WIP,Ł…ŁˆŲ§ŲÆ برگردانده ؓده Ų§Ų² «کار ŲÆŲ± Ų­Ų§Ł„ انجام», +Material Transfer (In Transit),انتقال Ł…ŁˆŲ§ŲÆ (ŲÆŲ± Ų­Ų§Ł„ Ų­Ł…Ł„ و نقل), +Materials are already received against the {0} {1},Ł…ŁˆŲ§ŲÆ قبلاً ŲÆŲ± مقابل {0} {1} دریافت ؓده Ų§Ų³ŲŖ, +Materials needs to be transferred to the work in progress warehouse for the job card {0},برای کارت کار باید Ł…ŁˆŲ§ŲÆ به انبار کار ŲÆŲ± Ų­Ų§Ł„ انجام انتقال داده ؓود {0}, +Max Qty (As Per Stock UOM),حداکثر ŲŖŲ¹ŲÆŲ§ŲÆ (ŲØŲ± Ų§Ų³Ų§Ų³ Ł…ŁˆŲ¬ŁˆŲÆŪŒ UOM), +Maximum Net Rate,حداکثر نرخ خالص, +Maximum Payment Amount,حداکثر مبلغ پرداختی, +Maximum Value,حداکثر Ų§Ų±Ų²Ų“, +Maximum quantity scanned for item {0}.,حداکثر مقدار اسکن ؓده برای Ł…ŁˆŲ±ŲÆ {0}., +Meeting,ملاقات, +Mention if non-standard Receivable account,Ų§ŚÆŲ± Ų­Ų³Ų§ŲØ ŲÆŲ±ŪŒŲ§ŁŲŖŁ†ŪŒ غیر استاندارد Ų§Ų³ŲŖ Ų±Ų§ ذکر Ś©Ł†ŪŒŲÆ, +Merge Invoices Based On,Ų§ŲÆŲŗŲ§Ł… ŁŲ§Ś©ŲŖŁˆŲ±Ł‡Ų§ ŲØŲ± Ų§Ų³Ų§Ų³, +Merge Progress,Ų§ŲÆŲŗŲ§Ł… پیؓرفت, +Merge Similar Account Heads,Ų§ŲÆŲŗŲ§Ł… سران Ų­Ų³Ų§ŲØ Ł‡Ų§ŪŒ مؓابه, +Merge taxes from multiple documents,Ų§ŲÆŲŗŲ§Ł… Ł…Ų§Ł„ŪŒŲ§ŲŖ Ų§Ų² اسناد Ł…ŲŖŲ¹ŲÆŲÆ, +Merged,Ų§ŲÆŲŗŲ§Ł… Ų“ŲÆ, +"Merging is only possible if following properties are same in both records. Is Group, Root Type, Company and Account Currency",Ų§ŲÆŲŗŲ§Ł… تنها ŲÆŲ± صورتی امکان پذیر Ų§Ų³ŲŖ که ویژگی Ł‡Ų§ŪŒ زیر ŲÆŲ± هر دو رکورد ŪŒŚ©Ų³Ų§Ł† باؓند. ŚÆŲ±ŁˆŁ‡ŲŒ Ł†ŁˆŲ¹ Ų±ŪŒŲ“Ł‡ŲŒ ؓرکت و Ų§Ų±Ų² Ų­Ų³Ų§ŲØ Ų§Ų³ŲŖ, +Merging {0} of {1},Ų§ŲÆŲŗŲ§Ł… {0} Ų§Ų² {1}, +Min Qty (As Per Stock UOM),حداقل ŲŖŲ¹ŲÆŲ§ŲÆ (ŲØŲ± Ų§Ų³Ų§Ų³ Ł…ŁˆŲ¬ŁˆŲÆŪŒ UOM), +Min Qty should be greater than Recurse Over Qty,Min Qty باید بیؓتر Ų§Ų² Recurse Over Qty ŲØŲ§Ų“ŲÆ, +Minimum Net Rate,حداقل نرخ خالص, +Minimum Payment Amount,حداقل مبلغ پرداختی, +Minimum Value,حداقل Ų§Ų±Ų²Ų“, +Mismatch,Ų¹ŲÆŁ… ŲŖŲ·Ų§ŲØŁ‚, +Missing,Ų¬Ų§ افتاده, +Missing Asset,دارایی گمؓده, +Missing Cost Center,مرکز Ł‡Ų²ŪŒŁ†Ł‡ Ų¬Ų§ افتاده, +Missing Finance Book,کتاب Ł…Ų§Ł„ŪŒ Ų¬Ų§ افتاده, +Missing Finished Good,Ų§Ų² ŲÆŲ³ŲŖ رفته به Ł¾Ų§ŪŒŲ§Ł† رسید, +Missing Formula,ŁŲ±Ł…ŁˆŁ„ Ų¬Ų§ افتاده, +Missing Items,Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ŪŒ Ų¬Ų§ افتاده, +Missing Payments App,برنامه پرداخت وجود ندارد, +Missing Serial No Bundle,باندل ؓماره Ų³Ų±ŪŒŲ§Ł„ Ų¬Ų§ افتاده, +Missing value,مقدار Ų§Ų² ŲÆŲ³ŲŖ رفته, +Modified By,تغییر داده ؓده توسط, +Modified On,اصلاح ؓده ŲÆŲ±, +Module Settings,ŲŖŁ†ŲøŪŒŁ…Ų§ŲŖ Ł…Ų§Ś˜ŁˆŁ„, +Move Stock,انتقال Ł…ŁˆŲ¬ŁˆŲÆŪŒ, +Move to Cart,جابجایی به Ų³ŲØŲÆ خرید, +Movement,جابجایی, +Moving up in tree ...,بالا رفتن ŲÆŲ± ŲÆŲ±Ų®ŲŖ..., +Multi-level BOM Creator,ایجاد کننده BOM چند سطحی, +Multiple Loyalty Programs found for Customer {}. Please select manually.,Ś†Ł†ŲÆŪŒŁ† برنامه وفاداری برای Ł…Ų“ŲŖŲ±ŪŒ {} پیدا Ų“ŲÆ. لطفا به صورت دستی انتخاب Ś©Ł†ŪŒŲÆ, +Multiple Warehouse Accounts,Ś†Ł†ŲÆŪŒŁ† Ų­Ų³Ų§ŲØ انبار, +Multiple items cannot be marked as finished item,چند Ł…ŁˆŲ±ŲÆ Ų±Ų§ Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł† به Ų¹Ł†ŁˆŲ§Ł† Ł…ŁˆŲ±ŲÆ ŲŖŁ…Ų§Ł… ؓده علامت گذاری کرد, +Must be a publicly accessible Google Sheets URL and adding Bank Account column is necessary for importing via Google Sheets,باید یک URL Ś©Ų§Ų±ŲØŲ±ŚÆā€ŒŁ†ŚÆŲ§Ų± Google برای Ų¹Ł…ŁˆŁ… قابل دسترسی ŲØŲ§Ų“ŲÆ و Ų§ŁŲ²ŁˆŲÆŁ† Ų³ŲŖŁˆŁ† Ų­Ų³Ų§ŲØ ŲØŲ§Ł†Ś©ŪŒ برای ŲÆŲ±ŁˆŁ†ā€ŒŲØŁŲ±ŲÆ Ų§Ų² Ų·Ų±ŪŒŁ‚ Google Sheets ضروری Ų§Ų³ŲŖ., +Named Place,مکان Ł†Ų§Ł…ŚÆŲ°Ų§Ų±ŪŒ ؓده, +Naming Series and Price Defaults,نام گذاری سری ها و پیؓ فرض Ł‡Ų§ŪŒ Ł‚ŪŒŁ…ŲŖ, +Net total calculation precision loss,خالص Ų§Ų² ŲÆŲ³ŲŖ دادن دقت محاسبه کل, +New Balance In Account Currency,ŲŖŲ±Ų§Ų² جدید به Ų§Ų±Ų² Ų­Ų³Ų§ŲØ, +New Event,رویداد جدید, +New Note,یادداؓت جدید, +New Task,ŁˆŲøŪŒŁŁ‡ جدید, +New Version,نسخه جدید, +Newsletter,خبرنامه, +No Answer,ŲØŲÆŁˆŁ† پاسخ, +No Customers found with selected options.,Ł‡ŪŒŚ† Ł…Ų“ŲŖŲ±ŪŒ ŲØŲ§ ŚÆŲ²ŪŒŁ†Ł‡ Ł‡Ų§ŪŒ انتخاب ؓده یافت نؓد., +No Items selected for transfer.,Ł‡ŪŒŚ† Ł…ŁˆŲ±ŲÆŪŒ برای انتقال انتخاب نؓده Ų§Ų³ŲŖ., +No Matching Bank Transactions Found,Ł‡ŪŒŚ† تراکنؓ ŲØŲ§Ł†Ś©ŪŒ Ł…Ł†Ų·ŲØŁ‚ŪŒ یافت نؓد, +No Notes,ŲØŲÆŁˆŁ† یادداؓت, +No Outstanding Invoices found for this party,Ł‡ŪŒŚ† صورتحساب Ł…Ų¹ŁˆŁ‚ŪŒ برای Ų§ŪŒŁ† طرف یافت نؓد, +No POS Profile found. Please create a New POS Profile first,Ł‡ŪŒŚ† Ł†Ł…Ų§ŪŒŁ‡ POS یافت نؓد. لطفا Ų§ŲØŲŖŲÆŲ§ یک Ł†Ł…Ų§ŪŒŁ‡ POS جدید ایجاد Ś©Ł†ŪŒŲÆ, +No Records for these settings.,Ł‡ŪŒŚ† رکوردی برای Ų§ŪŒŁ† ŲŖŁ†ŲøŪŒŁ…Ų§ŲŖ وجود ندارد., +No Stock Available Currently,Ł…ŁˆŲ¬ŁˆŲÆŪŒ ŲÆŲ± Ų­Ų§Ł„ Ų­Ų§Ų¶Ų± Ł…ŁˆŲ¬ŁˆŲÆ Ł†ŪŒŲ³ŲŖ, +No Summary,ŲØŲÆŁˆŁ† خلاصه, +No Tax Withholding data found for the current posting date.,Ł‡ŪŒŚ† داده کسر Ł…Ų§Ł„ŪŒŲ§ŲŖ برای تاریخ Ų§Ų±Ų³Ų§Ł„ ŁŲ¹Ł„ŪŒ یافت نؓد., +No Terms,ŲØŲÆŁˆŁ† ؓرایط, +No Unreconciled Invoices and Payments found for this party and account,Ł‡ŪŒŚ† فاکتور و پرداخت Ł†Ų§Ų³Ų§Ų²ŚÆŲ§Ų±ŪŒ برای Ų§ŪŒŁ† طرف و Ų­Ų³Ų§ŲØ پیدا نؓد, +No Unreconciled Payments found for this party,Ł‡ŪŒŚ† پرداخت Ł†Ų§Ų³Ų§Ų²ŚÆŲ§Ų±ŪŒ برای Ų§ŪŒŁ† طرف یافت نؓد, +No Work Orders were created,Ł‡ŪŒŚ† دستور کار ایجاد نؓد, +No additional fields available,Ł‡ŪŒŚ† ŁŪŒŁ„ŲÆ اضافی ŲÆŲ± ŲÆŲ³ŲŖŲ±Ų³ Ł†ŪŒŲ³ŲŖ, +No billing email found for customer: {0},Ł‡ŪŒŚ† Ų§ŪŒŁ…ŪŒŁ„ صورتحساب برای Ł…Ų“ŲŖŲ±ŪŒ پیدا نؓد: {0}, +No data found. Seems like you uploaded a blank file,داده ای یافت نؓد. به نظر Ł…ŪŒ Ų±Ų³ŲÆ Ų“Ł…Ų§ یک ŁŲ§ŪŒŁ„ Ų®Ų§Ł„ŪŒ Ų¢Ł¾Ł„ŁˆŲÆ کرده اید, +No employee was scheduled for call popup,Ł‡ŪŒŚ† Ś©Ų§Ų±Ł…Ł†ŲÆŪŒ برای ŁŲ±Ų§Ų®ŁˆŲ§Ł†ŪŒ برنامه ریزی نؓده بود, +No failed logs,Ł‡ŪŒŚ† لاگ Ł†Ų§Ł…ŁˆŁŁ‚ŪŒ Ł†ŪŒŲ³ŲŖ, +No item available for transfer.,Ł‡ŪŒŚ† Ų¢ŪŒŲŖŁ…ŪŒ برای انتقال Ł…ŁˆŲ¬ŁˆŲÆ Ł†ŪŒŲ³ŲŖ., +No items are available in sales orders {0} for production,Ł‡ŪŒŚ† Ł…ŁˆŲ±ŲÆŪŒ ŲÆŲ± Ų³ŁŲ§Ų±ā€ŒŲ“ā€ŒŁ‡Ų§ŪŒ فروؓ {0} برای ŲŖŁˆŁ„ŪŒŲÆ Ł…ŁˆŲ¬ŁˆŲÆ Ł†ŪŒŲ³ŲŖ, +No items are available in the sales order {0} for production,Ł‡ŪŒŚ† Ł…ŁˆŲ±ŲÆŪŒ ŲÆŲ± سفارؓ فروؓ {0} برای ŲŖŁˆŁ„ŪŒŲÆ Ł…ŁˆŲ¬ŁˆŲÆ Ł†ŪŒŲ³ŲŖ, +No matches occurred via auto reconciliation,Ł‡ŪŒŚ† Ł‡Ł…Ų®ŁˆŲ§Ł†ŪŒ ای Ų§Ų² Ų·Ų±ŪŒŁ‚ ŲŖŲ·ŲØŪŒŁ‚ خودکار Ų±Ų® نداد, +No more children on Left,دیگر Ł‡ŪŒŚ† ŁŲ±Ų²Ł†ŲÆŪŒ ŲÆŲ± سمت چپ وجود ندارد, +No more children on Right,دیگر Ł‡ŪŒŚ† ŁŲ±Ų²Ł†ŲÆŪŒ ŲÆŲ± سمت Ų±Ų§Ų³ŲŖ وجود ندارد, +No of Docs,ؓماره اسناد, +No of Employees,ŲŖŲ¹ŲÆŲ§ŲÆ کارمندان, +No of Months (Expense),ŲŖŲ¹ŲÆŲ§ŲÆ ماه ها (Ł‡Ų²ŪŒŁ†Ł‡), +No of Months (Revenue),ŲŖŲ¹ŲÆŲ§ŲÆ ماه ها (درآمد), +No open event,رویداد ŲØŲ§Ų² وجود ندارد, +No open task,Ł‡ŪŒŚ† ŁˆŲøŪŒŁŁ‡ بازی Ł†ŪŒŲ³ŲŖ, +No outstanding {0} found for the {1} {2} which qualify the filters you have specified.,Ł‡ŪŒŚ† {0} Ł…Ų¹ŁˆŁ‚Ų§ŲŖŪŒ برای {1} {2} که واجد ؓرایط ŁŪŒŁ„ŲŖŲ±Ł‡Ų§ŪŒŪŒ Ų§Ų³ŲŖ که Ų“Ł…Ų§ Ł…Ų“Ų®Ųµ کرده اید، یافت نؓد., +No primary email found for customer: {0},Ų§ŪŒŁ…ŪŒŁ„ Ų§ŲµŁ„ŪŒ برای Ł…Ų“ŲŖŲ±ŪŒ پیدا نؓد: {0}, +No records found in Allocation table,Ł‡ŪŒŚ† رکوردی ŲÆŲ± Ų¬ŲÆŁˆŁ„ تخصیص یافت نؓد, +No records found in the Invoices table,Ł‡ŪŒŚ† رکوردی ŲÆŲ± Ų¬ŲÆŁˆŁ„ ŁŲ§Ś©ŲŖŁˆŲ±Ł‡Ų§ یافت نؓد, +No records found in the Payments table,Ł‡ŪŒŚ† رکوردی ŲÆŲ± Ų¬ŲÆŁˆŁ„ پرداخت ها یافت نؓد, +No stock transactions can be created or modified before this date.,Ł‡ŪŒŚ† تراکنؓ Ł…ŁˆŲ¬ŁˆŲÆŪŒŪŒ Ų±Ų§ Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł† قبل Ų§Ų² Ų§ŪŒŁ† تاریخ ایجاد یا تغییر ŲÆŲ§ŲÆ., +No {0} Accounts found for this company.,Ł‡ŪŒŚ† Ų­Ų³Ų§ŲØ {0} برای Ų§ŪŒŁ† ؓرکت یافت نؓد., +No.,ؓماره, +No. of Employees,ŲŖŲ¹ŲÆŲ§ŲÆ کارمندان, +No. of parallel job cards which can be allowed on this workstation. Example: 2 would mean this workstation can process production for two Work Orders at a time.,ŲŖŲ¹ŲÆŲ§ŲÆ کارت Ś©Ų§Ų±Ł‡Ų§ŪŒ Ł…ŁˆŲ§Ų²ŪŒ که Ł…ŪŒ ŲŖŁˆŲ§Ł†Ł†ŲÆ ŲÆŲ± Ų§ŪŒŁ† Ų§ŪŒŲ³ŲŖŚÆŲ§Ł‡ کاری Ł…Ų¬Ų§Ų² باؓند. Ł…Ų«Ų§Ł„: 2 به Ų§ŪŒŁ† Ł…Ų¹Ł†ŪŒ Ų§Ų³ŲŖ که Ų§ŪŒŁ† Ų§ŪŒŲ³ŲŖŚÆŲ§Ł‡ کاری Ł…ŪŒ ŲŖŁˆŲ§Ł†ŲÆ ŲŖŁˆŁ„ŪŒŲÆ Ų±Ų§ برای دو دستور کار ŲÆŲ± یک زمان پردازؓ کند., +Note: Automatic log deletion only applies to logs of type Update Cost,ŲŖŁˆŲ¬Ł‡: حذف خودکار لاگ فقط برای ŚÆŲ²Ų§Ų±Ų“ā€ŒŁ‡Ų§ŪŒŪŒ Ų§Ų² Ł†ŁˆŲ¹ ŲØŁ‡ā€ŒŲ±ŁˆŲ²Ų±Ų³Ų§Ł†ŪŒ Ł‡Ų²ŪŒŁ†Ł‡ اعمال Ł…ŪŒā€ŒŲ“ŁˆŲÆ, +"Note: To merge the items, create a separate Stock Reconciliation for the old item {0}",ŲŖŁˆŲ¬Ł‡: برای Ų§ŲÆŲŗŲ§Ł… Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ŲŒ یک ŲŖŲ·ŲØŪŒŁ‚ Ł…ŁˆŲ¬ŁˆŲÆŪŒ جداگانه برای Ų¢ŪŒŲŖŁ… Ł‚ŲÆŪŒŁ…ŪŒ {0} ایجاد Ś©Ł†ŪŒŲÆ, +Notes HTML,یادداؓت Ł‡Ų§ŪŒ HTML, +Notification,اعلان, +Notification Settings,ŲŖŁ†ŲøŪŒŁ…Ų§ŲŖ اعلان, +Notify Reposting Error to Role,خطای Ų§Ų±Ų³Ų§Ł„ Ł…Ų¬ŲÆŲÆ به نقؓ Ų±Ų§ اطلاع ŲÆŁ‡ŪŒŲÆ, +Number of Days,ŲŖŲ¹ŲÆŲ§ŲÆ Ų±ŁˆŲ²Ł‡Ų§, +Numeric,عددی, +Numeric Inspection,بازرسی عددی, +Off,Ų®Ų§Ł…ŁˆŲ“, +Offsetting Account,ŲŖŲ³ŁˆŪŒŁ‡ Ų­Ų³Ų§ŲØ, +Offsetting for Accounting Dimension,جبران برای ŲØŲ¹ŲÆ حسابداری, +On Paid Amount,ŲØŲ§ مبلغ پرداخت ؓده, +On This Date,ŲÆŲ± Ų§ŪŒŁ† تاریخ, +On Track,ŲÆŲ± Ł…Ų³ŪŒŲ±, +"On expanding a row in the Items to Manufacture table, you'll see an option to 'Include Exploded Items'. Ticking this includes raw materials of the sub-assembly items in the production process.","ŲØŲ§ ŚÆŲ³ŲŖŲ±Ų“ یک ردیف ŲÆŲ± Ų¬ŲÆŁˆŁ„ Items to Manufacture، ŚÆŲ²ŪŒŁ†Ł‡ ای برای ""Ų“Ų§Ł…Ł„ Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ŪŒ گسترده ؓده"" Ų±Ų§ مؓاهده Ų®ŁˆŲ§Ł‡ŪŒŲÆ کرد. تیک زدن Ų§ŪŒŁ† Ų“Ų§Ł…Ł„ Ł…ŁˆŲ§ŲÆ Ų§ŁˆŁ„ŪŒŁ‡ Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ŪŒ زیر Ł…ŁˆŁ†ŲŖŲ§Ś˜ ŲÆŲ± ŁŲ±Ų¢ŪŒŁ†ŲÆ ŲŖŁˆŁ„ŪŒŲÆ Ł…ŪŒ ؓود.", +Once the Work Order is Closed. It can't be resumed.,Ł‡Ł†ŚÆŲ§Ł…ŪŒ که دستور کار بسته Ų“ŲÆ. Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł† آن Ų±Ų§ Ų§Ų² Ų³Ų± گرفت., +Only CSV and Excel files can be used to for importing data. Please check the file format you are trying to upload,برای ŲÆŲ±ŁˆŁ†ā€ŒŲØŁŲ±ŲÆ داده ها فقط Ł…ŪŒ ŲŖŁˆŲ§Ł† Ų§Ų² ŁŲ§ŪŒŁ„ Ł‡Ų§ŪŒ CSV و Excel استفاده کرد. لطفاً فرمت ŁŲ§ŪŒŁ„ŪŒ Ų±Ų§ که Ł…ŪŒā€ŒŲ®ŁˆŲ§Ł‡ŪŒŲÆ Ų¢Ł¾Ł„ŁˆŲÆ Ś©Ł†ŪŒŲÆ بررسی Ś©Ł†ŪŒŲÆ, +Only Deduct Tax On Excess Amount , فقط Ų§Ų² مقدار Ł…Ų§Ų²Ų§ŲÆ Ł…Ų§Ł„ŪŒŲ§ŲŖ کسر Ś©Ł†ŪŒŲÆ, +Only Include Allocated Payments,فقط Ų“Ų§Ł…Ł„ پرداخت Ł‡Ų§ŪŒ Ų§Ų®ŲŖŲµŲ§Ųµ داده ؓده Ų§Ų³ŲŖ, +Only Parent can be of type {0},فقط ŁˆŲ§Ł„ŲÆ Ł…ŪŒ ŲŖŁˆŲ§Ł†Ł†ŲÆ Ų§Ų² Ł†ŁˆŲ¹ {0} باؓند, +Only existing assets,فقط دارایی Ł‡Ų§ŪŒ Ł…ŁˆŲ¬ŁˆŲÆ, +"Only one Subcontracting Order can be created against a Purchase Order, cancel the existing Subcontracting Order to create a new one.",فقط یک سفارؓ Ł¾ŪŒŁ…Ų§Ł†Ś©Ų§Ų±ŪŒ فرعی Ų±Ų§ Ł…ŪŒ ŲŖŁˆŲ§Ł† ŲÆŲ± مقابل یک سفارؓ خرید ایجاد کرد، برای ایجاد یک سفارؓ جدید، سفارؓ Ł¾ŪŒŁ…Ų§Ł†Ś©Ų§Ų±ŪŒ فرعی Ł…ŁˆŲ¬ŁˆŲÆ Ų±Ų§ Ł„ŲŗŁˆ Ś©Ł†ŪŒŲÆ., +Only {0} are supported,فقط {0} Ł¾Ų“ŲŖŪŒŲØŲ§Ł†ŪŒ Ł…ŪŒ ؓود, +Open Activities HTML,Activity HTML Ų±Ų§ ŲØŲ§Ų² Ś©Ł†ŪŒŲÆ, +Open Call Log,لاگ ŲŖŁ…Ų§Ų³ Ų±Ų§ ŲØŲ§Ų² Ś©Ł†ŪŒŲÆ, +Open Events,Ų±ŁˆŪŒŲÆŲ§ŲÆŁ‡Ų§ŪŒ ŲØŲ§Ų², +Open Sales Orders,ŲØŲ§Ų² کردن Ų³ŁŲ§Ų±Ų“ā€ŒŁ‡Ų§ŪŒ فروؓ, +Open Task,ŁˆŲøŪŒŁŁ‡ ŲØŲ§Ų², +Open Tasks,وظایف ŲØŲ§Ų², +Opening & Closing,Ų§ŁŲŖŲŖŲ§Ų­ŪŒŁ‡ و Ų§Ų®ŲŖŲŖŲ§Ł…ŪŒŁ‡, +Opening Accumulated Depreciation must be less than or equal to {0},استهلاک انباؓته Ų§ŁŲŖŲŖŲ§Ų­ŪŒŁ‡ باید کمتر یا Ł…Ų³Ų§ŁˆŪŒ ŲØŲ§ {0} ŲØŲ§Ų“ŲÆ., +Opening Entry can not be created after Period Closing Voucher is created.,پس Ų§Ų² ایجاد Ś©ŁˆŁ¾Ł† بسته ؓدن ŲÆŁˆŲ±Ł‡ŲŒ ورودی ŲØŲ§Ų² Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł†ŲÆ ایجاد ؓود., +Opening Purchase Invoices have been created.,ŁŲ§Ś©ŲŖŁˆŲ±Ł‡Ų§ŪŒ خرید Ų§ŁŲŖŲŖŲ§Ų­ŪŒŁ‡ ایجاد ؓده Ų§Ų³ŲŖ., +Opening Sales Invoices have been created.,ŁŲ§Ś©ŲŖŁˆŲ±Ł‡Ų§ŪŒ فروؓ Ų§ŁŲŖŲŖŲ§Ų­ŪŒŁ‡ ایجاد ؓده Ų§Ų³ŲŖ., +Operating Cost Per BOM Quantity,Ł‡Ų²ŪŒŁ†Ł‡ Ų¹Ł…Ł„ŪŒŲ§ŲŖŪŒ به ازای هر مقدار BOM, +Operation time does not depend on quantity to produce,زمان Ų¹Ł…Ł„ŪŒŲ§ŲŖ به مقدار ŲŖŁˆŁ„ŪŒŲÆ بستگی ندارد, +Opportunity Amount (Company Currency),مقدار فرصت (Ų§Ų±Ų² ؓرکت), +Opportunity Owner,ŲµŲ§Ų­ŲØ فرصت, +Opportunity Source,منبع فرصت, +Opportunity Summary by Sales Stage,خلاصه فرصت ŲØŲ± Ų§Ų³Ų§Ų³ مرحله فروؓ, +Opportunity Summary by Sales Stage ,خلاصه فرصت ŲØŲ± Ų§Ų³Ų§Ų³ مرحله فروؓ , +Opportunity Value,Ų§Ų±Ų²Ų“ فرصت, +Order Status,وضعیت سفارؓ, +Other Info,سایر اطلاعات, +Out of stock,ŲŖŁ…Ų§Ł… ؓده, +Over Receipt,بیؓ Ų§Ų² رسید, +Over Receipt/Delivery of {0} {1} ignored for item {2} because you have {3} role.,بیؓ Ų§Ų² رسید/ŲŖŲ­ŁˆŪŒŁ„ {0} {1} برای Ł…ŁˆŲ±ŲÆ {2} Ł†Ų§ŲÆŪŒŲÆŁ‡ گرفته Ų“ŲÆ زیرا Ų“Ł…Ų§ نقؓ {3} Ų±Ų§ دارید., +Over Transfer Allowance,بیؓ Ų§Ų² کمک Ł‡Ų²ŪŒŁ†Ł‡ انتقال, +Overbilling of {0} {1} ignored for item {2} because you have {3} role.,اضافه صورتحساب {0} {1} برای Ł…ŁˆŲ±ŲÆ {2} Ł†Ų§ŲÆŪŒŲÆŁ‡ گرفته Ų“ŲÆ زیرا Ų“Ł…Ų§ نقؓ {3} Ų±Ų§ دارید., +Overbilling of {} ignored because you have {} role.,پرداخت بیؓ Ų§Ų² Ų­ŲÆ {} Ł†Ų§ŲÆŪŒŲÆŁ‡ گرفته Ų“ŲÆ زیرا Ų“Ł…Ų§ نقؓ {} Ų±Ų§ دارید., +Overdue Payment,پرداخت Ł…Ų¹ŁˆŁ‚Ł‡, +Overdue Payments,پرداخت Ł‡Ų§ŪŒ Ł…Ų¹ŁˆŁ‚, +Overdue Tasks,وظایف عقب افتاده, +Overview,بررسی Ų§Ų¬Ł…Ų§Ł„ŪŒ, +PDF Name,نام PDF, +POS Closing Failed,بسته ؓدن POS Ł†Ų§Ł…ŁˆŁŁ‚ بود, +POS Closing failed while running in a background process. You can resolve the {0} and retry the process again.,بسته ؓدن POS هنگام Ų§Ų¬Ų±Ų§ ŲÆŲ± یک ŁŲ±Ų¢ŪŒŁ†ŲÆ Ł¾Ų³ā€ŒŲ²Ł…ŪŒŁ†Ł‡ انجام نؓد. Ł…ŪŒ ŲŖŁˆŲ§Ł†ŪŒŲÆ {0} Ų±Ų§ Ų­Ł„ Ś©Ł†ŪŒŲÆ و ŲÆŁˆŲØŲ§Ų±Ł‡ Ų§ŪŒŁ† ŁŲ±Ų¢ŪŒŁ†ŲÆ Ų±Ų§ امتحان Ś©Ł†ŪŒŲÆ., +POS Invoices will be consolidated in a background process,ŁŲ§Ś©ŲŖŁˆŲ±Ł‡Ų§ŪŒ POS ŲÆŲ± یک ŁŲ±Ų¢ŪŒŁ†ŲÆ پس Ų²Ł…ŪŒŁ†Ł‡ ŲŖŁ„ŁŪŒŁ‚ Ł…ŪŒ Ų“ŁˆŁ†ŲÆ, +POS Invoices will be unconsolidated in a background process,ŁŲ§Ś©ŲŖŁˆŲ±Ł‡Ų§ŪŒ POS ŲÆŲ± یک ŁŲ±Ų¢ŪŒŁ†ŲÆ پس Ų²Ł…ŪŒŁ†Ł‡ ŲŖŲ¬Ł…ŪŒŲ¹ Ł†Ł…ŪŒ Ų“ŁˆŁ†ŲÆ, +POS Profile {} contains Mode of Payment {}. Please remove them to disable this mode.,Ł†Ł…Ų§ŪŒŁ‡ POS {} Ų“Ų§Ł…Ł„ حالت پرداخت {} Ų§Ų³ŲŖ. لطفاً آنها Ų±Ų§ حذف Ś©Ł†ŪŒŲÆ ŲŖŲ§ Ų§ŪŒŁ† حالت ŲŗŪŒŲ±ŁŲ¹Ų§Ł„ ؓود., +POS Search Fields,ŁŪŒŁ„ŲÆŁ‡Ų§ŪŒ جستجوی POS, +POS Setting,ŲŖŁ†ŲøŪŒŁ…Ų§ŲŖ POS, +Package No(s) already in use. Try from Package No {0},ؓماره (Ł‡Ų§ŪŒ) بسته ŲÆŲ± Ų­Ų§Ł„ Ų­Ų§Ų¶Ų± ŲÆŲ± Ų­Ų§Ł„ استفاده Ų§Ų³ŲŖ. Ų§Ų² بسته ؓماره {0} امتحان Ś©Ł†ŪŒŲÆ, +Packaging Slip From Delivery Note,برگه بسته ŲØŁ†ŲÆŪŒ Ų§Ų² یادداؓت ŲŖŲ­ŁˆŪŒŁ„, +Packed Items cannot be transferred internally,اقلام بسته ŲØŁ†ŲÆŪŒ ؓده Ų±Ų§ Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł† به صورت ŲÆŲ§Ų®Ł„ŪŒ منتقل کرد, +Packed Qty,ŲŖŲ¹ŲÆŲ§ŲÆ بسته ŲØŁ†ŲÆŪŒ ؓده, +Page Break After Each SoA,ؓکست صفحه پس Ų§Ų² هر SoA, +Paid Amount After Tax,مبلغ پرداختی پس Ų§Ų² کسر Ł…Ų§Ł„ŪŒŲ§ŲŖ, +Paid Amount After Tax (Company Currency),مبلغ پرداختی پس Ų§Ų² Ł…Ų§Ł„ŪŒŲ§ŲŖ (Ų§Ų±Ų² ؓرکت), +Paid From Account Type,پرداخت Ų§Ų² Ł†ŁˆŲ¹ Ų­Ų³Ų§ŲØ, +Paid To Account Type,پرداخت به Ł†ŁˆŲ¹ Ų­Ų³Ų§ŲØ, +Pallets,پالت, +Parameter Group,ŚÆŲ±ŁˆŁ‡ پارامتر, +Parameter Group Name,نام ŚÆŲ±ŁˆŁ‡ پارامتر, +Parcel Template,قالب بسته, +Parcel Template Name,نام قالب بسته, +Parcel weight cannot be 0,ŁˆŲ²Ł† بسته Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł†ŲÆ 0 ŲØŲ§Ų“ŲÆ, +Parcels,بسته ها, +Parent Account Missing,Ų­Ų³Ų§ŲØ ŁˆŲ§Ł„ŲÆŪŒŁ† Ų¬Ų§ افتاده Ų§Ų³ŲŖ, +Parent Document,سند ŁˆŲ§Ł„ŲÆ, +Parent Item {0} must not be a Fixed Asset,Ł…ŁˆŲ±ŲÆ Ų§ŲµŁ„ŪŒ {0} Ł†ŲØŲ§ŪŒŲÆ دارایی Ų«Ų§ŲØŲŖ ŲØŲ§Ų“ŲÆ, +Parent Row No,ؓماره ردیف ŁˆŲ§Ł„ŲÆ, +Parent Task {0} is not a Template Task,ŁˆŲøŪŒŁŁ‡ ŁˆŲ§Ł„ŲÆ {0} یک ŁˆŲøŪŒŁŁ‡ Ų§Ł„ŚÆŁˆ Ł†ŪŒŲ³ŲŖ, +Partial Material Transferred,Ł…ŁˆŲ§ŲÆ جزئی منتقل Ų“ŲÆ, +Partial Stock Reservation,رزرو جزئی Ł…ŁˆŲ¬ŁˆŲÆŪŒ, +Partial Success,Ł…ŁˆŁŁ‚ŪŒŲŖ جزئی, +"Partial stock can be reserved. For example, If you have a Sales Order of 100 units and the Available Stock is 90 units then a Stock Reservation Entry will be created for 90 units. ",Ł…ŁˆŲ¬ŁˆŲÆŪŒ جزئی قابل رزرو Ų§Ų³ŲŖ. به Ų¹Ł†ŁˆŲ§Ł† Ł…Ų«Ų§Ł„ŲŒ Ų§ŚÆŲ± Ų“Ł…Ų§ یک سفارؓ فروؓ 100 واحدی دارید و Ł…ŁˆŲ¬ŁˆŲÆŪŒ ŲÆŲ± ŲÆŲ³ŲŖŲ±Ų³ 90 واحد است، یک ورودی رزرو Ł…ŁˆŲ¬ŁˆŲÆŪŒ برای 90 واحد ایجاد Ł…ŪŒ ؓود. , +Partially Delivered,ŲŖŲ§ حدی ŲŖŲ­ŁˆŪŒŁ„ Ų“ŲÆ, +Partially Reconciled,ŲŖŲ§ حدی ŲŖŲ·ŲØŪŒŁ‚ کرد, +Partially Reserved,ŲŖŲ§ حدی رزرو ؓده Ų§Ų³ŲŖ, +Partly Paid,ŲŖŲ§ حدی پرداخت ؓده, +Partly Paid and Discounted,ŲŖŲ§ حدودی پرداخت ؓده و ŲØŲ§ تخفیف, +Partnership,ؓراکت, +Party Account No. (Bank Statement),ؓماره Ų­Ų³Ų§ŲØ طرف (صورتحساب ŲØŲ§Ł†Ś©ŪŒ), +Party Account {0} currency ({1}) and document currency ({2}) should be same,واحد Ł¾ŁˆŁ„ Ų­Ų³Ų§ŲØ طرف {0} ({1}) و واحد Ł¾ŁˆŁ„ سند ({2}) باید ŪŒŚ©Ų³Ų§Ł† باؓند, +Party IBAN (Bank Statement),طرف IBAN (صورتحساب ŲØŲ§Ł†Ś©ŪŒ), +Party Item Code,کد Ų¢ŪŒŲŖŁ… طرف, +Party Link,Ł„ŪŒŁ†Ś© طرف, +Party Name/Account Holder (Bank Statement),نام طرف / دارنده Ų­Ų³Ų§ŲØ (صورتحساب ŲØŲ§Ł†Ś©ŪŒ), +Party Specific Item,Ł…ŁˆŲ±ŲÆ Ų®Ų§Ųµ طرف, +Party Type and Party is required for Receivable / Payable account {0},Ł†ŁˆŲ¹ طرف و طرف برای Ų­Ų³Ų§ŲØ ŲÆŲ±ŪŒŲ§ŁŲŖŁ†ŪŒ / Ł¾Ų±ŲÆŲ§Ų®ŲŖŁ†ŪŒ {0} لازم Ų§Ų³ŲŖ, +Party can only be one of {0},طرف فقط Ł…ŪŒ ŲŖŁˆŲ§Ł†ŲÆ یکی Ų§Ų² {0} ŲØŲ§Ų“ŲÆ, +Passport Details,Ł…Ų“Ų®ŲµŲ§ŲŖ پاسپورت, +Pause Job,مکث کار, +Paused,مکث کرد, +Payment Amount (Company Currency),مبلغ پرداختی (Ų§Ų±Ų² ؓرکت), +"Payment Entry {0} is linked against Order {1}, check if it should be pulled as advance in this invoice.",Ų«ŲØŲŖ پرداخت {0} ŲØŲ§ سفارؓ {1} Ł…Ų±ŲŖŲØŲ· است، بررسی Ś©Ł†ŪŒŲÆ که آیا باید به Ų¹Ł†ŁˆŲ§Ł† پیؓ پرداخت ŲÆŲ± Ų§ŪŒŁ† فاکتور Ų¢ŁˆŲ±ŲÆŁ‡ ؓود., +Payment Ledger,دفتر کل پرداخت, +Payment Ledger Balance,ŲŖŲ±Ų§Ų² دفتر کل پرداخت, +Payment Ledger Entry,Ų«ŲØŲŖ دفتر پرداخت, +Payment Limit,Ł…Ų­ŲÆŁˆŲÆŪŒŲŖ پرداخت, +Payment Reconciliation Allocation,تخصیص ŲŖŲ·ŲØŪŒŁ‚ پرداخت, +Payment Reconciliation Job: {0} is running for this party. Can't reconcile now.,کار ŲŖŲ·ŲØŪŒŁ‚ پرداخت: {0} برای Ų§ŪŒŁ† طرف Ų§Ų¬Ų±Ų§ Ł…ŪŒ ؓود. الان Ł†Ł…ŪŒŲ“Ł‡ ŲŖŲ·ŲØŪŒŁ‚ کرد, +Payment Reconciliations,ŲŖŲ·ŲØŪŒŁ‚ Ł‡Ų§ŪŒ پرداخت, +Payment Request took too long to respond. Please try requesting for payment again.,پاسخ درخواست پرداخت Ų®ŪŒŁ„ŪŒ Ų·ŁˆŁ„ کؓید. لطفاً ŲÆŁˆŲØŲ§Ų±Ł‡ درخواست پرداخت Ś©Ł†ŪŒŲÆ., +Payment Requests cannot be created against: {0},درخواست Ł‡Ų§ŪŒ پرداخت Ų±Ų§ Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł† ŲÆŲ± مقابل: {0} ایجاد کرد, +Payment Terms Status for Sales Order,وضعیت ؓرایط پرداخت برای سفارؓ فروؓ, +Payment Terms from orders will be fetched into the invoices as is,ؓرایط پرداخت Ų§Ų² Ų³ŁŲ§Ų±Ų“ā€ŒŁ‡Ų§ Ł‡Ł…Ų§Ł†Ų·ŁˆŲ± که هست ŲÆŲ± ŁŲ§Ś©ŲŖŁˆŲ±Ł‡Ų§ Ų¢ŁˆŲ±ŲÆŁ‡ Ł…ŪŒ ؓود, +Payment Unlink Error,خطای Ł„ŲŗŁˆ Ł¾ŪŒŁˆŁ†ŲÆ پرداخت, +Payment of {0} received successfully.,پرداخت {0} ŲØŲ§ Ł…ŁˆŁŁ‚ŪŒŲŖ دریافت Ų“ŲÆ., +Payment of {0} received successfully. Waiting for other requests to complete...,پرداخت {0} ŲØŲ§ Ł…ŁˆŁŁ‚ŪŒŲŖ دریافت Ų“ŲÆ. ŲÆŲ± انتظار ŲŖŚ©Ł…ŪŒŁ„ درخواست Ł‡Ų§ŪŒ دیگر..., +Payment request failed,درخواست پرداخت انجام نؓد, +Payment term {0} not used in {1},Ł…ŲÆŲŖ پرداخت {0} ŲÆŲ± {1} استفاده نؓده Ų§Ų³ŲŖ, +Pending processing,ŲÆŲ± انتظار پردازؓ, +Per Received,هر دریافت ؓده, +Percentage (%),ŲÆŲ±ŲµŲÆ (%), +Percentage you are allowed to order beyond the Blanket Order quantity.,درصدی که Ł…Ų¬Ų§Ų² به سفارؓ آن Ł‡Ų³ŲŖŪŒŲÆ فراتر Ų§Ų² مقدار سفارؓ Ś©Ł„ŪŒ., +Percentage you are allowed to sell beyond the Blanket Order quantity.,درصدی که Ł…Ų¬Ų§Ų² به فروؓ آن Ł‡Ų³ŲŖŪŒŲÆ فراتر Ų§Ų² مقدار سفارؓ Ś©Ł„ŪŒ., +Period Closed,ŲÆŁˆŲ±Ł‡ بسته Ų§Ų³ŲŖ, +Period Closing Settings,ŲŖŁ†ŲøŪŒŁ…Ų§ŲŖ Ų§Ų®ŲŖŲŖŲ§Ł…ŪŒŁ‡ ŲÆŁˆŲ±Ł‡, +Period Details,جزئیات ŲÆŁˆŲ±Ł‡, +Phone Ext.,تلفن خارجی, +Pick List Incomplete,فهرست Ų§Ł†ŲŖŲ®Ų§ŲØŪŒ ناقص Ų§Ų³ŲŖ, +Pick Manually,انتخاب دستی, +Pick Serial / Batch Based On,انتخاب Ų³Ų±ŪŒŲ§Ł„ / دسته ŲØŲ± Ų§Ų³Ų§Ų³, +Pick Serial / Batch No,انتخاب Ų³Ų±ŪŒŲ§Ł„ / ؓماره دسته, +Picked Qty (in Stock UOM),ŲŖŲ¹ŲÆŲ§ŲÆ انتخاب ؓده (ŲÆŲ± انبار UOM), +Pickup,سوار کردن, +Pickup Contact Person,Ų“Ų®Ųµ ŲŖŁ…Ų§Ų³ ŁˆŲ§Ł†ŲŖ, +Pickup Date,تاریخ ŲŖŲ­ŁˆŪŒŁ„, +Pickup Date cannot be before this day,تاریخ ŲŖŲ­ŁˆŪŒŁ„ Ł†Ł…ŪŒā€ŒŲŖŁˆŲ§Ł†ŲÆ قبل Ų§Ų² Ų§ŪŒŁ† روز ŲØŲ§Ų“ŲÆ, +Pickup From,ŁˆŲ§Ł†ŲŖ Ų§Ų², +Pickup To time should be greater than Pickup From time,Pickup To time باید بیؓتر Ų§Ų² Pickup From Time ŲØŲ§Ų“ŲÆ, +Pickup Type,Ł†ŁˆŲ¹ پیکاپ, +Pickup from,ŁˆŲ§Ł†ŲŖ Ų§Ų², +Pickup to,ŁˆŲ§Ł†ŲŖ به, +Pipeline By,Ų®Ų· Ł„ŁˆŁ„Ł‡ توسط, +Plaid Link Failed,Ł¾ŪŒŁˆŁ†ŲÆ Plaid Ł†Ų§Ł…ŁˆŁŁ‚ بود, +Plaid Link Refresh Required,ŲØŲ§Ų²Ų®ŁˆŲ§Ł†ŪŒ Ł¾ŪŒŁˆŁ†ŲÆ Ų“Ų·Ų±Ł†Ų¬ŪŒ Ł…ŁˆŲ±ŲÆ Ł†ŪŒŲ§Ų² Ų§Ų³ŲŖ, +Plaid Link Updated,Ł¾ŪŒŁˆŁ†ŲÆ Ų“Ų·Ų±Ł†Ų¬ŪŒ به روز Ų“ŲÆ, +Plan to Request Qty,برنامه ریزی برای مقدار درخواست, +Please Set Priority,لطفا Ų§ŁˆŁ„ŁˆŪŒŲŖ Ų±Ų§ ŲŖŲ¹ŪŒŪŒŁ† Ś©Ł†ŪŒŲÆ, +Please Specify Account,لطفا Ų­Ų³Ų§ŲØ Ų±Ų§ Ł…Ų“Ų®Ųµ Ś©Ł†ŪŒŲÆ, +Please add 'Supplier' role to user {0}.,"لطفا نقؓ ""ŲŖŲ§Ł…ŪŒŁ† کننده"" Ų±Ų§ به کاربر {0} اضافه Ś©Ł†ŪŒŲÆ.", +Please add Request for Quotation to the sidebar in Portal Settings.,لطفاً درخواست برای پیؓ فاکتور Ų±Ų§ به Ł†ŁˆŲ§Ų± Ś©Ł†Ų§Ų±ŪŒ ŲÆŲ± ŲŖŁ†ŲøŪŒŁ…Ų§ŲŖ Ł¾ŁˆŲ±ŲŖŲ§Ł„ اضافه Ś©Ł†ŪŒŲÆ., +Please add Root Account for - {0},لطفاً Ų­Ų³Ų§ŲØ Ų±ŪŒŲ“Ł‡ برای - {0} اضافه Ś©Ł†ŪŒŲÆ, +Please add atleast one Serial No / Batch No,لطفاً حداقل یک ؓماره Ų³Ų±ŪŒŲ§Ł„ / ؓماره دسته اضافه Ś©Ł†ŪŒŲÆ, +Please add the Bank Account column,لطفا Ų³ŲŖŁˆŁ† Ų­Ų³Ų§ŲØ ŲØŲ§Ł†Ś©ŪŒ Ų±Ų§ اضافه Ś©Ł†ŪŒŲÆ, +Please add the account to root level Company - {0},لطفاً Ų­Ų³Ų§ŲØ Ų±Ų§ به ؓرکت Ų³Ų·Ų­ Ų±ŪŒŲ“Ł‡ اضافه Ś©Ł†ŪŒŲÆ - {0}, +Please add {1} role to user {0}.,لطفاً نقؓ {1} Ų±Ų§ به کاربر {0} اضافه Ś©Ł†ŪŒŲÆ., +Please adjust the qty or edit {0} to proceed.,لطفاً ŲŖŲ¹ŲÆŲ§ŲÆ Ų±Ų§ ŲŖŁ†ŲøŪŒŁ… Ś©Ł†ŪŒŲÆ یا برای ادامه {0} Ų±Ų§ ویرایؓ Ś©Ł†ŪŒŲÆ., +Please attach CSV file,لطفا ŁŲ§ŪŒŁ„ CSV Ų±Ų§ پیوست Ś©Ł†ŪŒŲÆ, +Please cancel and amend the Payment Entry,لطفاً Ų«ŲØŲŖ پرداخت Ų±Ų§ Ł„ŲŗŁˆ و اصلاح Ś©Ł†ŪŒŲÆ, +Please cancel payment entry manually first,لطفاً Ų§ŲØŲŖŲÆŲ§ Ų«ŲØŲŖ پرداخت Ų±Ų§ به صورت دستی Ł„ŲŗŁˆ Ś©Ł†ŪŒŲÆ, +Please cancel related transaction.,لطفا تراکنؓ Ł…Ų±ŲØŁˆŲ·Ł‡ Ų±Ų§ Ł„ŲŗŁˆ Ś©Ł†ŪŒŲÆ., +Please check Process Deferred Accounting {0} and submit manually after resolving errors.,لطفاً Process Deferred Accounting {0} Ų±Ų§ بررسی Ś©Ł†ŪŒŲÆ و پس Ų§Ų² رفع خطاها Ų±Ų§ به صورت دستی Ų§Ų±Ų³Ų§Ł„ Ś©Ł†ŪŒŲÆ., +Please check either with operations or FG Based Operating Cost.,لطفاً ŲØŲ§ Ų¹Ł…Ł„ŪŒŲ§ŲŖ یا Ł‡Ų²ŪŒŁ†Ł‡ Ų¹Ł…Ł„ŪŒŲ§ŲŖŪŒ Ł…ŲØŲŖŁ†ŪŒ ŲØŲ± FG بررسی Ś©Ł†ŪŒŲÆ., +Please check the error message and take necessary actions to fix the error and then restart the reposting again.,لطفاً Ł¾ŪŒŲ§Ł… Ų®Ų·Ų§ Ų±Ų§ بررسی Ś©Ł†ŪŒŲÆ و اقدامات لازم Ų±Ų§ برای رفع Ų®Ų·Ų§ انجام ŲÆŁ‡ŪŒŲÆ و سپس Ų§Ų±Ų³Ų§Ł„ Ł…Ų¬ŲÆŲÆ Ų±Ų§ Ł…Ų¬ŲÆŲÆŲ§Ł‹ Ų±Ų§Ł‡ā€ŒŲ§Ł†ŲÆŲ§Ų²ŪŒ Ś©Ł†ŪŒŲÆ., +Please check your email to confirm the appointment,لطفا Ų§ŪŒŁ…ŪŒŁ„ خود Ų±Ų§ برای تایید قرار ملاقات بررسی Ś©Ł†ŪŒŲÆ, +Please contact any of the following users to extend the credit limits for {0}: {1},لطفاً برای ŲŖŁ…ŲÆŪŒŲÆ Ł…Ų­ŲÆŁˆŲÆŪŒŲŖ Ų§Ų¹ŲŖŲØŲ§Ų± برای {0} ŲØŲ§ هر یک Ų§Ų² کاربران زیر ŲŖŁ…Ų§Ų³ بگیرید: {1}, +Please contact any of the following users to {} this transaction.,لطفاً ŲØŲ§ هر یک Ų§Ų² کاربران زیر برای {} Ų§ŪŒŁ† تراکنؓ ŲŖŁ…Ų§Ų³ بگیرید., +Please contact your administrator to extend the credit limits for {0}.,لطفاً برای ŲŖŁ…ŲÆŪŒŲÆ Ł…Ų­ŲÆŁˆŲÆŪŒŲŖ Ų§Ų¹ŲŖŲØŲ§Ų± برای {0} ŲØŲ§ سرپرست خود ŲŖŁ…Ų§Ų³ بگیرید., +Please create Landed Cost Vouchers against Invoices that have 'Update Stock' enabled.,لطفاً ŲÆŲ± ŲØŲ±Ų§ŲØŲ± ŁŲ§Ś©ŲŖŁˆŲ±Ł‡Ų§ŪŒŪŒ که Ā«ŲØŁ‡ā€ŒŲ±ŁˆŲ²Ų±Ų³Ų§Ł†ŪŒ Ł…ŁˆŲ¬ŁˆŲÆŪŒĀ» Ų±Ų§ فعال Ś©Ų±ŲÆŁ‡ā€ŒŲ§Ł†ŲÆŲŒ Ś©ŁˆŁ¾Ł†ā€ŒŁ‡Ų§ŪŒ Ł‡Ų²ŪŒŁ†Ł‡ ŲŖŁ…Ų§Ł… ؓده ŲŖŲ§ ŲÆŲ±ŲØ انبار ایجاد Ś©Ł†ŪŒŲÆ., +Please create a new Accounting Dimension if required.,لطفاً ŲÆŲ± صورت Ł†ŪŒŲ§Ų² یک ŲØŲ¹ŲÆ حسابداری جدید ایجاد Ś©Ł†ŪŒŲÆ., +Please create purchase from internal sale or delivery document itself,لطفا خرید Ų±Ų§ Ų§Ų² فروؓ ŲÆŲ§Ų®Ł„ŪŒ یا سند ŲŖŲ­ŁˆŪŒŁ„ خود ایجاد Ś©Ł†ŪŒŲÆ, +"Please delete Product Bundle {0}, before merging {1} into {2}",لطفاً قبل Ų§Ų² Ų§ŲÆŲŗŲ§Ł… {1} ŲÆŲ± {2}، باندل Ł…Ų­ŲµŁˆŁ„ {0} Ų±Ų§ حذف Ś©Ł†ŪŒŲÆ, +Please do not book expense of multiple assets against one single Asset.,لطفا Ł‡Ų²ŪŒŁ†Ł‡ چند دارایی Ų±Ų§ ŲÆŲ± مقابل یک دارایی Ų«ŲØŲŖ Ł†Ś©Ł†ŪŒŲÆ., +Please enable only if the understand the effects of enabling this.,لطفاً فقط ŲÆŲ± صورتی فعال Ś©Ł†ŪŒŲÆ که تأثیرات فعال کردن آن Ų±Ų§ درک Ś©Ł†ŪŒŲÆ., +Please enable {0} in the {1}.,لطفاً {0} Ų±Ų§ ŲÆŲ± {1} فعال Ś©Ł†ŪŒŲÆ., +Please enable {} in {} to allow same item in multiple rows,لطفاً {} Ų±Ų§ ŲÆŲ± {} فعال Ś©Ł†ŪŒŲÆ ŲŖŲ§ یک Ł…ŁˆŲ±ŲÆ ŲÆŲ± Ś†Ł†ŲÆŪŒŁ† ردیف Ł…Ų¬Ų§Ų² ŲØŲ§Ų“ŲÆ, +Please ensure {} account is a Balance Sheet account.,لطفاً مطمئن ؓوید که Ų­Ų³Ų§ŲØ {} یک Ų­Ų³Ų§ŲØ ترازنامه Ų§Ų³ŲŖ., +Please ensure {} account {} is a Receivable account.,لطفاً مطمئن ؓوید که {} Ų­Ų³Ų§ŲØ {} یک Ų­Ų³Ų§ŲØ ŲÆŲ±ŪŒŲ§ŁŲŖŁ†ŪŒ Ų§Ų³ŲŖ., +Please enter Root Type for account- {0},لطفاً Ł†ŁˆŲ¹ Ų±ŪŒŲ“Ł‡ Ų±Ų§ برای Ų­Ų³Ų§ŲØ وارد Ś©Ł†ŪŒŲÆ- {0}, +Please enter Serial Nos,لطفا ؓماره Ł‡Ų§ŪŒ Ų³Ų±ŪŒŲ§Ł„ Ų±Ų§ وارد Ś©Ł†ŪŒŲÆ, +Please enter Shipment Parcel information,لطفا اطلاعات بسته Ų­Ł…Ł„ و نقل Ų±Ų§ وارد Ś©Ł†ŪŒŲÆ, +Please enter Stock Items consumed during the Repair.,لطفاً Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ŪŒ Ł…ŁˆŲ¬ŁˆŲÆŪŒ مصرف ؓده ŲÆŲ± Ų·ŁˆŁ„ ŲŖŲ¹Ł…ŪŒŲ± Ų±Ų§ وارد Ś©Ł†ŪŒŲÆ., +Please enter mobile number first.,لطفا Ų§ŲØŲŖŲÆŲ§ ؓماره Ł…ŁˆŲØŲ§ŪŒŁ„ Ų±Ų§ وارد Ś©Ł†ŪŒŲÆ, +Please enter quantity for item {0},لطفاً مقدار Ł…ŁˆŲ±ŲÆ {0} Ų±Ų§ وارد Ś©Ł†ŪŒŲÆ, +Please enter serial nos,لطفا ؓماره Ų³Ų±ŪŒŲ§Ł„ Ų±Ų§ وارد Ś©Ł†ŪŒŲÆ, +"Please first set Last Name, Email and Phone for the user",لطفا Ų§ŲØŲŖŲÆŲ§ نام Ų®Ų§Ł†ŁˆŲ§ŲÆŚÆŪŒŲŒ Ų§ŪŒŁ…ŪŒŁ„ و تلفن Ų±Ų§ برای کاربر ŲŖŁ†ŲøŪŒŁ… Ś©Ł†ŪŒŲÆ, +Please fix overlapping time slots for {0},لطفاً بازه Ł‡Ų§ŪŒ Ų²Ł…Ų§Ł†ŪŒ Ł‡Ł…Ł¾ŁˆŲ“Ų§Ł†ŪŒ Ų±Ų§ برای {0} اصلاح Ś©Ł†ŪŒŲÆ, +Please fix overlapping time slots for {0}.,لطفاً بازه Ł‡Ų§ŪŒ Ų²Ł…Ų§Ł†ŪŒ Ł‡Ł…Ł¾ŁˆŲ“Ų§Ł†ŪŒ Ų±Ų§ برای {0} برطرف Ś©Ł†ŪŒŲÆ., +Please import accounts against parent company or enable {} in company master.,لطفاً Ų­Ų³Ų§ŲØā€ŒŁ‡Ų§ Ų±Ų§ ŲÆŲ± مقابل ؓرکت Ł…Ų§ŲÆŲ± وارد Ś©Ł†ŪŒŲÆ یا {} Ų±Ų§ ŲÆŲ± ؓرکت Ų§ŲµŁ„ŪŒ فعال Ś©Ł†ŪŒŲÆ., +Please make sure the file you are using has 'Parent Account' column present in the header.,لطفاً مطمئن ؓوید که ŁŲ§ŪŒŁ„ŪŒ که استفاده Ł…ŪŒā€ŒŚ©Ł†ŪŒŲÆ دارای Ų³ŲŖŁˆŁ† «حساب ŁˆŲ§Ł„ŲÆĀ» ŲÆŲ± سرصفحه ŲØŲ§Ų“ŲÆ., +Please mention 'Weight UOM' along with Weight.,"لطفا ""ŁˆŲ²Ł† UOM"" Ų±Ų§ همراه ŲØŲ§ ŁˆŲ²Ł† ذکر Ś©Ł†ŪŒŲÆ.", +Please mention the Current and New BOM for replacement.,لطفاً BOM ŁŲ¹Ł„ŪŒ و جدید Ų±Ų§ برای Ų¬Ų§ŪŒŚÆŲ²ŪŒŁ†ŪŒ ذکر Ś©Ł†ŪŒŲÆ., +Please rectify and try again.,لطفاً اصلاح Ś©Ł†ŪŒŲÆ و ŲÆŁˆŲØŲ§Ų±Ł‡ امتحان Ś©Ł†ŪŒŲÆ., +Please refresh or reset the Plaid linking of the Bank {}.,لطفاً Ł¾ŪŒŁˆŁ†ŲÆ Plaid بانک {} Ų±Ų§ ŲØŲ§Ų²Ų®ŁˆŲ§Ł†ŪŒ یا ŲØŲ§Ų²Ł†Ų“Ų§Ł†ŪŒ Ś©Ł†ŪŒŲÆ., +Please save before proceeding.,لطفا قبل Ų§Ų² ادامه Ų°Ų®ŪŒŲ±Ł‡ Ś©Ł†ŪŒŲÆ., +Please select Bank Account,لطفا Ų­Ų³Ų§ŲØ ŲØŲ§Ł†Ś©ŪŒ Ų±Ų§ انتخاب Ś©Ł†ŪŒŲÆ, +Please select Finished Good Item for Service Item {0},لطفاً Ų¢ŪŒŲŖŁ… Ś©Ų§Ł„Ų§ŪŒ ŲŖŁ…Ų§Ł… ؓده Ų±Ų§ برای Ų¢ŪŒŲŖŁ… سرویس {0} انتخاب Ś©Ł†ŪŒŲÆ, +Please select Serial/Batch Nos to reserve or change Reservation Based On to Qty.,لطفاً Ų“Ł…Ų§Ų±Ł‡ā€ŒŁ‡Ų§ŪŒ Ų³Ų±ŪŒŲ§Ł„/دسته Ų±Ų§ برای رزرو انتخاب Ś©Ł†ŪŒŲÆ یا رزرو ŲØŲ±Ų§Ų³Ų§Ų³ ŲŖŲ¹ŲÆŲ§ŲÆ Ų±Ų§ تغییر ŲÆŁ‡ŪŒŲÆ., +Please select Subcontracting Order instead of Purchase Order {0},لطفاً به جای سفارؓ خرید، سفارؓ قرارداد فرعی Ų±Ų§ انتخاب Ś©Ł†ŪŒŲÆ {0}, +Please select Unrealized Profit / Loss account or add default Unrealized Profit / Loss account account for company {0},لطفاً Ų­Ų³Ų§ŲØ سود / Ų²ŪŒŲ§Ł† تحقق Ł†ŪŒŲ§ŁŲŖŁ‡ Ų±Ų§ انتخاب Ś©Ł†ŪŒŲÆ یا Ų­Ų³Ų§ŲØ سود / Ų²ŪŒŲ§Ł† پیؓ فرض Ų±Ų§ برای ؓرکت اضافه Ś©Ł†ŪŒŲÆ {0}, +Please select a Subcontracting Purchase Order.,لطفاً سفارؓ خرید Ł¾ŪŒŁ…Ų§Ł†Ś©Ų§Ų±ŪŒ فرعی Ų±Ų§ انتخاب Ś©Ł†ŪŒŲÆ., +Please select a Warehouse,لطفاً یک انبار انتخاب Ś©Ł†ŪŒŲÆ, +Please select a Work Order first.,لطفاً Ų§ŲØŲŖŲÆŲ§ یک دستور کار Ų±Ų§ انتخاب Ś©Ł†ŪŒŲÆ., +Please select a country,لطفا یک کؓور Ų±Ų§ انتخاب Ś©Ł†ŪŒŲÆ, +Please select a customer for fetching payments.,لطفاً یک Ł…Ų“ŲŖŲ±ŪŒ Ų±Ų§ برای واکؓی پرداخت ها انتخاب Ś©Ł†ŪŒŲÆ., +Please select a date,لطفا تاریخ Ų±Ų§ انتخاب Ś©Ł†ŪŒŲÆ, +Please select a date and time,لطفا تاریخ و زمان Ų±Ų§ انتخاب Ś©Ł†ŪŒŲÆ, +Please select a row to create a Reposting Entry,لطفاً یک ردیف برای ایجاد یک ورودی Ų§Ų±Ų³Ų§Ł„ Ł…Ų¬ŲÆŲÆ انتخاب Ś©Ł†ŪŒŲÆ, +Please select a supplier for fetching payments.,لطفاً یک ŲŖŲ§Ł…ŪŒŁ† کننده برای واکؓی پرداخت ها انتخاب Ś©Ł†ŪŒŲÆ., +Please select a valid Purchase Order that has Service Items.,لطفاً یک سفارؓ خرید Ł…Ų¹ŲŖŲØŲ± که دارای Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ŪŒ Ų®ŲÆŁ…Ų§ŲŖŪŒ Ų§Ų³ŲŖ انتخاب Ś©Ł†ŪŒŲÆ., +Please select a valid Purchase Order that is configured for Subcontracting.,لطفاً یک سفارؓ خرید Ł…Ų¹ŲŖŲØŲ± که برای قرارداد فرعی Ł¾ŪŒŚ©Ų±ŲØŁ†ŲÆŪŒ ؓده است، انتخاب Ś©Ł†ŪŒŲÆ., +Please select an item code before setting the warehouse.,لطفاً قبل Ų§Ų² ŲŖŁ†ŲøŪŒŁ… انبار یک کد Ų¢ŪŒŲŖŁ… Ų±Ų§ انتخاب Ś©Ł†ŪŒŲÆ., +Please select items,لطفا Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ Ų±Ų§ انتخاب Ś©Ł†ŪŒŲÆ, +Please select items to unreserve.,لطفا Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ Ų±Ų§ برای Ł„ŲŗŁˆ رزرو انتخاب Ś©Ł†ŪŒŲÆ., +Please select only one row to create a Reposting Entry,لطفاً فقط یک ردیف Ų±Ų§ برای ایجاد یک ورودی Ų§Ų±Ų³Ų§Ł„ Ł…Ų¬ŲÆŲÆ انتخاب Ś©Ł†ŪŒŲÆ, +Please select rows to create Reposting Entries,لطفاً Ų±ŲÆŪŒŁā€ŒŁ‡Ų§ŪŒŪŒ Ų±Ų§ برای ایجاد ŁˆŲ±ŁˆŲÆŪŒā€ŒŁ‡Ų§ŪŒ Ų§Ų±Ų³Ų§Ł„ Ł…Ų¬ŲÆŲÆ انتخاب Ś©Ł†ŪŒŲÆ, +Please select the required filters,لطفا ŁŪŒŁ„ŲŖŲ±Ł‡Ų§ŪŒ Ł…ŁˆŲ±ŲÆ Ł†ŪŒŲ§Ų² Ų±Ų§ انتخاب Ś©Ł†ŪŒŲÆ, +Please select valid document type.,لطفا Ł†ŁˆŲ¹ سند Ł…Ų¹ŲŖŲØŲ± Ų±Ų§ انتخاب Ś©Ł†ŪŒŲÆ., +Please set Account,لطفا Ų­Ų³Ų§ŲØ Ų±Ų§ ŲŖŁ†ŲøŪŒŁ… Ś©Ł†ŪŒŲÆ, +Please set Accounting Dimension {} in {},لطفاً ŲØŲ¹ŲÆ حسابداری {} Ų±Ų§ ŲÆŲ± {} ŲŖŁ†ŲøŪŒŁ… Ś©Ł†ŪŒŲÆ, +Please set Email/Phone for the contact,لطفا Ų§ŪŒŁ…ŪŒŁ„/تلفن Ų±Ų§ برای Ł…Ų®Ų§Ų·ŲØ ŲŖŁ†ŲøŪŒŁ… Ś©Ł†ŪŒŲÆ, +Please set Fiscal Code for the customer '%s',"لطفاً کد Ł…Ų§Ł„ŪŒ Ų±Ų§ برای Ł…Ų“ŲŖŲ±ŪŒ ""%s"" ŲŖŁ†ŲøŪŒŁ… Ś©Ł†ŪŒŲÆ", +Please set Fixed Asset Account in {} against {}.,لطفاً Ų­Ų³Ų§ŲØ دارایی Ų«Ų§ŲØŲŖ Ų±Ų§ ŲÆŲ± {} ŲÆŲ± مقابل {} ŲŖŁ†ŲøŪŒŁ… Ś©Ł†ŪŒŲÆ., +Please set Parent Row No for item {0},لطفاً ؓماره ردیف ŁˆŲ§Ł„ŲÆ Ų±Ų§ برای Ų¢ŪŒŲŖŁ… {0} ŲŖŁ†ŲøŪŒŁ… Ś©Ł†ŪŒŲÆ, +Please set Root Type,لطفا Root Type Ų±Ų§ ŲŖŁ†ŲøŪŒŁ… Ś©Ł†ŪŒŲÆ, +Please set Tax ID for the customer '%s',"لطفاً ؓناسه Ł…Ų§Ł„ŪŒŲ§ŲŖŪŒ Ų±Ų§ برای Ł…Ų“ŲŖŲ±ŪŒ ""%s"" ŲŖŁ†ŲøŪŒŁ… Ś©Ł†ŪŒŲÆ", +Please set VAT Accounts in {0},لطفاً Ų­Ų³Ų§ŲØā€ŒŁ‡Ų§ŪŒ VAT Ų±Ų§ ŲÆŲ± {0} ŲŖŁ†ŲøŪŒŁ… Ś©Ł†ŪŒŲÆ, +"Please set Vat Accounts for Company: ""{0}"" in UAE VAT Settings","لطفاً Ų­Ų³Ų§ŲØā€ŒŁ‡Ų§ŪŒ Ł…Ų§Ł„ŪŒŲ§ŲŖ ŲØŲ± Ų§Ų±Ų²Ų“ Ų§ŁŲ²ŁˆŲÆŁ‡ Ų±Ų§ برای ؓرکت ŲŖŁ†ŲøŪŒŁ… Ś©Ł†ŪŒŲÆ: ""{0}"" ŲÆŲ± ŲŖŁ†ŲøŪŒŁ…Ų§ŲŖ Ł…Ų§Ł„ŪŒŲ§ŲŖ ŲØŲ± Ų§Ų±Ų²Ų“ Ų§ŁŲ²ŁˆŲÆŁ‡ Ų§Ł…Ų§Ų±Ų§ŲŖ", +Please set a Cost Center for the Asset or set an Asset Depreciation Cost Center for the Company {},لطفاً یک مرکز Ł‡Ų²ŪŒŁ†Ł‡ برای دارایی یا یک مرکز Ł‡Ų²ŪŒŁ†Ł‡ استهلاک دارایی برای ؓرکت ŲŖŁ†ŲøŪŒŁ… Ś©Ł†ŪŒŲÆ {}, +Please set a default Holiday List for Company {0},لطفاً یک فهرست ŲŖŲ¹Ų·ŪŒŁ„Ų§ŲŖ Ł¾ŪŒŲ“ā€ŒŁŲ±Ų¶ برای ؓرکت {0} ŲŖŁ†ŲøŪŒŁ… Ś©Ł†ŪŒŲÆ, +Please set an Address on the Company '%s',"لطفاً یک Ų¢ŲÆŲ±Ų³ ŲÆŲ± ؓرکت ""%s"" ŲŖŁ†ŲøŪŒŁ… Ś©Ł†ŪŒŲÆ", +Please set an Expense Account in the Items table,لطفاً یک Ų­Ų³Ų§ŲØ Ł‡Ų²ŪŒŁ†Ł‡ ŲÆŲ± Ų¬ŲÆŁˆŁ„ Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ ŲŖŁ†ŲøŪŒŁ… Ś©Ł†ŪŒŲÆ, +Please set default Exchange Gain/Loss Account in Company {},لطفاً Ų­Ų³Ų§ŲØ سود/Ų²ŪŒŲ§Ł† مبادله Ł¾ŪŒŲ“ā€ŒŁŲ±Ų¶ Ų±Ų§ ŲÆŲ± ؓرکت ŲŖŁ†ŲøŪŒŁ… Ś©Ł†ŪŒŲÆ {}, +Please set default Expense Account in Company {0},لطفاً Ų­Ų³Ų§ŲØ Ł‡Ų²ŪŒŁ†Ł‡ Ł¾ŪŒŲ“ā€ŒŁŲ±Ų¶ Ų±Ų§ ŲÆŲ± ؓرکت {0} ŲŖŁ†ŲøŪŒŁ… Ś©Ł†ŪŒŲÆ, +Please set default cost of goods sold account in company {0} for booking rounding gain and loss during stock transfer,لطفاً Ų­Ų³Ų§ŲØ Ł‡Ų²ŪŒŁ†Ł‡ Ł¾ŪŒŲ“ā€ŒŁŲ±Ų¶ Ś©Ų§Ł„Ų§Ł‡Ų§ŪŒ ŁŲ±ŁˆŲ®ŲŖŁ‡ā€ŒŲ“ŲÆŁ‡ ŲÆŲ± ؓرکت {0} Ų±Ų§ برای رزرو سود و Ų²ŪŒŲ§Ł† ŲÆŲ± Ų­ŪŒŁ† انتقال Ł…ŁˆŲ¬ŁˆŲÆŪŒ ŲŖŁ†ŲøŪŒŁ… Ś©Ł†ŪŒŲÆ, +Please set filters,لطفا ŁŪŒŁ„ŲŖŲ±Ł‡Ų§ Ų±Ų§ ŲŖŁ†ŲøŪŒŁ… Ś©Ł†ŪŒŲÆ, +Please set one of the following:,لطفا یکی Ų§Ų² Ł…ŁˆŲ§Ų±ŲÆ زیر Ų±Ų§ ŲŖŁ†ŲøŪŒŁ… Ś©Ł†ŪŒŲÆ:, +Please set the cost center field in {0} or setup a default Cost Center for the Company.,لطفاً ŁŪŒŁ„ŲÆ مرکز Ł‡Ų²ŪŒŁ†Ł‡ Ų±Ų§ ŲÆŲ± {0} ŲŖŁ†ŲøŪŒŁ… Ś©Ł†ŪŒŲÆ یا یک مرکز Ł‡Ų²ŪŒŁ†Ł‡ Ł¾ŪŒŲ“ā€ŒŁŲ±Ų¶ برای ؓرکت ŲŖŁ†ŲøŪŒŁ… Ś©Ł†ŪŒŲÆ., +Please set {0} in BOM Creator {1},لطفاً {0} Ų±Ų§ ŲÆŲ± BOM Creator {1} ŲŖŁ†ŲøŪŒŁ… Ś©Ł†ŪŒŲÆ, +Please share this email with your support team so that they can find and fix the issue.,لطفاً Ų§ŪŒŁ† Ų§ŪŒŁ…ŪŒŁ„ Ų±Ų§ ŲØŲ§ ŲŖŪŒŁ… Ł¾Ų“ŲŖŪŒŲØŲ§Ł†ŪŒ خود به Ų§Ų“ŲŖŲ±Ų§Ś© بگذارید ŲŖŲ§ آنها ŲØŲŖŁˆŲ§Ł†Ł†ŲÆ مؓکل Ų±Ų§ پیدا کرده و برطرف کنند., +Please specify a {0},لطفاً یک {0} Ų±Ų§ Ł…Ų“Ų®Ųµ Ś©Ł†ŪŒŲÆ, +Please try again in an hour.,لطفا یک Ų³Ų§Ų¹ŲŖ دیگر ŲÆŁˆŲØŲ§Ų±Ł‡ امتحان Ś©Ł†ŪŒŲÆ., +Please update Repair Status.,لطفاً وضعیت ŲŖŲ¹Ł…ŪŒŲ± Ų±Ų§ به روز Ś©Ł†ŪŒŲÆ., +Portal User,کاربر Ł¾ŁˆŲ±ŲŖŲ§Ł„, +Portal Users,کاربران Ł¾ŁˆŲ±ŲŖŲ§Ł„, +"Previous Year is not closed, please close it first",Ų³Ų§Ł„ قبل ŲŖŲ¹Ų·ŪŒŁ„ Ł†ŪŒŲ³ŲŖŲŒ لطفا Ų§ŁˆŁ„ آن Ų±Ų§ ŲØŲØŁ†ŲÆŪŒŲÆ, +Price ({0}),Ł‚ŪŒŁ…ŲŖ ({0}), +Price List Defaults,Ł„ŪŒŲ³ŲŖ Ł‚ŪŒŁ…ŲŖ پیؓ فرض, +Price Per Unit ({0}),Ł‚ŪŒŁ…ŲŖ هر واحد ({0}), +Primary Address and Contact,Ų¢ŲÆŲ±Ų³ و Ł…Ų®Ų§Ų·ŲØ Ų§ŲµŁ„ŪŒ, +Primary Contact,Ł…Ų®Ų§Ų·ŲØ Ų§ŲµŁ„ŪŒ, +Primary Party,طرف Ų§ŲµŁ„ŪŒ, +Primary Role,نقؓ Ų§ŲµŁ„ŪŒ, +Print Format Builder,فرمت Ų³Ų§Ų² چاپ, +Print Style,Ų³ŲØŚ© چاپ, +Printing,چاپ, +Priority cannot be lesser than 1.,Ų§ŁˆŁ„ŁˆŪŒŲŖ Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł†ŲÆ کمتر Ų§Ų² 1 ŲØŲ§Ų“ŲÆ., +Priority is mandatory,Ų§ŁˆŁ„ŁˆŪŒŲŖ Ų§Ł„Ų²Ų§Ł…ŪŒ Ų§Ų³ŲŖ, +Probability,Ų§Ų­ŲŖŁ…Ų§Ł„, +Process Loss,Ų§Ų² ŲÆŲ³ŲŖ دادن ŁŲ±Ų¢ŪŒŁ†ŲÆ, +Process Loss Percentage cannot be greater than 100,ŲÆŲ±ŲµŲÆ Ų¶Ų±Ų± ŁŲ±Ų¢ŪŒŁ†ŲÆ Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł†ŲÆ بیؓتر Ų§Ų² 100 ŲØŲ§Ų“ŲÆ, +Process Loss Qty,ŲŖŲ¹ŲÆŲ§ŲÆ Ų§Ų² ŲÆŲ³ŲŖ دادن ŁŲ±Ų¢ŪŒŁ†ŲÆ, +Process Loss Report,ŚÆŲ²Ų§Ų±Ų“ Ų§Ų² ŲÆŲ³ŲŖ دادن ŁŲ±Ų¢ŪŒŁ†ŲÆ, +Process Loss Value,Ų§Ų±Ų²Ų“ Ų§Ų² ŲÆŲ³ŲŖ دادن ŁŲ±Ų¢ŪŒŁ†ŲÆ, +Process Payment Reconciliation,ŁŲ±Ų¢ŪŒŁ†ŲÆ ŲŖŲ·ŲØŪŒŁ‚ پرداخت, +Process Payment Reconciliation Log,ŚÆŲ²Ų§Ų±Ų“ ŲŖŲ·ŲØŪŒŁ‚ پرداخت ŁŲ±Ų¢ŪŒŁ†ŲÆ, +Process Payment Reconciliation Log Allocations,تخصیص ŚÆŲ²Ų§Ų±Ų“ ŲŖŲ·ŲØŪŒŁ‚ پرداخت ŁŲ±Ų¢ŪŒŁ†ŲÆ, +Process Subscription,ŁŲ±Ų¢ŪŒŁ†ŲÆ Ų§Ų“ŲŖŲ±Ų§Ś©, +Processed BOMs,BOM Ł‡Ų§ŪŒ پردازؓ ؓده, +Processing Sales! Please Wait...,ŲÆŲ± Ų­Ų§Ł„ پردازؓ فروؓ! لطفا ŲµŲØŲ± Ś©Ł†ŪŒŲÆ..., +Produced / Received Qty,ŲŖŲ¹ŲÆŲ§ŲÆ ŲŖŁˆŁ„ŪŒŲÆ / دریافت ؓده, +Product Price ID,ؓناسه Ł‚ŪŒŁ…ŲŖ Ł…Ų­ŲµŁˆŁ„, +Production Plan Already Submitted,Ų·Ų±Ų­ ŲŖŁˆŁ„ŪŒŲÆ قبلا Ų§Ų±Ų³Ų§Ł„ ؓده Ų§Ų³ŲŖ, +Production Plan Item Reference,Ł…Ų±Ų¬Ų¹ Ų¢ŪŒŲŖŁ… Ų·Ų±Ų­ ŲŖŁˆŁ„ŪŒŲÆ, +Production Plan Qty,مقدار برنامه ŲŖŁˆŁ„ŪŒŲÆ, +Production Plan Sub Assembly Item,Ų¢ŪŒŲŖŁ… زیر Ł…ŁˆŁ†ŲŖŲ§Ś˜ برنامه ŲŖŁˆŁ„ŪŒŲÆ, +Production Plan Sub-assembly Item,Ų¢ŪŒŲŖŁ… زیر Ł…ŁˆŁ†ŲŖŲ§Ś˜ برنامه ŲŖŁˆŁ„ŪŒŲÆ, +Production Plan Summary,خلاصه برنامه ŲŖŁˆŁ„ŪŒŲÆ, +Profile,Ł…Ų“Ų®ŲµŲ§ŲŖ, +Profit and Loss Summary,خلاصه سود و Ų²ŪŒŲ§Ł†, +Progress,پیؓرفت, +Progress (%),پیؓرفت (%), +Project Progress:,پیؓرفت Ł¾Ų±ŁˆŚ˜Ł‡:, +Prompt Qty,ŲŖŲ¹ŲÆŲ§ŲÆ Ų±Ų§ اعلان Ś©Ł†ŪŒŲÆ, +Prospect,Ł…Ų“ŲŖŲ±ŪŒ ŲØŲ§Ł„Ł‚ŁˆŁ‡, +Prospect Lead,سرنخ Ł…Ų“ŲŖŲ±ŪŒ ŲØŲ§Ł„Ł‚ŁˆŁ‡, +Prospect Opportunity,فرصت Ł…Ų“ŲŖŲ±ŪŒ ŲØŲ§Ł„Ł‚ŁˆŁ‡, +Prospect Owner,مالک Ł…Ų“ŲŖŲ±ŪŒ ŲØŲ§Ł„Ł‚ŁˆŁ‡, +Prospect {0} already exists,Ł…Ų“ŲŖŲ±ŪŒ ŲØŲ§Ł„Ł‚ŁˆŁ‡ {0} Ų§Ų² قبل وجود ŲÆŲ§Ų±ŲÆ, +Provisional Expense Account,Ų­Ų³Ų§ŲØ Ł‡Ų²ŪŒŁ†Ł‡ Ł…ŁˆŁ‚ŲŖ, +Purchase Order Item reference is missing in Subcontracting Receipt {0},Ł…Ų±Ų¬Ų¹ Ų¢ŪŒŲŖŁ… سفارؓ خرید ŲÆŲ± رسید Ł¾ŪŒŁ…Ų§Ł†Ś©Ų§Ų±ŪŒ فرعی وجود ندارد {0}, +Purchase Orders {0} are un-linked,Ų³ŁŲ§Ų±Ų“ā€ŒŁ‡Ų§ŪŒ خرید {0} Ł„ŲŗŁˆ Ł¾ŪŒŁˆŁ†ŲÆ هستند, +Purchase Receipt (Draft) will be auto-created on submission of Subcontracting Receipt.,رسید خرید (پیؓ Ł†ŁˆŪŒŲ³) به صورت خودکار ŲØŲ§ ارائه رسید Ł¾ŪŒŁ…Ų§Ł†Ś©Ų§Ų±ŪŒ فرعی ایجاد Ł…ŪŒ ؓود., +Purchase Receipt {0} created.,رسید خرید {0} ایجاد Ų“ŲÆ., +Purchase Value,Ų§Ų±Ų²Ų“ خرید, +Purchases,Ų®Ų±ŪŒŲÆŁ‡Ų§, +Purposes Required,اهداف Ł…ŁˆŲ±ŲÆ Ł†ŪŒŲ§Ų², +Putaway Rule,Ł‚Ų§Ł†ŁˆŁ† Putaway, +Putaway Rule already exists for Item {0} in Warehouse {1}.,Ł‚Ų§Ł†ŁˆŁ† Putaway Ų§Ų² قبل برای Ł…ŁˆŲ±ŲÆ {0} ŲÆŲ± انبار {1} وجود ŲÆŲ§Ų±ŲÆ., +Qty , ŲŖŲ¹ŲÆŲ§ŲÆ, +Qty After Transaction,ŲŖŲ¹ŲÆŲ§ŲÆ ŲØŲ¹ŲÆ Ų§Ų² تراکنؓ, +Qty As Per BOM,مقدار Ų·ŲØŁ‚ BOM, +Qty Change,تغییر ŲŖŲ¹ŲÆŲ§ŲÆ, +Qty In Stock,ŲŖŲ¹ŲÆŲ§ŲÆ Ł…ŁˆŲ¬ŁˆŲÆ ŲÆŲ± انبار, +Qty Per Unit,ŲŖŲ¹ŲÆŲ§ŲÆ ŲÆŲ± هر واحد, +Qty To Produce,ŲŖŲ¹ŲÆŲ§ŲÆ برای ŲŖŁˆŁ„ŪŒŲÆ, +Qty and Rate,ŲŖŲ¹ŲÆŲ§ŲÆ و نرخ, +Qty as Per Stock UOM,ŲŖŲ¹ŲÆŲ§ŲÆ به ازای Ł…ŁˆŲ¬ŁˆŲÆŪŒ UOM, +Qty for which recursion isn't applicable.,ŲŖŲ¹ŲÆŲ§ŲÆ که بازگؓت برای آنها قابل اعمال Ł†ŪŒŲ³ŲŖ., +Qty in Stock UOM,ŲŖŲ¹ŲÆŲ§ŲÆ Ł…ŁˆŲ¬ŁˆŲÆ ŲÆŲ± انبار UOM, +Qty of Finished Goods Item should be greater than 0.,ŲŖŲ¹ŲÆŲ§ŲÆ Ś©Ų§Ł„Ų§Ł‡Ų§ŪŒ ŲŖŁ…Ų§Ł… ؓده باید بیؓتر Ų§Ų² 0 ŲØŲ§Ų“ŲÆ., +Qty to Be Consumed,مقدار قابل مصرف, +Qty to Build,ŲŖŲ¹ŲÆŲ§ŲÆ برای Ų³Ų§Ų®ŲŖ, +Qty to Fetch,ŲŖŲ¹ŲÆŲ§ŲÆ برای واکؓی, +Qty to Produce,ŲŖŲ¹ŲÆŲ§ŲÆ برای ŲŖŁˆŁ„ŪŒŲÆ, +Qualification Status,وضعیت ŲµŁ„Ų§Ų­ŪŒŲŖ, +Qualified,واجد ؓرایط, +Qualified By,واجد ؓرایط توسط, +Qualified on,واجد ؓرایط, +Quality Inspection Parameter,پارامتر بازرسی کیفیت, +Quality Inspection Parameter Group,ŚÆŲ±ŁˆŁ‡ Ł¾Ų§Ų±Ų§Ł…ŲŖŲ±Ł‡Ų§ŪŒ بازرسی کیفیت, +Quality Inspection Settings,ŲŖŁ†ŲøŪŒŁ…Ų§ŲŖ بازرسی کیفیت, +Quality Inspection(s),بازرسی(Ł‡Ų§ŪŒ) کیفیت, +Quantity is required,مقدار Ł…ŁˆŲ±ŲÆ Ł†ŪŒŲ§Ų² Ų§Ų³ŲŖ, +"Quantity must be greater than zero, and less or equal to {0}",مقدار باید بزرگتر Ų§Ų² صفر و کمتر یا Ł…Ų³Ų§ŁˆŪŒ ŲØŲ§ {0} ŲØŲ§Ų“ŲÆ., +Quantity to Produce should be greater than zero.,مقدار ŲŖŁˆŁ„ŪŒŲÆ باید بیؓتر Ų§Ų² صفر ŲØŲ§Ų“ŲÆ., +Quantity to Scan,مقدار برای اسکن, +Quarter {0} {1},سه ماهه {0} {1}, +Quotation Number,ؓماره پیؓ فاکتور, +Quoted Amount,مبلغ نقل ؓده, +Rate Difference with Purchase Invoice,تفاوت نرخ ŲØŲ§ فاکتور خرید, +Rate Section,ŲØŲ®Ų“ Ų§Ł…ŲŖŪŒŲ§Ų² ŲÆŁ‡ŪŒ, +Rate of Stock UOM,نرخ Ł…ŁˆŲ¬ŁˆŲÆŪŒ UOM, +Ratios,نسبت ها, +Raw Material Cost Per Qty,Ł‡Ų²ŪŒŁ†Ł‡ Ł…ŁˆŲ§ŲÆ Ų®Ų§Ł… به ازای هر ŲŖŲ¹ŲÆŲ§ŲÆ, +Raw Material Item,Ł…ŁˆŲ±ŲÆ Ł…ŁˆŲ§ŲÆ Ų®Ų§Ł…, +Raw Material Value,Ų§Ų±Ų²Ų“ Ł…ŁˆŲ§ŲÆ Ų®Ų§Ł…, +Raw Materials Consumption ,مصرف Ł…ŁˆŲ§ŲÆ Ų§ŁˆŁ„ŪŒŁ‡ , +Raw Materials Warehouse,انبار Ł…ŁˆŲ§ŲÆ Ų§ŁˆŁ„ŪŒŁ‡, +Reading Value,مقدار Ų®ŁˆŲ§Ł†ŲÆŁ†, +Reason for hold:,ŲÆŁ„ŪŒŁ„ نگه داؓتن:, +Rebuild Tree,ŲÆŲ±Ų®ŲŖ Ų±Ų§ بازسازی Ś©Ł†ŪŒŲÆ, +Recalculate Incoming/Outgoing Rate,محاسبه Ł…Ų¬ŲÆŲÆ نرخ ورودی/خروجی, +Recalculating Purchase Cost against this Project...,محاسبه Ł…Ų¬ŲÆŲÆ Ł‡Ų²ŪŒŁ†Ł‡ خرید ŲÆŲ± مقابل Ų§ŪŒŁ† Ł¾Ų±ŁˆŚ˜Ł‡..., +Receivable/Payable Account,Ų­Ų³Ų§ŲØ ŲÆŲ±ŪŒŲ§ŁŲŖŁ†ŪŒ/Ł¾Ų±ŲÆŲ§Ų®ŲŖŁ†ŪŒ, +Receivable/Payable Account: {0} doesn't belong to company {1},Ų­Ų³Ų§ŲØ ŲÆŲ±ŪŒŲ§ŁŲŖŁ†ŪŒ/Ł¾Ų±ŲÆŲ§Ų®ŲŖŁ†ŪŒ: {0} به ؓرکت {1} تعلق ندارد, +Received Amount After Tax,مبلغ دریافتی پس Ų§Ų² کسر Ł…Ų§Ł„ŪŒŲ§ŲŖ, +Received Amount After Tax (Company Currency),مبلغ دریافتی پس Ų§Ų² کسر Ł…Ų§Ł„ŪŒŲ§ŲŖ (Ų§Ų±Ų² ؓرکت), +Received Amount cannot be greater than Paid Amount,مبلغ دریافتی Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł†ŲÆ بیؓتر Ų§Ų² مبلغ پرداختی ŲØŲ§Ų“ŲÆ, +Received Qty in Stock UOM,ŲŖŲ¹ŲÆŲ§ŲÆ ŲÆŲ± انبار UOM دریافت Ų“ŲÆ, +Recent Orders,Ų³ŁŲ§Ų±Ų“ā€ŒŁ‡Ų§ŪŒ اخیر, +Reconcile the Bank Transaction,تراکنؓ ŲØŲ§Ł†Ś©ŪŒ Ų±Ų§ ŲŖŲ·ŲØŪŒŁ‚ ŲÆŁ‡ŪŒŲÆ, +Reconciled Entries,Ų«ŲØŲŖ Ł‡Ų§ŪŒ ŲŖŲ·ŲØŪŒŁ‚ ؓده, +Reconciliation Error Log,لاگ خطای ŲŖŲ·ŲØŪŒŁ‚, +Reconciliation Logs,Ł„Ų§ŚÆā€ŒŁ‡Ų§ŪŒ Ł…Ų±ŲØŁˆŲ· به ŲŖŲ·ŲØŪŒŁ‚, +Reconciliation Progress,پیؓرفت ŲŖŲ·ŲØŪŒŁ‚, +Recording HTML,Ų¶ŲØŲ· HTML, +Recoverable Standard Rated expenses should not be set when Reverse Charge Applicable is Y,Ų²Ł…Ų§Ł†ŪŒ که ؓارژ Ł…Ų¹Ś©ŁˆŲ³ قابل اعمال Y است، Ł‡Ų²ŪŒŁ†Ł‡ā€ŒŁ‡Ų§ŪŒ استاندارد قابل بازیافت Ł†ŲØŲ§ŪŒŲÆ ŲŖŁ†ŲøŪŒŁ… Ų“ŁˆŁ†ŲÆ, +Recurse Every (As Per Transaction UOM),تکرار هر (ŲØŲ± Ų§Ų³Ų§Ų³ UOM تراکنؓ), +Recurse Over Qty cannot be less than 0,Recurse Over Qty Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł†ŲÆ کمتر Ų§Ų² 0 ŲØŲ§Ų“ŲÆ, +Reference Date for Early Payment Discount,تاریخ Ł…Ų±Ų¬Ų¹ برای تخفیف پرداخت Ų²ŁˆŲÆŁ‡Ł†ŚÆŲ§Ł…, +Reference Detail,جزئیات Ł…Ų±Ų¬Ų¹, +Reference DocType,Ł…Ų±Ų¬Ų¹ DocType, +Reference Exchange Rate,نرخ Ų§Ų±Ų² Ł…Ų±Ų¬Ų¹, +Reference No,ؓماره Ł…Ų±Ų¬Ų¹, +Reference number of the invoice from the previous system,ؓماره Ł…Ų±Ų¬Ų¹ فاکتور Ų§Ų² Ų³ŪŒŲ³ŲŖŁ… Ł‚ŲØŁ„ŪŒ, +References {0} of type {1} had no outstanding amount left before submitting the Payment Entry. Now they have a negative outstanding amount.,Ł…Ų±Ų§Ų¬Ų¹ {0} Ų§Ų² Ł†ŁˆŲ¹ {1} قبل Ų§Ų² Ų§Ų±Ų³Ų§Ł„ Ų«ŲØŲŖ پرداخت، مبلغ Ł…Ų¹ŁˆŁ‚Ł‡ ای ŲØŲ§Ł‚ŪŒ نمانده بود. Ų§Ś©Ł†ŁˆŁ† آنها یک مبلغ Ł…Ų¹ŁˆŁ‚Ł‡ Ł…Ł†ŁŪŒ دارند., +Refresh Google Sheet,برگه Google Ų±Ų§ ŲØŲ§Ų²Ų®ŁˆŲ§Ł†ŪŒ Ś©Ł†ŪŒŲÆ, +Refresh Plaid Link,Ł¾ŪŒŁˆŁ†ŲÆ Ų“Ų·Ų±Ł†Ų¬ŪŒ Ų±Ų§ تازه Ś©Ł†ŪŒŲÆ, +Regenerate Closing Stock Balance,بازسازی ŲŖŲ±Ų§Ų² Ł…ŁˆŲ¬ŁˆŲÆŪŒ Ų§Ų®ŲŖŲŖŲ§Ł…ŪŒŁ‡, +Rejected Serial and Batch Bundle,باندل Ų³Ų±ŪŒŲ§Ł„ و دسته Ų±ŲÆ ؓده, +Remarks Column Length,Ų·ŁˆŁ„ Ų³ŲŖŁˆŁ† اظهارات, +Repair,ŲŖŲ¹Ł…ŪŒŲ±, +Repair Asset,دارایی ŲŖŲ¹Ł…ŪŒŲ±, +Repair Details,جزئیات ŲŖŲ¹Ł…ŪŒŲ±, +Report Error,ŚÆŲ²Ų§Ų±Ų“ Ų®Ų·Ų§, +Report Filters,ŚÆŲ²Ų§Ų±Ų“ ŁŪŒŁ„ŲŖŲ±Ł‡Ų§, +Report View,Ł†Ł…Ų§ŪŒ ŚÆŲ²Ų§Ų±Ų“, +Repost Accounting Ledger,بازنؓر دفتر کل حسابداری, +Repost Accounting Ledger Items,بازنؓر اقلام دفتر کل حسابداری, +Repost Accounting Ledger Settings,بازنؓر ŲŖŁ†ŲøŪŒŁ…Ų§ŲŖ دفتر کل حسابداری, +Repost Allowed Types,بازنؓر Ų§Ł†ŁˆŲ§Ų¹ Ł…Ų¬Ų§Ų², +Repost Error Log,لاگ خطای Ų§Ų±Ų³Ų§Ł„ Ł…Ų¬ŲÆŲÆ, +Repost Item Valuation,Ų§Ų±Ų³Ų§Ł„ Ł…Ų¬ŲÆŲÆ Ų§Ų±Ų²Ų“ گذاری Ł…ŁˆŲ±ŲÆ, +Repost Payment Ledger,Ų§Ų±Ų³Ų§Ł„ Ł…Ų¬ŲÆŲÆ دفتر کل پرداخت, +Repost Payment Ledger Items,Ų§Ų±Ų³Ų§Ł„ Ł…Ų¬ŲÆŲÆ اقلام دفتر کل پرداخت, +Repost Status,وضعیت بازنؓر, +Repost has started in the background,Ų§Ų±Ų³Ų§Ł„ Ł…Ų¬ŲÆŲÆ ŲÆŲ± پس Ų²Ł…ŪŒŁ†Ł‡ ؓروع ؓده Ų§Ų³ŲŖ, +Repost in background,بازنؓر ŲÆŲ± پس Ų²Ł…ŪŒŁ†Ł‡, +Repost started in the background,بازنؓر ŲÆŲ± Ł¾Ų³ā€ŒŲ²Ł…ŪŒŁ†Ł‡ ؓروع Ų“ŲÆ, +Reposting Data File,Ų§Ų±Ų³Ų§Ł„ Ł…Ų¬ŲÆŲÆ ŁŲ§ŪŒŁ„ داده, +Reposting Info,بازنؓر اطلاعات, +Reposting Progress,بازنؓر پیؓرفت, +Reposting entries created: {0},Ų§Ų±Ų³Ų§Ł„ Ł…Ų¬ŲÆŲÆ ورودی Ł‡Ų§ŪŒ ایجاد ؓده: {0}, +Reposting has been started in the background.,Ų§Ų±Ų³Ų§Ł„ Ł…Ų¬ŲÆŲÆ ŲÆŲ± Ł¾Ų³ā€ŒŲ²Ł…ŪŒŁ†Ł‡ Ų¢ŲŗŲ§Ų² ؓده Ų§Ų³ŲŖ., +Reposting in the background.,بازنؓر ŲÆŲ± پس Ų²Ł…ŪŒŁ†Ł‡, +Represents a Financial Year. All accounting entries and other major transactions are tracked against the Fiscal Year.,نؓان دهنده یک Ų³Ų§Ł„ Ł…Ų§Ł„ŪŒ Ų§Ų³ŲŖ. Ś©Ł„ŪŒŁ‡ Ų«ŲØŲŖā€ŒŁ‡Ų§ŪŒ حسابداری و سایر ŲŖŲ±Ų§Ś©Ł†Ų“ā€ŒŁ‡Ų§ŪŒ عمده ŲÆŲ± ŲØŲ±Ų§ŲØŲ± Ų³Ų§Ł„ Ł…Ų§Ł„ŪŒ ردیابی Ł…ŪŒ Ų“ŁˆŁ†ŲÆ., +Request Parameters,Ł¾Ų§Ų±Ų§Ł…ŲŖŲ±Ł‡Ų§ŪŒ درخواست, +Request Timeout,درخواست مهلت Ų²Ł…Ų§Ł†ŪŒ, +Reservation Based On,رزرو ŲØŲ± Ų§Ų³Ų§Ų³, +Reserve,Ų°Ų®ŪŒŲ±Ł‡, +Reserve Stock,Ų°Ų®ŪŒŲ±Ł‡ Ł…ŁˆŲ¬ŁˆŲÆŪŒ, +"Reserved Qty ({0}) cannot be a fraction. To allow this, disable '{1}' in UOM {3}.","ŲŖŲ¹ŲÆŲ§ŲÆ رزرو ؓده ({0}) Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł†ŲÆ کسری ŲØŲ§Ų“ŲÆ. برای اجازه دادن به Ų§ŪŒŁ† کار، ""{1}"" Ų±Ų§ ŲÆŲ± UOM {3} ŲŗŪŒŲ±ŁŲ¹Ų§Ł„ Ś©Ł†ŪŒŲÆ.", +Reserved Qty for Production Plan,ŲŖŲ¹ŲÆŲ§ŲÆ رزرو ؓده برای Ų·Ų±Ų­ ŲŖŁˆŁ„ŪŒŲÆ, +Reserved Qty for Subcontract,مقدار رزرو ؓده برای قرارداد فرعی, +Reserved Qty should be greater than Delivered Qty.,ŲŖŲ¹ŲÆŲ§ŲÆ رزرو ؓده باید بیؓتر Ų§Ų² ŲŖŲ¹ŲÆŲ§ŲÆ ŲŖŲ­ŁˆŪŒŁ„ ؓده ŲØŲ§Ų“ŲÆ., +Reserved Serial No.,ؓماره Ų³Ų±ŪŒŲ§Ł„ رزرو ؓده, +Reserved Stock,Ł…ŁˆŲ¬ŁˆŲÆŪŒ رزرو ؓده, +Reserved Stock for Batch,Ł…ŁˆŲ¬ŁˆŲÆŪŒ رزرو ؓده برای دسته, +Reserved for POS Transactions,برای معاملات POS رزرو ؓده Ų§Ų³ŲŖ, +Reserved for Production,برای ŲŖŁˆŁ„ŪŒŲÆ Ł…Ų­ŁŁˆŲø Ų§Ų³ŲŖ, +Reserved for Production Plan,برای Ų·Ų±Ų­ ŲŖŁˆŁ„ŪŒŲÆ Ł…Ų­ŁŁˆŲø Ų§Ų³ŲŖ, +Reserved for Sub Contracting,برای Ł¾ŪŒŁ…Ų§Ł†Ś©Ų§Ų±ŪŒ فرعی Ł…Ų­ŁŁˆŲø Ų§Ų³ŲŖ, +Reserving Stock...,رزرو Ł…ŁˆŲ¬ŁˆŲÆŪŒ..., +Reset Company Default Values,ŲØŲ§Ų²Ł†Ų“Ų§Ł†ŪŒ Ł…Ł‚Ų§ŲÆŪŒŲ± پیؓ فرض ؓرکت, +Reset Plaid Link,ŲØŲ§Ų²Ł†Ų“Ų§Ł†ŪŒ Ł¾ŪŒŁˆŁ†ŲÆ Plaid, +Resolution Due,سررسید Ų­Ł„ و فصل, +Response and Resolution,پاسخ و Ų­Ł„ و فصل, +Restart,Ų±Ų§Ł‡ā€ŒŲ§Ł†ŲÆŲ§Ų²ŪŒ Ł…Ų¬ŲÆŲÆ, +Restore Asset,بازیابی دارایی, +Restrict,Ł…Ų­ŲÆŁˆŲÆ کردن, +Restrict Items Based On,Ł…ŁˆŲ§Ų±ŲÆ Ų±Ų§ ŲØŲ± Ų§Ų³Ų§Ų³ Ł…Ų­ŲÆŁˆŲÆ Ś©Ł†ŪŒŲÆ, +Result Key,Ś©Ł„ŪŒŲÆ Ł†ŲŖŪŒŲ¬Ł‡, +Resume Job,Ų±Ų²ŁˆŁ…Ł‡ کاری, +Retried,ŲÆŁˆŲØŲ§Ų±Ł‡ امتحان Ų“ŲÆ, +Retry,ŲÆŁˆŲØŲ§Ų±Ł‡ امتحان Ś©Ł†ŪŒŲÆ, +Retry Failed Transactions,تراکنؓ Ł‡Ų§ŪŒ Ł†Ų§Ł…ŁˆŁŁ‚ Ų±Ų§ ŲÆŁˆŲØŲ§Ų±Ł‡ امتحان Ś©Ł†ŪŒŲÆ, +Return Against,بازگؓت ŲÆŲ± مقابل, +Return Against Subcontracting Receipt,Ų§Ų³ŲŖŲ±ŲÆŲ§ŲÆ ŲÆŲ± مقابل رسید Ł¾ŪŒŁ…Ų§Ł†Ś©Ų§Ų±ŪŒ فرعی, +Return Components,برگرداندن Ų§Ų¬Ų²Ų§Ų”, +Return Issued,Ų­ŁˆŲ§Ł„Ł‡ بازگؓت ŲµŲ§ŲÆŲ± Ų“ŲÆ, +Return Qty,ŲŖŲ¹ŲÆŲ§ŲÆ برگؓت, +Return Qty from Rejected Warehouse,ŲŖŲ¹ŲÆŲ§ŲÆ بازگرداندن Ų§Ų² انبار Ł…Ų±Ų¬ŁˆŲ¹ŪŒ, +Return of Components,بازگؓت Ų§Ų¬Ų²Ų§Ų”, +Returned,بازگؓت, +Returned Against,بازگؓت ŲÆŲ± ŲØŲ±Ų§ŲØŲ±, +Returned Qty ,مقدار برگردانده ؓده , +Returned Qty in Stock UOM,ŲŖŲ¹ŲÆŲ§ŲÆ برگردانده ؓده ŲÆŲ± انبار UOM, +Returned exchange rate is neither integer not float.,نرخ Ų§Ų±Ų² برگؓتی نه Ų¹ŲÆŲÆ صحیح Ų§Ų³ŲŖ و نه Ų“Ł†Ų§ŁˆŲ±., +Revaluation Journals,ŲÆŁŲŖŲ±Ł‡Ų§ŪŒ Ų±ŁˆŲ²Ł†Ų§Ł…Ł‡ تجدید ارزیابی, +Revaluation Surplus,Ł…Ų§Ų²Ų§ŲÆ تجدید ارزیابی, +Revenue,درآمد, +Reversal Of,Ł…Ų¹Ś©ŁˆŲ³ ؓدن, +Role Allowed to Create/Edit Back-dated Transactions,نقؓ Ł…Ų¬Ų§Ų² برای ایجاد/ویرایؓ تراکنؓ Ł‡Ų§ŪŒ Ł‚ŲÆŪŒŁ…ŪŒ, +Role Allowed to Over Bill , نقؓ Ł…Ų¬Ų§Ų² به Over Bill, +Role Allowed to Over Deliver/Receive,نقؓ Ł…Ų¬Ų§Ų² به ŲŖŲ­ŁˆŪŒŁ„/دریافت بیؓ Ų§Ų² Ų­ŲÆ, +Role Allowed to Override Stop Action,نقؓ Ł…Ų¬Ų§Ų² برای Ł„ŲŗŁˆ عمل ŲŖŁˆŁ‚Ł, +Role allowed to bypass Credit Limit,نقؓ Ł…Ų¬Ų§Ų² برای دور زدن Ł…Ų­ŲÆŁˆŲÆŪŒŲŖ Ų§Ų¹ŲŖŲØŲ§Ų±, +"Root Type for {0} must be one of the Asset, Liability, Income, Expense and Equity",Ł†ŁˆŲ¹ Ų±ŪŒŲ“Ł‡ برای {0} باید یکی Ų§Ų² دارایی، ŲØŲÆŁ‡ŪŒŲŒ ŲÆŲ±Ų¢Ł…ŲÆŲŒ Ł‡Ų²ŪŒŁ†Ł‡ و Ų­Ł‚ŁˆŁ‚ صاحبان Ł…ŁˆŲ¬ŁˆŲÆŪŒ ŲØŲ§Ų“ŲÆ., +Round Free Qty,دور ŲŖŲ¹ŲÆŲ§ŲÆ Ų±Ų§ŪŒŚÆŲ§Ł†, +Round Off Tax Amount,دور کردن مبلغ Ł…Ų§Ł„ŪŒŲ§ŲŖ, +Round Tax Amount Row-wise,ŚÆŲ±ŲÆ کردن مقدار Ł…Ų§Ł„ŪŒŲ§ŲŖ Ł…ŲØŲŖŁ†ŪŒ ŲØŲ± ردیف, +Rounding Loss Allowance,Ų²ŪŒŲ§Ł† ŚÆŲ±ŲÆ کردن Ł…Ų¬Ų§Ų², +Rounding Loss Allowance should be between 0 and 1,Ų²ŪŒŲ§Ł† ŚÆŲ±ŲÆ کردن Ł…Ų¬Ų§Ų² باید ŲØŪŒŁ† 0 و 1 ŲØŲ§Ų“ŲÆ, +Rounding gain/loss Entry for Stock Transfer,ŚÆŲ±ŲÆ کردن Ų«ŲØŲŖ سود/Ų²ŪŒŲ§Ł† برای انتقال Ł…ŁˆŲ¬ŁˆŲÆŪŒ, +Row #,ردیف #, +Row # {0}:,ردیف ؓماره {0}:, +Row #{0}: A reorder entry already exists for warehouse {1} with reorder type {2}.,ردیف #{0}: یک ورودی سفارؓ Ł…Ų¬ŲÆŲÆ Ų§Ų² قبل برای انبار {1} ŲØŲ§ Ł†ŁˆŲ¹ سفارؓ Ł…Ų¬ŲÆŲÆ {2} وجود ŲÆŲ§Ų±ŲÆ., +Row #{0}: Acceptance Criteria Formula is incorrect.,ردیف #{0}: ŁŲ±Ł…ŁˆŁ„ Ł…Ų¹ŪŒŲ§Ų±Ł‡Ų§ŪŒ پذیرؓ نادرست Ų§Ų³ŲŖ., +Row #{0}: Acceptance Criteria Formula is required.,ردیف #{0}: ŁŲ±Ł…ŁˆŁ„ Ł…Ų¹ŪŒŲ§Ų±Ł‡Ų§ŪŒ پذیرؓ Ų§Ł„Ų²Ų§Ł…ŪŒ Ų§Ų³ŲŖ., +Row #{0}: Accepted Warehouse and Rejected Warehouse cannot be same,ردیف #{0}: انبار Ł¾Ų°ŪŒŲ±ŁŲŖŁ‡ ؓده و انبار Ł…Ų±Ų¬ŁˆŲ¹ŪŒ Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł†Ł†ŲÆ ŪŒŚ©Ų³Ų§Ł† باؓند, +Row #{0}: Accepted Warehouse is mandatory for the accepted Item {1},ردیف #{0}: انبار Ł¾Ų°ŪŒŲ±ŁŲŖŁ‡ ؓده برای Ł…ŁˆŲ±ŲÆ Ł¾Ų°ŪŒŲ±ŁŲŖŁ‡ ؓده اجباری Ų§Ų³ŲŖ {1}, +Row #{0}: Allocated amount:{1} is greater than outstanding amount:{2} for Payment Term {3},ردیف #{0}: مبلغ تخصیص ŪŒŲ§ŁŲŖŁ‡:{1} بیؓتر Ų§Ų² مبلغ Ł…Ų¹ŁˆŁ‚ Ų§Ų³ŲŖ:{2} برای Ł…ŲÆŲŖ پرداخت {3}, +Row #{0}: Amount must be a positive number,ردیف #{0}: مقدار باید یک Ų¹ŲÆŲÆ Ł…Ų«ŲØŲŖ ŲØŲ§Ų“ŲÆ, +Row #{0}: BOM is not specified for subcontracting item {0},ردیف #{0}: BOM برای قرارداد فرعی Ł…ŁˆŲ±ŲÆ {0} Ł…Ų“Ų®Ųµ نؓده Ų§Ų³ŲŖ, +Row #{0}: Batch No {1} is already selected.,ردیف #{0}: ؓماره دسته {1} قبلاً انتخاب ؓده Ų§Ų³ŲŖ., +Row #{0}: Cannot allocate more than {1} against payment term {2},ردیف #{0}: Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł† بیؓ Ų§Ų² {1} Ų±Ų§ ŲÆŲ± مقابل Ł…ŲÆŲŖ پرداخت {2} تخصیص ŲÆŲ§ŲÆ, +Row #{0}: Cannot transfer more than Required Qty {1} for Item {2} against Job Card {3},ردیف #{0}: Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł† بیؓ Ų§Ų² مقدار لازم {1} برای Ł…ŁˆŲ±ŲÆ {2} ŲÆŲ± مقابل کارت کار {3} انتقال ŲÆŲ§ŲÆ, +Row #{0}: Consumed Asset {1} cannot be Draft,ردیف #{0}: دارایی مصرف ؓده {1} Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł†ŲÆ پیؓ Ł†ŁˆŪŒŲ³ ŲØŲ§Ų“ŲÆ, +Row #{0}: Consumed Asset {1} cannot be cancelled,ردیف #{0}: دارایی مصرف ؓده {1} قابل Ł„ŲŗŁˆ Ł†ŪŒŲ³ŲŖ, +Row #{0}: Consumed Asset {1} cannot be the same as the Target Asset,ردیف #{0}: دارایی مصرف ؓده {1} Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł†ŲÆ ŲØŲ§ دارایی هدف ŪŒŚ©Ų³Ų§Ł† ŲØŲ§Ų“ŲÆ, +Row #{0}: Consumed Asset {1} cannot be {2},ردیف #{0}: دارایی مصرف ؓده {1} Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł†ŲÆ {2} ŲØŲ§Ų“ŲÆ, +Row #{0}: Consumed Asset {1} does not belong to company {2},ردیف #{0}: دارایی مصرف ؓده {1} به ؓرکت {2} تعلق ندارد, +Row #{0}: Cumulative threshold cannot be less than Single Transaction threshold,ردیف #{0}: آستانه ŲŖŲ¬Ł…Ų¹ŪŒ Ł†Ł…ŪŒā€ŒŲŖŁˆŲ§Ł†ŲÆ کمتر Ų§Ų² آستانه یک تراکنؓ ŲØŲ§Ų“ŲÆ, +Row #{0}: Dates overlapping with other row,ردیف #{0}: تاریخ ها ŲØŲ§ ردیف دیگر Ł‡Ł…Ł¾ŁˆŲ“Ų§Ł†ŪŒ دارند, +Row #{0}: Default BOM not found for FG Item {1},ردیف #{0}: BOM Ł¾ŪŒŲ“ā€ŒŁŲ±Ų¶ برای Ų¢ŪŒŲŖŁ… Ś©Ų§Ł„Ų§ŪŒ ŲŖŁ…Ų§Ł… ؓده {1} یافت نؓد, +Row #{0}: Expense Account not set for the Item {1}. {2},ردیف #{0}: Ų­Ų³Ų§ŲØ Ł‡Ų²ŪŒŁ†Ł‡ برای Ł…ŁˆŲ±ŲÆ {1} ŲŖŁ†ŲøŪŒŁ… نؓده Ų§Ų³ŲŖ. {2}, +Row #{0}: Finished Good Item Qty can not be zero,ردیف #{0}: مقدار Ų¢ŪŒŲŖŁ… Ś©Ų§Ł„Ų§ŪŒ ŲŖŁ…Ų§Ł… ؓده Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł†ŲÆ صفر ŲØŲ§Ų“ŲÆ, +Row #{0}: Finished Good Item is not specified for service item {1},ردیف #{0}: Ų¢ŪŒŲŖŁ… Ś©Ų§Ł„Ų§ŪŒ ŲŖŁ…Ų§Ł… ؓده برای Ł…ŁˆŲ±ŲÆ Ų®ŲÆŁ…Ų§ŲŖŪŒ {1} Ł…Ų“Ų®Ųµ نؓده Ų§Ų³ŲŖ, +Row #{0}: Finished Good Item {1} must be a sub-contracted item,ردیف #{0}: Ų¢ŪŒŲŖŁ… Ś©Ų§Ł„Ų§ŪŒ ŲŖŁ…Ų§Ł… ؓده {1} باید یک Ų¢ŪŒŲŖŁ… قرارداد فرعی ŲØŲ§Ų“ŲÆ, +Row #{0}: Finished Good reference is mandatory for Scrap Item {1}.,ردیف #{0}: Ł…Ų±Ų¬Ų¹ Ś©Ų§Ł„Ų§ŪŒ ŲŖŁ…Ų§Ł… ؓده برای ضایعات {1} اجباری Ų§Ų³ŲŖ., +"Row #{0}: For {1}, you can select reference document only if account gets credited",ردیف #{0}: برای {1}، فقط ŲÆŲ± صورتی Ł…ŪŒā€ŒŲŖŁˆŲ§Ł†ŪŒŲÆ سند Ł…Ų±Ų¬Ų¹ Ų±Ų§ انتخاب Ś©Ł†ŪŒŲÆ که Ų­Ų³Ų§ŲØ Ų§Ų¹ŲŖŲØŲ§Ų± ؓود, +"Row #{0}: For {1}, you can select reference document only if account gets debited",ردیف #{0}: برای {1}، فقط ŲÆŲ± صورتی Ł…ŪŒā€ŒŲŖŁˆŲ§Ł†ŪŒŲÆ سند Ł…Ų±Ų¬Ų¹ Ų±Ų§ انتخاب Ś©Ł†ŪŒŲÆ که Ų­Ų³Ų§ŲØ بدهکار ؓود, +Row #{0}: From Date cannot be before To Date,ردیف #{0}: From Date Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł†ŲÆ قبل Ų§Ų² To Date ŲØŲ§Ų“ŲÆ, +Row #{0}: Item {1} does not exist,ردیف #{0}: Ł…ŁˆŲ±ŲÆ {1} وجود ندارد, +"Row #{0}: Item {1} has been picked, please reserve stock from the Pick List.",ردیف #{0}: Ł…ŁˆŲ±ŲÆ {1} انتخاب ؓده است، لطفاً Ł…ŁˆŲ¬ŁˆŲÆŪŒ Ų±Ų§ Ų§Ų² فهرست انتخاب رزرو Ś©Ł†ŪŒŲÆ., +Row #{0}: Item {1} is not a service item,ردیف #{0}: Ł…ŁˆŲ±ŲÆ {1} یک Ł…ŁˆŲ±ŲÆ Ų®ŲÆŁ…Ų§ŲŖŪŒ Ł†ŪŒŲ³ŲŖ, +Row #{0}: Item {1} is not a stock item,ردیف #{0}: Ł…ŁˆŲ±ŲÆ {1} یک Ś©Ų§Ł„Ų§ŪŒ Ł…ŁˆŲ¬ŁˆŲÆŪŒ Ł†ŪŒŲ³ŲŖ, +Row #{0}: Only {1} available to reserve for the Item {2},ردیف #{0}: فقط {1} برای رزرو Ł…ŁˆŲ±ŲÆ {2} Ł…ŁˆŲ¬ŁˆŲÆ Ų§Ų³ŲŖ, +Row #{0}: Please select Item Code in Assembly Items,ردیف #{0}: لطفاً کد Ų¢ŪŒŲŖŁ… Ų±Ų§ ŲÆŲ± Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ŪŒ Ų§Ų³Ł…ŲØŁ„ŪŒ انتخاب Ś©Ł†ŪŒŲÆ, +Row #{0}: Please select the BOM No in Assembly Items,ردیف #{0}: لطفاً ؓماره BOM Ų±Ų§ ŲÆŲ± Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ŪŒ Ų§Ų³Ł…ŲØŁ„ŪŒ انتخاب Ś©Ł†ŪŒŲÆ, +Row #{0}: Please select the Sub Assembly Warehouse,ردیف #{0}: لطفاً انبار زیر Ł…ŁˆŁ†ŲŖŲ§Ś˜ Ų±Ų§ انتخاب Ś©Ł†ŪŒŲÆ, +Row #{0}: Please update deferred revenue/expense account in item row or default account in company master,ردیف #{0}: لطفاً Ų­Ų³Ų§ŲØ درآمد/Ł‡Ų²ŪŒŁ†Ł‡ Ł…Ų¹ŁˆŁ‚ Ų±Ų§ ŲÆŲ± ردیف Ų¢ŪŒŲŖŁ… یا Ų­Ų³Ų§ŲØ Ł¾ŪŒŲ“ā€ŒŁŲ±Ų¶ ŲÆŲ± Ų§ŲµŁ„ŪŒ ؓرکت ŲØŁ‡ā€ŒŲ±ŁˆŲ²Ų±Ų³Ų§Ł†ŪŒ Ś©Ł†ŪŒŲÆ., +Row #{0}: Qty increased by {1},ردیف #{0}: ŲŖŲ¹ŲÆŲ§ŲÆ ŲØŲ§ {1} افزایؓ یافت, +Row #{0}: Qty must be a positive number,ردیف #{0}: ŲŖŲ¹ŲÆŲ§ŲÆ باید یک Ų¹ŲÆŲÆ Ł…Ų«ŲØŲŖ ŲØŲ§Ų“ŲÆ, +Row #{0}: Qty should be less than or equal to Available Qty to Reserve (Actual Qty - Reserved Qty) {1} for Iem {2} against Batch {3} in Warehouse {4}.,ردیف #{0}: ŲŖŲ¹ŲÆŲ§ŲÆ باید کمتر یا ŲØŲ±Ų§ŲØŲ± ŲØŲ§ ŲŖŲ¹ŲÆŲ§ŲÆ Ł…ŁˆŲ¬ŁˆŲÆ برای رزرو (ŲŖŲ¹ŲÆŲ§ŲÆ ŁˆŲ§Ł‚Ų¹ŪŒ - ŲŖŲ¹ŲÆŲ§ŲÆ رزرو ؓده) {1} برای Iem {2} ŲÆŲ± مقابل دسته {3} ŲÆŲ± انبار {4} ŲØŲ§Ų“ŲÆ., +Row #{0}: Quantity to reserve for the Item {1} should be greater than 0.,ردیف #{0}: مقدار قابل رزرو برای Ł…ŁˆŲ±ŲÆ {1} باید بیؓتر Ų§Ų² 0 ŲØŲ§Ų“ŲÆ., +Row #{0}: Rate must be same as {1}: {2} ({3} / {4}),ردیف #{0}: نرخ باید مانند {1} ŲØŲ§Ų“ŲÆ: {2} ({3} / {4}), +Row #{0}: Received Qty must be equal to Accepted + Rejected Qty for Item {1},ردیف #{0}: ŲŖŲ¹ŲÆŲ§ŲÆ دریافتی باید ŲØŲ±Ų§ŲØŲ± ŲØŲ§ ŲŖŲ¹ŲÆŲ§ŲÆ Ł¾Ų°ŪŒŲ±ŁŲŖŁ‡ ؓده + Ų±ŲÆ ؓده برای Ł…ŁˆŲ±ŲÆ {1} ŲØŲ§Ų“ŲÆ., +Row #{0}: Rejected Qty cannot be set for Scrap Item {1}.,Ų³Ų·Ų± #{0}: ŲŖŲ¹ŲÆŲ§ŲÆ Ų±ŲÆ ؓده Ų±Ų§ Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł† برای ضایعات {1} ŲŖŁ†ŲøŪŒŁ… کرد., +Row #{0}: Rejected Warehouse is mandatory for the rejected Item {1},ردیف #{0}: انبار Ł…Ų±Ų¬ŁˆŲ¹ŪŒ برای Ł…ŁˆŲ±ŲÆ Ų±ŲÆ ؓده اجباری Ų§Ų³ŲŖ {1}, +Row #{0}: Scrap Item Qty cannot be zero,ردیف #{0}: ŲŖŲ¹ŲÆŲ§ŲÆ Ł…ŁˆŲ±ŲÆ ضایعات Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł†ŲÆ صفر ŲØŲ§Ų“ŲÆ, +Row #{0}: Serial No {1} for Item {2} is not available in {3} {4} or might be reserved in another {5}.,ردیف #{0}: ؓماره Ų³Ų±ŪŒŲ§Ł„ {1} برای Ų¢ŪŒŲŖŁ… {2} ŲÆŲ± {3} {4} Ł…ŁˆŲ¬ŁˆŲÆ Ł†ŪŒŲ³ŲŖ یا ممکن Ų§Ų³ŲŖ ŲÆŲ± {5} دیگری رزرو ؓده ŲØŲ§Ų“ŲÆ., +Row #{0}: Serial No {1} is already selected.,ردیف #{0}: ؓماره Ų³Ų±ŪŒŲ§Ł„ {1} قبلاً انتخاب ؓده Ų§Ų³ŲŖ., +Row #{0}: Start Time and End Time are required,ردیف #{0}: زمان ؓروع و زمان Ł¾Ų§ŪŒŲ§Ł† Ł…ŁˆŲ±ŲÆ Ł†ŪŒŲ§Ų² Ų§Ų³ŲŖ, +Row #{0}: Start Time must be before End Time,ردیف #{0}: زمان ؓروع باید قبل Ų§Ų² زمان Ł¾Ų§ŪŒŲ§Ł† ŲØŲ§Ų“ŲÆ, +Row #{0}: Status is mandatory,ردیف #{0}: وضعیت اجباری Ų§Ų³ŲŖ, +Row #{0}: Stock cannot be reserved for Item {1} against a disabled Batch {2}.,ردیف #{0}: Ł…ŁˆŲ¬ŁˆŲÆŪŒ Ų±Ų§ Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł† برای Ų¢ŪŒŲŖŁ… {1} ŲÆŲ± مقابل دسته ŲŗŪŒŲ±ŁŲ¹Ų§Ł„ ؓده {2} رزرو کرد., +Row #{0}: Stock cannot be reserved for a non-stock Item {1},ردیف #{0}: Ł…ŁˆŲ¬ŁˆŲÆŪŒ Ų±Ų§ Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł† برای یک Ś©Ų§Ł„Ų§ŪŒ غیر Ł…ŁˆŲ¬ŁˆŲÆŪŒ رزرو کرد {1}, +Row #{0}: Stock cannot be reserved in group warehouse {1}.,ردیف #{0}: Ł…ŁˆŲ¬ŁˆŲÆŪŒ ŲÆŲ± انبار ŚÆŲ±ŁˆŁ‡ŪŒ {1} قابل رزرو Ł†ŪŒŲ³ŲŖ., +Row #{0}: Stock is already reserved for the Item {1}.,ردیف #{0}: Ł…ŁˆŲ¬ŁˆŲÆŪŒ قبلاً برای Ł…ŁˆŲ±ŲÆ {1} رزرو ؓده Ų§Ų³ŲŖ., +Row #{0}: Stock is reserved for item {1} in warehouse {2}.,ردیف #{0}: Ł…ŁˆŲ¬ŁˆŲÆŪŒ برای Ś©Ų§Ł„Ų§ŪŒ {1} ŲÆŲ± انبار {2} رزرو ؓده Ų§Ų³ŲŖ., +Row #{0}: Stock not available to reserve for Item {1} against Batch {2} in Warehouse {3}.,ردیف #{0}: Ł…ŁˆŲ¬ŁˆŲÆŪŒ برای رزرو Ł…ŁˆŲ±ŲÆ {1} ŲÆŲ± مقابل دسته {2} ŲÆŲ± انبار {3} Ł…ŁˆŲ¬ŁˆŲÆ Ł†ŪŒŲ³ŲŖ., +Row #{0}: Stock not available to reserve for the Item {1} in Warehouse {2}.,ردیف #{0}: Ł…ŁˆŲ¬ŁˆŲÆŪŒ برای رزرو Ł…ŁˆŲ±ŲÆ {1} ŲÆŲ± انبار {2} Ł…ŁˆŲ¬ŁˆŲÆ Ł†ŪŒŲ³ŲŖ., +Row #{0}: You cannot use the inventory dimension '{1}' in Stock Reconciliation to modify the quantity or valuation rate. Stock reconciliation with inventory dimensions is intended solely for performing opening entries.,ردیف #{0}: Ł†Ł…ŪŒā€ŒŲŖŁˆŲ§Ł†ŪŒŲÆ Ų§Ų² ŲØŲ¹ŲÆ Ł…ŁˆŲ¬ŁˆŲÆŪŒ Ā«{1}Ā» ŲÆŲ± ŲŖŲ·ŲØŪŒŁ‚ Ł…ŁˆŲ¬ŁˆŲÆŪŒ برای تغییر مقدار یا نرخ ارزیابی استفاده Ś©Ł†ŪŒŲÆ. ŲŖŲ·ŲØŪŒŁ‚ Ł…ŁˆŲ¬ŁˆŲÆŪŒ ŲØŲ§ Ų§ŲØŲ¹Ų§ŲÆ Ł…ŁˆŲ¬ŁˆŲÆŪŒ صرفاً برای انجام ورودی Ł‡Ų§ŪŒ Ų§ŁŲŖŲŖŲ§Ų­ŪŒŁ‡ ŲÆŲ± نظر گرفته ؓده Ų§Ų³ŲŖ., +Row #{0}: You must select an Asset for Item {1}.,ردیف #{0}: باید یک دارایی برای Ł…ŁˆŲ±ŲÆ {1} انتخاب Ś©Ł†ŪŒŲÆ., +Row #{0}: {1} is not a valid reading field. Please refer to the field description.,ردیف #{0}: {1} یک ŁŪŒŁ„ŲÆ Ų®ŁˆŲ§Ł†ŲÆŁ†ŪŒ Ł…Ų¹ŲŖŲØŲ± Ł†ŪŒŲ³ŲŖ. لطفا به توضیحات ŁŪŒŁ„ŲÆ مراجعه Ś©Ł†ŪŒŲÆ., +Row #{0}: {1} of {2} should be {3}. Please update the {1} or select a different account.,ردیف #{0}: {1} Ų§Ų² {2} باید {3} ŲØŲ§Ų“ŲÆ. لطفاً {1} Ų±Ų§ به روز Ś©Ł†ŪŒŲÆ یا Ų­Ų³Ų§ŲØ دیگری Ų±Ų§ انتخاب Ś©Ł†ŪŒŲÆ., +Row #{1}: Warehouse is mandatory for stock Item {0},ردیف #{1}: انبار برای Ś©Ų§Ł„Ų§ŪŒ Ł…ŁˆŲ¬ŁˆŲÆŪŒ {0} اجباری Ų§Ų³ŲŖ, +Row #{}: Finance Book should not be empty since you're using multiple.,ردیف #{}: کتاب Ł…Ų§Ł„ŪŒ Ł†ŲØŲ§ŪŒŲÆ Ų®Ų§Ł„ŪŒ ŲØŲ§Ų“ŲÆ زیرا Ų§Ų² چندگانه استفاده Ł…ŪŒ Ś©Ł†ŪŒŲÆ., +Row #{}: Please use a different Finance Book.,ردیف #{}: لطفاً Ų§Ų² کتاب Ł…Ų§Ł„ŪŒ دیگری استفاده Ś©Ł†ŪŒŲÆ., +Row #{}: item {} has been picked already.,ردیف #{}: Ł…ŁˆŲ±ŲÆ {} قبلاً انتخاب ؓده Ų§Ų³ŲŖ., +Row #{}: {} {} doesn't belong to Company {}. Please select valid {}.,ردیف #{}: {} {} به ؓرکت {} تعلق ندارد. لطفاً {} Ł…Ų¹ŲŖŲØŲ± Ų±Ų§ انتخاب Ś©Ł†ŪŒŲÆ., +Row No {0}: Warehouse is required. Please set a Default Warehouse for Item {1} and Company {2},ردیف ؓماره {0}: انبار Ł…ŁˆŲ±ŲÆ Ł†ŪŒŲ§Ų² Ų§Ų³ŲŖ. لطفاً یک انبار پیؓ فرض برای Ł…ŁˆŲ±ŲÆ {1} و ؓرکت {2} ŲŖŁ†ŲøŪŒŁ… Ś©Ł†ŪŒŲÆ, +Row Number,ؓماره ردیف, +Row {0},ردیف {0}, +"Row {0} picked quantity is less than the required quantity, additional {1} {2} required.",مقدار Ų§Ł†ŲŖŲ®Ų§ŲØŪŒ ردیف {0} کمتر Ų§Ų² مقدار Ł…ŁˆŲ±ŲÆ Ł†ŪŒŲ§Ų² است، {1} {2} اضافی Ł…ŁˆŲ±ŲÆ Ł†ŪŒŲ§Ų² Ų§Ų³ŲŖ., +Row {0}# Item {1} cannot be transferred more than {2} against {3} {4},ردیف {0}# Ł…ŁˆŲ±ŲÆ {1} Ų±Ų§ Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł† بیؓ Ų§Ų² {2} ŲÆŲ± ŲØŲ±Ų§ŲØŲ± {3} {4} منتقل کرد, +Row {0}# Item {1} not found in 'Raw Materials Supplied' table in {2} {3},ردیف {0}# Ł…ŁˆŲ±ŲÆ {1} ŲÆŲ± Ų¬ŲÆŁˆŁ„ Ā«Ł…ŁˆŲ§ŲÆ Ų®Ų§Ł… عرضه ؓده» ŲÆŲ± {2} {3} یافت نؓد, +Row {0}: Accepted Qty and Rejected Qty can't be zero at the same time.,ردیف {0}: ŲŖŲ¹ŲÆŲ§ŲÆ Ł¾Ų°ŪŒŲ±ŁŲŖŁ‡ ؓده و ŲŖŲ¹ŲÆŲ§ŲÆ Ų±ŲÆ ؓده Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł†Ł†ŲÆ همزمان صفر باؓند., +Row {0}: Account {1} and Party Type {2} have different account types,ردیف {0}: Ų­Ų³Ų§ŲØ {1} و Ł†ŁˆŲ¹ طرف {2} Ų§Ł†ŁˆŲ§Ų¹ مختلف Ų­Ų³Ų§ŲØ دارند, +Row {0}: Allocated amount {1} must be less than or equal to invoice outstanding amount {2},ردیف {0}: مبلغ تخصیص ŪŒŲ§ŁŲŖŁ‡ {1} باید کمتر یا ŲØŲ±Ų§ŲØŲ± ŲØŲ§ مبلغ Ł…Ų¹ŁˆŁ‚ فاکتور {2} ŲØŲ§Ų“ŲÆ., +Row {0}: Allocated amount {1} must be less than or equal to remaining payment amount {2},ردیف {0}: مبلغ تخصیص ŪŒŲ§ŁŲŖŁ‡ {1} باید کمتر یا Ł…Ų³Ų§ŁˆŪŒ ŲØŲ§ مبلغ پرداخت ŲØŲ§Ł‚ŪŒ مانده ŲØŲ§Ų“ŲÆ {2}, +Row {0}: Both Debit and Credit values cannot be zero,ردیف {0}: هر دو مقدار ŲØŲÆŁ‡ŪŒ و Ų§Ų¹ŲŖŲØŲ§Ų± Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł†Ł†ŲÆ صفر باؓند, +Row {0}: Cost Center {1} does not belong to Company {2},ردیف {0}: مرکز Ł‡Ų²ŪŒŁ†Ł‡ {1} به ؓرکت {2} تعلق ندارد, +Row {0}: Either Delivery Note Item or Packed Item reference is mandatory.,ردیف {0}: Ł…Ų±Ų¬Ų¹ Ł…ŁˆŲ±ŲÆ یادداؓت ŲŖŲ­ŁˆŪŒŁ„ یا Ś©Ų§Ł„Ų§ŪŒ بسته ŲØŁ†ŲÆŪŒ ؓده اجباری Ų§Ų³ŲŖ., +Row {0}: Expense Head changed to {1} as no Purchase Receipt is created against Item {2}.,ردیف {0}: Ų³Ų± Ł‡Ų²ŪŒŁ†Ł‡ به {1} تغییر کرد زیرا Ł‡ŪŒŚ† رسید خریدی ŲÆŲ± ŲØŲ±Ų§ŲØŲ± Ł…ŁˆŲ±ŲÆ {2} ایجاد نؓد., +Row {0}: Expense Head changed to {1} because account {2} is not linked to warehouse {3} or it is not the default inventory account,ردیف {0}: Ų³Ų± Ł‡Ų²ŪŒŁ†Ł‡ به {1} تغییر کرد زیرا Ų­Ų³Ų§ŲØ {2} به انبار {3} Ł…Ų±ŲŖŲØŲ· Ł†ŪŒŲ³ŲŖ یا Ų­Ų³Ų§ŲØ Ł…ŁˆŲ¬ŁˆŲÆŪŒ Ł¾ŪŒŲ“ā€ŒŁŲ±Ų¶ Ł†ŪŒŲ³ŲŖ, +Row {0}: Expense Head changed to {1} because expense is booked against this account in Purchase Receipt {2},ردیف {0}: Ų³Ų± Ł‡Ų²ŪŒŁ†Ł‡ به {1} تغییر کرد زیرا Ł‡Ų²ŪŒŁ†Ł‡ ŲÆŲ± قبض خرید {2} ŲÆŲ± مقابل Ų§ŪŒŁ† Ų­Ų³Ų§ŲØ رزرو ؓده Ų§Ų³ŲŖ., +Row {0}: From Warehouse is mandatory for internal transfers,ردیف {0}: Ų§Ų² انبار برای نقل و انتقالات ŲÆŲ§Ų®Ł„ŪŒ اجباری Ų§Ų³ŲŖ, +Row {0}: Item Tax template updated as per validity and rate applied,ردیف {0}: Ų§Ł„ŚÆŁˆŪŒ Ł…Ų§Ł„ŪŒŲ§ŲŖ Ų¢ŪŒŲŖŁ… ŲØŲ± Ų§Ų³Ų§Ų³ Ų§Ų¹ŲŖŲØŲ§Ų± و نرخ اعمال ؓده به روز Ų“ŲÆ, +Row {0}: Item rate has been updated as per valuation rate since its an internal stock transfer,ردیف {0}: نرخ اقلام ŲØŲ±Ų§Ų³Ų§Ų³ نرخ ارزیابی ŲØŁ‡ā€ŒŲ±ŁˆŲ²Ų±Ų³Ų§Ł†ŪŒ ؓده است، زیرا یک انتقال ŲÆŲ§Ų®Ł„ŪŒ Ł…ŁˆŲ¬ŁˆŲÆŪŒ Ų§Ų³ŲŖ, +Row {0}: Item {1} must be a stock item.,ردیف {0}: Ł…ŁˆŲ±ŲÆ {1} باید یک Ś©Ų§Ł„Ų§ŪŒ Ł…ŁˆŲ¬ŁˆŲÆŪŒ ŲØŲ§Ų“ŲÆ., +Row {0}: Item {1} must be a subcontracted item.,ردیف {0}: Ł…ŁˆŲ±ŲÆ {1} باید یک Ų¢ŪŒŲŖŁ… قرارداد فرعی ŲØŲ§Ų“ŲÆ., +Row {0}: Packed Qty must be equal to {1} Qty.,ردیف {0}: ŲŖŲ¹ŲÆŲ§ŲÆ بسته ŲØŁ†ŲÆŪŒ ؓده باید ŲØŲ±Ų§ŲØŲ± ŲØŲ§ {1} ŲŖŲ¹ŲÆŲ§ŲÆ ŲØŲ§Ų“ŲÆ., +Row {0}: Packing Slip is already created for Item {1}.,ردیف {0}: برگه بسته ŲØŁ†ŲÆŪŒ قبلاً برای Ł…ŁˆŲ±ŲÆ {1} ایجاد ؓده Ų§Ų³ŲŖ., +Row {0}: Payment Term is mandatory,ردیف {0}: Ł…ŲÆŲŖ پرداخت اجباری Ų§Ų³ŲŖ, +Row {0}: Please provide a valid Delivery Note Item or Packed Item reference.,ردیف {0}: لطفاً یک Ł…ŁˆŲ±ŲÆ یادداؓت ŲŖŲ­ŁˆŪŒŁ„ Ł…Ų¹ŲŖŲØŲ± یا Ł…Ų±Ų¬Ų¹ Ś©Ų§Ł„Ų§ŪŒ بسته ŲØŁ†ŲÆŪŒ ؓده ارائه ŲÆŁ‡ŪŒŲÆ., +Row {0}: Please select a BOM for Item {1}.,ردیف {0}: لطفاً یک BOM برای Ł…ŁˆŲ±ŲÆ {1} انتخاب Ś©Ł†ŪŒŲÆ., +Row {0}: Please select an active BOM for Item {1}.,ردیف {0}: لطفاً یک BOM فعال برای Ł…ŁˆŲ±ŲÆ {1} انتخاب Ś©Ł†ŪŒŲÆ., +Row {0}: Please select an valid BOM for Item {1}.,ردیف {0}: لطفاً یک BOM Ł…Ų¹ŲŖŲØŲ± برای Ł…ŁˆŲ±ŲÆ {1} انتخاب Ś©Ł†ŪŒŲÆ., +Row {0}: Project must be same as the one set in the Timesheet: {1}.,ردیف {0}: Ł¾Ų±ŁˆŚ˜Ł‡ باید مانند آنچه ŲÆŲ± صفحه زمان ŲŖŁ†ŲøŪŒŁ… ؓده Ų§Ų³ŲŖ: {1} ŲØŲ§Ų“ŲÆ., +Row {0}: Purchase Invoice {1} has no stock impact.,ردیف {0}: فاکتور خرید {1} تأثیری ŲØŲ± Ł…ŁˆŲ¬ŁˆŲÆŪŒ ندارد., +Row {0}: Qty cannot be greater than {1} for the Item {2}.,ردیف {0}: ŲŖŲ¹ŲÆŲ§ŲÆ Ł†Ł…ŪŒā€ŒŲŖŁˆŲ§Ł†ŲÆ بیؓتر Ų§Ų² {1} برای Ł…ŁˆŲ±ŲÆ {2} ŲØŲ§Ų“ŲÆ., +Row {0}: Qty in Stock UOM can not be zero.,ردیف {0}: ŲŖŲ¹ŲÆŲ§ŲÆ Ł…ŁˆŲ¬ŁˆŲÆŪŒ UOM ŲÆŲ± انبار Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł†ŲÆ صفر ŲØŲ§Ų“ŲÆ., +Row {0}: Qty must be greater than 0.,ردیف {0}: ŲŖŲ¹ŲÆŲ§ŲÆ باید بیؓتر Ų§Ų² 0 ŲØŲ§Ų“ŲÆ., +Row {0}: Shift cannot be changed since the depreciation has already been processed,ردیف {0}: Shift Ų±Ų§ Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł† تغییر ŲÆŲ§ŲÆ زیرا استهلاک قبلاً پردازؓ ؓده Ų§Ų³ŲŖ, +Row {0}: Target Warehouse is mandatory for internal transfers,ردیف {0}: انبار هدف برای نقل و انتقالات ŲÆŲ§Ų®Ł„ŪŒ اجباری Ų§Ų³ŲŖ, +"Row {0}: To set {1} periodicity, difference between from and to date must be greater than or equal to {2}",ردیف {0}: برای ŲŖŁ†ŲøŪŒŁ… ŲŖŁ†Ų§ŁˆŲØ {1}، تفاوت ŲØŪŒŁ† تاریخ و تاریخ باید بزرگتر یا Ł…Ų³Ų§ŁˆŪŒ ŲØŲ§ {2} ŲØŲ§Ų“ŲÆ., +Row {0}: {1} account already applied for Accounting Dimension {2},ردیف {0}: Ų­Ų³Ų§ŲØ {1} قبلاً برای ŲØŲ¹ŲÆ حسابداری {2} اعمال ؓده Ų§Ų³ŲŖ, +Row {0}: {1} {2} cannot be same as {3} (Party Account) {4},ردیف {0}: {1} {2} Ł†Ł…ŪŒā€ŒŲŖŁˆŲ§Ł†ŲÆ مانند {3} (Ų­Ų³Ų§ŲØ طرف) {4}, +Row({0}): Outstanding Amount cannot be greater than actual Outstanding Amount {1} in {2},ردیف ({0}): مبلغ Ł…Ų¹ŁˆŁ‚ Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł†ŲÆ بیؓتر Ų§Ų² مبلغ Ł…Ų¹ŁˆŁ‚Ł‡ ŁˆŲ§Ł‚Ų¹ŪŒ {1} ŲÆŲ± {2} ŲØŲ§Ų“ŲÆ., +Rows with Same Account heads will be merged on Ledger,ردیف Ł‡Ų§ŪŒŪŒ ŲØŲ§ Ų³Ų±Ł‡Ų§ŪŒ Ų­Ų³Ų§ŲØ ŪŒŚ©Ų³Ų§Ł† ŲÆŲ± دفتر کل Ų§ŲÆŲŗŲ§Ł… Ł…ŪŒ Ų“ŁˆŁ†ŲÆ, +Rows: {0} have 'Payment Entry' as reference_type. This should not be set manually.,"Ų±ŲÆŪŒŁā€ŒŁ‡Ų§: {0} دارای ""Ų«ŲØŲŖ پرداخت"" به Ų¹Ł†ŁˆŲ§Ł† reference_type هستند. Ų§ŪŒŁ† Ł†ŲØŲ§ŪŒŲÆ به صورت دستی ŲŖŁ†ŲøŪŒŁ… ؓود.", +Rows: {0} in {1} section are Invalid. Reference Name should point to a valid Payment Entry or Journal Entry.,Ų±ŲÆŪŒŁā€ŒŁ‡Ų§: {0} ŲÆŲ± ŲØŲ®Ų“ {1} نامعتبر Ų§Ų³ŲŖ. نام Ł…Ų±Ų¬Ų¹ باید به یک Ų«ŲØŲŖ پرداخت Ł…Ų¹ŲŖŲØŲ± یا Ų«ŲØŲŖ دفتر Ų±ŁˆŲ²Ł†Ų§Ł…Ł‡ اؓاره کند., +Run parallel job cards in a workstation,اجرای Ł…ŁˆŲ§Ų²ŪŒ کارت Ł‡Ų§ŪŒ ŲÆŲ± یک Ų§ŪŒŲ³ŲŖŚÆŲ§Ł‡ کاری, +Running,ŲÆŲ± Ų­Ų§Ł„ Ų§Ų¬Ų±Ų§, +SCO Supplied Item,Ł…ŁˆŲ±ŲÆ عرضه ؓده SCO, +SLA Fulfilled On,SLA ŲŖŚ©Ł…ŪŒŁ„ Ų“ŲÆ, +SLA Fulfilled On Status,SLA ŲÆŲ± وضعیت ŲŖŚ©Ł…ŪŒŁ„ Ų“ŲÆ, +SLA Paused On,SLA Ł…ŲŖŁˆŁ‚Ł Ų“ŲÆ, +SLA will be applied if {1} is set as {2}{3},Ų§ŚÆŲ± {1} به Ų¹Ł†ŁˆŲ§Ł† {2}{3} ŲŖŁ†ŲøŪŒŁ… ؓود، SLA اعمال Ų®ŁˆŲ§Ł‡ŲÆ Ų“ŲÆ, +SLA will be applied on every {0},SLA ŲÆŲ± هر {0} اعمال Ų®ŁˆŲ§Ł‡ŲÆ Ų“ŲÆ, +SMS Settings,ŲŖŁ†ŲøŪŒŁ…Ų§ŲŖ Ł¾ŪŒŲ§Ł…Ś©, +Salary Currency,Ų§Ų±Ų² Ų­Ł‚ŁˆŁ‚ و ŲÆŲ³ŲŖŁ…Ų²ŲÆ, +Sales Invoice {0} must be deleted before cancelling this Sales Order,فاکتور فروؓ {0} باید قبل Ų§Ų² Ł„ŲŗŁˆ Ų§ŪŒŁ† سفارؓ فروؓ حذف ؓود, +Sales Order Packed Item,سفارؓ فروؓ Ś©Ų§Ł„Ų§ŪŒ بسته ŲØŁ†ŲÆŪŒ ؓده, +Sales Order Reference,Ł…Ų±Ų¬Ų¹ سفارؓ فروؓ, +Sales Order Status,وضعیت سفارؓ فروؓ, +"Sales Order {0} already exists against Customer's Purchase Order {1}. To allow multiple Sales Orders, Enable {2} in {3}",سفارؓ فروؓ {0} ŲÆŲ± مقابل سفارؓ خرید Ł…Ų“ŲŖŲ±ŪŒ {1} وجود ŲÆŲ§Ų±ŲÆ. برای Ł…Ų¬Ų§Ų² کردن Ś†Ł†ŲÆŪŒŁ† سفارؓ فروؓ، {2} Ų±Ų§ ŲÆŲ± {3} فعال Ś©Ł†ŪŒŲÆ, +Sales Partner , ؓریک فروؓ, +Sales Partner Item,Ł…ŁˆŲ±ŲÆ ؓریک فروؓ, +Sales Pipeline Analytics,ŲŖŲ¬Ų²ŪŒŁ‡ و ŲŖŲ­Ł„ŪŒŁ„ Ų®Ų· Ł„ŁˆŁ„Ł‡ فروؓ, +Sales Update Frequency in Company and Project,فرکانس به روز Ų±Ų³Ų§Ł†ŪŒ فروؓ ŲÆŲ± ؓرکت و Ł¾Ų±ŁˆŚ˜Ł‡, +Sales Value,Ų§Ų±Ų²Ų“ فروؓ, +Salvage Value Percentage,ŲÆŲ±ŲµŲÆ Ų§Ų±Ų²Ų“ نجات, +Same item and warehouse combination already entered.,همان کالا و ترکیب انبار قبلا وارد ؓده Ų§Ų³ŲŖ., +Savings,پس انداز, +Scan Batch No,اسکن ؓماره دسته, +Scan Mode,حالت اسکن, +Scan Serial No,اسکن ؓماره Ų³Ų±ŪŒŲ§Ł„, +Scan barcode for item {0},اسکن بارکد برای Ł…ŁˆŲ±ŲÆ {0}, +"Scan mode enabled, existing quantity will not be fetched.",حالت اسکن فعال است، مقدار Ł…ŁˆŲ¬ŁˆŲÆ واکؓی Ł†Ų®ŁˆŲ§Ł‡ŲÆ Ų“ŲÆ., +Scanned Quantity,مقدار اسکن ؓده, +Scheduled Time Logs,ŚÆŲ²Ų§Ų±Ų“ Ł‡Ų§ŪŒ زمان برنامه ریزی ؓده, +Scheduler is Inactive. Can't trigger job now.,Ų²Ł…Ų§Ł†ā€ŒŲØŁ†ŲÆ ŲŗŪŒŲ±ŁŲ¹Ų§Ł„ Ų§Ų³ŲŖ. Ų§Ś©Ł†ŁˆŁ† Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł† کار Ų±Ų§ Ų¢ŲŗŲ§Ų² کرد., +Scheduler is Inactive. Can't trigger jobs now.,Ų²Ł…Ų§Ł†ā€ŒŲØŁ†ŲÆ ŲŗŪŒŲ±ŁŲ¹Ų§Ł„ Ų§Ų³ŲŖ. Ų§Ś©Ł†ŁˆŁ† Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł† کارها Ų±Ų§ Ų¢ŲŗŲ§Ų² کرد., +Scheduler is inactive. Cannot enqueue job.,زمانبند ŲŗŪŒŲ±ŁŲ¹Ų§Ł„ Ų§Ų³ŲŖ. Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł† کار Ų±Ų§ ŲÆŲ± Ł†ŁˆŲØŲŖ ŚÆŲ°Ų§Ų“ŲŖ., +Scheduler is inactive. Cannot merge accounts.,زمانبند ŲŗŪŒŲ±ŁŲ¹Ų§Ł„ Ų§Ų³ŲŖ. Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł† Ų­Ų³Ų§ŲØ ها Ų±Ų§ Ų§ŲÆŲŗŲ§Ł… کرد., +Scheduling,برنامه ریزی, +Scrap & Process Loss,ضایعات و Ų§Ų² ŲÆŲ³ŲŖ دادن ŁŲ±Ų¢ŪŒŁ†ŲÆ, +Scrap Asset,دارایی قراضه, +Scrap Cost Per Qty,Ł‡Ų²ŪŒŁ†Ł‡ قراضه ŲÆŲ± هر ŲŖŲ¹ŲÆŲ§ŲÆ, +Scrap Item Code,کد Ų¢ŪŒŲŖŁ… ضایعات, +Scrap Item Name,نام Ų¢ŪŒŲŖŁ… ضایعات, +"Search by item code, serial number or barcode",جستجو ŲØŲ± Ų§Ų³Ų§Ų³ کد Ų¢ŪŒŲŖŁ…ŲŒ ؓماره Ų³Ų±ŪŒŲ§Ł„ یا بارکد, +Secondary Party,طرف Ų«Ų§Ł†ŁˆŪŒŁ‡, +Secondary Role,نقؓ Ų«Ų§Ł†ŁˆŪŒŁ‡, +Segregate Serial / Batch Bundle,جداسازی باندل Ų³Ų±ŪŒŲ§Ł„ / دسته, +Select Accounting Dimension.,ŲØŲ¹ŲÆ حسابداری Ų±Ų§ انتخاب Ś©Ł†ŪŒŲÆ., +Select Alternative Items for Sales Order,Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ŪŒ Ų¬Ų§ŪŒŚÆŲ²ŪŒŁ† Ų±Ų§ برای سفارؓ فروؓ انتخاب Ś©Ł†ŪŒŲÆ, +Select Batch No,ؓماره دسته Ų±Ų§ انتخاب Ś©Ł†ŪŒŲÆ, +Select Corrective Operation,Ų¹Ł…Ł„ŪŒŲ§ŲŖ Ų§ŲµŁ„Ų§Ų­ŪŒ Ų±Ų§ انتخاب Ś©Ł†ŪŒŲÆ, +Select Date of Birth. This will validate Employees age and prevent hiring of under-age staff.,تاریخ ŲŖŁˆŁ„ŲÆ Ų±Ų§ انتخاب Ś©Ł†ŪŒŲÆ. Ų§ŪŒŁ† Ų§Ł…Ų± سن کارکنان Ų±Ų§ تأیید Ł…ŪŒ کند و Ų§Ų² Ų§Ų³ŲŖŲ®ŲÆŲ§Ł… کارکنان زیر سن Ų¬Ł„ŁˆŚÆŪŒŲ±ŪŒ Ł…ŪŒ کند., +"Select Date of joining. It will have impact on the first salary calculation, Leave allocation on pro-rata bases.",تاریخ عضویت Ų±Ų§ انتخاب Ś©Ł†ŪŒŲÆ. ŲÆŲ± Ų§ŁˆŁ„ŪŒŁ† محاسبه Ų­Ł‚ŁˆŁ‚ŲŒ تخصیص Ł…Ų±Ų®ŲµŪŒ به Ł†Ų³ŲØŲŖŲŒ تاثیر Ų®ŁˆŲ§Ł‡ŲÆ ŲÆŲ§Ų“ŲŖ., +Select Dimension,Dimension Ų±Ų§ انتخاب Ś©Ł†ŪŒŲÆ, +Select Finished Good,Finished Good Ų±Ų§ انتخاب Ś©Ł†ŪŒŲÆ, +Select Items for Quality Inspection,Ł…ŁˆŲ§Ų±ŲÆ Ų±Ų§ برای بازرسی کیفیت انتخاب Ś©Ł†ŪŒŲÆ, +Select Serial No,ؓماره Ų³Ų±ŪŒŲ§Ł„ Ų±Ų§ انتخاب Ś©Ł†ŪŒŲÆ, +Select Serial and Batch,Ų³Ų±ŪŒŲ§Ł„ و دسته Ų±Ų§ انتخاب Ś©Ł†ŪŒŲÆ, +Select Time,زمان Ų±Ų§ انتخاب Ś©Ł†ŪŒŲÆ, +Select Vouchers to Match,Ś©ŁˆŁ¾Ł†ā€ŒŁ‡Ų§ Ų±Ų§ برای مطابقت انتخاب Ś©Ł†ŪŒŲÆ, +Select Warehouses to get Stock for Materials Planning,برای ŲØŲÆŲ³ŲŖ Ų¢ŁˆŲ±ŲÆŁ† انبار برای برنامه ریزی Ł…ŁˆŲ§ŲÆŲŒ انبارها Ų±Ų§ انتخاب Ś©Ł†ŪŒŲÆ, +Select a Company this Employee belongs to.,ؓرکتی Ų±Ų§ انتخاب Ś©Ł†ŪŒŲÆ که Ų§ŪŒŁ† کارمند به آن تعلق ŲÆŲ§Ų±ŲÆ., +Select a Customer,یک Ł…Ų“ŲŖŲ±ŪŒ انتخاب Ś©Ł†ŪŒŲÆ, +Select an Item Group.,یک ŚÆŲ±ŁˆŁ‡ Ų¢ŪŒŲŖŁ… Ų±Ų§ انتخاب Ś©Ł†ŪŒŲÆ., +Select an item from each set to be used in the Sales Order.,Ų§Ų² هر Ł…Ų¬Ł…ŁˆŲ¹Ł‡ یک Ł…ŁˆŲ±ŲÆ Ų±Ų§ برای استفاده ŲÆŲ± سفارؓ فروؓ انتخاب Ś©Ł†ŪŒŲÆ., +Select the Default Workstation where the Operation will be performed. This will be fetched in BOMs and Work Orders.,Ų§ŪŒŲ³ŲŖŚÆŲ§Ł‡ کاری Ł¾ŪŒŲ“ā€ŒŁŲ±Ų¶ Ų±Ų§ که ŲÆŲ± آن Ų¹Ł…Ł„ŪŒŲ§ŲŖ انجام Ł…ŪŒā€ŒŲ“ŁˆŲÆŲŒ انتخاب Ś©Ł†ŪŒŲÆ. Ų§ŪŒŁ† ŲÆŲ± BOM ها و دستور کارها واکؓی Ł…ŪŒ ؓود., +Select the Item to be manufactured.,Ł…ŁˆŲ±ŲÆŪŒ Ų±Ų§ که باید ŲŖŁˆŁ„ŪŒŲÆ ؓود انتخاب Ś©Ł†ŪŒŲÆ., +"Select the Item to be manufactured. The Item name, UoM, Company, and Currency will be fetched automatically.",Ł…ŁˆŲ±ŲÆŪŒ Ų±Ų§ که باید ŲŖŁˆŁ„ŪŒŲÆ ؓود انتخاب Ś©Ł†ŪŒŲÆ. نام Ł…ŁˆŲ±ŲÆŲŒ UoM، ؓرکت و Ų§Ų±Ų² به طور خودکار واکؓی Ł…ŪŒ ؓود., +Select the Warehouse,انبار Ų±Ų§ انتخاب Ś©Ł†ŪŒŲÆ, +Select the date and your timezone,تاریخ و منطقه Ų²Ł…Ų§Ł†ŪŒ خود Ų±Ų§ انتخاب Ś©Ł†ŪŒŲÆ, +Select the raw materials (Items) required to manufacture the Item,Ł…ŁˆŲ§ŲÆ Ų®Ų§Ł… (Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§) Ł…ŁˆŲ±ŲÆ Ł†ŪŒŲ§Ų² برای ŲŖŁˆŁ„ŪŒŲÆ Ų¢ŪŒŲŖŁ… Ų±Ų§ انتخاب Ś©Ł†ŪŒŲÆ, +Select your weekly off day,روز ŲŖŲ¹Ų·ŪŒŁ„ Ł‡ŁŲŖŚÆŪŒ خود Ų±Ų§ انتخاب Ś©Ł†ŪŒŲÆ, +Selected Vouchers,Ś©ŁˆŁ¾Ł† Ł‡Ų§ŪŒ انتخاب ؓده, +Selected date is,تاریخ انتخاب ؓده Ų§Ų³ŲŖ, +Selected document must be in submitted state,سند انتخاب ؓده باید ŲÆŲ± حالت Ų§Ų±Ų³Ų§Ł„ ؓده ŲØŲ§Ų“ŲÆ, +Self delivery,ŲŖŲ­ŁˆŪŒŁ„ خود, +Sell Asset,فروؓ دارایی, +Send Attached Files,Ų§Ų±Ų³Ų§Ł„ ŁŲ§ŪŒŁ„ Ł‡Ų§ŪŒ پیوست, +Send Document Print,Ų§Ų±Ų³Ų§Ł„ سند چاپ, +Send Emails,Ų§Ų±Ų³Ų§Ł„ Ų§ŪŒŁ…ŪŒŁ„, +Sequential,Ł…ŲŖŁˆŲ§Ł„ŪŒ, +Serial & Batch Item,Ų¢ŪŒŲŖŁ… Ų³Ų±ŪŒŲ§Ł„ و دسته ای, +Serial & Batch Item Settings,ŲŖŁ†ŲøŪŒŁ…Ų§ŲŖ Ų¢ŪŒŲŖŁ… Ų³Ų±ŪŒŲ§Ł„ و دسته ای, +Serial / Batch Bundle,باندل Ų³Ų±ŪŒŲ§Ł„ / دسته, +Serial / Batch Bundle Missing,باندل Ų³Ų±ŪŒŲ§Ł„ / دسته Ų¬Ų§ افتاده, +Serial / Batch No,ؓماره Ų³Ų±ŪŒŲ§Ł„ / دسته, +Serial / Batch Nos,ؓماره Ł‡Ų§ŪŒ Ų³Ų±ŪŒŲ§Ł„ / دسته ای, +Serial No Ledger,دفتر کل ؓماره Ų³Ų±ŪŒŲ§Ł„, +Serial No Range,Ł…Ų­ŲÆŁˆŲÆŁ‡ ؓماره Ų³Ų±ŪŒŲ§Ł„, +Serial No and Batch for Finished Good,ؓماره Ų³Ų±ŪŒŲ§Ł„ و دسته ای برای Finished Good, +Serial No is mandatory,ؓماره Ų³Ų±ŪŒŲ§Ł„ اجباری Ų§Ų³ŲŖ, +Serial No {0} already exists,ؓماره Ų³Ų±ŪŒŲ§Ł„ {0} Ų§Ų² قبل وجود ŲÆŲ§Ų±ŲÆ, +Serial No {0} already scanned,ؓماره Ų³Ų±ŪŒŲ§Ł„ {0} قبلاً اسکن ؓده Ų§Ų³ŲŖ, +Serial No {0} does not exists,ؓماره Ų³Ų±ŪŒŲ§Ł„ {0} وجود ندارد, +Serial No {0} is already added,ؓماره Ų³Ų±ŪŒŲ§Ł„ {0} قبلاً اضافه ؓده Ų§Ų³ŲŖ, +Serial Nos,ؓماره Ł‡Ų§ŪŒ Ų³Ų±ŪŒŲ§Ł„, +Serial Nos / Batch Nos,ؓماره Ł‡Ų§ŪŒ Ų³Ų±ŪŒŲ§Ł„ / ؓماره Ł‡Ų§ŪŒ دسته ای, +Serial Nos Mismatch,Ų¹ŲÆŁ… ŲŖŲ·Ų§ŲØŁ‚ ؓماره Ł‡Ų§ŪŒ Ų³Ų±ŪŒŲ§Ł„, +Serial Nos are created successfully,ؓماره Ł‡Ų§ŪŒ Ų³Ų±ŪŒŲ§Ł„ ŲØŲ§ Ł…ŁˆŁŁ‚ŪŒŲŖ ایجاد Ų“ŲÆ, +"Serial Nos are reserved in Stock Reservation Entries, you need to unreserve them before proceeding.",ؓماره Ł‡Ų§ŪŒ Ų³Ų±ŪŒŲ§Ł„ ŲÆŲ± ورودی Ł‡Ų§ŪŒ رزرو Ł…ŁˆŲ¬ŁˆŲÆŪŒ رزرو ؓده Ų§Ł†ŲÆŲŒ قبل Ų§Ų² ادامه باید آنها Ų±Ų§ Ł„ŲŗŁˆ رزرو Ś©Ł†ŪŒŲÆ., +Serial and Batch,Ų³Ų±ŪŒŲ§Ł„ و دسته, +Serial and Batch Bundle,باندل Ų³Ų±ŪŒŲ§Ł„ و دسته, +Serial and Batch Bundle created,باندل Ų³Ų±ŪŒŲ§Ł„ و دسته ایجاد Ų“ŲÆ, +Serial and Batch Bundle updated,باندل Ų³Ų±ŪŒŲ§Ł„ و دسته به روز Ų“ŲÆ, +Serial and Batch Details,جزئیات Ų³Ų±ŪŒŲ§Ł„ و دسته, +Serial and Batch Entry,Ų«ŲØŲŖ Ų³Ų±ŪŒŲ§Ł„ و دسته, +Serial and Batch No,ؓماره Ų³Ų±ŪŒŲ§Ł„ و دسته, +Serial and Batch Nos,ؓماره Ų³Ų±ŪŒŲ§Ł„ و دسته, +Serial and Batch Nos will be auto-reserved based on Pick Serial / Batch Based On,ؓماره Ł‡Ų§ŪŒ Ų³Ų±ŪŒŲ§Ł„ و دسته ŲØŲ± Ų§Ų³Ų§Ų³ انتخاب Ų³Ų±ŪŒŲ§Ł„ / دسته ŲØŲ± Ų§Ų³Ų§Ų³ به صورت خودکار رزرو Ł…ŪŒ Ų“ŁˆŁ†ŲÆ, +Serial and Batch Reservation,رزرو Ų³Ų±ŪŒŲ§Ł„ و دسته, +Serial and Batch Summary,خلاصه Ų³Ų±ŪŒŲ§Ł„ و دسته ای, +Service Cost Per Qty,Ł‡Ų²ŪŒŁ†Ł‡ Ų®ŲÆŁ…Ų§ŲŖ ŲÆŲ± هر ŲŖŲ¹ŲÆŲ§ŲÆ, +Service Expense Total Amount,Ł‡Ų²ŪŒŁ†Ł‡ Ų®ŲÆŁ…Ų§ŲŖ کل مبلغ, +Service Expenses,Ł‡Ų²ŪŒŁ†Ł‡ Ł‡Ų§ŪŒ Ų®ŲÆŁ…Ų§ŲŖ, +Service Item,Ų¢ŪŒŲŖŁ… Ų®ŲÆŁ…Ų§ŲŖ, +Service Item Qty,ŲŖŲ¹ŲÆŲ§ŲÆ Ł…ŁˆŲ±ŲÆ Ų®ŲÆŁ…Ų§ŲŖ, +Service Item Qty / Finished Good Qty,ŲŖŲ¹ŲÆŲ§ŲÆ Ų¢ŪŒŲŖŁ… Ų®ŲÆŁ…Ų§ŲŖ / ŲŖŲ¹ŲÆŲ§ŲÆ Ś©Ų§Ł„Ų§ŪŒ ŲŖŁ…Ų§Ł… ؓده, +Service Item UOM,Ł…ŁˆŲ±ŲÆ Ų®ŲÆŁ…Ų§ŲŖ UOM, +Service Item {0} is disabled.,Ł…ŁˆŲ±ŲÆ سرویس {0} ŲŗŪŒŲ±ŁŲ¹Ų§Ł„ Ų§Ų³ŲŖ., +Service Item {0} must be a non-stock item.,Ł…ŁˆŲ±ŲÆ سرویس {0} باید یک Ś©Ų§Ł„Ų§ŪŒ غیر Ł…ŁˆŲ¬ŁˆŲÆŪŒ ŲØŲ§Ų“ŲÆ., +Service Items,Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ŪŒ Ų®ŲÆŁ…Ų§ŲŖŪŒ, +Service Level Agreement for {0} {1} already exists.,قرارداد Ų³Ų·Ų­ سرویس برای {0} {1} Ų§Ų² قبل وجود ŲÆŲ§Ų±ŲÆ., +Service Level Name,نام Ų³Ų·Ų­ Ų®ŲÆŁ…Ų§ŲŖ, +Service Provider,ارائه دهنده Ų®ŲÆŁ…Ų§ŲŖ, +Set Default Supplier,ŲŖŲ§Ł…ŪŒŁ† کننده پیؓ فرض Ų±Ų§ ŲŖŁ†ŲøŪŒŁ… Ś©Ł†ŪŒŲÆ, +Set From Warehouse,Ł…Ų¬Ł…ŁˆŲ¹Ł‡ Ų§Ų² انبار, +Set Landed Cost Based on Purchase Invoice Rate,ŲŖŁ†ŲøŪŒŁ… Ł‡Ų²ŪŒŁ†Ł‡ ŲŖŁ…Ų§Ł… ؓده ŲŖŲ§ ŲÆŲ±ŲØ انبار ŲØŲ± Ų§Ų³Ų§Ų³ نرخ فاکتور خرید, +Set Loyalty Program,ŲŖŁ†ŲøŪŒŁ… برنامه وفاداری, +Set Operating Cost / Scrape Items From Sub-assemblies,ŲŖŁ†ŲøŪŒŁ… Ł‡Ų²ŪŒŁ†Ł‡ Ų¹Ł…Ł„ŪŒŲ§ŲŖŪŒ / Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ŪŒ ضایعات Ų§Ų² زیر Ł…ŁˆŁ†ŲŖŲ§Ś˜Ł‡Ų§, +Set Operating Cost Based On BOM Quantity,Ł‡Ų²ŪŒŁ†Ł‡ Ų¹Ł…Ł„ŪŒŲ§ŲŖŪŒ Ų±Ų§ ŲØŲ± Ų§Ų³Ų§Ų³ مقدار BOM ŲŖŁ†ŲøŪŒŁ… Ś©Ł†ŪŒŲÆ, +Set Process Loss Item Quantity,مقدار Ł…ŁˆŲ±ŲÆ Ų§Ų² ŲÆŲ³ŲŖ دادن ŁŲ±Ų¢ŪŒŁ†ŲÆ Ų±Ų§ ŲŖŁ†ŲøŪŒŁ… Ś©Ł†ŪŒŲÆ, +Set Project Status,ŲŖŁ†ŲøŪŒŁ… وضعیت Ł¾Ų±ŁˆŚ˜Ł‡, +Set Quantity,ŲŖŁ†ŲøŪŒŁ… مقدار, +Set Response Time for Priority {0} in row {1}.,زمان پاسخ Ų±Ų§ برای Ų§ŁˆŁ„ŁˆŪŒŲŖ {0} ŲÆŲ± ردیف {1} ŲŖŁ†ŲøŪŒŁ… Ś©Ł†ŪŒŲÆ., +Set Valuation Rate Based on Source Warehouse,نرخ Ų§Ų±Ų²Ų“ گذاری Ų±Ų§ ŲØŲ± Ų§Ų³Ų§Ų³ انبار منبع ŲŖŁ†ŲøŪŒŁ… Ś©Ł†ŪŒŲÆ, +Set Warehouse,Ł…Ų¬Ł…ŁˆŲ¹Ł‡ انبار, +Set default {0} account for non stock items,ŲŖŁ†ŲøŪŒŁ… Ų­Ų³Ų§ŲØ Ł¾ŪŒŲ“ā€ŒŁŲ±Ų¶ {0} Ų±Ų§ برای Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ŪŒ غیر Ł…ŁˆŲ¬ŁˆŲÆŪŒ, +Set fieldname from which you want to fetch the data from the parent form.,نام ŁŪŒŁ„ŲÆŪŒ Ų±Ų§ که Ł…ŪŒā€ŒŲ®ŁˆŲ§Ł‡ŪŒŲÆ ŲÆŲ§ŲÆŁ‡ā€ŒŁ‡Ų§ Ų±Ų§ Ų§Ų² فرم ŁˆŲ§Ł„ŲÆ دریافت Ś©Ł†ŪŒŲÆŲŒ ŲŖŁ†ŲøŪŒŁ… Ś©Ł†ŪŒŲÆ., +Set quantity of process loss item:,ŲŖŁ†ŲøŪŒŁ… مقدار Ł…ŁˆŲ±ŲÆ Ų§Ų² ŲÆŲ³ŲŖ دادن ŁŲ±Ų¢ŪŒŁ†ŲÆ:, +Set the Planned Start Date (an Estimated Date at which you want the Production to begin),تاریخ ؓروع برنامه ریزی ؓده Ų±Ų§ ŲŖŁ†ŲøŪŒŁ… Ś©Ł†ŪŒŲÆ (تاریخ ŲŖŲ®Ł…ŪŒŁ†ŪŒ که ŲÆŲ± آن Ł…ŪŒ Ų®ŁˆŲ§Ł‡ŪŒŲÆ ŲŖŁˆŁ„ŪŒŲÆ ؓروع ؓود), +Set the status manually.,وضعیت Ų±Ų§ به صورت دستی ŲŖŁ†ŲøŪŒŁ… Ś©Ł†ŪŒŲÆ., +Set {0} in asset category {1} for company {2},ŲŖŁ†ŲøŪŒŁ… {0} ŲÆŲ± دسته دارایی {1} برای ؓرکت {2}, +Sets 'Accepted Warehouse' in each row of the Items table.,انبار Ł¾Ų°ŪŒŲ±ŁŲŖŁ‡ ؓده Ų±Ų§ ŲÆŲ± هر ردیف Ų§Ų² Ų¬ŲÆŁˆŁ„ Ų¢ŪŒŲŖŁ… ها ŲŖŁ†ŲøŪŒŁ… Ł…ŪŒ کند., +Sets 'Rejected Warehouse' in each row of the Items table.,انبار Ł…Ų±Ų¬ŁˆŲ¹ŪŒ Ų±Ų§ ŲÆŲ± هر ردیف Ų§Ų² Ų¬ŲÆŁˆŁ„ Ų¢ŪŒŲŖŁ… ها ŲŖŁ†ŲøŪŒŁ… Ł…ŪŒ کند., +Sets 'Reserve Warehouse' in each row of the Supplied Items table.,انبار Ų°Ų®ŪŒŲ±Ł‡ Ų±Ų§ ŲÆŲ± هر ردیف Ų§Ų² Ų¬ŲÆŁˆŁ„ اقلام عرضه ؓده ŲŖŁ†ŲøŪŒŁ… Ł…ŪŒ کند., +Setting Item Locations...,ŲŖŁ†ŲøŪŒŁ… مکان Ł…ŁˆŲ±ŲÆ..., +Setting the account as a Company Account is necessary for Bank Reconciliation,ŲŖŁ†ŲøŪŒŁ… Ų­Ų³Ų§ŲØ به Ų¹Ł†ŁˆŲ§Ł† Ų­Ų³Ų§ŲØ ؓرکت برای ŲŖŲ³ŁˆŪŒŁ‡ Ų­Ų³Ų§ŲØ ŲØŲ§Ł†Ś©ŪŒ ضروری Ų§Ų³ŲŖ, +Setting {} is required,ŲŖŁ†ŲøŪŒŁ… {} Ł…ŁˆŲ±ŲÆ Ł†ŪŒŲ§Ų² Ų§Ų³ŲŖ, +Setup your organization,سازمان خود Ų±Ų§ Ų±Ų§Ł‡ā€ŒŲ§Ł†ŲÆŲ§Ų²ŪŒ Ś©Ł†ŪŒŲÆ, +Shift,تغییر مکان, +Shift Factor,ضریب تغییر, +Shift Name,نام ؓیفت, +Shipment,Ų­Ł…Ł„ و نقل, +Shipment Amount,مبلغ Ų­Ł…Ł„ و نقل, +Shipment Delivery Note,یادداؓت ŲŖŲ­ŁˆŪŒŁ„ Ų­Ł…Ł„ و نقل, +Shipment ID,ؓناسه Ų­Ł…Ł„ و نقل, +Shipment Information,اطلاعات Ų­Ł…Ł„ و نقل, +Shipment Parcel,بسته Ų­Ł…Ł„ و نقل, +Shipment Parcel Template,قالب بسته Ų­Ł…Ł„ و نقل, +Shipment Type,Ł†ŁˆŲ¹ Ų­Ł…Ł„ و نقل, +Shipment details,جزئیات Ų­Ł…Ł„ و نقل, +Shipping Address Details,جزئیات Ų¢ŲÆŲ±Ų³ Ų­Ł…Ł„ و نقل, +Shipping Address Template,Ų§Ł„ŚÆŁˆŪŒ Ų¢ŲÆŲ±Ų³ Ų­Ł…Ł„ و نقل, +Show Balances in Chart Of Accounts,Ł†Ł…Ų§ŪŒŲ“ Ł…ŁˆŲ¬ŁˆŲÆŪŒ ŲÆŲ± Ł†Ł…ŁˆŲÆŲ§Ų± Ų­Ų³Ų§ŲØ, +Show Barcode Field in Stock Transactions,Ł†Ł…Ų§ŪŒŲ“ ŁŪŒŁ„ŲÆ بارکد ŲÆŲ± معاملات Ł…ŁˆŲ¬ŁˆŲÆŪŒ, +Show Dimension Wise Stock,Ł†Ł…Ų§ŪŒŲ“ Ł…ŁˆŲ¬ŁˆŲÆŪŒ ŲØŲ± Ų§Ų³Ų§Ų³ Ų§ŲØŲ¹Ų§ŲÆ, +Show Disabled Warehouses,Ł†Ł…Ų§ŪŒŲ“ Ų§Ł†ŲØŲ§Ų±Ł‡Ų§ŪŒ ŲŗŪŒŲ±ŁŲ¹Ų§Ł„, +Show Failed Logs,Ł†Ł…Ų§ŪŒŲ“ لاگ Ł‡Ų§ŪŒ Ł†Ų§Ł…ŁˆŁŁ‚, +Show GL Balance,Ł†Ł…Ų§ŪŒŲ“ ŲŖŲ±Ų§Ų² دفتر کل, +Show Item Name,Ł†Ł…Ų§ŪŒŲ“ نام Ų¢ŪŒŲŖŁ…, +Show Ledger View,Ł†Ł…Ų§ŪŒŲ“ Ł†Ł…Ų§ŪŒ دفتر کل, +Show Net Values in Party Account,Ł†Ł…Ų§ŪŒŲ“ Ų§Ų±Ų²Ų“ خالص ŲÆŲ± Ų­Ų³Ų§ŲØ طرف, +Show Pay Button in Purchase Order Portal,Ł†Ł…Ų§ŪŒŲ“ دکمه پرداخت ŲÆŲ± Ł¾ŁˆŲ±ŲŖŲ§Ł„ سفارؓ خرید, +Show Preview,Ł†Ł…Ų§ŪŒŲ“ پیؓ Ł†Ł…Ų§ŪŒŲ“, +Show Remarks,Ł†Ł…Ų§ŪŒŲ“ اظهارات, +Show Taxes as Table in Print,Ł†Ł…Ų§ŪŒŲ“ Ł…Ų§Ł„ŪŒŲ§ŲŖ به Ų¹Ł†ŁˆŲ§Ł† Ų¬ŲÆŁˆŁ„ ŲÆŲ± چاپ, +Show net values in opening and closing columns,Ł†Ł…Ų§ŪŒŲ“ Ł…Ł‚Ų§ŲÆŪŒŲ± خالص ŲÆŲ± Ų³ŲŖŁˆŁ† Ų§ŁŲŖŲŖŲ§Ų­ŪŒŁ‡ و Ų§Ų®ŲŖŲŖŲ§Ł…ŪŒŁ‡, +Show only the Immediate Upcoming Term,فقط Ų¹ŲØŲ§Ų±ŲŖ فوری Ų¢ŪŒŁ†ŲÆŁ‡ Ų±Ų§ نؓان ŲÆŁ‡ŪŒŲÆ, +Show pending entries,Ł†Ł…Ų§ŪŒŲ“ ورودی Ł‡Ų§ŪŒ معلق, +Show with upcoming revenue/expense,Ł†Ł…Ų§ŪŒŲ“ ŲØŲ§ درآمد/Ł‡Ų²ŪŒŁ†Ł‡ آتی, +"Simple Python Expression, Example: doc.status == 'Open' and doc.issue_type == 'Bug'",Ų¹ŲØŲ§Ų±ŲŖ ساده Ł¾Ų§ŪŒŲŖŁˆŁ†ŲŒ Ł…Ų«Ų§Ł„: doc.status == 'Open' و doc.issue_type == 'Bug', +Simultaneous,همزمان, +Skip Available Sub Assembly Items,صرف نظر Ų§Ų² Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ŪŒ زیر Ł…ŁˆŁ†ŲŖŲ§Ś˜ Ł…ŁˆŲ¬ŁˆŲÆ, +Skipped,Ų±ŲÆ Ų“ŲÆ, +Skipping Tax Withholding Category {0} as there is no associated account set for Company {1} in it.,Ų±ŲÆ ؓدن Ų§Ų² دسته Ł…Ų§Ł„ŪŒŲ§ŲŖ کسر Ł…Ų§Ł„ŪŒŲ§ŲŖ {0} زیرا Ł‡ŪŒŚ† Ų­Ų³Ų§ŲØ Ł…Ų±ŲŖŲØŲ·ŪŒ برای ؓرکت {1} ŲÆŲ± آن ŲŖŁ†ŲøŪŒŁ… نؓده Ų§Ų³ŲŖ., +"Skipping {0} of {1}, {2}",پرؓ Ų§Ų² {0} Ų§Ų² {1}، {2}, +Sold by,ŁŲ±ŁˆŲ®ŲŖŁ‡ ؓده توسط, +Something went wrong please try again,Ų§Ų“ŲŖŲØŲ§Ł‡ŪŒ Ų±Ų® داده لطفا ŲÆŁˆŲØŲ§Ų±Ł‡ تلاؓ Ś©Ł†ŪŒŲÆ, +Source Exchange Rate,نرخ مبادله منبع, +Source Fieldname,نام ŁŪŒŁ„ŲÆ منبع, +South Africa VAT Account,Ų­Ų³Ų§ŲØ Ł…Ų§Ł„ŪŒŲ§ŲŖ ŲØŲ± Ų§Ų±Ų²Ų“ Ų§ŁŲ²ŁˆŲÆŁ‡ Ų¢ŁŲ±ŪŒŁ‚Ų§ŪŒ Ų¬Ł†ŁˆŲØŪŒ, +South Africa VAT Settings,ŲŖŁ†ŲøŪŒŁ…Ų§ŲŖ Ł…Ų§Ł„ŪŒŲ§ŲŖ ŲØŲ± Ų§Ų±Ų²Ų“ Ų§ŁŲ²ŁˆŲÆŁ‡ Ų¢ŁŲ±ŪŒŁ‚Ų§ŪŒ Ų¬Ł†ŁˆŲØŪŒ, +Spacer,اسپیسر, +Split Asset,ŲŖŁ‚Ų³ŪŒŁ… دارایی, +Split Early Payment Discount Loss into Income and Tax Loss,Ų²ŪŒŲ§Ł† تخفیف پرداخت Ų²ŁˆŲÆŁ‡Ł†ŚÆŲ§Ł… Ų±Ų§ به درآمد و Ų¶Ų±Ų± Ł…Ų§Ł„ŪŒŲ§ŲŖ ŲŖŁ‚Ų³ŪŒŁ… Ś©Ł†ŪŒŲÆ, +Split From,ŲŖŁ‚Ų³ŪŒŁ… Ų§Ų², +Split Qty,ŲŖŁ‚Ų³ŪŒŁ… ŲŖŲ¹ŲÆŲ§ŲÆ, +Split qty cannot be grater than or equal to asset qty,مقدار ŲŖŁ‚Ų³ŪŒŁ… Ł†Ł…ŪŒā€ŒŲŖŁˆŲ§Ł†ŲÆ بیؓتر یا Ł…Ų³Ų§ŁˆŪŒ ŲŖŲ¹ŲÆŲ§ŲÆ دارایی ŲØŲ§Ų“ŲÆ, +Splitting {0} {1} into {2} rows as per Payment Terms,ŲŖŁ‚Ų³ŪŒŁ… {0} {1} به Ų±ŲÆŪŒŁā€ŒŁ‡Ų§ŪŒ {2} Ų·ŲØŁ‚ ؓرایط پرداخت, +Stage,صحنه, +Stale Days should start from 1.,Ų±ŁˆŲ²Ł‡Ų§ŪŒ Ł‚ŲÆŪŒŁ…ŪŒ باید Ų§Ų² 1 ؓروع ؓود., +Standard Description,Ų“Ų±Ų­ استاندارد, +Standard Rated Expenses,Ł‡Ų²ŪŒŁ†Ł‡ Ł‡Ų§ŪŒ رتبه ŲØŁ†ŲÆŪŒ استاندارد, +"Standard Terms and Conditions that can be added to Sales and Purchases. Examples: Validity of the offer, Payment Terms, Safety and Usage, etc.",ؓرایط و ضوابط استاندارد که Ł…ŪŒ ŲŖŁˆŲ§Ł†ŲÆ به خرید و فروؓ اضافه ؓود. Ł…Ų«Ų§Ł„: Ų§Ų¹ŲŖŲØŲ§Ų± Ł¾ŪŒŲ“Ł†Ł‡Ų§ŲÆŲŒ ؓرایط پرداخت، Ų§ŪŒŁ…Ł†ŪŒ و استفاده و ŲŗŪŒŲ±Ł‡., +Standard rated supplies in {0},منابع دارای رتبه استاندارد ŲÆŲ± {0}, +Start / Resume,ؓروع / Ų§Ų² سرگیری, +Start Date should be lower than End Date,تاریخ ؓروع باید کمتر Ų§Ų² تاریخ Ł¾Ų§ŪŒŲ§Ł† ŲØŲ§Ų“ŲÆ, +Start Deletion,ؓروع حذف, +Start Import,ؓروع ŲÆŲ±ŁˆŁ†ā€ŒŲØŁŲ±ŲÆ, +Start Job,ؓروع کار, +Start Merge,Ų§ŲÆŲŗŲ§Ł… Ų±Ų§ ؓروع Ś©Ł†ŪŒŲÆ, +Start Reposting,بازنؓر Ų±Ų§ ؓروع Ś©Ł†ŪŒŲÆ, +Start Time can't be greater than or equal to End Time for {0}.,زمان ؓروع Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł†ŲÆ بزرگتر یا Ł…Ų³Ų§ŁˆŪŒ ŲØŲ§ زمان Ł¾Ų§ŪŒŲ§Ł† برای {0} ŲØŲ§Ų“ŲÆ., +Started a background job to create {1} {0},یک کار Ł¾Ų³ā€ŒŲ²Ł…ŪŒŁ†Ł‡ برای ایجاد {1} {0} ؓروع کرد, +Status Details,جزئیات وضعیت, +Status Illustration,Ł…ŲµŁˆŲ± سازی وضعیت, +Status set to rejected as there are one or more rejected readings.,وضعیت Ų±ŲÆ Ų“ŲÆ زیرا یک یا چند قرائت Ų±ŲÆ ؓده وجود ŲÆŲ§Ų±ŲÆ., +Stock Closing,Ų§Ų®ŲŖŲŖŲ§Ł…ŪŒŁ‡ Ł…ŁˆŲ¬ŁˆŲÆŪŒ, +Stock Consumed During Repair,Ł…ŁˆŲ¬ŁˆŲÆŪŒ مصرف ؓده ŲÆŲ± Ų­ŪŒŁ† ŲŖŲ¹Ł…ŪŒŲ±, +Stock Consumption Details,جزئیات مصرف Ł…ŁˆŲ¬ŁˆŲÆŪŒ, +Stock Entries already created for Work Order {0}: {1},Ų«ŲØŲŖ Ł‡Ų§ŪŒ Ł…ŁˆŲ¬ŁˆŲÆŪŒ قبلاً برای دستور کار {0} ایجاد Ų“ŲÆŁ‡ā€ŒŲ§Ł†ŲÆ: {1}, +Stock Ledger Invariant Check,چک Ų«Ų§ŲØŲŖ دفتر کل Ł…ŁˆŲ¬ŁˆŲÆŪŒ, +Stock Ledger Variance,ŁˆŲ§Ų±ŪŒŲ§Ł†Ų³ دفتر کل Ł…ŁˆŲ¬ŁˆŲÆŪŒ, +Stock Movement,Ų¬Ų§ŲØŁ‡ā€ŒŲ¬Ų§ŪŒŪŒ Ł…ŁˆŲ¬ŁˆŲÆŪŒ, +Stock Planning,برنامه ریزی Ł…ŁˆŲ¬ŁˆŲÆŪŒ, +Stock Reposting Settings,ŲŖŁ†ŲøŪŒŁ…Ų§ŲŖ Ų§Ų±Ų³Ų§Ł„ Ł…Ų¬ŲÆŲÆ Ł…ŁˆŲ¬ŁˆŲÆŪŒ, +Stock Reservation,رزرو Ł…ŁˆŲ¬ŁˆŲÆŪŒ, +Stock Reservation Entries Cancelled,ورودی Ł‡Ų§ŪŒ رزرو Ł…ŁˆŲ¬ŁˆŲÆŪŒ Ł„ŲŗŁˆ Ų“ŲÆ, +Stock Reservation Entries Created,Ł†ŁˆŲ“ŲŖŁ‡ Ł‡Ų§ŪŒ رزرو Ł…ŁˆŲ¬ŁˆŲÆŪŒ ایجاد Ų“ŲÆ, +Stock Reservation Entry,Ų«ŲØŲŖ رزرو Ł…ŁˆŲ¬ŁˆŲÆŪŒ, +Stock Reservation Entry cannot be updated as it has been delivered.,ورودی رزرو Ł…ŁˆŲ¬ŁˆŲÆŪŒ Ų±Ų§ Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł† به ŲÆŁ„ŪŒŁ„ ŲŖŲ­ŁˆŪŒŁ„ گرفتن به روز کرد., +"Stock Reservation Entry created against a Pick List cannot be updated. If you need to make changes, we recommend canceling the existing entry and creating a new one.",Ų«ŲØŲŖ رزرو Ł…ŁˆŲ¬ŁˆŲÆŪŒ ایجاد ؓده ŲÆŲ± ŲØŲ±Ų§ŲØŲ± فهرست Ų§Ł†ŲŖŲ®Ų§ŲØŪŒ Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł†ŲÆ به روز ؓود. Ų§ŚÆŲ± Ł†ŪŒŲ§Ų² به ایجاد تغییرات دارید، ŲŖŁˆŲµŪŒŁ‡ Ł…ŪŒ Ś©Ł†ŪŒŁ… Ų«ŲØŲŖ Ł…ŁˆŲ¬ŁˆŲÆ Ų±Ų§ Ł„ŲŗŁˆ Ś©Ł†ŪŒŲÆ و یک Ų«ŲØŲŖ جدید ایجاد Ś©Ł†ŪŒŲÆ., +Stock Reservation Warehouse Mismatch,Ų¹ŲÆŁ… ŲŖŲ·Ų§ŲØŁ‚ انبار رزرو انبار, +Stock Reservation can only be created against {0}.,رزرو Ł…ŁˆŲ¬ŁˆŲÆŪŒ فقط Ł…ŪŒ ŲŖŁˆŲ§Ł†ŲÆ ŲÆŲ± مقابل {0} ایجاد ؓود., +Stock Reserved Qty (in Stock UOM),ŲŖŲ¹ŲÆŲ§ŲÆ Ł…ŁˆŲ¬ŁˆŲÆŪŒ رزرو ؓده (ŲÆŲ± انبار UOM), +Stock Transactions Settings,ŲŖŁ†ŲøŪŒŁ…Ų§ŲŖ معاملات Ł…ŁˆŲ¬ŁˆŲÆŪŒ, +Stock UOM Quantity,مقدار Ł…ŁˆŲ¬ŁˆŲÆŪŒ UOM, +Stock Unreservation,Ų¹ŲÆŁ… رزرو Ł…ŁˆŲ¬ŁˆŲÆŪŒ, +Stock Validations,Ų§Ų¹ŲŖŲØŲ§Ų±Ų³Ł†Ų¬ŪŒ Ł…ŁˆŲ¬ŁˆŲÆŪŒ, +Stock cannot be reserved in group warehouse {0}.,Ł…ŁˆŲ¬ŁˆŲÆŪŒ ŲÆŲ± انبار ŚÆŲ±ŁˆŁ‡ŪŒ {0} قابل رزرو Ł†ŪŒŲ³ŲŖ., +Stock cannot be reserved in the group warehouse {0}.,Ł…ŁˆŲ¬ŁˆŲÆŪŒ ŲÆŲ± انبار ŚÆŲ±ŁˆŁ‡ŪŒ {0} قابل رزرو Ł†ŪŒŲ³ŲŖ., +Stock not available for Item {0} in Warehouse {1}.,Ł…ŁˆŲ¬ŁˆŲÆŪŒ برای Ś©Ų§Ł„Ų§ŪŒ {0} ŲÆŲ± انبار {1} Ł…ŁˆŲ¬ŁˆŲÆ Ł†ŪŒŲ³ŲŖ., +Stock quantity not enough for Item Code: {0} under warehouse {1}. Available quantity {2} {3}.,مقدار Ł…ŁˆŲ¬ŁˆŲÆŪŒ برای کد Ł…ŁˆŲ±ŲÆ کافی Ł†ŪŒŲ³ŲŖ: {0} ŲÆŲ± انبار {1}. مقدار Ł…ŁˆŲ¬ŁˆŲÆ {2} {3}., +Stock transactions that are older than the mentioned days cannot be modified.,معاملات Ł…ŁˆŲ¬ŁˆŲÆŪŒ ŲØŲ§ قدمت بیؓتر Ų§Ų² Ų±ŁˆŲ²Ł‡Ų§ŪŒ Ł…Ų°Ś©ŁˆŲ± قابل تغییر Ł†Ł…ŪŒ ŲØŲ§Ų“ŲÆ., +Stock/Accounts can not be frozen as processing of backdated entries is going on. Please try again later.,Ł…ŁˆŲ¬ŁˆŲÆŪŒ/Ų­Ų³Ų§ŲØā€ŒŁ‡Ų§ Ų±Ų§ Ł†Ł…ŪŒā€ŒŲŖŁˆŲ§Ł† Ł…Ų³ŲÆŁˆŲÆ کرد زیرا پردازؓ ŁˆŲ±ŁˆŲÆŪŒā€ŒŁ‡Ų§ŪŒ ŲØŁ‡ā€ŒŲŖŲ§Ų±ŪŒŲ® ŲÆŲ± Ų­Ų§Ł„ انجام Ų§Ų³ŲŖ. لطفاً ŲØŲ¹ŲÆŲ§Ł‹ ŲÆŁˆŲØŲ§Ų±Ł‡ امتحان Ś©Ł†ŪŒŲÆ., +Sub Assemblies & Raw Materials,زیر Ł…ŁˆŁ†ŲŖŲ§Ś˜Ł‡Ų§ و Ł…ŁˆŲ§ŲÆ Ų§ŁˆŁ„ŪŒŁ‡, +Sub Assembly Item,Ų¢ŪŒŲŖŁ… زیر Ł…ŁˆŁ†ŲŖŲ§Ś˜, +Sub Assembly Item Code,کد Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ŪŒ زیر Ł…ŁˆŁ†ŲŖŲ§Ś˜, +Sub Assembly Items,Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ŪŒ زیر Ł…ŁˆŁ†ŲŖŲ§Ś˜, +Sub Assembly Warehouse,انبار زیر Ł…ŁˆŁ†ŲŖŲ§Ś˜, +Sub Operation,Ų¹Ł…Ł„ŪŒŲ§ŲŖ فرعی, +Sub Operations,Ų¹Ł…Ł„ŪŒŲ§ŲŖ فرعی, +Subcontract BOM,قرارداد فرعی BOM, +Subcontract Order,سفارؓ قرارداد فرعی, +Subcontract Order Summary,خلاصه سفارؓ قرارداد فرعی, +Subcontract Return,بازگؓت قرارداد فرعی, +Subcontracting BOM,Ł¾ŪŒŁ…Ų§Ł†Ś©Ų§Ų±ŪŒ فرعی BOM, +Subcontracting Order,سفارؓ Ł¾ŪŒŁ…Ų§Ł†Ś©Ų§Ų±ŪŒ فرعی, +Subcontracting Order (Draft) will be auto-created on submission of Purchase Order.,سفارؓ Ł¾ŪŒŁ…Ų§Ł†Ś©Ų§Ų±ŪŒ فرعی (پیؓ Ł†ŁˆŪŒŲ³) ŲØŲ§ Ų§Ų±Ų³Ų§Ł„ سفارؓ خرید به صورت خودکار ایجاد Ł…ŪŒ ؓود., +Subcontracting Order Item,Ł…ŁˆŲ±ŲÆ سفارؓ Ł¾ŪŒŁ…Ų§Ł†Ś©Ų§Ų±ŪŒ فرعی, +Subcontracting Order Service Item,Ų¢ŪŒŲŖŁ… Ų®ŲÆŁ…Ų§ŲŖ سفارؓ قرارداد فرعی, +Subcontracting Order Supplied Item,اقلام عرضه ؓده سفارؓ Ł¾ŪŒŁ…Ų§Ł†Ś©Ų§Ų±ŪŒ فرعی, +Subcontracting Order {0} created.,سفارؓ قرارداد فرعی {0} ایجاد Ų“ŲÆ., +Subcontracting Purchase Order,سفارؓ خرید Ł¾ŪŒŁ…Ų§Ł†Ś©Ų§Ų±ŪŒ فرعی, +Subcontracting Receipt,رسید Ł¾ŪŒŁ…Ų§Ł†Ś©Ų§Ų±ŪŒ فرعی, +Subcontracting Receipt Item,Ų¢ŪŒŲŖŁ… رسید Ł¾ŪŒŁ…Ų§Ł†Ś©Ų§Ų±ŪŒ, +Subcontracting Receipt Supplied Item,Ų¢ŪŒŲŖŁ… عرضه ؓده رسید Ł¾ŪŒŁ…Ų§Ł†Ś©Ų§Ų±ŪŒ فرعی, +Subcontracting Settings,ŲŖŁ†ŲøŪŒŁ…Ų§ŲŖ Ł¾ŪŒŁ…Ų§Ł†Ś©Ų§Ų±ŪŒ فرعی, +Subdivision,زیر Ł…Ų¬Ł…ŁˆŲ¹Ł‡, +Submit Action Failed,اقدام Ų§Ų±Ų³Ų§Ł„ نؓد, +Submit After Import,Ų§Ų±Ų³Ų§Ł„ پس Ų§Ų² ŲÆŲ±ŁˆŁ†ā€ŒŲØŁŲ±ŲÆ, +Submit ERR Journals?,ŲÆŁŲŖŲ±Ł‡Ų§ŪŒ Ų±ŁˆŲ²Ł†Ų§Ł…Ł‡ ERR Ų§Ų±Ų³Ų§Ł„ ؓود؟, +Submit Generated Invoices,ŁŲ§Ś©ŲŖŁˆŲ±Ł‡Ų§ŪŒ ŲŖŁˆŁ„ŪŒŲÆ ؓده Ų±Ų§ Ų§Ų±Ų³Ų§Ł„ Ś©Ł†ŪŒŲÆ, +Succeeded,Ł…ŁˆŁŁ‚ Ų“ŲÆ, +Succeeded Entries,ورودی Ł‡Ų§ŪŒ Ł…ŁˆŁŁ‚, +"Successfully changed Stock UOM, please redefine conversion factors for new UOM.",UOM Ł…ŁˆŲ¬ŁˆŲÆŪŒ ŲØŲ§ Ł…ŁˆŁŁ‚ŪŒŲŖ تغییر کرد، لطفاً ŁŲ§Ś©ŲŖŁˆŲ±Ł‡Ų§ŪŒ ŲŖŲØŲÆŪŒŁ„ Ų±Ų§ برای UOM جدید ŲÆŁˆŲØŲ§Ų±Ł‡ تعریف Ś©Ł†ŪŒŲÆ., +Successfully imported {0},{0} ŲØŲ§ Ł…ŁˆŁŁ‚ŪŒŲŖ ŲÆŲ±ŁˆŁ†ā€ŒŲØŁŲ±ŲÆ Ų“ŲÆ, +"Successfully imported {0} record out of {1}. Click on Export Errored Rows, fix the errors and import again.",{0} رکورد Ų§Ų² {1} ŲØŲ§ Ł…ŁˆŁŁ‚ŪŒŲŖ ŲÆŲ±ŁˆŁ†ā€ŒŲØŁŲ±ŲÆ Ų“ŲÆ. روی Export Errored Rows Ś©Ł„ŪŒŚ© Ś©Ł†ŪŒŲÆŲŒ خطاها Ų±Ų§ برطرف کرده و ŲÆŁˆŲØŲ§Ų±Ł‡ وارد Ś©Ł†ŪŒŲÆ., +Successfully imported {0} record.,{0} رکورد ŲØŲ§ Ł…ŁˆŁŁ‚ŪŒŲŖ ŲÆŲ±ŁˆŁ†ā€ŒŲØŁŲ±ŲÆ Ų“ŲÆ., +"Successfully imported {0} records out of {1}. Click on Export Errored Rows, fix the errors and import again.",{0} رکورد Ų§Ų² {1} ŲØŲ§ Ł…ŁˆŁŁ‚ŪŒŲŖ ŲÆŲ±ŁˆŁ†ā€ŒŲØŁŲ±ŲÆ Ų“ŲÆ. روی Export Errored Rows Ś©Ł„ŪŒŚ© Ś©Ł†ŪŒŲÆŲŒ خطاها Ų±Ų§ برطرف کرده و ŲÆŁˆŲØŲ§Ų±Ł‡ وارد Ś©Ł†ŪŒŲÆ., +Successfully imported {0} records.,{0} رکورد ŲØŲ§ Ł…ŁˆŁŁ‚ŪŒŲŖ ŲÆŲ±ŁˆŁ†ā€ŒŲØŁŲ±ŲÆ Ų“ŲÆ., +Successfully linked to Customer,ŲØŲ§ Ł…ŁˆŁŁ‚ŪŒŲŖ به Ł…Ų“ŲŖŲ±ŪŒ Ł¾ŪŒŁˆŁ†ŲÆ داده Ų“ŲÆ, +Successfully linked to Supplier,ŲØŲ§ Ł…ŁˆŁŁ‚ŪŒŲŖ به ŲŖŲ§Ł…ŪŒŁ† کننده Ł¾ŪŒŁˆŁ†ŲÆ داده Ų“ŲÆ, +Successfully merged {0} out of {1}.,{0} Ų§Ų² {1} ŲØŲ§ Ł…ŁˆŁŁ‚ŪŒŲŖ Ų§ŲÆŲŗŲ§Ł… Ų“ŲÆ., +Successfully updated {0},ŲØŲ§ Ł…ŁˆŁŁ‚ŪŒŲŖ به روز Ų“ŲÆ {0}, +"Successfully updated {0} record out of {1}. Click on Export Errored Rows, fix the errors and import again.",{0} رکورد Ų§Ų² {1} ŲØŲ§ Ł…ŁˆŁŁ‚ŪŒŲŖ به روز Ų“ŲÆ. روی Export Errored Rows Ś©Ł„ŪŒŚ© Ś©Ł†ŪŒŲÆŲŒ خطاها Ų±Ų§ برطرف کرده و ŲÆŁˆŲØŲ§Ų±Ł‡ وارد Ś©Ł†ŪŒŲÆ., +Successfully updated {0} record.,رکورد {0} ŲØŲ§ Ł…ŁˆŁŁ‚ŪŒŲŖ به روز Ų“ŲÆ., +"Successfully updated {0} records out of {1}. Click on Export Errored Rows, fix the errors and import again.",{0} رکورد Ų§Ų² {1} ŲØŲ§ Ł…ŁˆŁŁ‚ŪŒŲŖ به روز Ų“ŲÆ. روی Export Errored Rows Ś©Ł„ŪŒŚ© Ś©Ł†ŪŒŲÆŲŒ خطاها Ų±Ų§ برطرف کرده و ŲÆŁˆŲØŲ§Ų±Ł‡ وارد Ś©Ł†ŪŒŲÆ., +Successfully updated {0} records.,رکورد {0} ŲØŲ§ Ł…ŁˆŁŁ‚ŪŒŲŖ به روز Ų“ŲÆ., +Sum of Repair Cost and Value of Consumed Stock Items.,Ł…Ų¬Ł…ŁˆŲ¹ Ł‡Ų²ŪŒŁ†Ł‡ ŲŖŲ¹Ł…ŪŒŲ± و Ų§Ų±Ų²Ų“ Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ŪŒ Ł…ŁˆŲ¬ŁˆŲÆŪŒ مصرف ؓده., +Supplied Item,Ł…ŁˆŲ±ŲÆ عرضه ؓده, +Supplier Address Details,جزئیات Ų¢ŲÆŲ±Ų³ ŲŖŲ§Ł…ŪŒŁ† کننده, +Supplier Contact,ŲŖŁ…Ų§Ų³ ŲØŲ§ ŲŖŲ§Ł…ŪŒŁ† کننده, +Supplier Group Item,Ł…ŁˆŲ±ŲÆ ŚÆŲ±ŁˆŁ‡ ŲŖŲ§Ł…ŪŒŁ† کننده, +Supplier Info,اطلاعات ŲŖŲ§Ł…ŪŒŁ† کننده, +Supplier Invoice,فاکتور ŲŖŲ§Ł…ŪŒŁ† کننده, +Supplier Item,Ł…ŁˆŲ±ŲÆ ŲŖŲ§Ł…ŪŒŁ† کننده, +Supplier Portal Users,کاربران Ł¾ŁˆŲ±ŲŖŲ§Ł„ ŲŖŲ§Ł…ŪŒŁ† کننده, +Supplier Primary Address,Ų¢ŲÆŲ±Ų³ Ų§ŲµŁ„ŪŒ ŲŖŲ§Ł…ŪŒŁ† کننده, +Supplier Primary Contact,ŲŖŁ…Ų§Ų³ Ų§ŲµŁ„ŪŒ ŲŖŲ§Ł…ŪŒŁ† کننده, +Supplier Warehouse mandatory for sub-contracted {0},انبار ŲŖŲ§Ł…ŪŒŁ† کننده برای Ł‚Ų±Ų§Ų±ŲÆŲ§ŲÆŁ‡Ų§ŪŒ فرعی {0} اجباری Ų§Ų³ŲŖ, +Supplies subject to the reverse charge provision,Ł„ŁˆŲ§Ų²Ł… Ł…Ų“Ł…ŁˆŁ„ ارائه ؓارژ Ł…Ų¹Ś©ŁˆŲ³, +Sync Now,Ų§Ś©Ł†ŁˆŁ† همگام سازی Ś©Ł†ŪŒŲÆ, +Sync Started,همگام سازی ؓروع Ų“ŲÆ, +System Settings,ŲŖŁ†ŲøŪŒŁ…Ų§ŲŖ Ų³ŪŒŲ³ŲŖŁ…, +System will automatically create the serial numbers / batch for the Finished Good on submission of work order,Ų³ŪŒŲ³ŲŖŁ… به طور خودکار ؓماره Ų³Ų±ŪŒŲ§Ł„ / دسته ای Ų±Ų§ برای Ś©Ų§Ł„Ų§ŪŒ ŲŖŁ…Ų§Ł… ؓده ŲÆŲ± هنگام Ų§Ų±Ų³Ų§Ł„ سفارؓ ایجاد Ł…ŪŒ کند, +System will not check over billing since amount for Item {0} in {1} is zero,Ų³ŪŒŲ³ŲŖŁ… صورتحساب Ų±Ų§ بررسی Ł†Ł…ŪŒā€ŒŚ©Ł†ŲÆ زیرا مبلغ Ł…ŁˆŲ±ŲÆ {0} ŲÆŲ± {1} صفر Ų§Ų³ŲŖ, +TDS Payable,TDS قابل پرداخت, +Target Asset,دارایی هدف, +Target Asset Location,مکان دارایی Ł…ŁˆŲ±ŲÆ نظر, +Target Asset {0} cannot be cancelled,دارایی هدف {0} قابل Ł„ŲŗŁˆ Ł†ŪŒŲ³ŲŖ, +Target Asset {0} cannot be submitted,دارایی هدف {0} قابل Ų§Ų±Ų³Ų§Ł„ Ł†ŪŒŲ³ŲŖ, +Target Asset {0} cannot be {1},دارایی هدف {0} Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł†ŲÆ {1} ŲØŲ§Ų“ŲÆ, +Target Asset {0} does not belong to company {1},دارایی هدف {0} به ؓرکت {1} تعلق ندارد, +Target Asset {0} needs to be composite asset,دارایی هدف {0} باید دارایی ترکیبی ŲØŲ§Ų“ŲÆ, +Target Batch No,ؓماره دسته هدف, +Target Exchange Rate,نرخ Ų§Ų±Ų² هدف, +Target Fieldname (Stock Ledger Entry),نام ŁŪŒŁ„ŲÆ هدف (Ų«ŲØŲŖ دفتر کل Ł…ŁˆŲ¬ŁˆŲÆŪŒ), +Target Fixed Asset Account,Ų­Ų³Ų§ŲØ دارایی Ų«Ų§ŲØŲŖ هدف, +Target Has Batch No,هدف دارای Batch No, +Target Has Serial No,هدف دارای ؓماره Ų³Ų±ŪŒŲ§Ł„, +Target Incoming Rate,نرخ ورودی هدف, +Target Is Fixed Asset,هدف دارایی Ų«Ų§ŲØŲŖ Ų§Ų³ŲŖ, +Target Item Code,کد Ų¢ŪŒŲŖŁ… هدف, +Target Item Name,نام Ų¢ŪŒŲŖŁ… هدف, +Target Item {0} is neither a Fixed Asset nor a Stock Item,Ł…ŁˆŲ±ŲÆ هدف {0} نه یک دارایی Ų«Ų§ŲØŲŖ Ų§Ų³ŲŖ و نه یک Ś©Ų§Ł„Ų§ŪŒ Ł…ŁˆŲ¬ŁˆŲÆŪŒ, +Target Item {0} must be a Fixed Asset item,Ł…ŁˆŲ±ŲÆ هدف {0} باید یک Ł…ŁˆŲ±ŲÆ دارایی Ų«Ų§ŲØŲŖ ŲØŲ§Ų“ŲÆ, +Target Item {0} must be a Stock Item,Ł…ŁˆŲ±ŲÆ هدف {0} باید یک Ł…ŁˆŲ±ŲÆ Ł…ŁˆŲ¬ŁˆŲÆŪŒ ŲØŲ§Ų“ŲÆ, +Target Qty must be a positive number,ŲŖŲ¹ŲÆŲ§ŲÆ هدف باید یک Ų¹ŲÆŲÆ Ł…Ų«ŲØŲŖ ŲØŲ§Ų“ŲÆ, +Target Serial No,ؓماره Ų³Ų±ŪŒŲ§Ł„ هدف, +Target Warehouse is mandatory for Decapitalization,انبار هدف برای کاهؓ Ų³Ų±Ł…Ų§ŪŒŁ‡ اجباری Ų§Ų³ŲŖ, +Target Warehouse is set for some items but the customer is not an internal customer.,انبار هدف برای برخی Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ ŲŖŁ†ŲøŪŒŁ… ؓده Ų§Ų³ŲŖ Ų§Ł…Ų§ Ł…Ų“ŲŖŲ±ŪŒŲŒ یک Ł…Ų“ŲŖŲ±ŪŒ ŲÆŲ§Ų®Ł„ŪŒ Ł†ŪŒŲ³ŲŖ., +Task {0} depends on Task {1}. Please add Task {1} to the Tasks list.,ŁˆŲøŪŒŁŁ‡ {0} به ŁˆŲøŪŒŁŁ‡ {1} بستگی ŲÆŲ§Ų±ŲÆ. لطفاً ŁˆŲøŪŒŁŁ‡ {1} Ų±Ų§ به Ł„ŪŒŲ³ŲŖ وظایف اضافه Ś©Ł†ŪŒŲÆ., +Tax Amount,مقدار Ł…Ų§Ł„ŪŒŲ§ŲŖ, +Tax Amount will be rounded on a row(items) level,مقدار Ł…Ų§Ł„ŪŒŲ§ŲŖ ŲÆŲ± Ų³Ų·Ų­ ردیف (Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§) ŚÆŲ±ŲÆ Ł…ŪŒ ؓود, +Tax Refunds provided to Tourists under the Tax Refunds for Tourists Scheme,بازپرداخت Ł…Ų§Ł„ŪŒŲ§ŲŖ ŲØŲ± Ų§Ų³Ų§Ų³ Ų·Ų±Ų­ بازپرداخت Ł…Ų§Ł„ŪŒŲ§ŲŖ برای گردؓگران به گردؓگران ارائه Ł…ŪŒ ؓود, +Tax Settings,ŲŖŁ†ŲøŪŒŁ…Ų§ŲŖ Ł…Ų§Ł„ŪŒŲ§ŲŖŪŒ, +Tax Withheld Vouchers,Ś©ŁˆŁ¾Ł† Ł‡Ų§ŪŒ Ł…Ų§Ł„ŪŒŲ§ŲŖŪŒ کسر ؓده, +Tax Withholding Category {} against Company {} for Customer {} should have Cumulative Threshold value.,رده Ł…Ų§Ł„ŪŒŲ§ŲŖŪŒ {} ŲÆŲ± ŲØŲ±Ų§ŲØŲ± ؓرکت {} برای Ł…Ų“ŲŖŲ±ŪŒ {} باید مقدار آستانه ŲŖŲ¬Ł…Ų¹ŪŒ داؓته ŲØŲ§Ų“ŲÆ., +Tax Withholding Details,جزئیات کسر Ł…Ų§Ł„ŪŒŲ§ŲŖ, +Tax Withholding Net Total,کل خالص کسر Ł…Ų§Ł„ŪŒŲ§ŲŖ, +Tax will be withheld only for amount exceeding the cumulative threshold,Ł…Ų§Ł„ŪŒŲ§ŲŖ فقط برای Ł…ŲØŁ„ŲŗŪŒ که بیؓ Ų§Ų² آستانه ŲŖŲ¬Ł…Ų¹ŪŒ باؓد، کسر Ų®ŁˆŲ§Ł‡ŲÆ Ų“ŲÆ, +Team,ŲŖŪŒŁ…, +Telephony Call Type,Ł†ŁˆŲ¹ ŲŖŁ…Ų§Ų³ ŲŖŁ„ŁŁ†ŪŒ, +Template Item Selected,Ų¢ŪŒŲŖŁ… Ų§Ł„ŚÆŁˆ انتخاب Ų“ŲÆ, +Template Options,ŚÆŲ²ŪŒŁ†Ł‡ Ł‡Ų§ŪŒ Ų§Ł„ŚÆŁˆ, +Template Task,ŁˆŲøŪŒŁŁ‡ Ų§Ł„ŚÆŁˆ, +Template Warnings,Ł‡Ų“ŲÆŲ§Ų±Ł‡Ų§ŪŒ Ų§Ł„ŚÆŁˆ, +Terms & Conditions,ؓرایط و ضوابط, +Terms Template,Ų§Ł„ŚÆŁˆŪŒ ؓرایط, +Territory Item,Ł‚Ł„Ł…Ų±Łˆ Ł…ŁˆŲ±ŲÆ, +Territory Wise Sales,فروؓ Ų§Ų² نظر منطقه, +The Condition '{0}' is invalid,"Ų“Ų±Ų· ""{0}"" نامعتبر Ų§Ų³ŲŖ", +The Document Type {0} must have a Status field to configure Service Level Agreement,Ł†ŁˆŲ¹ سند {0} باید دارای یک ŁŪŒŁ„ŲÆ وضعیت برای Ł¾ŪŒŚ©Ų±ŲØŁ†ŲÆŪŒ قرارداد Ų³Ų·Ų­ سرویس ŲØŲ§Ų“ŲÆ, +"The GL Entries will be cancelled in the background, it can take a few minutes.",ŁˆŲ±ŁˆŲÆŪŒā€ŒŁ‡Ų§ŪŒ دفتر کل ŲÆŲ± Ł¾Ų³ā€ŒŲ²Ł…ŪŒŁ†Ł‡ Ł„ŲŗŁˆ Ł…ŪŒā€ŒŲ“ŁˆŁ†ŲÆŲŒ ممکن Ų§Ų³ŲŖ چند ŲÆŁ‚ŪŒŁ‚Ł‡ Ų·ŁˆŁ„ بکؓد., +"The Payment Request {0} is already paid, cannot process payment twice",درخواست پرداخت {0} قبلاً پرداخت ؓده است، Ł†Ł…ŪŒā€ŒŲŖŁˆŲ§Ł† پرداخت Ų±Ų§ دو ŲØŲ§Ų± پردازؓ کرد, +"The Pick List having Stock Reservation Entries cannot be updated. If you need to make changes, we recommend canceling the existing Stock Reservation Entries before updating the Pick List.",فهرست Ų§Ł†ŲŖŲ®Ų§ŲØŪŒ دارای ورودی Ł‡Ų§ŪŒ رزرو Ł…ŁˆŲ¬ŁˆŲÆŪŒ Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł†ŲÆ به روز ؓود. Ų§ŚÆŲ± Ł†ŪŒŲ§Ų² به ایجاد تغییرات دارید، ŲŖŁˆŲµŪŒŁ‡ Ł…ŪŒā€ŒŚ©Ł†ŪŒŁ… قبل Ų§Ų² ŲØŁ‡ā€ŒŲ±ŁˆŲ²Ų±Ų³Ų§Ł†ŪŒ فهرست Ų§Ł†ŲŖŲ®Ų§ŲØŲŒ ŁˆŲ±ŁˆŲÆŪŒā€ŒŁ‡Ų§ŪŒ رزرو Ł…ŁˆŲ¬ŁˆŲÆŪŒ Ų±Ų§ Ł„ŲŗŁˆ Ś©Ł†ŪŒŲÆ., +The Process Loss Qty has reset as per job cards Process Loss Qty,Process Loss Qty Ł…Ų·Ų§ŲØŁ‚ ŲØŲ§ کارت Ś©Ų§Ų±Ł‡Ų§ŪŒ Process Loss Ty ŲØŲ§Ų²Ł†Ų“Ų§Ł†ŪŒ ؓده Ų§Ų³ŲŖ, +The currency of invoice {} ({}) is different from the currency of this dunning ({}).,واحد Ł¾ŁˆŁ„ فاکتور {} ({}) ŲØŲ§ واحد Ł¾ŁˆŁ„ Ų§ŪŒŁ† ŲÆŁˆŁ†ŪŒŁ†ŚÆ ({}) Ł…ŲŖŁŲ§ŁˆŲŖ Ų§Ų³ŲŖ., +The default BOM for that item will be fetched by the system. You can also change the BOM.,BOM Ł¾ŪŒŲ“ā€ŒŁŲ±Ų¶ برای آن Ł…ŁˆŲ±ŲÆ توسط Ų³ŪŒŲ³ŲŖŁ… واکؓی Ł…ŪŒā€ŒŲ“ŁˆŲÆ. Ų“Ł…Ų§ Ł‡Ł…Ś†Ł†ŪŒŁ† Ł…ŪŒ ŲŖŁˆŲ§Ł†ŪŒŲÆ BOM Ų±Ų§ تغییر ŲÆŁ‡ŪŒŲÆ., +The field {0} in row {1} is not set,ŁŪŒŁ„ŲÆ {0} ŲÆŲ± ردیف {1} ŲŖŁ†ŲøŪŒŁ… نؓده Ų§Ų³ŲŖ, +"The following Items, having Putaway Rules, could not be accomodated:",Ł…ŁˆŲ§Ų±ŲÆ زیر که دارای Ł‚ŁˆŲ§Ł†ŪŒŁ† Putaway Ł‡Ų³ŲŖŁ†ŲÆŲŒ قابل استفاده Ł†ŪŒŲ³ŲŖŁ†ŲÆ:, +The following assets have failed to automatically post depreciation entries: {0},دارایی Ł‡Ų§ŪŒ زیر به طور خودکار ورودی Ł‡Ų§ŪŒ استهلاک Ų±Ų§ پست نکرده اند: {0}, +The items {0} and {1} are present in the following {2} :,Ł…ŁˆŲ§Ų±ŲÆ {0} و {1} ŲÆŲ± {2} زیر Ł…ŁˆŲ¬ŁˆŲÆ هستند:, +The operation {0} can not add multiple times,Ų¹Ł…Ł„ŪŒŲ§ŲŖ {0} Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł†ŲÆ Ś†Ł†ŲÆŪŒŁ† ŲØŲ§Ų± اضافه کند, +The operation {0} can not be the sub operation,Ų¹Ł…Ł„ŪŒŲ§ŲŖ {0} Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł†ŲÆ Ų¹Ł…Ł„ŪŒŲ§ŲŖ فرعی ŲØŲ§Ų“ŲÆ, +The reserved stock will be released when you update items. Are you certain you wish to proceed?,ŲØŲ§ ŲØŁ‡ā€ŒŲ±ŁˆŲ²Ų±Ų³Ų§Ł†ŪŒ Ł…ŁˆŲ§Ų±ŲÆŲŒ Ł…ŁˆŲ¬ŁˆŲÆŪŒ رزرو ؓده Ų¢Ų²Ų§ŲÆ Ł…ŪŒā€ŒŲ“ŁˆŲÆ. آیا مطمئن Ł‡Ų³ŲŖŪŒŲÆ که Ł…ŪŒ Ų®ŁˆŲ§Ł‡ŪŒŲÆ ادامه ŲÆŁ‡ŪŒŲÆŲŸ, +The reserved stock will be released. Are you certain you wish to proceed?,Ł…ŁˆŲ¬ŁˆŲÆŪŒ رزرو ؓده Ų¢Ų²Ų§ŲÆ Ų®ŁˆŲ§Ł‡ŲÆ Ų“ŲÆ. آیا مطمئن Ł‡Ų³ŲŖŪŒŲÆ که Ł…ŪŒ Ų®ŁˆŲ§Ł‡ŪŒŲÆ ادامه ŲÆŁ‡ŪŒŲÆŲŸ, +The selected {0} does not contain the selected Asset Item.,{0} انتخاب ؓده حاوی Ł…ŁˆŲ±ŲÆ دارایی Ų§Ł†ŲŖŲ®Ų§ŲØŪŒ Ł†ŪŒŲ³ŲŖ., +"The stock has been reserved for the following Items and Warehouses, un-reserve the same to {0} the Stock Reconciliation:

{1}",Ł…ŁˆŲ¬ŁˆŲÆŪŒ برای اقلام و Ų§Ł†ŲØŲ§Ų±Ł‡Ų§ŪŒ زیر رزرو ؓده است، همان Ų±Ų§ ŲÆŲ± {0} ŲŖŲ·ŲØŪŒŁ‚ Ł…ŁˆŲ¬ŁˆŲÆŪŒ Ł„ŲŗŁˆ Ś©Ł†ŪŒŲÆ:

{1}, +"The sync has started in the background, please check the {0} list for new records.",همگام سازی ŲÆŲ± پس Ų²Ł…ŪŒŁ†Ł‡ ؓروع ؓده است، لطفاً Ł„ŪŒŲ³ŲŖ {0} Ų±Ų§ برای Ų±Ś©ŁˆŲ±ŲÆŁ‡Ų§ŪŒ جدید بررسی Ś©Ł†ŪŒŲÆ., +"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 total Issue / Transfer quantity {0} in Material Request {1} cannot be greater than allowed requested quantity {2} for Item {3},Ł…Ų¬Ł…ŁˆŲ¹ مقدار Ų­ŁˆŲ§Ł„Ł‡ / انتقال {0} ŲÆŲ± درخواست Ł…ŁˆŲ§ŲÆ {1} Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł†ŲÆ بیؓتر Ų§Ų² مقدار Ł…Ų¬Ų§Ų² درخواستی {2} برای Ų¢ŪŒŲŖŁ… {3} ŲØŲ§Ų“ŲÆ, +The total Issue / Transfer quantity {0} in Material Request {1} cannot be greater than requested quantity {2} for Item {3},Ł…Ų¬Ł…ŁˆŲ¹ مقدار Ų­ŁˆŲ§Ł„Ł‡ / انتقال {0} ŲÆŲ± درخواست Ł…ŁˆŲ§ŲÆ {1} Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł†ŲÆ بیؓتر Ų§Ų² مقدار درخواستی {2} برای Ų¢ŪŒŲŖŁ… {3} ŲØŲ§Ų“ŲÆ, +"The users with this Role are allowed to create/modify a stock transaction, even though the transaction is frozen.",کاربران دارای Ų§ŪŒŁ† نقؓ Ł…Ų¬Ų§Ų² به ایجاد/تغییر تراکنؓ Ł…ŁˆŲ¬ŁˆŲÆŪŒ Ł‡Ų³ŲŖŁ†ŲÆŲŒ حتی Ų§ŚÆŲ± تراکنؓ Ł…Ų³ŲÆŁˆŲÆ ؓده ŲØŲ§Ų“ŲÆ., +The warehouse where you store finished Items before they are shipped.,Ų§Ł†ŲØŲ§Ų±ŪŒ که اقلام ŲŖŁ…Ų§Ł… ؓده Ų±Ų§ قبل Ų§Ų² Ų§Ų±Ų³Ų§Ł„ ŲÆŲ± آن Ų°Ų®ŪŒŲ±Ł‡ Ł…ŪŒ Ś©Ł†ŪŒŲÆ., +"The warehouse where you store your raw materials. Each required item can have a separate source warehouse. Group warehouse also can be selected as source warehouse. On submission of the Work Order, the raw materials will be reserved in these warehouses for production usage.",Ų§Ł†ŲØŲ§Ų±ŪŒ که Ł…ŁˆŲ§ŲÆ Ų§ŁˆŁ„ŪŒŁ‡ خود Ų±Ų§ ŲÆŲ± آن Ł†ŚÆŁ‡ŲÆŲ§Ų±ŪŒ Ł…ŪŒ Ś©Ł†ŪŒŲÆ. هر Ś©Ų§Ł„Ų§ŪŒ Ł…ŁˆŲ±ŲÆ Ł†ŪŒŲ§Ų² Ł…ŪŒ ŲŖŁˆŲ§Ł†ŲÆ یک انبار منبع جداگانه داؓته ŲØŲ§Ų“ŲÆ. انبار ŚÆŲ±ŁˆŁ‡ŪŒ Ł†ŪŒŲ² Ł…ŪŒ ŲŖŁˆŲ§Ł†ŲÆ به Ų¹Ł†ŁˆŲ§Ł† انبار منبع انتخاب ؓود. پس Ų§Ų² Ų§Ų±Ų³Ų§Ł„ دستور کار، Ł…ŁˆŲ§ŲÆ Ų§ŁˆŁ„ŪŒŁ‡ ŲÆŲ± Ų§ŪŒŁ† انبارها برای استفاده ŲŖŁˆŁ„ŪŒŲÆ رزرو Ł…ŪŒ ؓود., +The warehouse where your Items will be transferred when you begin production. Group Warehouse can also be selected as a Work in Progress warehouse.,Ų§Ł†ŲØŲ§Ų±ŪŒ که هنگام ؓروع ŲŖŁˆŁ„ŪŒŲÆŲŒ اقلام Ų“Ł…Ų§ ŲÆŲ± آن منتقل Ł…ŪŒ Ų“ŁˆŁ†ŲÆ. انبار ŚÆŲ±ŁˆŁ‡ŪŒ Ł‡Ł…Ś†Ł†ŪŒŁ† Ł…ŪŒ ŲŖŁˆŲ§Ł†ŲÆ به Ų¹Ł†ŁˆŲ§Ł† انبار Work in Progress انتخاب ؓود., +The {0} {1} is used to calculate the valuation cost for the finished good {2}.,{0} {1} برای محاسبه Ł‡Ų²ŪŒŁ†Ł‡ ارزیابی Ś©Ų§Ł„Ų§ŪŒ Ł†Ł‡Ų§ŪŒŪŒ {2} استفاده Ł…ŪŒā€ŒŲ“ŁˆŲÆ., +There are no Failed transactions,Ł‡ŪŒŚ† تراکنؓ Ł†Ų§Ł…ŁˆŁŁ‚ŪŒ وجود ندارد, +There are no slots available on this date,Ł‡ŪŒŚ† Ų§Ų³Ł„Ų§ŲŖŪŒ ŲÆŲ± Ų§ŪŒŁ† تاریخ Ł…ŁˆŲ¬ŁˆŲÆ Ł†ŪŒŲ³ŲŖ, +There are only {0} asset created or linked to {1}. Please create or link {2} Assets with respective document.,فقط {0} دارایی ایجاد ؓده یا به {1} Ł¾ŪŒŁˆŁ†ŲÆ داده ؓده Ų§Ų³ŲŖ. لطفاً {2} دارایی ها Ų±Ų§ ŲØŲ§ سند Ł…Ų±ŲØŁˆŲ·Ł‡ ایجاد یا Ł¾ŪŒŁˆŁ†ŲÆ ŲÆŁ‡ŪŒŲÆ., +"There are two options to maintain valuation of stock. FIFO (first in - first out) and Moving Average. To understand this topic in detail please visit Item Valuation, FIFO and Moving Average.",دو ŚÆŲ²ŪŒŁ†Ł‡ برای حفظ Ų§Ų±Ų²Ų“ Ł…ŁˆŲ¬ŁˆŲÆŪŒ وجود ŲÆŲ§Ų±ŲÆ. FIFO (Ų§ŁˆŁ„ŪŒŁ† ورود - Ų§ŁˆŁ„ŪŒŁ† خروج) و Ł…ŪŒŲ§Ł†ŚÆŪŒŁ† متحرک. برای درک جزئیات Ų§ŪŒŁ† Ł…ŁˆŲ¶ŁˆŲ¹ŲŒ لطفاً Ų§Ų² هدف Ų§Ų±Ų²Ų“ Ų§Ł‚Ł„Ų§Ł…ŲŒ FIFO و Ł…ŪŒŲ§Ł†ŚÆŪŒŁ† متحرک., +There aren't any item variants for the selected item,Ł‡ŪŒŚ† ŚÆŁˆŁ†Ł‡ Ų¢ŪŒŲŖŁ…ŪŒ برای Ų¢ŪŒŲŖŁ… Ų§Ł†ŲŖŲ®Ų§ŲØŪŒ وجود ندارد, +There is already a valid Lower Deduction Certificate {0} for Supplier {1} against category {2} for this time period.,ŲÆŲ± Ų­Ų§Ł„ Ų­Ų§Ų¶Ų± یک ŚÆŁˆŲ§Ł‡ŪŒ کسر کمتر Ł…Ų¹ŲŖŲØŲ± {0} برای ŲŖŲ§Ł…ŪŒŁ† کننده {1} ŲÆŲ± ŲØŲ±Ų§ŲØŲ± دسته {2} برای Ų§ŪŒŁ† ŲÆŁˆŲ±Ł‡ Ų²Ł…Ų§Ł†ŪŒ وجود ŲÆŲ§Ų±ŲÆ., +There is already an active Subcontracting BOM {0} for the Finished Good {1}.,ŲÆŲ± Ų­Ų§Ł„ Ų­Ų§Ų¶Ų± یک BOM قرارداد فرعی فعال {0} برای Ś©Ų§Ł„Ų§ŪŒ Ł†Ł‡Ų§ŪŒŪŒ {1} وجود ŲÆŲ§Ų±ŲÆ., +There must be atleast 1 Finished Good in this Stock Entry,باید حداقل 1 Ś©Ų§Ł„Ų§ŪŒ ŲŖŁ…Ų§Ł… ؓده ŲÆŲ± Ų§ŪŒŁ† Ų«ŲØŲŖ Ł…ŁˆŲ¬ŁˆŲÆŪŒ وجود داؓته ŲØŲ§Ų“ŲÆ, +There was an error creating Bank Account while linking with Plaid.,هنگام Ł¾ŪŒŁˆŁ†ŲÆ ŲØŲ§ Plaid خطایی ŲÆŲ± ایجاد Ų­Ų³Ų§ŲØ ŲØŲ§Ł†Ś©ŪŒ روی ŲÆŲ§ŲÆ., +There was an error syncing transactions.,هنگام Ł‡Ł…ŚÆŲ§Ł…ā€ŒŲ³Ų§Ų²ŪŒ ŲŖŲ±Ų§Ś©Ł†Ų“ā€ŒŁ‡Ų§ خطایی روی ŲÆŲ§ŲÆ., +There was an error updating Bank Account {} while linking with Plaid.,هنگام ŲØŁ‡ā€ŒŲ±ŁˆŲ²Ų±Ų³Ų§Ł†ŪŒ Ų­Ų³Ų§ŲØ ŲØŲ§Ł†Ś©ŪŒ {} هنگام Ł¾ŪŒŁˆŁ†ŲÆ ŲØŲ§ Plaid خطایی روی ŲÆŲ§ŲÆ., +There was an issue connecting to Plaid's authentication server. Check browser console for more information,Ł…Ų“Ś©Ł„ŪŒ ŲÆŲ± Ų§ŲŖŲµŲ§Ł„ به سرور تأیید Ų§Ų¹ŲŖŲØŲ§Ų± Plaid وجود ŲÆŲ§Ų“ŲŖ. برای اطلاعات بیؓتر Ś©Ł†Ų³ŁˆŁ„ Ł…Ų±ŁˆŲ±ŚÆŲ± Ų±Ų§ بررسی Ś©Ł†ŪŒŲÆ, +There were issues unlinking payment entry {0}.,Ł…Ų“Ś©Ł„Ų§ŲŖŪŒ ŲÆŲ± قطع Ł¾ŪŒŁˆŁ†ŲÆ Ų«ŲØŲŖ پرداخت {0} وجود ŲÆŲ§Ų“ŲŖ., +This Account has '0' balance in either Base Currency or Account Currency,Ų§ŪŒŁ† Ų­Ų³Ų§ŲØ دارای Ł…ŁˆŲ¬ŁˆŲÆŪŒ '0' به Ų§Ų±Ų² Ł¾Ų§ŪŒŁ‡ یا Ų§Ų±Ų² Ų­Ų³Ų§ŲØ Ų§Ų³ŲŖ, +This field is used to set the 'Customer'.,"Ų§ŪŒŁ† ŁŪŒŁ„ŲÆ برای ŲŖŁ†ŲøŪŒŁ… ""Ł…Ų“ŲŖŲ±ŪŒ"" استفاده Ł…ŪŒ ؓود.", +This filter will be applied to Journal Entry.,Ų§ŪŒŁ† ŁŪŒŁ„ŲŖŲ± برای Ų«ŲØŲŖ دفتر Ų±ŁˆŲ²Ł†Ų§Ł…Ł‡ اعمال Ų®ŁˆŲ§Ł‡ŲÆ Ų“ŲÆ., +This is a Template BOM and will be used to make the work order for {0} of the item {1},Ų§ŪŒŁ† یک Ų§Ł„ŚÆŁˆŪŒ BOM Ų§Ų³ŲŖ و برای ایجاد دستور کار برای {0} Ł…ŁˆŲ±ŲÆ {1} استفاده Ų®ŁˆŲ§Ł‡ŲÆ Ų“ŲÆ., +This is considered dangerous from accounting point of view.,Ų§ŪŒŁ† Ų§Ų² نظر حسابداری خطرناک ŲŖŁ„Ł‚ŪŒ Ł…ŪŒ ؓود., +"This is enabled by default. If you want to plan materials for sub-assemblies of the Item you're manufacturing leave this enabled. If you plan and manufacture the sub-assemblies separately, you can disable this checkbox.",Ų§ŪŒŁ† به طور پیؓ فرض فعال Ų§Ų³ŲŖ. Ų§ŚÆŲ± Ł…ŪŒā€ŒŲ®ŁˆŲ§Ł‡ŪŒŲÆ Ł…ŁˆŲ§ŲÆ Ų±Ų§ برای زیر Ł…ŁˆŁ†ŲŖŲ§Ś˜ Ł‡Ų§ŪŒ Ų¢ŪŒŲŖŁ…ŪŒ که ŲÆŲ± Ų­Ų§Ł„ ŲŖŁˆŁ„ŪŒŲÆ آن Ł‡Ų³ŲŖŪŒŲÆ ŲØŲ±Ł†Ų§Ł…Ł‡ā€ŒŲ±ŪŒŲ²ŪŒ Ś©Ł†ŪŒŲÆŲŒ Ų§ŪŒŁ† ŚÆŲ²ŪŒŁ†Ł‡ Ų±Ų§ فعال Ś©Ł†ŪŒŲÆ. Ų§ŚÆŲ± زیر Ł…ŁˆŁ†ŲŖŲ§Ś˜ ها Ų±Ų§ جداگانه برنامه ریزی و ŲŖŁˆŁ„ŪŒŲÆ Ł…ŪŒ Ś©Ł†ŪŒŲÆŲŒ Ł…ŪŒ ŲŖŁˆŲ§Ł†ŪŒŲÆ Ų§ŪŒŁ† چک باکس Ų±Ų§ ŲŗŪŒŲ±ŁŲ¹Ų§Ł„ Ś©Ł†ŪŒŲÆ., +"This is for raw material Items that'll be used to create finished goods. If the Item is an additional service like 'washing' that'll be used in the BOM, keep this unchecked.","Ų§ŪŒŁ† برای اقلام Ł…ŁˆŲ§ŲÆ Ų®Ų§Ł… Ų§Ų³ŲŖ که برای ایجاد Ś©Ų§Ł„Ų§Ł‡Ų§ŪŒ Ł†Ł‡Ų§ŪŒŪŒ استفاده Ł…ŪŒ ؓود. Ų§ŚÆŲ± Ł…ŁˆŲ±ŲÆ یک سرویس اضافی مانند ""ؓستن"" Ų§Ų³ŲŖ که ŲÆŲ± BOM استفاده Ł…ŪŒ ؓود، Ų§ŪŒŁ† Ł…ŁˆŲ±ŲÆ Ų±Ų§ علامت Ł†Ų²Ł†ŪŒŲÆ.", +This item filter has already been applied for the {0},Ų§ŪŒŁ† ŁŪŒŁ„ŲŖŲ± Ł…ŁˆŲ±ŲÆ قبلاً برای {0} اعمال ؓده Ų§Ų³ŲŖ, +This option can be checked to edit the 'Posting Date' and 'Posting Time' fields.,Ų§ŪŒŁ† ŚÆŲ²ŪŒŁ†Ł‡ برای ویرایؓ ŁŪŒŁ„ŲÆŁ‡Ų§ŪŒ «تاریخ ارسال» و «زمان ارسال» قابل بررسی Ų§Ų³ŲŖ., +This schedule was created when Asset {0} was adjusted through Asset Value Adjustment {1}.,Ų§ŪŒŁ† برنامه Ų²Ł…Ų§Ł†ŪŒ ایجاد Ų“ŲÆ که دارایی {0} Ų§Ų² Ų·Ų±ŪŒŁ‚ ŲŖŲ¹ŲÆŪŒŁ„ Ų§Ų±Ų²Ų“ دارایی {1} ŲŖŁ†ŲøŪŒŁ… Ų“ŲÆ., +This schedule was created when Asset {0} was consumed through Asset Capitalization {1}.,Ų§ŪŒŁ† برنامه Ų²Ł…Ų§Ł†ŪŒ ایجاد Ų“ŲÆ که دارایی {0} Ų§Ų² Ų·Ų±ŪŒŁ‚ Ų³Ų±Ł…Ų§ŪŒŁ‡ گذاری دارایی {1} مصرف Ų“ŲÆ., +This schedule was created when Asset {0} was repaired through Asset Repair {1}.,Ų§ŪŒŁ† برنامه Ų²Ł…Ų§Ł†ŪŒ ایجاد Ų“ŲÆ که دارایی {0} Ų§Ų² Ų·Ų±ŪŒŁ‚ ŲŖŲ¹Ł…ŪŒŲ± دارایی {1} ŲŖŲ¹Ł…ŪŒŲ± Ų“ŲÆ., +This schedule was created when Asset {0} was restored on Asset Capitalization {1}'s cancellation.,Ų§ŪŒŁ† برنامه Ų²Ł…Ų§Ł†ŪŒ ایجاد Ų“ŲÆ که دارایی {0} ŲÆŲ± Ł„ŲŗŁˆ دارایی ŲØŲ§ حروف بزرگ {1} بازیابی Ų“ŲÆ., +This schedule was created when Asset {0} was restored.,Ų§ŪŒŁ† برنامه Ų²Ł…Ų§Ł†ŪŒ ایجاد Ų“ŲÆ که دارایی {0} بازیابی Ų“ŲÆ., +This schedule was created when Asset {0} was returned through Sales Invoice {1}.,Ų§ŪŒŁ† برنامه Ų²Ł…Ų§Ł†ŪŒ ایجاد Ų“ŲÆ که دارایی {0} Ų§Ų² Ų·Ų±ŪŒŁ‚ فاکتور فروؓ {1} برگردانده Ų“ŲÆ., +This schedule was created when Asset {0} was scrapped.,Ų§ŪŒŁ† برنامه Ų²Ł…Ų§Ł†ŪŒ ایجاد Ų“ŲÆ که دارایی {0} Ł„ŲŗŁˆ Ų“ŲÆ., +This schedule was created when Asset {0} was sold through Sales Invoice {1}.,Ų§ŪŒŁ† برنامه Ų²Ł…Ų§Ł†ŪŒ ایجاد Ų“ŲÆ که دارایی {0} Ų§Ų² Ų·Ų±ŪŒŁ‚ فاکتور فروؓ {1} ŁŲ±ŁˆŲ®ŲŖŁ‡ Ų“ŲÆ., +This schedule was created when Asset {0} was updated after being split into new Asset {1}.,Ų§ŪŒŁ† برنامه Ų²Ł…Ų§Ł†ŪŒ ایجاد Ų“ŲÆ که دارایی {0} پس Ų§Ų² ŲŖŁ‚Ų³ŪŒŁ… به دارایی جدید {1} به روز Ų“ŲÆ., +This schedule was created when Asset {0}'s Asset Repair {1} was cancelled.,Ų§ŪŒŁ† برنامه Ų²Ł…Ų§Ł†ŪŒ ایجاد Ų“ŲÆ که ŲŖŲ¹Ł…ŪŒŲ± دارایی {0} {1} Ł„ŲŗŁˆ Ų“ŲÆ., +This schedule was created when Asset {0}'s Asset Value Adjustment {1} was cancelled.,Ų§ŪŒŁ† برنامه Ų²Ł…Ų§Ł†ŪŒ ایجاد Ų“ŲÆ که ŲŖŲ¹ŲÆŪŒŁ„ Ų§Ų±Ų²Ų“ دارایی {0} {1} Ł„ŲŗŁˆ Ų“ŲÆ., +This schedule was created when Asset {0}'s shifts were adjusted through Asset Shift Allocation {1}.,Ų§ŪŒŁ† برنامه Ų²Ł…Ų§Ł†ŪŒ ایجاد Ų“ŲÆ که تغییرات دارایی {0} Ų§Ų² Ų·Ų±ŪŒŁ‚ تخصیص تغییر دارایی {1} ŲŖŁ†ŲøŪŒŁ… Ų“ŲÆ., +This schedule was created when new Asset {0} was split from Asset {1}.,Ų§ŪŒŁ† برنامه Ų²Ł…Ų§Ł†ŪŒ ایجاد Ų“ŲÆ که دارایی جدید {0} Ų§Ų² دارایی {1} Ų¬ŲÆŲ§ Ų“ŲÆ., +"This table is used to set details about the 'Item', 'Qty', 'Basic Rate', etc.","Ų§ŪŒŁ† Ų¬ŲÆŁˆŁ„ برای ŲŖŁ†ŲøŪŒŁ… جزئیات Ł…Ų±ŲØŁˆŲ· به ""Ų¢ŪŒŲŖŁ…""، ""مقدار""، ""نرخ Ł¾Ų§ŪŒŁ‡"" و ŲŗŪŒŲ±Ł‡ استفاده Ł…ŪŒ ؓود.", +This {} will be treated as material transfer.,Ų§ŪŒŁ† {} به Ų¹Ł†ŁˆŲ§Ł† انتقال Ł…ŁˆŲ§ŲÆ ŲÆŲ± نظر گرفته Ł…ŪŒ ؓود., +Threshold for Suggestion (In Percentage),آستانه Ł¾ŪŒŲ“Ł†Ł‡Ų§ŲÆ (ŲÆŲ± ŲÆŲ±ŲµŲÆ), +Time Taken to Deliver,زمان صرف ؓده برای ŲŖŲ­ŁˆŪŒŁ„, +Time in mins,زمان به ŲÆŁ‚ŪŒŁ‚Ł‡, +Time in mins.,زمان به ŲÆŁ‚ŪŒŁ‚Ł‡., +Time slot is not available,بازه Ų²Ł…Ų§Ł†ŪŒ ŲÆŲ± ŲÆŲ³ŲŖŲ±Ų³ Ł†ŪŒŲ³ŲŖ, +To Delivery Date,ŲŖŲ§ تاریخ ŲŖŲ­ŁˆŪŒŁ„, +To Doctype,برای Doctype, +To Due Date,به تاریخ سررسید, +To Payment Date,به تاریخ پرداخت, +To Reference Date,به تاریخ Ł…Ų±Ų¬Ų¹, +To add Operations tick the 'With Operations' checkbox.,"برای Ų§ŁŲ²ŁˆŲÆŁ† Ų¹Ł…Ł„ŪŒŲ§ŲŖŲŒ کادر ""ŲØŲ§ Ų¹Ł…Ł„ŪŒŲ§ŲŖ"" Ų±Ų§ علامت ŲØŲ²Ł†ŪŒŲÆ.", +To add subcontracted Item's raw materials if include exploded items is disabled.,Ų§ŁŲ²ŁˆŲÆŁ† Ł…ŁˆŲ§ŲÆ Ų®Ų§Ł… Ł‚Ų±Ų§Ų±ŲÆŲ§ŲÆŪŒ فرعی ŲÆŲ± صورت وجود Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ŪŒ گسترده ؓده ŲŗŪŒŲ±ŁŲ¹Ų§Ł„ Ų§Ų³ŲŖ., +To apply condition on parent field use parent.field_name and to apply condition on child table use doc.field_name. Here field_name could be based on the actual column name of the respective field.,برای اعمال Ų“Ų±Ų· ŲÆŲ± ŁŪŒŁ„ŲÆ ŁˆŲ§Ł„ŲÆ Ų§Ų² parent.field_name و برای اعمال Ų“Ų±Ų· ŲÆŲ± Ų¬ŲÆŁˆŁ„ فرزند Ų§Ų² doc.field_name استفاده Ś©Ł†ŪŒŲÆ. ŲÆŲ± Ų§ŪŒŁ†Ų¬Ų§ field_name Ł…ŪŒ ŲŖŁˆŲ§Ł†ŲÆ ŲØŲ± Ų§Ų³Ų§Ų³ نام Ų³ŲŖŁˆŁ† ŁˆŲ§Ł‚Ų¹ŪŒ ŁŪŒŁ„ŲÆ Ł…Ų±ŲØŁˆŲ·Ł‡ ŲØŲ§Ų“ŲÆ., +To be Delivered to Customer,برای ŲŖŲ­ŁˆŪŒŁ„ به Ł…Ų“ŲŖŲ±ŪŒ, +To cancel a {} you need to cancel the POS Closing Entry {}.,برای Ł„ŲŗŁˆ یک {}، باید Ų«ŲØŲŖ Ų§Ų®ŲŖŲŖŲ§Ł…ŪŒŁ‡ POS {} Ų±Ų§ Ł„ŲŗŁˆ Ś©Ł†ŪŒŲÆ., +"To enable Capital Work in Progress Accounting,",برای فعال کردن حسابداری کار Ų³Ų±Ł…Ų§ŪŒŁ‡ ای،, +To include non-stock items in the material request planning. i.e. Items for which 'Maintain Stock' checkbox is unticked.,"گنجاندن Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ŪŒ غیر Ł…ŁˆŲ¬ŁˆŲÆŪŒ ŲÆŲ± برنامه ریزی درخواست Ł…ŁˆŲ§ŲÆ. به Ų¹Ł†ŁˆŲ§Ł† Ł…Ų«Ų§Ł„ Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ŪŒŪŒ که چک باکس ""Ł†ŚÆŁ‡ŲÆŲ§Ų±ŪŒ Ł…ŁˆŲ¬ŁˆŲÆŪŒ"" برای آنها علامت گذاری نؓده Ų§Ų³ŲŖ.", +To submit the invoice without purchase order please set {0} as {1} in {2},برای Ų§Ų±Ų³Ų§Ł„ فاکتور ŲØŲÆŁˆŁ† سفارؓ خرید لطفاً {0} Ų±Ų§ به Ų¹Ł†ŁˆŲ§Ł† {1} ŲÆŲ± {2} ŲŖŁ†ŲøŪŒŁ… Ś©Ł†ŪŒŲÆ, +To submit the invoice without purchase receipt please set {0} as {1} in {2},برای Ų§Ų±Ų³Ų§Ł„ فاکتور ŲØŲÆŁˆŁ† رسید خرید، لطفاً {0} Ų±Ų§ به Ų¹Ł†ŁˆŲ§Ł† {1} ŲÆŲ± {2} ŲŖŁ†ŲøŪŒŁ… Ś©Ł†ŪŒŲÆ., +"To use a different finance book, please uncheck 'Include Default FB Assets'",برای استفاده Ų§Ų² یک کتاب Ł…Ų§Ł„ŪŒ Ł…ŲŖŁŲ§ŁˆŲŖŲŒ لطفاً علامت «ؓامل ŲÆŲ§Ų±Ų§ŪŒŪŒā€ŒŁ‡Ų§ŪŒ Ł¾ŪŒŲ“ā€ŒŁŲ±Ų¶ FBĀ» Ų±Ų§ بردارید., +"To use a different finance book, please uncheck 'Include Default FB Entries'",برای استفاده Ų§Ų² یک کتاب Ł…Ų§Ł„ŪŒ Ł…ŲŖŁŲ§ŁˆŲŖŲŒ لطفاً علامت «ؓامل ŁˆŲ±ŁˆŲÆŪŒā€ŒŁ‡Ų§ŪŒ Ł¾ŪŒŲ“ā€ŒŁŲ±Ų¶ FBĀ» Ų±Ų§ بردارید., +Total Active Items,Ł…Ų¬Ł…ŁˆŲ¹ Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ŪŒ فعال, +Total Allocations,Ł…Ų¬Ł…ŁˆŲ¹ تخصیص ها, +Total Asset,کل دارایی, +Total Asset Cost,Ł‡Ų²ŪŒŁ†Ł‡ کل دارایی, +Total Billing Hours,کل Ų³Ų§Ų¹ŲŖ صورتحساب, +Total Contribution Amount Against Invoices: {0},Ł…Ų¬Ł…ŁˆŲ¹ مبلغ مؓارکت ŲÆŲ± ŲØŲ±Ų§ŲØŲ± ŁŲ§Ś©ŲŖŁˆŲ±Ł‡Ų§: {0}, +Total Contribution Amount Against Orders: {0},کل مبلغ مؓارکت ŲÆŲ± ŲØŲ±Ų§ŲØŲ± Ų³ŁŲ§Ų±Ų“ā€ŒŁ‡Ų§: {0}, +Total Equity,Ł…Ų¬Ł…ŁˆŲ¹ Ų­Ł‚ŁˆŁ‚ صاحبان Ł…ŁˆŲ¬ŁˆŲÆŪŒ, +Total Incoming Bills,Ł…Ų¬Ł…ŁˆŲ¹ صورتحساب Ł‡Ų§ŪŒ دریافتی, +Total Incoming Payment,کل پرداخت ورودی, +Total Incoming Value (Receipt),Ł…Ų¬Ł…ŁˆŲ¹ Ų§Ų±Ų²Ų“ ورودی (رسید), +Total Interest,سود کل, +Total Issues,Ł…Ų¬Ł…ŁˆŲ¹ مؓکلات, +Total Items,Ł…Ų¬Ł…ŁˆŲ¹ Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§, +Total Liability,کل Ł…Ų³Ų¦ŁˆŁ„ŪŒŲŖ, +Total Operation Time,کل زمان Ų¹Ł…Ł„ŪŒŲ§ŲŖ, +Total Other Charges,Ł…Ų¬Ł…ŁˆŲ¹ سایر Ł‡Ų²ŪŒŁ†Ł‡ ها, +Total Outgoing Bills,Ł…Ų¬Ł…ŁˆŲ¹ صورتحساب خروجی, +Total Outgoing Payment,کل پرداخت خروجی, +Total Outgoing Value (Consumption),کل Ų§Ų±Ų²Ų“ خروجی (مصرف), +Total Purchase Amount,کل مبلغ خرید, +Total Purchase Cost has been updated,کل Ł‡Ų²ŪŒŁ†Ł‡ خرید به روز ؓده Ų§Ų³ŲŖ, +Total Repair Cost,کل Ł‡Ų²ŪŒŁ†Ł‡ ŲŖŲ¹Ł…ŪŒŲ±, +Total Reposting Count,ŲŖŲ¹ŲÆŲ§ŲÆ کل بازنؓر, +Total Sales Amount,کل مبلغ فروؓ, +Total Stock Value,Ų§Ų±Ų²Ų“ کل Ł…ŁˆŲ¬ŁˆŲÆŪŒ, +Total Supplied Qty,ŲŖŲ¹ŲÆŲ§ŲÆ کل عرضه ؓده, +Total Time (in Mins),زمان کل (به ŲÆŁ‚ŪŒŁ‚Ł‡), +Total Value,Ų§Ų±Ų²Ų“ کل, +Total Value Difference (Incoming - Outgoing),تفاوت Ų§Ų±Ų²Ų“ کل (ورودی - خروجی), +Total Views,کل ŲØŲ§Ų²ŲÆŪŒŲÆŁ‡Ų§, +Total Warehouses,کل انبارها, +Total percentage against cost centers should be 100,ŲÆŲ±ŲµŲÆ کل ŲÆŲ± مقابل مراکز Ł‡Ų²ŪŒŁ†Ł‡ باید 100 ŲØŲ§Ų“ŲÆ, +Tracking Status,وضعیت پیگیری, +Tracking Status Info,اطلاعات وضعیت ردیابی, +Tracking URL,URL پیگیری, +Transaction Deletion Document: {0} is running for this Company. {1},سند حذف تراکنؓ: {0} برای Ų§ŪŒŁ† ؓرکت ŲÆŲ± Ų­Ų§Ł„ Ų§Ų¬Ų±Ų§ Ų§Ų³ŲŖ. {1}, +Transaction Deletion Record,رکورد حذف تراکنؓ, +Transaction Deletion Record Item,Ł…ŁˆŲ±ŲÆ رکورد حذف تراکنؓ, +Transaction Exchange Rate,نرخ Ų§Ų±Ų² معاملات, +Transaction Settings,ŲŖŁ†ŲøŪŒŁ…Ų§ŲŖ تراکنؓ, +Transactions against the Company already exist! Chart of Accounts can only be imported for a Company with no transactions.,معاملات ŲÆŲ± مقابل ؓرکت ŲÆŲ± Ų­Ų§Ł„ Ų­Ų§Ų¶Ų± وجود ŲÆŲ§Ų±ŲÆ! Ł†Ł…ŁˆŲÆŲ§Ų± Ų­Ų³Ų§ŲØ ها فقط برای ؓرکتی ŲØŲÆŁˆŁ† تراکنؓ قابل ŲÆŲ±ŁˆŁ†ā€ŒŲØŁŲ±ŲÆ Ų§Ų³ŲŖ., +Transfer Asset,انتقال دارایی, +Transfer From Warehouses,انتقال Ų§Ų² انبارها, +Transferring cannot be done to an Employee. Please enter location where Asset {0} has to be transferred,انتقال به کارمند امکان پذیر Ł†ŪŒŲ³ŲŖ. لطفاً Ł…Ś©Ų§Ł†ŪŒ Ų±Ų§ وارد Ś©Ł†ŪŒŲÆ که دارایی {0} باید ŲÆŲ± آنجا منتقل ؓود, +Transit,ŲŖŲ±Ų§Ł†Ų²ŪŒŲŖ, +Transit Entry,ورودی Ų­Ł…Ł„ و نقل, +Truncates 'Remarks' column to set character length,"Ų³ŲŖŁˆŁ† ""Remarks"" Ų±Ų§ برای ŲŖŁ†ŲøŪŒŁ… Ų·ŁˆŁ„ کاراکتر Ś©ŁˆŲŖŲ§Ł‡ Ł…ŪŒ کند", +Type Of Call,Ł†ŁˆŲ¹ ŲŖŁ…Ų§Ų³, +Type of Transaction,Ł†ŁˆŲ¹ تراکنؓ, +UAE VAT 201,Ł…Ų§Ł„ŪŒŲ§ŲŖ ŲØŲ± Ų§Ų±Ų²Ų“ Ų§ŁŲ²ŁˆŲÆŁ‡ Ų§Ł…Ų§Ų±Ų§ŲŖ متحده عربی 201, +UAE VAT Account,Ų­Ų³Ų§ŲØ Ł…Ų§Ł„ŪŒŲ§ŲŖ ŲØŲ± Ų§Ų±Ų²Ų“ Ų§ŁŲ²ŁˆŲÆŁ‡ Ų§Ł…Ų§Ų±Ų§ŲŖ متحده عربی, +UAE VAT Accounts,Ų­Ų³Ų§ŲØ Ł‡Ų§ŪŒ Ł…Ų§Ł„ŪŒŲ§ŲŖ ŲØŲ± Ų§Ų±Ų²Ų“ Ų§ŁŲ²ŁˆŲÆŁ‡ Ų§Ł…Ų§Ų±Ų§ŲŖ متحده عربی, +UAE VAT Settings,ŲŖŁ†ŲøŪŒŁ…Ų§ŲŖ Ł…Ų§Ł„ŪŒŲ§ŲŖ ŲØŲ± Ų§Ų±Ų²Ų“ Ų§ŁŲ²ŁˆŲÆŁ‡ Ų§Ł…Ų§Ų±Ų§ŲŖ متحده عربی, +UOM conversion factor required for UOM: {0} in Item: {1},ضریب ŲŖŲØŲÆŪŒŁ„ UOM Ł…ŁˆŲ±ŲÆ Ł†ŪŒŲ§Ų² برای UOM: {0} ŲÆŲ± Ł…ŁˆŲ±ŲÆ: {1}, +UnReconcile,ŲŖŲ·ŲØŪŒŁ‚ نکردن, +Unable to find variable:,قادر به ŪŒŲ§ŁŲŖŁ† Ł…ŲŖŲŗŪŒŲ± Ł†ŪŒŲ³ŲŖ:, +Unassigned Qty,ŲŖŲ¹ŲÆŲ§ŲÆ ŲŖŲ¹ŪŒŪŒŁ† نؓده, +"Under Working Hours table, you can add start and end times for a Workstation. For example, a Workstation may be active from 9 am to 1 pm, then 2 pm to 5 pm. You can also specify the working hours based on shifts. While scheduling a Work Order, the system will check for the availability of the Workstation based on the working hours specified.",ŲÆŲ± Ų¬ŲÆŁˆŁ„ Ų³Ų§Ų¹Ų§ŲŖ کاری، Ł…ŪŒ ŲŖŁˆŲ§Ł†ŪŒŲÆ زمان ؓروع و Ł¾Ų§ŪŒŲ§Ł† یک Ų§ŪŒŲ³ŲŖŚÆŲ§Ł‡ کاری Ų±Ų§ اضافه Ś©Ł†ŪŒŲÆ. به Ų¹Ł†ŁˆŲ§Ł† Ł…Ų«Ų§Ł„ŲŒ یک Ų§ŪŒŲ³ŲŖŚÆŲ§Ł‡ کاری ممکن Ų§Ų³ŲŖ Ų§Ų² Ų³Ų§Ų¹ŲŖ 9 ŲµŲØŲ­ ŲŖŲ§ 1 ŲØŲ¹ŲÆ Ų§Ų² ظهر و سپس Ų§Ų² 2 ŲØŲ¹ŲÆ Ų§Ų² ظهر ŲŖŲ§ 5 ŲØŲ¹ŲÆ Ų§Ų² ظهر فعال ŲØŲ§Ų“ŲÆ. Ł‡Ł…Ś†Ł†ŪŒŁ† Ł…ŪŒ ŲŖŁˆŲ§Ł†ŪŒŲÆ Ų³Ų§Ų¹ŲŖ کاری Ų±Ų§ ŲØŲ± Ų§Ų³Ų§Ų³ ؓیفت ها Ł…Ų“Ų®Ųµ Ś©Ł†ŪŒŲÆ. هنگام برنامه ریزی یک دستور کار، Ų³ŪŒŲ³ŲŖŁ… ŲØŲ± Ų§Ų³Ų§Ų³ Ų³Ų§Ų¹Ų§ŲŖ کاری Ł…Ų“Ų®Ųµ Ų“ŲÆŁ‡ŲŒ ŲÆŲ± ŲÆŲ³ŲŖŲ±Ų³ ŲØŁˆŲÆŁ† Ų§ŪŒŲ³ŲŖŚÆŲ§Ł‡ کاری Ų±Ų§ بررسی Ł…ŪŒ کند., +Unit of Measure (UOM),واحد اندازه گیری (UOM), +Unlinked,ŲØŲÆŁˆŁ† Ł¾ŪŒŁˆŁ†ŲÆ, +Unqualified,فاقد ŲµŁ„Ų§Ų­ŪŒŲŖ, +Unrealized Profit / Loss Account,Ų­Ų³Ų§ŲØ سود / Ų²ŪŒŲ§Ł† تحقق Ł†ŪŒŲ§ŁŲŖŁ‡, +Unrealized Profit / Loss account for intra-company transfers,Ų­Ų³Ų§ŲØ سود / Ų²ŪŒŲ§Ł† تحقق Ł†ŪŒŲ§ŁŲŖŁ‡ برای نقل و انتقالات ŲÆŲ±ŁˆŁ† ؓرکتی, +Unrealized Profit/Loss account for intra-company transfers,Ų­Ų³Ų§ŲØ سود/Ų²ŪŒŲ§Ł† تحقق Ł†ŪŒŲ§ŁŲŖŁ‡ برای نقل و انتقالات ŲÆŲ±ŁˆŁ† ؓرکتی, +Unreconcile Payment,پرداخت ناسازگار, +Unreconcile Payment Entries,ورودی Ł‡Ų§ŪŒ پرداخت ناسازگار, +Unreconcile Transaction,تراکنؓ ناسازگار, +Unreconciled Amount,مبلغ ناسازگار, +Unreconciled Entries,ورودی Ł‡Ų§ŪŒ ŲŖŲ·ŲØŪŒŁ‚ نگرفته, +Unreserve,Ł„ŲŗŁˆ رزرو Ś©Ł†ŪŒŲÆ, +Unreserve Stock,Ų°Ų®ŪŒŲ±Ł‡ Ł…ŁˆŲ¬ŁˆŲÆŪŒ, +Unreserving Stock...,Ų¹ŲÆŁ… رزرو Ł…ŁˆŲ¬ŁˆŲÆŪŒ..., +Up,بالا, +Update Billed Amount in Delivery Note,مبلغ صورتحساب ŲÆŲ± یادداؓت ŲŖŲ­ŁˆŪŒŁ„ Ų±Ų§ به روز Ś©Ł†ŪŒŲÆ, +Update Existing Price List Rate,به روز Ų±Ų³Ų§Ł†ŪŒ نرخ Ł„ŪŒŲ³ŲŖ Ł‚ŪŒŁ…ŲŖ Ł…ŁˆŲ¬ŁˆŲÆ, +Update Existing Records,به روز Ų±Ų³Ų§Ł†ŪŒ Ų±Ś©ŁˆŲ±ŲÆŁ‡Ų§ŪŒ Ł…ŁˆŲ¬ŁˆŲÆ, +Update Rate as per Last Purchase,نرخ به روز Ų±Ų³Ų§Ł†ŪŒ ŲØŲ± Ų§Ų³Ų§Ų³ Ų¢Ų®Ų±ŪŒŁ† خرید, +Update Total Purchase Cost,به روز Ų±Ų³Ų§Ł†ŪŒ کل Ł‡Ų²ŪŒŁ†Ł‡ خرید, +Update frequency of Project,فرکانس به روز Ų±Ų³Ų§Ł†ŪŒ Ł¾Ų±ŁˆŚ˜Ł‡, +Update stock must be enabled for the purchase invoice {0},ŲØŁ‡ā€ŒŲ±ŁˆŲ²Ų±Ų³Ų§Ł†ŪŒ Ł…ŁˆŲ¬ŁˆŲÆŪŒ باید برای فاکتور خرید فعال ؓود {0}, +Updated via 'Time Log' (In Minutes),"به روز ؓده Ų§Ų² Ų·Ų±ŪŒŁ‚ ""Time Log"" (به ŲÆŁ‚ŪŒŁ‚Ł‡)", +Updating Work Order status,به روز Ų±Ų³Ų§Ł†ŪŒ وضعیت دستور کار, +"Updating {0} of {1}, {2}",ŲÆŲ± Ų­Ų§Ł„ به روز Ų±Ų³Ų§Ł†ŪŒ {0} Ų§Ų² {1}، {2}, +Upload Bank Statement,بارگذاری صورتحساب ŲØŲ§Ł†Ś©ŪŒ, +Use 'Repost in background' button to trigger background job. Job can only be triggered when document is in Queued or Failed status.,برای فعال کردن کار Ł¾Ų³ā€ŒŲ²Ł…ŪŒŁ†Ł‡ Ų§Ų² دکمه «بازنؓر ŲÆŲ± Ł¾Ų³ā€ŒŲ²Ł…ŪŒŁ†Ł‡Ā» استفاده Ś©Ł†ŪŒŲÆ. کار فقط Ų²Ł…Ų§Ł†ŪŒ Ł…ŪŒ ŲŖŁˆŲ§Ł†ŲÆ فعال ؓود که سند ŲÆŲ± وضعیت صف یا Ł†Ų§Ł…ŁˆŁŁ‚ ŲØŲ§Ų“ŲÆ., +Use Batch-wise Valuation,استفاده Ų§Ų² Ų§Ų±Ų²Ų“ گذاری دسته ای, +Use Company Default Round Off Cost Center,Ų§Ų² مرکز Ł‡Ų²ŪŒŁ†Ł‡ دور پیؓ فرض ؓرکت استفاده Ś©Ł†ŪŒŲÆ, +Use Company default Cost Center for Round off,Ų§Ų² مرکز Ł‡Ų²ŪŒŁ†Ł‡ Ł¾ŪŒŲ“ā€ŒŁŲ±Ų¶ ؓرکت برای دور کردن استفاده Ś©Ł†ŪŒŲÆ, +Use HTTP Protocol,استفاده Ų§Ų² Ł¾Ų±ŁˆŲŖŚ©Ł„ HTTP, +Use Item based reposting,Ų§Ų² Ų§Ų±Ų³Ų§Ł„ Ł…Ų¬ŲÆŲÆ ŲØŲ± Ų§Ų³Ų§Ų³ Ų¢ŪŒŲŖŁ… استفاده Ś©Ł†ŪŒŲÆ, +Use Serial No / Batch Fields,Ų§Ų² ŁŪŒŁ„ŲÆŁ‡Ų§ŪŒ ؓماره Ų³Ų±ŪŒŲ§Ł„ / دسته استفاده Ś©Ł†ŪŒŲÆ, +Use Transaction Date Exchange Rate,Ų§Ų² نرخ مبادله تاریخ تراکنؓ استفاده Ś©Ł†ŪŒŲÆ, +User {0}: Removed Employee Self Service role as there is no mapped employee.,کاربر {0}: نقؓ خود سرویس کارمند حذف Ų“ŲÆ زیرا کارمند نگاؓت ؓده وجود ندارد., +User {0}: Removed Employee role as there is no mapped employee.,کاربر {0}: نقؓ کارمند حذف Ų“ŲÆ زیرا کارمند نگاؓت ؓده وجود ندارد., +Users can enable the checkbox If they want to adjust the incoming rate (set using purchase receipt) based on the purchase invoice rate.,Ų§ŚÆŲ± کاربران ŲØŲ®ŁˆŲ§Ł‡Ł†ŲÆ نرخ ورودی (ŲŖŁ†ŲøŪŒŁ… ŲØŲ§ استفاده Ų§Ų² رسید خرید) Ų±Ų§ ŲØŲ± Ų§Ų³Ų§Ų³ نرخ فاکتور خرید ŲŖŁ†ŲøŪŒŁ… Ś©Ł†Ł†ŲÆŲŒ Ł…ŪŒ ŲŖŁˆŲ§Ł†Ł†ŲÆ کادر انتخاب Ų±Ų§ فعال کنند., +Users with this role are allowed to over bill above the allowance percentage,Ś©Ų§Ų±ŲØŲ±Ų§Ł†ŪŒ که Ų§ŪŒŁ† نقؓ Ų±Ų§ دارند Ł…Ų¬Ų§Ų² به اضافه صورتحساب بالاتر Ų§Ų² ŲÆŲ±ŲµŲÆ Ł…Ų¬Ų§Ų² هستند, +Users with this role are allowed to over deliver/receive against orders above the allowance percentage,Ś©Ų§Ų±ŲØŲ±Ų§Ł†ŪŒ که Ų§ŪŒŁ† نقؓ Ų±Ų§ دارند Ł…Ų¬Ų§Ų² به بیؓ ŲŖŲ­ŁˆŪŒŁ„/دریافت Ų³ŁŲ§Ų±Ų“ā€ŒŁ‡Ų§ بالاتر Ų§Ų² ŲÆŲ±ŲµŲÆ Ł…Ų¬Ų§Ų² هستند, +Using negative stock disables FIFO/Moving average valuation when inventory is negative.,استفاده Ų§Ų² Ł…ŁˆŲ¬ŁˆŲÆŪŒ Ł…Ł†ŁŪŒŲŒ Ų§Ų±Ų²Ų“ گذاری FIFO / Ł…ŪŒŲ§Ł†ŚÆŪŒŁ† متحرک Ų±Ų§ Ų²Ł…Ų§Ł†ŪŒ که Ł…ŁˆŲ¬ŁˆŲÆŪŒ کالا Ł…Ł†ŁŪŒ است، ŲŗŪŒŲ±ŁŲ¹Ų§Ł„ Ł…ŪŒ کند., +VAT Accounts,Ų­Ų³Ų§ŲØ Ł‡Ų§ŪŒ Ł…Ų§Ł„ŪŒŲ§ŲŖ ŲØŲ± Ų§Ų±Ų²Ų“ Ų§ŁŲ²ŁˆŲÆŁ‡, +VAT Amount (AED),مقدار VAT (AED), +VAT Audit Report,ŚÆŲ²Ų§Ų±Ų“ حسابرسی Ł…Ų§Ł„ŪŒŲ§ŲŖ ŲØŲ± Ų§Ų±Ų²Ų“ Ų§ŁŲ²ŁˆŲÆŁ‡, +VAT on Expenses and All Other Inputs,Ł…Ų§Ł„ŪŒŲ§ŲŖ ŲØŲ± Ų§Ų±Ų²Ų“ Ų§ŁŲ²ŁˆŲÆŁ‡ Ł‡Ų²ŪŒŁ†Ł‡ ها و سایر ورودی ها, +VAT on Sales and All Other Outputs,Ł…Ų§Ł„ŪŒŲ§ŲŖ ŲØŲ± Ų§Ų±Ų²Ų“ Ų§ŁŲ²ŁˆŲÆŁ‡ ŲØŲ± فروؓ و سایر خروجی ها, +Valid From must be after {0} as last GL Entry against the cost center {1} posted on this date,Ł…Ų¹ŲŖŲØŲ± Ų§Ų² باید ŲØŲ¹ŲÆ Ų§Ų² {0} به Ų¹Ł†ŁˆŲ§Ł† Ų¢Ų®Ų±ŪŒŁ† Ų«ŲØŲŖ دفتر کل ŲÆŲ± ŲØŲ±Ų§ŲØŲ± مرکز Ł‡Ų²ŪŒŁ†Ł‡ {1} پست ؓده ŲÆŲ± Ų§ŪŒŁ† تاریخ ŲØŲ§Ų“ŲÆ, +Validate Negative Stock,Ų§Ų¹ŲŖŲØŲ§Ų± Ł…ŁˆŲ¬ŁˆŲÆŪŒ Ł…Ł†ŁŪŒ, +Validate Stock on Save,Ų§Ų¹ŲŖŲØŲ§Ų± Ł…ŁˆŲ¬ŁˆŲÆŪŒ ŲÆŲ± Ų°Ų®ŪŒŲ±Ł‡, +Valuation Field Type,Ł†ŁˆŲ¹ ŁŪŒŁ„ŲÆ Ų§Ų±Ų²Ų“ گذاری, +Valuation Rate (In / Out),نرخ Ų§Ų±Ų²Ų“ گذاری (ورودی/خروجی), +Valuation rate for customer provided items has been set to zero.,نرخ Ų§Ų±Ų²Ų“ گذاری برای Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ŪŒ ارائه ؓده توسط Ł…Ų“ŲŖŲ±ŪŒ صفر ŲŖŲ¹ŪŒŪŒŁ† ؓده Ų§Ų³ŲŖ., +Value Based Inspection,بازرسی Ł…ŲØŲŖŁ†ŪŒ ŲØŲ± Ų§Ų±Ų²Ų“, +Value Change,تغییر Ų§Ų±Ų²Ų“, +Value Details,جزئیات Ų§Ų±Ų²Ų“, +Value of Goods,Ų§Ų±Ų²Ų“ کالاها, +Value of goods cannot be 0,Ų§Ų±Ų²Ų“ کالا Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł†ŲÆ 0 ŲØŲ§Ų“ŲÆ, +Verification failed please check the link,تأیید انجام نؓد لطفاً Ł¾ŪŒŁˆŁ†ŲÆ Ų±Ų§ بررسی Ś©Ł†ŪŒŲÆ, +Via Landed Cost Voucher,Ų§Ų² Ų·Ų±ŪŒŁ‚ Ś©ŁˆŁ¾Ł† Ł‡Ų²ŪŒŁ†Ł‡ ŲŖŁ…Ų§Ł… ؓده ŲŖŲ§ ŲÆŲ±ŲØ انبار, +View BOM Update Log,مؓاهده لاگ ŲØŁ‡ā€ŒŲ±ŁˆŲ²Ų±Ų³Ų§Ł†ŪŒ BOM, +View Exchange Gain/Loss Journals,مؓاهده ŲÆŁŲŖŲ±Ł‡Ų§ŪŒ Ų±ŁˆŲ²Ł†Ų§Ł…Ł‡ سود/Ų²ŪŒŲ§Ł† ŲŖŲØŲ§ŲÆŁ„, +View General Ledger,مؓاهده دفتر کل Ł…Ų±Ś©Ų²ŪŒ, +View Ledgers,مؓاهده دفتر کل ها, +Visits,ŲØŲ§Ų²ŲÆŪŒŲÆŁ‡Ų§, +Voice Call Settings,ŲŖŁ†ŲøŪŒŁ…Ų§ŲŖ ŲŖŁ…Ų§Ų³ صوتی, +Voucher,Ś©ŁˆŁ¾Ł†, +Voucher Name,نام Ś©ŁˆŁ¾Ł†, +Voucher No is mandatory,ؓماره Ś©ŁˆŁ¾Ł† Ų§Ł„Ų²Ų§Ł…ŪŒ Ų§Ų³ŲŖ, +Voucher Qty,مقدار Ś©ŁˆŁ¾Ł†, +Voucher Subtype,Ų²ŪŒŲ±Ł†ŁˆŲ¹ Ś©ŁˆŁ¾Ł†, +Voucher {0} is over-allocated by {1},Ś©ŁˆŁ¾Ł† {0} توسط {1} بیؓ Ų§Ų² Ų­ŲÆ تخصیص داده ؓده Ų§Ų³ŲŖ, +Voucher {0} value is broken: {1},Ų§Ų±Ų²Ų“ Ś©ŁˆŁ¾Ł† {0} Ų®Ų±Ų§ŲØ Ų§Ų³ŲŖ: {1}, +Voucher-wise Balance,ŲŖŲ±Ų§Ų² Ł…ŲØŲŖŁ†ŪŒ ŲØŲ± Ś©ŁˆŁ¾Ł†, +"WARNING: Exotel app has been separated from ERPNext, please install the app to continue using Exotel integration.",هؓدار: برنامه Exotel Ų§Ų² ERPNext Ų¬ŲÆŲ§ ؓده است، لطفاً برای ادامه استفاده Ų§Ų² Exotel ŪŒŚ©Ł¾Ų§Ų±Ś†Ł‡ سازی، برنامه Ų±Ų§ نصب Ś©Ł†ŪŒŲÆ., +WIP Composite Asset,دارایی ترکیبی «کار ŲÆŲ± Ų­Ų§Ł„ انجام», +Waiting for payment...,ŲÆŲ± انتظار پرداخت..., +Warehouse Capacity for Item '{0}' must be greater than the existing stock level of {1} {2}.,"ظرفیت انبار برای Ų¢ŪŒŲŖŁ… ""{0}"" باید بیؓتر Ų§Ų² Ų³Ų·Ų­ Ł…ŁˆŲ¬ŁˆŲÆŪŒ Ł…ŁˆŲ¬ŁˆŲÆ ŲÆŲ± {1} {2} ŲØŲ§Ų“ŲÆ.", +Warehouse Details,جزئیات انبار, +Warehouse Disabled?,انبار غیر فعال است؟, +Warehouse Settings,ŲŖŁ†ŲøŪŒŁ…Ų§ŲŖ انبار, +Warehouse Wise Stock Balance,ŲŖŲ±Ų§Ų² Ł…ŁˆŲ¬ŁˆŲÆŪŒ Ł…ŲØŲŖŁ†ŪŒ ŲØŲ± انبار, +Warehouse wise Stock Value,Ų§Ų±Ų²Ų“ Ł…ŁˆŲ¬ŁˆŲÆŪŒ Ł…ŲØŲŖŁ†ŪŒ ŲØŲ± انبار, +Warehouse {0} does not belong to Company {1}.,انبار {0} متعلق به ؓرکت {1} Ł†ŪŒŲ³ŲŖ., +"Warehouse {0} is not linked to any account, please mention the account in the warehouse record or set default inventory account in company {1}.",انبار {0} به Ł‡ŪŒŚ† حسابی Ł…Ų±ŲŖŲØŲ· Ł†ŪŒŲ³ŲŖŲŒ لطفاً Ų­Ų³Ų§ŲØ Ų±Ų§ ŲÆŲ± سابقه انبار ذکر Ś©Ł†ŪŒŲÆ یا Ų­Ų³Ų§ŲØ Ł…ŁˆŲ¬ŁˆŲÆŪŒ پیؓ فرض Ų±Ų§ ŲÆŲ± ؓرکت {1} ŲŖŁ†ŲøŪŒŁ… Ś©Ł†ŪŒŲÆ., +Warehouse's Stock Value has already been booked in the following accounts:,Ų§Ų±Ų²Ų“ Ł…ŁˆŲ¬ŁˆŲÆŪŒ انبار قبلاً ŲÆŲ± Ų­Ų³Ų§ŲØ Ł‡Ų§ŪŒ زیر رزرو ؓده Ų§Ų³ŲŖ:, +Warning - Row {0}: Billing Hours are more than Actual Hours,هؓدار - ردیف {0}: Ų³Ų§Ų¹Ų§ŲŖ صورتحساب بیؓتر Ų§Ų² Ų³Ų§Ų¹ŲŖā€ŒŁ‡Ų§ŪŒ ŁˆŲ§Ł‚Ų¹ŪŒ Ų§Ų³ŲŖ, +Warning on Negative Stock,هؓدار ŲÆŲ± Ł…ŁˆŲ±ŲÆ Ł…ŁˆŲ¬ŁˆŲÆŪŒ Ł…Ł†ŁŪŒ, +Warning!,هؓدار!, +Watch Video,ŲŖŁ…Ų§Ų“Ų§ŪŒ ویدیو, +Website Script,اسکریپت وب سایت, +Website Theme,ŲŖŁ… وب سایت, +Week {0} {1},هفته {0} {1}, +Weekly Time to send,زمان Ų§Ų±Ų³Ų§Ł„ Ł‡ŁŲŖŚÆŪŒ, +Weight (kg),ŁˆŲ²Ł† (Ś©ŪŒŁ„ŁˆŚÆŲ±Ł…), +"When a parent warehouse is chosen, the system conducts stock checks against the associated child warehouses",Ł‡Ł†ŚÆŲ§Ł…ŪŒ که یک انبار Ų§ŲµŁ„ŪŒ انتخاب Ł…ŪŒ ؓود، Ų³ŪŒŲ³ŲŖŁ… بررسی Ł‡Ų§ŪŒ Ł…ŁˆŲ¬ŁˆŲÆŪŒ Ų±Ų§ ŲÆŲ± مقابل Ų§Ł†ŲØŲ§Ų±Ł‡Ų§ŪŒ فرزند Ł…Ų±ŲŖŲØŲ· انجام Ł…ŪŒ دهد, +"When creating an Item, entering a value for this field will automatically create an Item Price at the backend.",هنگام ایجاد یک Ų¢ŪŒŲŖŁ…ŲŒ ŲØŲ§ وارد کردن یک مقدار برای Ų§ŪŒŁ† ŁŪŒŁ„ŲÆŲŒ به طور خودکار Ł‚ŪŒŁ…ŲŖ Ų¢ŪŒŲŖŁ… ŲÆŲ± قسمت Ł¾Ų“ŲŖŪŒŲØŲ§Ł† ایجاد Ł…ŪŒ ؓود., +"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.",هنگام ŲŖŁ‡ŪŒŁ‡ فاکتور خرید Ų§Ų² سفارؓ خرید، به جای Ų§Ų±Ų« بردن آن Ų§Ų² سفارؓ خرید، Ų§Ų² نرخ مبادله ŲÆŲ± تاریخ تراکنؓ فاکتور استفاده Ś©Ł†ŪŒŲÆ. فقط برای فاکتور خرید اعمال Ł…ŪŒ ؓود., +Width (cm),Ų¹Ų±Ų¶ (Ų³Ų§Ł†ŲŖŪŒ Ł…ŲŖŲ±), +Withdrawal,ŲØŲ±ŲÆŲ§Ų“ŲŖ Ų§Ų² Ų­Ų³Ų§ŲØ, +Work Order / Subcontract PO,دستور کار / PO قرارداد فرعی, +Work Order Consumed Materials,Ł…ŁˆŲ§ŲÆ Ł…ŲµŲ±ŁŪŒ دستور کار, +Workflow,Ų¬Ų±ŪŒŲ§Ł† کار, +Workflow Action,عمل ŚÆŲ±ŲÆŲ“ کار, +Workflow State,وضعیت ŚÆŲ±ŲÆŲ“ کار, +Workstation Dashboard,داؓبورد Ų§ŪŒŲ³ŲŖŚÆŲ§Ł‡ کاری, +Workstation Status,وضعیت Ų§ŪŒŲ³ŲŖŚÆŲ§Ł‡ کاری, +Workstation Type,Ł†ŁˆŲ¹ Ų§ŪŒŲ³ŲŖŚÆŲ§Ł‡ کاری, +Workstations,Ų§ŪŒŲ³ŲŖŚÆŲ§Ł‡ Ł‡Ų§ŪŒ کاری, +Write Off Limit,Ł…Ų­ŲÆŁˆŲÆŪŒŲŖ Ł†ŁˆŲ“ŲŖŁ†, +Wrong Company,ؓرکت اؓتباه, +Wrong Template,Ų§Ł„ŚÆŁˆŪŒ اؓتباه, +You are not authorized to make/edit Stock Transactions for Item {0} under warehouse {1} before this time.,Ų“Ł…Ų§ Ł…Ų¬Ų§Ų² به انجام/ویرایؓ معاملات Ł…ŁˆŲ¬ŁˆŲÆŪŒ برای Ś©Ų§Ł„Ų§ŪŒ {0} ŲÆŲ± انبار {1} قبل Ų§Ų² Ų§ŪŒŁ† زمان Ł†ŪŒŲ³ŲŖŪŒŲÆ., +You are picking more than required quantity for the item {0}. Check if there is any other pick list created for the sales order {1}.,Ų“Ł…Ų§ ŲÆŲ± Ų­Ų§Ł„ انتخاب بیؓ Ų§Ų² مقدار Ł…ŁˆŲ±ŲÆ Ł†ŪŒŲ§Ų² برای Ł…ŁˆŲ±ŲÆ {0} Ł‡Ų³ŲŖŪŒŲÆ. بررسی Ś©Ł†ŪŒŲÆ که آیا Ł„ŪŒŲ³ŲŖ انتخاب دیگری برای سفارؓ فروؓ {1} ایجاد ؓده Ų§Ų³ŲŖ., +"You can set it as a machine name or operation type. For example, stiching machine 12",Ł…ŪŒ ŲŖŁˆŲ§Ł†ŪŒŲÆ آن Ų±Ų§ به Ų¹Ł†ŁˆŲ§Ł† نام Ł…Ų§Ų“ŪŒŁ† یا Ł†ŁˆŲ¹ Ų¹Ł…Ł„ŪŒŲ§ŲŖ ŲŖŁ†ŲøŪŒŁ… Ś©Ł†ŪŒŲÆ. مثلا Ł…Ų§Ų“ŪŒŁ† دوخت 12, +You can't make any changes to Job Card since Work Order is closed.,Ų§Ų² Ų¢Ł†Ų¬Ų§ŪŒŪŒ که دستور کار بسته ؓده است، Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł†ŪŒŲÆ Ł‡ŪŒŚ† تغییری ŲÆŲ± کارت کار ایجاد Ś©Ł†ŪŒŲÆ., +You can't redeem Loyalty Points having more value than the Rounded Total.,Ł†Ł…ŪŒā€ŒŲŖŁˆŲ§Ł†ŪŒŲÆ Ų§Ł…ŲŖŪŒŲ§Ų²Ł‡Ų§ŪŒ وفاداری Ų±Ų§ که Ų§Ų±Ų²Ų“ بیؓتری Ų§Ų² Ł…Ų¬Ł…ŁˆŲ¹ ŚÆŲ±ŲÆ ؓده ŲÆŲ§Ų±Ł†ŲÆŲŒ Ł¾Ų³ā€ŒŲ®Ų±ŪŒŲÆ Ś©Ł†ŪŒŲÆ., +You cannot change the rate if BOM is mentioned against any Item.,Ų§ŚÆŲ± BOM ŲÆŲ± ŲØŲ±Ų§ŲØŲ± هر Ł…ŁˆŲ±ŲÆŪŒ ذکر ؓده باؓد، Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł†ŪŒŲÆ نرخ Ų±Ų§ تغییر ŲÆŁ‡ŪŒŲÆ., +You cannot create a {0} within the closed Accounting Period {1},Ų“Ł…Ų§ Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł†ŪŒŲÆ یک {0} ŲÆŲ± ŲÆŁˆŲ±Ł‡ حسابداری بسته {1} ایجاد Ś©Ł†ŪŒŲÆ, +You cannot create/amend any accounting entries till this date.,ŲŖŲ§ Ų§ŪŒŁ† تاریخ Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł†ŪŒŲÆ Ł‡ŪŒŚ† ورودی حسابداری ایجاد یا اصلاح Ś©Ł†ŪŒŲÆ., +You cannot repost item valuation before {},Ų“Ł…Ų§ Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł†ŪŒŲÆ ارزیابی Ł…ŁˆŲ±ŲÆ Ų±Ų§ قبل Ų§Ų² {} ŲÆŁˆŲØŲ§Ų±Ł‡ Ų§Ų±Ų³Ų§Ł„ Ś©Ł†ŪŒŲÆ, +You have entered a duplicate Delivery Note on Row,Ų“Ł…Ų§ یک یادداؓت ŲŖŲ­ŁˆŪŒŁ„ تکراری ŲÆŲ± ردیف وارد کرده اید, +You haven't created a {0} yet,Ų“Ł…Ų§ Ł‡Ł†ŁˆŲ² یک {0} ایجاد نکرده اید, +You need to cancel POS Closing Entry {} to be able to cancel this document.,برای Ų§ŪŒŁ†Ś©Ł‡ ŲØŲŖŁˆŲ§Ł†ŪŒŲÆ Ų§ŪŒŁ† سند Ų±Ų§ Ł„ŲŗŁˆ Ś©Ł†ŪŒŲÆŲŒ باید Ų«ŲØŲŖ Ų§Ų®ŲŖŲŖŲ§Ł…ŪŒŁ‡ POS {} Ų±Ų§ Ł„ŲŗŁˆ Ś©Ł†ŪŒŲÆ., +Your Name (required),نام Ų“Ł…Ų§ (Ų§Ł„Ų²Ų§Ł…ŪŒ), +Your email has been verified and your appointment has been scheduled,Ų§ŪŒŁ…ŪŒŁ„ Ų“Ł…Ų§ تایید ؓده و قرار ملاقات Ų“Ł…Ų§ ŲŖŲ¹ŪŒŪŒŁ† ؓده Ų§Ų³ŲŖ, +Zero Balance,ŲŖŲ±Ų§Ų² صفر, +Zero Rated,دارای Ų§Ł…ŲŖŪŒŲ§Ų² صفر, +Zero quantity,مقدار صفر, +`Allow Negative rates for Items`,«نرخ Ł‡Ų§ŪŒ Ł…Ł†ŁŪŒ برای Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ Ł…Ų¬Ų§Ų² Ų§Ų³ŲŖĀ», +as a percentage of finished item quantity,به Ų¹Ł†ŁˆŲ§Ł† درصدی Ų§Ų² مقدار Ś©Ų§Ł„Ų§ŪŒ ŲŖŁ…Ų§Ł… ؓده, +at,ŲÆŲ±, +description,Ų“Ų±Ų­, +discount applied,تخفیف اعمال Ų“ŲÆ, +doc_type,Ł†ŁˆŲ¹_doc, +exchangerate.host,مبادله.Ł…ŪŒŲ²ŲØŲ§Ł†, +is already,Ų§Ų³ŲŖ ŲÆŲ± Ų­Ų§Ł„ Ų­Ų§Ų¶Ų±, +must be between 0 and 100,باید ŲØŪŒŁ† 0 ŲŖŲ§ 100 ŲØŲ§Ų“ŲÆ, +or its descendants,یا فرزندان آن, +out of 5,Ų§Ų² 5, +payments app is not installed. Please install it from {0} or {1},برنامه پرداخت نصب نؓده Ų§Ų³ŲŖ لطفاً آن Ų±Ų§ Ų§Ų² {0} یا {1} نصب Ś©Ł†ŪŒŲÆ, +payments app is not installed. Please install it from {} or {},برنامه پرداخت نصب نؓده Ų§Ų³ŲŖ لطفاً آن Ų±Ų§ Ų§Ų² {} یا {} نصب Ś©Ł†ŪŒŲÆ, +performing either one below:,انجام هر یک Ų§Ų² Ł…ŁˆŲ§Ų±ŲÆ زیر:, +product bundle item row's name in sales order. Also indicates that picked item is to be used for a product bundle,نام ردیف Ų¢ŪŒŲŖŁ…ā€ŒŁ‡Ų§ŪŒ باندل Ł…Ų­ŲµŁˆŁ„ ŲÆŲ± سفارؓ فروؓ. Ł‡Ł…Ś†Ł†ŪŒŁ† نؓان Ł…ŪŒ دهد که Ų¢ŪŒŲŖŁ… انتخاب ؓده قرار Ų§Ų³ŲŖ برای یک باندل Ł…Ų­ŲµŁˆŁ„ استفاده ؓود, +ratings,رتبه ŲØŁ†ŲÆŪŒ ها, +subscription is already cancelled.,Ų§Ų“ŲŖŲ±Ų§Ś© ŲÆŲ± Ų­Ų§Ł„ Ų­Ų§Ų¶Ų± Ł„ŲŗŁˆ ؓده Ų§Ų³ŲŖ., +temporary name,نام Ł…ŁˆŁ‚ŲŖ, +to unallocate the amount of this Return Invoice before cancelling it.,برای تخصیص مبلغ Ų§ŪŒŁ† فاکتور برگؓتی قبل Ų§Ų² Ł„ŲŗŁˆ آن., +variance,ŁˆŲ§Ų±ŪŒŲ§Ł†Ų³, +via BOM Update Tool,Ų§Ų² Ų·Ų±ŪŒŁ‚ BOM Update Tool, +will be,Ų®ŁˆŲ§Ł‡ŲÆ بود, +{0} {1} has submitted Assets. Remove Item {2} from table to continue.,{0} {1} ŲÆŲ§Ų±Ų§ŪŒŪŒā€ŒŁ‡Ų§ Ų±Ų§ Ų§Ų±Ų³Ų§Ł„ کرده Ų§Ų³ŲŖ. برای Ų§ŲÆŲ§Ł…Ł‡ŲŒ Ų¢ŪŒŲŖŁ… {2} Ų±Ų§ Ų§Ų² Ų¬ŲÆŁˆŁ„ حذف Ś©Ł†ŪŒŲÆ., +{0} Account not found against Customer {1}.,{0} Ų­Ų³Ų§ŲØ ŲÆŲ± مقابل Ł…Ų“ŲŖŲ±ŪŒ پیدا نؓد {1}., +{0} Budget for Account {1} against {2} {3} is {4}. It {5} exceed by {6},{0} ŲØŁˆŲÆŲ¬Ł‡ برای Ų­Ų³Ų§ŲØ {1} ŲÆŲ± ŲØŲ±Ų§ŲØŲ± {2} {3} {4} Ų§Ų³ŲŖ. {5} Ų§Ų² {6} بیؓتر Ų§Ų³ŲŖ, +{0} Transaction(s) Reconciled,{0} تراکنؓ(Ł‡Ų§ŪŒ) ŲŖŲ·ŲØŪŒŁ‚ Ų“ŲÆ, +{0} account is not of type {1},Ų­Ų³Ų§ŲØ {0} Ų§Ų² Ł†ŁˆŲ¹ {1} Ł†ŪŒŲ³ŲŖ, +{0} account not found while submitting purchase receipt,هنگام Ų§Ų±Ų³Ų§Ł„ رسید خرید، Ų­Ų³Ų§ŲØ {0} پیدا نؓد, +{0} and {1},{0} و {1}, +{0} cannot be used as a Main Cost Center because it has been used as child in Cost Center Allocation {1},{0} Ł†Ł…ŪŒā€ŒŲŖŁˆŲ§Ł†ŲÆ ŲØŁ‡ā€ŒŲ¹Ł†ŁˆŲ§Ł† مرکز Ł‡Ų²ŪŒŁ†Ł‡ Ų§ŲµŁ„ŪŒ استفاده ؓود زیرا ŲØŁ‡ā€ŒŲ¹Ł†ŁˆŲ§Ł† فرزند ŲÆŲ± تخصیص مرکز Ł‡Ų²ŪŒŁ†Ł‡ {1} استفاده ؓده Ų§Ų³ŲŖ., +{0} cannot be zero,{0} Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł†ŲÆ صفر ŲØŲ§Ų“ŲÆ, +{0} currency must be same as company's default currency. Please select another account.,Ų§Ų±Ų² {0} باید ŲØŲ§ واحد Ł¾ŁˆŁ„ Ł¾ŪŒŲ“ā€ŒŁŲ±Ų¶ ؓرکت ŪŒŚ©Ų³Ų§Ł† ŲØŲ§Ų“ŲÆ. لطفا Ų­Ų³Ų§ŲØ دیگری Ų±Ų§ انتخاب Ś©Ł†ŪŒŲÆ., +{0} entered twice {1} in Item Taxes,{0} دو ŲØŲ§Ų± {1} ŲÆŲ± Ł…Ų§Ł„ŪŒŲ§ŲŖ Ų¢ŪŒŲŖŁ… وارد Ų“ŲÆ, +{0} has Payment Term based allocation enabled. Select a Payment Term for Row #{1} in Payment References section,{0} تخصیص Ł…ŲØŲŖŁ†ŪŒ ŲØŲ± Ł…ŲÆŲŖ پرداخت Ų±Ų§ فعال کرده Ų§Ų³ŲŖ. ŲÆŲ± ŲØŲ®Ų“ Ł…Ų±Ų§Ų¬Ų¹ پرداخت، یک ؓرایط پرداخت برای ردیف #{1} انتخاب Ś©Ł†ŪŒŲÆ, +{0} is a mandatory Accounting Dimension.
Please set a value for {0} in Accounting Dimensions section.,{0} یک بعد حسابداری اجباری است.
لطفاً یک مقدار برای {0} ŲÆŲ± ŲØŲ®Ų“ Ų§ŲØŲ¹Ų§ŲÆ حسابداری ŲŖŁ†ŲøŪŒŁ… Ś©Ł†ŪŒŲÆ., +{0} is added multiple times on rows: {1},{0} Ś†Ł†ŲÆŪŒŁ† ŲØŲ§Ų± ŲÆŲ± ردیف ها اضافه Ł…ŪŒ ؓود: {1}, +{0} is already running for {1},{0} ŲÆŲ± Ų­Ų§Ł„ Ų­Ų§Ų¶Ų± برای {1} ŲÆŲ± Ų­Ų§Ł„ Ų§Ų¬Ų±Ų§ Ų§Ų³ŲŖ, +{0} is mandatory for account {1},{0} برای Ų­Ų³Ų§ŲØ {1} اجباری Ų§Ų³ŲŖ, +{0} qty of Item {1} is being received into Warehouse {2} with capacity {3}.,{0} ŲŖŲ¹ŲÆŲ§ŲÆ Ł…ŁˆŲ±ŲÆ {1} ŲÆŲ± انبار {2} ŲØŲ§ ظرفیت {3} ŲÆŲ± Ų­Ų§Ł„ دریافت Ų§Ų³ŲŖ., +"{0} units are reserved for Item {1} in Warehouse {2}, please un-reserve the same to {3} the Stock Reconciliation.",{0} واحد برای Ł…ŁˆŲ±ŲÆ {1} ŲÆŲ± انبار {2} رزرو ؓده است، لطفاً همان Ų±Ų§ ŲÆŲ± {3} ŲŖŲ·ŲØŪŒŁ‚ Ł…ŁˆŲ¬ŁˆŲÆŪŒ Ł„ŲŗŁˆ Ś©Ł†ŪŒŲÆ., +{0} units of Item {1} is picked in another Pick List.,{0} واحد Ų§Ų² Ł…ŁˆŲ±ŲÆ {1} ŲÆŲ± فهرست انتخاب دیگری انتخاب ؓده Ų§Ų³ŲŖ., +"{0} units of {1} are required in {2}{3}, on {4} {5} for {6} to complete the transaction.",{0} واحد Ų§Ų² {1} ŲÆŲ± {2}{3}، ŲÆŲ± {4} {5} برای {6} برای ŲŖŚ©Ł…ŪŒŁ„ تراکنؓ Ł…ŁˆŲ±ŲÆ Ł†ŪŒŲ§Ų² Ų§Ų³ŲŖ., +{0} units of {1} needed in {2} on {3} {4} to complete this transaction.,برای ŲŖŚ©Ł…ŪŒŁ„ Ų§ŪŒŁ† تراکنؓ به {0} واحد Ų§Ų² {1} ŲÆŲ± {2} ŲÆŲ± {3} {4} Ł†ŪŒŲ§Ų² Ų§Ų³ŲŖ., +{0} will be given as discount.,{0} به Ų¹Ł†ŁˆŲ§Ł† تخفیف داده Ł…ŪŒ ؓود., +{0} {1} Manually,{0} {1} به صورت دستی, +{0} {1} Partially Reconciled,{0} {1} ŲŖŲ§ حدی ŲŖŲ·ŲØŪŒŁ‚ کرد, +"{0} {1} cannot be updated. If you need to make changes, we recommend canceling the existing entry and creating a new one.",{0} {1} Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł†ŲÆ به روز ؓود. Ų§ŚÆŲ± Ł†ŪŒŲ§Ų² به ایجاد تغییرات دارید، ŲŖŁˆŲµŪŒŁ‡ Ł…ŪŒ Ś©Ł†ŪŒŁ… ورودی Ł…ŁˆŲ¬ŁˆŲÆ Ų±Ų§ Ł„ŲŗŁˆ Ś©Ł†ŪŒŲÆ و یک ورودی جدید ایجاد Ś©Ł†ŪŒŲÆ., +{0} {1} has already been fully paid.,{0} {1} قبلاً به طور کامل پرداخت ؓده Ų§Ų³ŲŖ., +{0} {1} has already been partly paid. Please use the 'Get Outstanding Invoice' or the 'Get Outstanding Orders' button to get the latest outstanding amounts.,{0} {1} قبلاً ŲŖŲ§ حدی پرداخت ؓده Ų§Ų³ŲŖ. لطفاً Ų§Ų² دکمه «دریافت صورتحساب Ł…Ų¹ŁˆŁ‚Ā» یا «دریافت Ų³ŁŲ§Ų±Ų“ā€ŒŁ‡Ų§ŪŒ Ł…Ų¹ŁˆŁ‚Ā» برای دریافت Ų¢Ų®Ų±ŪŒŁ† مبالغ Ł…Ų¹ŁˆŁ‚ استفاده Ś©Ł†ŪŒŲÆ., +{0} {1} is allocated twice in this Bank Transaction,{0} {1} دو ŲØŲ§Ų± ŲÆŲ± Ų§ŪŒŁ† تراکنؓ ŲØŲ§Ł†Ś©ŪŒ تخصیص داده ؓده Ų§Ų³ŲŖ, +{0} {1} is not in any active Fiscal Year,{0} {1} ŲÆŲ± Ł‡ŪŒŚ† Ų³Ų§Ł„ Ł…Ų§Ł„ŪŒ ŁŲ¹Ų§Ł„ŪŒ Ł†ŪŒŲ³ŲŖ, +{0} {1} is on hold,{0} {1} ŲÆŲ± انتظار Ų§Ų³ŲŖ, +{0} {1} not allowed to be reposted. Modify {2} to enable reposting.,{0} {1} Ł…Ų¬Ų§Ų² به Ų§Ų±Ų³Ų§Ł„ Ł…Ų¬ŲÆŲÆ Ł†ŪŒŲ³ŲŖ. برای فعال کردن Ų§Ų±Ų³Ų§Ł„ Ł…Ų¬ŲÆŲÆŲŒ {2} Ų±Ų§ تغییر ŲÆŁ‡ŪŒŲÆ., +{0} {1} via CSV File,{0} {1} Ų§Ų² Ų·Ų±ŪŒŁ‚ ŁŲ§ŪŒŁ„ CSV, +{0} {1}: Account {2} is a Group Account and group accounts cannot be used in transactions,{0} {1}: Ų­Ų³Ų§ŲØ {2} یک Ų­Ų³Ų§ŲØ ŚÆŲ±ŁˆŁ‡ŪŒ Ų§Ų³ŲŖ و Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł† Ų§Ų² Ų­Ų³Ų§ŲØ Ł‡Ų§ŪŒ ŚÆŲ±ŁˆŁ‡ŪŒ ŲÆŲ± تراکنؓ ها استفاده کرد, +{0} {1}: Cost Center is required for 'Profit and Loss' account {2}.,"{0} {1}: مرکز Ł‡Ų²ŪŒŁ†Ł‡ برای Ų­Ų³Ų§ŲØ ""سود و Ų²ŪŒŲ§Ł†"" {2} لازم Ų§Ų³ŲŖ.", +{0} {1}: Cost Center {2} is a group cost center and group cost centers cannot be used in transactions,{0} {1}: مرکز Ł‡Ų²ŪŒŁ†Ł‡ {2} یک مرکز Ł‡Ų²ŪŒŁ†Ł‡ ŚÆŲ±ŁˆŁ‡ŪŒ Ų§Ų³ŲŖ و مراکز Ł‡Ų²ŪŒŁ†Ł‡ ŚÆŲ±ŁˆŁ‡ŪŒ Ų±Ų§ Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł† ŲÆŲ± تراکنؓ ها استفاده کرد, +{0}% of total invoice value will be given as discount.,{0}% Ų§Ų² Ų§Ų±Ų²Ų“ کل فاکتور به Ų¹Ł†ŁˆŲ§Ł† تخفیف داده Ł…ŪŒ ؓود., +{0}'s {1} cannot be after {2}'s Expected End Date.,{1} {0} Ł†Ł…ŪŒ ŲŖŁˆŲ§Ł†ŲÆ ŲØŲ¹ŲÆ Ų§Ų² تاریخ Ł¾Ų§ŪŒŲ§Ł† Ł…ŁˆŲ±ŲÆ انتظار {2} ŲØŲ§Ų“ŲÆ., +{item_name}'s Sample Size ({sample_size}) cannot be greater than the Accepted Quantity ({accepted_quantity}),اندازه Ł†Ł…ŁˆŁ†Ł‡ {item_name} ({sample_size}) Ł†Ł…ŪŒā€ŒŲŖŁˆŲ§Ł†ŲÆ بیؓتر Ų§Ų² مقدار Ł…ŁˆŲ±ŲÆ Ł‚ŲØŁˆŁ„ ({accepted_quantity}) ŲØŲ§Ų“ŲÆ., +{} Available,{} ŲÆŲ± ŲÆŲ³ŲŖŲ±Ų³, +{} To Deliver,{} برای ŲŖŲ­ŁˆŪŒŁ„, +{} To Receive,{} برای دریافت, +{} Assigned,{} Ų§Ų®ŲŖŲµŲ§Ųµ ŪŒŲ§ŁŲŖŁ‡, +{} Available,{} ŲÆŲ± ŲÆŲ³ŲŖŲ±Ų³, +{} Open,{} ŲØŲ§Ų², +{} Pending,{} انتظار, +{} To Bill,{} برای صورتحساب, +{} is a child company.,{} یک ؓرکت فرزند Ų§Ų³ŲŖ., +{} {} is already linked with another {},{} {} قبلاً ŲØŲ§ {} دیگری Ł¾ŪŒŁˆŁ†ŲÆ ؓده Ų§Ų³ŲŖ, +{} {} is already linked with {} {},{} {} قبلاً ŲØŲ§ {} {} Ł¾ŪŒŁˆŁ†ŲÆ داده ؓده Ų§Ų³ŲŖ, diff --git a/erpnext/translations/sv.csv b/erpnext/translations/sv.csv index 4a53c63fafa..ca0782fb728 100644 --- a/erpnext/translations/sv.csv +++ b/erpnext/translations/sv.csv @@ -8743,3 +8743,3086 @@ WhatsApp,WhatsApp, Make a call,Ringa ett samtal, Approve,GodkƤnna, Reject,Avvisa, + Address, Adress, + Amount,Belopp, + Is Child Table,Ƅr Undertabell, + Name,Namn, + Rate,Moms %, + Summary,Ɩversikt, +"""SN-01::10"" for ""SN-01"" to ""SN-10""","""SN-01::10"" fƶr ""SN-01"" till ""SN-10""", +# In Stock,# I Lager, +# Req'd Items,# Erfodrade Artiklar, +% Finished Item Quantity,% FƤrdig Artikel Kvantitet, +% Occupied,% Upptagen, +% Picked,% Plockad, +% Process Loss,% Process Fƶrlust, +% Returned,% Retur, +'Account' in the Accounting section of Customer {0},"""Konto"" i Bokfƶring Sektion fƶr Kund {0}", +'Allow Multiple Sales Orders Against a Customer's Purchase Order',"""TillĆ„t flera FƶrsƤljning Order mot Kund Inkƶp Order""", +'Default {0} Account' in Company {1},"""Standard {0} Konto"" i Bolag {1}", +'To Package No.' cannot be less than 'From Package No.',"""Till Paket Nummer."" fĆ„r inte vara lƤgre Ƥn ""FrĆ„n Paket Nummer.""", +'{0}' account is already used by {1}. Use another account.,'{0}' konto anvƤnds redan av {1}. AnvƤnd ett annat konto., +'{0}' should be in company currency {1}.,"""{0}"" ska vara i bolag valuta {1}.", +(A) Qty After Transaction,(A) Kvantitet Efter Transaktion, +(B) Expected Qty After Transaction,(B) FƶrvƤntad Kvantitet Efter Transaktion, +(C) Total Qty in Queue,(C) Totalt Kvantitet i Kƶ, +(C) Total qty in queue,(C) Totalt Kvantitet i Kƶ, +(D) Balance Stock Value,(D) Saldo Lager VƤrde, +(E) Balance Stock Value in Queue,(E) Saldo Lager VƤrde i Kƶ, +(F) Change in Stock Value,(F) FƶrƤndring i Lager VƤrde, +(G) Sum of Change in Stock Value,(G) Summan av FƶrƤndring i Lager VƤrde, +(H) Change in Stock Value (FIFO Queue),(H) FƶrƤndring av Lager VƤrde (FIFO), +(H) Valuation Rate,(H) Grund Pris, +(I) Valuation Rate,(I) Grund Pris, +(J) Valuation Rate as per FIFO,(J) Grund Pris enligt FIFO, +(K) Valuation = Value (D) Ć· Qty (A),(K) VƤrdering = VƤrde (D) Ć· Kvantitet (A), +", with the inventory {0}: {1}",", med inventering {0}: {1}", +0-30 Days,0-30 Dagar, +3 Yearly,Var Tredje ƅr, +30-60 Days,30-60 Dagar, +60-90 Days,60 - 90 Dagar, +90 Above,90+ Dagar, +"
+

Note

+
    +
  • +You can use Jinja tags in Subject and Body fields for dynamic values. +
  • + All fields in this doctype are available under the doc object and all fields for the customer to whom the mail will go to is available under the customer object. +
+

Examples

+ +
    +
  • Subject:

    Statement Of Accounts for {{ customer.customer_name }}

  • +
  • Body:

    +
    Hello {{ customer.customer_name }},
    PFA your Statement Of Accounts from {{ doc.from_date }} to {{ doc.to_date }}.
  • +
+","
+

Observera

+
    +
  • +Du kan anvƤnda Jinja Taggar i Ƅmne och Huvudtext-fƤlt fƶr dynamiska vƤrden. +
  • + Alla fƤlt i denna doctype Ƥr tillgƤngliga under doc-objekt och alla fƤlt fƶr kund som e-post meddelande gĆ„r till Ƥr tillgƤngliga under customer-objekt. +
+

Exempel

+ +
    +
  • Ƅmne:

    Konto besked fƶr {{ customer.customer_name }}

  • +
  • Huvudtext:

    +
    Hej {{ customer.customer_name }},
    Kontoutdrag frƄn {{ doc.from_date }} till {{ doc.to_date }}.
    +
+", +"
Other Details
","
Ɩvriga Detaljer
", +"
No Matching Bank Transactions Found
","
Inga avstƤmda Bank Transaktioner hittades
", +"
+

All dimensions in centimeter only

+
","
+

Alla mƄtt endast i centimeter

+
", +"

About Product Bundle

+ +

Aggregate group of Items into another Item. This is useful if you are bundling a certain Items into a package and you maintain stock of the packed Items and not the aggregate Item.

+

The package Item will have Is Stock Item as No and Is Sales Item as Yes.

+

Example:

+

If you are selling Laptops and Backpacks separately and have a special price if the customer buys both, then the Laptop + Backpack will be a new Product Bundle Item.

","

Om Artikel Paket

+ +

Paketera grupp av artiklar till annan Artikel. Det Ƥr anvƤndbart om man paketerar vissa Artiklar i paket och har lager av packade Artiklar och inte paketarad Artikel.Artikel kommer att ha Artikel. Ƅr Lagerar Artikel som Nej och Ƅr FƶrsƤljning Artikel som Ja.

+

Exempel:

+

Om man sƤljer bƤrbara datorer och ryggsƤckar separat och har specialpris om kunder kƶper bƄda, sƄ kommer det att vara bƤrbar dator + ryggsƤck som paket artikel.", +"

Currency Exchange Settings Help

+

There are 3 variables that could be used within the endpoint, result key and in values of the parameter.

+

Exchange rate between {from_currency} and {to_currency} on {transaction_date} is fetched by the API.

+

Example: If your endpoint is exchange.com/2021-08-01, then, you will have to input exchange.com/{transaction_date}

","

ValutavƤxling InstƤllningar HjƤlp

+

Det finns 3 variabler som kan anvƤndas av slutpunkt, resultat nyckel och i parameter vƤrde.

+

VƤxelkurs mellan {from_currency} och {to_currency} {transaction_date} hƤmtas av API.

+

Exempel: Om slutpunkt Ƥr exchange.com/2021-08-01 mƄste du ange exchange.com/{transaction_date}

", +"

Body Text and Closing Text Example

+ +
We have noticed that you have not yet paid invoice {{sales_invoice}} for {{frappe.db.get_value(""Currency"", currency, ""symbol"")}} {{outstanding_amount}}. This is a friendly reminder that the invoice was due on {{due_date}}. Please pay the amount due immediately to avoid any further dunning cost.
+ +

How to get fieldnames

+ +

The fieldnames you can use in your template are the fields in the document. You can find out the fields of any documents via Setup > Customize Form View and selecting the document type (e.g. Sales Invoice)

+ +

Templating

+ +

Templates are compiled using the Jinja Templating Language. To learn more about Jinja, read this documentation.

","

Huvudtext och exempel pƄ Avslutande Text

+ +
Vi har mƤrkt att ni Ƥnnu inte har betalat faktura {{sales_invoice}} fƶr {{frappe.db.get_value(""Currency"", currency, ""symbol"")}} {{outstanding_amount}}. Detta Ƥr en vƤnlig pƄminnelse om att fakturan var fƶrfallen {{due_date}}. VƤnligen betala fƶrfallen belopp omedelbart fƶr att undvika ytterligare kostnader.
+ +

Hur fƄr man fƤltnamn

+ +

FƤltnamn man kan anvƤnda i mall Ƥr fƤlt i dokument. Du kan ta reda pƄ fƤlt namn fƶr alla dokument via InstƤllningar > Anpassa formulƤrvy och vƤlj dokument typ (t.ex. FƶrsƤljning Faktura)

+ +

Mall

+ +

Mallar kompileras med Jinja Templating Language. Om du vill veta mer om Jinja lƤs denna dokumentation.

", +"

Contract Template Example

+ +
Contract for Customer {{ party_name }}
+
+-Valid From : {{ start_date }} 
+-Valid To : {{ end_date }}
+
+ +

How to get fieldnames

+ +

The field names you can use in your Contract Template are the fields in the Contract for which you are creating the template. You can find out the fields of any documents via Setup > Customize Form View and selecting the document type (e.g. Contract)

+ +

Templating

+ +

Templates are compiled using the Jinja Templating Language. To learn more about Jinja, read this documentation.

","

Exempel pƄ Avtal Mall

+ +
Avtal fƶr Kund {{ party_name }}
+
+-Giltigt frƄn: {{ start_date }}
+-GƤller till: {{ end_date }}
+
+ +

Hur fƄr man fƤltnamn

+ +

FƤltnamn du kan anvƤnda i avtal mall Ƥr fƤlt i avtal som du skapar mallen fƶr. Du kan ta reda pƄ fƤlt namn fƶr alla dokument via InstƤllningar > Anpassa formulƤrvy och vƤlj dokument typ (t.ex. Avtal)

+ +

Mall

+ +

Mallar kompileras med Jinja Templating Language. Om du vill veta mer om Jinja lƤser du den hƤr dokumentationen.

", +"

Standard Terms and Conditions Example

+ +
Delivery Terms for Order number {{ name }}
+
+-Order Date : {{ transaction_date }} 
+-Expected Delivery Date : {{ delivery_date }}
+
+ +

How to get fieldnames

+ +

The fieldnames you can use in your email template are the fields in the document from which you are sending the email. You can find out the fields of any documents via Setup > Customize Form View and selecting the document type (e.g. Sales Invoice)

+ +

Templating

+ +

Templates are compiled using the Jinja Templating Language. To learn more about Jinja, read this documentation.

","

Exempel pƄ Standard Villkor

+ +
Leverans Villkor fƶr Order Nummer {{ name }}
+
+-Order Datum: {{ transaction_date }}
+-FƶrvƤntat Leverans Datum: {{ delivery_date }}
+
+ +

Hur hƤmtas fƤltnamn

+ +

FƤltnamn som kan anvƤndas i E-post Mall Ƥr fƤlt i Dokument som man skickar E-post meddelande frƄn. Man kan ta reda pƄ fƤlt namn fƶr alla dokument via InstƤllning > Anpassa FormulƤr Vy och vƤlja Dokument Typ (t.ex. FƶrsƤljning Faktura)

+ +

Skriva Mallar

+ +

Mallar kompileras med Jinja Mall SprƄk. LƤs mer om Jinja dokumentation:

", +"
Or
","
Eller
", +"","", +"","", +"","", +"

In your Email Template, you can use the following special variables: +

+
    +
  • + {{ update_password_link }}: A link where your supplier can set a new password to log into your portal. +
  • +
  • + {{ portal_link }}: A link to this RFQ in your supplier portal. +
  • +
  • + {{ supplier_name }}: The company name of your supplier. +
  • +
  • + {{ contact.salutation }} {{ contact.last_name }}: The contact person of your supplier. +
  • + {{ user_fullname }}: Your full name. +
  • +
+

+

Apart from these, you can access all values in this RFQ, like {{ message_for_supplier }} or {{ terms }}.

","

I E-post Mall kan fƶljande specialvariabler anvƤndas: +

+
    +
  • + {{ update_password_link }} : LƤnk dƤr leverantƶr kan ange nytt lƶsenord fƶr att logga in pĆ„ leverantƶr portal. +
  • +
  • + {{ portal_link }}: LƤnk till offert begƤran pĆ„ leverantƶr portal. +
  • +
  • + {{ supplier_name }}: Leverantƶr namn. +
  • +
  • + {{ contact.salutation }} {{ contact.last_name }}: Kontakt person fƶr leverantƶr. +
  • +
  • + {{ user_fullname } }: AnvƤndarens fullstƤndiga namn. +
  • +
+

+

+

Fƶrutom dessa kan man komma Ƅt alla vƤrden i offert begƤran, som {{ message_for_supplier }} eller {{ terms }}.

", +"
Message Example
+ +<p> Thank You for being a part of {{ doc.company }}! We hope you are enjoying the service.</p> + +<p> Please find enclosed the E Bill statement. The outstanding amount is {{ doc.grand_total }}.</p> + +<p> We don't want you to be spending time running around in order to pay for your Bill.
After all, life is beautiful and the time you have in hand should be spent to enjoy it!
So here are our little ways to help you get more time for life! </p> + +<a href=""{{ payment_url }}""> click here to pay </a> + +
+","
Meddelande Exempel
+ +<p> Tack fƶr att ni Ƥr en del av {{ doc.company }}! Vi hoppas att ni gillar tjƤnst.</p> + +<p> VƤnligen se bifogat E-faktura. UtestƄende belopp Ƥr {{ doc.grand_total }}.</p> + +<p> Vi vill inte att ni ska spendera tid med att springa runt fƶr att betala faktura.
Livet Ƥr trots allt vackert och den tid man har bƶr spenderas fƶr att njuta av livet!
SƄ hƤr Ƥr vƄra smƄ sƤtt att hjƤlpa er att fƄ mer tid fƶr livet! < /p> + +<a href=""{{ payment_url }}""> klicka hƤr fƶr att betala </a> + +
+", +"
Message Example
+ +<p>Dear {{ doc.contact_person }},</p> + +<p>Requesting payment for {{ doc.doctype }}, {{ doc.name }} for {{ doc.grand_total }}.</p> + +<a href=""{{ payment_url }}""> click here to pay </a> + +
+","
Meddelande Exempel
+ +<p>Hej {{ doc.contact_person }},</p> + +<p>BegƤr betalning fƶr {{ doc.doctype }}, {{ doc.name }} fƶr {{ doc.grand_total }}.</p> + +<a href=""{{ payment_url }}""> klicka hƤr fƶr att betala </a> + +
+", +"Masters & Reports","InstƤllningar & Rapporter", +"Quick Access","GenvƤgar", +"Reports & Masters","Rapporter & InstƤllningar", +"Reports & Masters","Rapporter & InstƤllningar", +"Settings","InstƤllningar", +"Shortcuts","GenvƤgar", +"Your Shortcuts + + + + + + ","GenvƤgar + + + + + + ", +"Your Shortcuts","GenvƤgar", +Grand Total: {0},Totalt: {0}, +Outstanding Amount: {0},UtestƄende belopp: {0}, +" + + + + + + + + + + + + + + + + + +
Child DocumentNon Child Document
+

To access parent document field use parent.fieldname and to access child table document field use doc.fieldname

+ +
+

To access document field use doc.fieldname

+
+

Example: parent.doctype == ""Stock Entry"" and doc.item_code == ""Test""

+ +
+

Example: doc.doctype == ""Stock Entry"" and doc.purpose == ""Manufacture""

+
+ + + + + + +"," + + Underordnad Dokument + Ɩverordnad Dokument + + + + + +

Fƶr att komma Ƅt ƶverordnad dokument fƤlt anvƤnd parent.fieldname och fƶr att komma Ƅt underordnad dokument fƤlt anvƤnd doc.fieldname

+ + + +

Fƶr att komma Ƅt dokument fƤlt anvƤnd doc.fieldname

+ + + + +

Exampel: parent.doctype == ""Stock Entry"" and doc.item_code == ""Test""

+ + + +

Exampel: doc.doctype == ""Stock Entry"" and doc.purpose == ""Produktion""

+ + + + + + + + + + + +", +A Holiday List can be added to exclude counting these days for the Workstation.,Helg Lista kan lƤggas till fƶr att utesluta dessa dagar fƶr Arbetsstation., +A Packing Slip can only be created for Draft Delivery Note.,Packsedel kan endast skapas fƶr utkast till FƶrsƤljning Fƶljesedel., +"A Price List is a collection of Item Prices either Selling, Buying, or both","Prislista Ƥr samling av artikel priser som antingen sƤljs, kƶpes eller bĆ„da", +A Reconciliation Job {0} is running for the same filters. Cannot reconcile now,AvstƤmning jobb {0} kƶrs fƶr samma filter. Kan inte stƤmma av nu, +A Transaction Deletion Document: {0} is triggered for {0},Transaktion Borttagning jobb utlƶst fƶr {0} , +A customer must have primary contact email.,Kund mĆ„ste ha primƤr kontakt e-post adress., +A driver must be set to submit.,Fƶrare mĆ„ste anges fƶr att godkƤnna., +A template with tax category {0} already exists. Only one template is allowed with each tax category,Mall med moms kategori {0} finns redan. Endast en mall Ƥr tillĆ„ten med varje moms kategori, +API Details,API Detaljer, +AWB Number,AWB Nummer, +Abbreviation: {0} must appear only once,Fƶrkortning: {0} fĆ„r endast visas en gĆ„ng, +About Us Settings,Om Oss InstƤllningar, +About {0} minute remaining,Cirka {0} minut kvar, +About {0} minutes remaining,Cirka {0} minuter kvar, +About {0} seconds remaining,Cirka {0} sekunder kvar, +Acceptance Criteria Formula,Acceptans Kriterier Formel , +Acceptance Criteria Value,Acceptans Kriterier VƤrde, +Accepted Qty in Stock UOM,GodkƤnd Kvantitet (per Lager Enhet), +Access Key,ƅtkomst Nyckel, +Access Key is required for Service Provider: {0},ƅtkomst Nyckel erfordras fƶr TjƤnsteleverantƶr: {0}, +Account Balance (From),Konto Saldo (FrĆ„n), +Account Balance (To),Konto Saldo (Till), +Account Closing Balance,Konto StƤngning Saldo, +Account Currency (From),Konto Valuta (FrĆ„n), +Account Currency (To),Konto Valuta (Till), +Account Opening Balance,Ɩppning Saldo, +Account not Found,Konto ej funnen, +Account {0} added multiple times,Konto {0} har lagts till flera gĆ„nger, +Accounting Dimension Filter,Bokfƶring Dimension Filter, +Accounting Dimensions Filter,Bokfƶring Dimension Filter, +Accounting Entry for {0},Bokfƶring Post fƶr {0}, +Accounts Closing,Bokfƶring LĆ„sning, +Accounts Missing Error,Konton Saknas Fel, +Accounts Receivable/Payable,Fordringar/Skulder, +Accounts to Merge,Konton som ska SlĆ„s Samman, +Action If Quality Inspection Is Rejected,ƅtgƤrd om Kvalitet Kontroll Ƥr Avvisad, +Action If Same Rate is Not Maintained,ƅtgƤrd om Marginal inte BibehĆ„lls, +Action if Same Rate is Not Maintained Throughout Sales Cycle,ƅtgƤrd om samma Pris inte bibehĆ„lls under FƶrsƤljning, +Active Status,Aktiv Status, +Actual Balance Qty,Faktiskt Saldo Kvantitet, +Actual Expense,Faktisk Kostnad, +Actual Posting,Faktisk Postning, +Actual Qty in Warehouse,Faktisk Kvantitet pĆ„ Lager, +Actual Time,Verklig Tid, +Add Columns in Transaction Currency,LƤgg till kolumner i Transaktion Valuta, +Add Corrective Operation Cost in Finished Good Valuation,LƤgg till Korrigerande Driftkostnad i FƤrdig Artikel, +Add Discount,LƤgg till Rabatt, +Add Items in the Purpose Table,LƤgg till Artiklar i Syfte Tabell, +Add Lead to Prospect,LƤgg till Potentiell Kund till Prospekt, +Add Local Holidays,LƤgg till Lokal Helgdag, +Add Manually,LƤgg till Manuellt, +Add Or Deduct,LƤgg till eller Dra av, +Add Serial / Batch Bundle,LƤgg till Serie / Parti Paket, +Add Serial / Batch No,LƤgg till Serie/Parti Nummer, +Add Serial / Batch No (Rejected Qty),LƤgg till Serie/Parti Nummer (Avvisad Kvantitet), +Add Stock,LƤgg till Lager, +Add Sub Assembly,LƤgg till Delmontering, +Add Template,LƤgg till Mall, +Add a Note,LƤgg till Notering, +Add details,LƤgg till Detaljer, +Add to Prospect,LƤgg till Prospekt, +Added By,Lagt till Av, +Added On,Tillagd, +Added Supplier Role to User {0}.,Lade till Leverantƶr Roll till AnvƤndare {0}., +Added {1} Role to User {0}.,Lade till {1} roll till anvƤndare {0}., +Adding Lead to Prospect...,LƤgger till Potentiell Kund..., +Additional,Extra, +Additional Asset Cost,Extra TillgĆ„ng Kostnad, +Additional Cost Per Qty,Extra Kostnad per Kvantitet, +Additional Info,Extra Information, +Address And Contacts,Adress & Kontakter, +Adjust Asset Value,Justera TillgĆ„ng VƤrde, +Adjustment Against,Justering Mot, +Adjustment based on Purchase Invoice rate,Justering Baserad pĆ„ Inkƶp Faktura Pris, +Advance Account: {0} must be in either customer billing currency: {1} or Company default currency: {2},Fƶrskott Konto: {0} mĆ„ste vara antingen i kundens fakturering valuta: {1} eller bolag standard valuta: {2}, +Advance Payment,Fƶrskott Betalning, +Advance Tax,Fƶrskott Moms, +Advance Taxes and Charges,Fƶrskott Moms och Avgifter, +Advance paid against {0} {1} cannot be greater than Grand Total {2},Fƶrskott Betalning mot {0} {1} kan inte vara stƶrre Ƥn Totalt Belopp {2}, +Advance payments allocated against orders will only be fetched,Fƶrskott betalningar allokerade mot order kommer att hƤmtas, +Affected Transactions,Berƶrda Transaktioner, +Against Customer Order {0},Mot Kund Order {0}, +Against Supplier Invoice {0},Mot Leverantƶr Faktura {0}, +Against Voucher No,Mot Verifikat Nummer, +Age ({0}),ƅlder ({0}), +Ageing Range,ƅldring Intervall, +Agent Busy Message,Agent Upptaget Meddelande, +Agent Group,Agent Grupp, +Agent Unavailable Message,Agent OtillgƤnglig Meddelande, +Aggregate a group of Items into another Item. This is useful if you are maintaining the stock of the packed items and not the bundled item,Paketera flera Artiklar till ett annat Artikel. AnvƤndbart om lager baseras pĆ„ packade artiklar och inte ingĆ„ende artiklar, +Algorithm,Algoritm, +All Activities,Alla Aktivitet, +All Activities HTML,Alla Aktivitet HTML, +All Items,Alla Artiklar, +All Sales Transactions can be tagged against multiple Sales Persons so that you can set and monitor targets.,Alla fƶrsƤljning transaktioner kan taggas mot flera sƤljare fƶr att ange och ƶvervaka mĆ„l., +All allocations have been successfully reconciled,Alla tilldelningar Ƥr avstƤmda, +All items have already been received,Alla Artiklar Ƥr redan mottagna, +All items in this document already have a linked Quality Inspection.,Alla Artiklar i detta dokument har redan lƤnkad Kvalitet Kontroll., +All the Comments and Emails will be copied from one document to another newly created document(Lead -> Opportunity -> Quotation) throughout the CRM documents.,Alla kommentarer och E-post meddelande kommer att kopieras frĆ„n ett dokument till ett annat nyskapad dokument (Potentiell Kund -> Mƶjlighet -> FƶrsƤljning Offert) genom hela SƤljstƶd process., +"All the required items (raw materials) will be fetched from BOM and populated in this table. Here you can also change the Source Warehouse for any item. And during the production, you can track transferred raw materials from this table.",Alla nƶdvƤndiga artiklar (rĆ„material) kommer att hƤmtas frĆ„n stycklista och lƤggs till denna tabell. HƤr kan du ocksĆ„ Ƥndra hƤmtlager fƶr valfri artikel. Och under produktion kan du spĆ„ra ƶverfƶrd rĆ„material frĆ„n denna tabell., +Allocate Payment Request,Tilldela Betalning BegƤran, +Allocated Entries,Tilldelade Poster, +Allocated To:,Tilldelad Till:, +Allocations,Tilldelningar, +Allow,TillĆ„t, +Allow Alternative Item must be checked on Item {},TillĆ„t Alternativ Artikel mĆ„ste vara vald fƶr Artikel {}, +Allow Continuous Material Consumption,TillĆ„t Kontinuerlig Material Fƶrbrukning, +Allow Excess Material Transfer,TillĆ„t Ɩverflƶdig Material Ɩverfƶring, +Allow Internal Transfers at Arm's Length Price,TillĆ„t Interna Ɩverfƶringar till Marknad Pris, +Allow Item to be Added Multiple Times in a Transaction,TillĆ„t att Artikel lƤggs till flera gĆ„nger i Transaktion, +Allow Lead Duplication based on Emails,TillĆ„t Potentiella Kunder Duplicering baserat pĆ„ E-post Meddelande, +Allow Negative rates for Items,TillĆ„t Negativa Priser fƶr Artiklar, +Allow Or Restrict Dimension,TillĆ„t eller BegrƤnsa Dimension, +Allow Partial Reservation,TillĆ„t Partiell Reservation, +Allow Purchase,TillĆ„t Inkƶp, +Allow Sales,TillĆ„t FƶrsƤljning, +Allow Sales Order Creation For Expired Quotation,TillĆ„t att FƶrsƤljning Order skapas fƶr Fƶrfallen FƶrsƤljning Offert, +Allow User to Edit Discount,TillĆ„t AnvƤndare att Redigera Rabatt, +Allow User to Edit Rate,TillĆ„t AnvƤndare att Redigera Pris, +Allow Zero Rate,TillĆ„t Noll Pris, +Allow material consumptions without immediately manufacturing finished goods against a Work Order,TillĆ„t Material Fƶrbrukning utan att omedelbart producera fƤrdiga artiklar mot Arbetsorder, +Allow multi-currency invoices against single party account ,TillĆ„t Fler Valuta Fakturor mot Parti Konto, +Allow to Edit Stock UOM Qty for Purchase Documents,TillĆ„t att redigera Lager Enhet Kvantitet fƶr Inkƶp Dokument, +Allow to Edit Stock UOM Qty for Sales Documents,TillĆ„t att redigera Lager Enhet Kvantitet fƶr FƶrsƤljning Dokument, +Allow transferring raw materials even after the Required Quantity is fulfilled,TillĆ„t ƶverfƶring av rĆ„material Ƥven efter att Erfordrad Kvantitet Ƥr uppfylld, +Allowed,TillĆ„ten, +Allowed Dimension,TillĆ„ten Dimension, +Allowed Doctypes,TillĆ„tna Dokument, +Allowed Items,TillĆ„tna Artiklar, +Allowed primary roles are 'Customer' and 'Supplier'. Please select one of these roles only.,TillĆ„tna primƤra roller Ƥr 'Kund' och 'Leverantƶr'. VƤlj endast en av dessa roller., +Allows to keep aside a specific quantity of inventory for a particular order.,TillĆ„ter reservation av specifierad artikel kvantitet fƶr angiven order., +Already Picked,Redan Plockad, +Alternative Items,Alternativa Artiklar, +"Alternatively, you can download the template and fill your data in.",Alternativt kan du ladda ner mall och fylla i dina uppgifter., +Amount (AED),Belopp (AED), +Amount Eligible for Commission,Provision Belopp, +Amount in Account Currency,Belopp i Konto Valuta, +Amount in party's bank account currency,Belopp i partens Bank Konto Valuta, +Amount in transaction currency,Belopp i transaktion valuta, +An Item Group is a way to classify items based on types.,Artikel grupp Ƥr ett sƤtt att klassificera artiklar baserat pĆ„ typer., +An error has been appeared while reposting item valuation via {0},Fel har uppstĆ„tt vid ompostering av artikel vƤrdering via {0}, +An error has occurred during {0}. Check {1} for more details,Fel har uppstĆ„tt under {0}. Kontrollera {1} fƶr mer information,Error Log +Annual Revenue,ƅrlig OmsƤtning, +"Another Cost Center Allocation record {0} applicable from {1}, hence this allocation will be applicable upto {2}","Annan Resultat Enhet Tilldelning Post {0} Ƥr tillƤmplig frĆ„n {1}, dƤrfƶr kommer denna tilldelning att gƤlla upp till {2}", +"Any one of following filters required: warehouse, Item Code, Item Group","NĆ„got av fƶljande filter erfordras: Lager, Artikelkod, Artikelgrupp", +Applicable Dimension,TillƤmpad Dimension, +Applicable On Account,TillƤmplig PĆ„ Konto, +Applied on each reading.,TillƤmpas vid varje lƤsning., +Applied putaway rules.,TillƤmpad LƤggundan Regler, +Apply Putaway Rule,TillƤmpa LƤgg Undan Regel, +Apply Recursion Over (As Per Transaction UOM),TillƤmpa Rekursion Ɩver (per Transaktion Enhet), +Apply SLA for Resolution Time,TillƤmpa Service NivĆ„ Avtal fƶr Resolution Tid, +Apply TDS,TillƤmpa TDS, +Apply Tax Withholding Amount ,TillƤmpa Moms Avdrag Belopp, +Apply restriction on dimension values,TillƤmpa begrƤnsning pĆ„ dimension vƤrde, +Apply to All Inventory Documents,TillƤmpa pĆ„ Alla Lager Dokument, +Apply to Document,TillƤmpa pĆ„ Dokument, +Appointment Created Successfully,Mƶte Bokad!, +Appointment Scheduling Disabled,Bokning av Mƶten Inaktiverad, +Appointment Scheduling has been disabled for this site,Bokning av Mƶten Ƥr Inaktiverad fƶr denna Webbplats, +Appointment was created. But no lead was found. Please check the email to confirm,Mƶte Skapad. Men inget Poteentiel Kund hittades. Kontrollera e-post meddelande fƶr att bekrƤfta, +Approximately match the description/party name against parties,UngefƤrlig avstƤmning av beskrivning/partinamn mot parti, +Are you sure you want to clear all demo data?,Ƅr du sƤker pĆ„ att du vill ta bort alla demodata?, +Are you sure you want to delete this Item?,Ƅr du sƤker pĆ„ att du vill ta bort detta Artikel?, +Are you sure you want to restart this subscription?,Ƅr du sƤker pĆ„ att du vill starta om denna prenumeration?, +As on Date,Som den, +"As there are existing submitted transactions against item {0}, you can not change the value of {1}.",Eftersom det finns befintliga godkƤAda transaktioner mot artikel {0} kan man inte Ƥndra vƤrdet pĆ„ {1}., +"As there are negative stock, you can not enable {0}.",Eftersom det finns negativ lager kan du inte aktivera {0}., +"As there are reserved stock, you cannot disable {0}.",Eftersom det finns reserverat lager kan du inte inaktivera {0}., +"As there are sufficient Sub Assembly Items, Work Order is not required for Warehouse {0}.",Eftersom det finns tillrƤckligt med Undermontering Artiklar erfordras inte Arbetsorder fƶr Lager {0}., +"As {0} is enabled, you can not enable {1}.",Eftersom {0} Ƥr aktiverad kan du inte aktivera {1}., +Assembly Items,Montering Artiklar, +Asset Activity,TillgĆ„ng Aktivitet, +Asset Capitalization,TillgĆ„ng Bokning, +Asset Capitalization Asset Item,TillgĆ„ng Bokning TillgĆ„ng Post, +Asset Capitalization Service Item,TillgĆ„ng Bokning Service Post, +Asset Capitalization Stock Item,TillgĆ„ng Bokning Lager Post, +Asset Depreciation Details,TillgĆ„ng Avskrivning Detaljer, +Asset Depreciation Schedule,TillgĆ„ng Avskrivning Schema, +Asset Depreciation Schedule for Asset {0} and Finance Book {1} is not using shift based depreciation,TillgĆ„ng Avskrivning Schema fƶr TillgĆ„ng {0} och Finans Register {1} anvƤnder inte skift baserad avskrivning, +Asset Depreciation Schedule not found for Asset {0} and Finance Book {1},TillgĆ„ng Avskrivning Schema finns inte fƶr TillgĆ„ng {0} och Finans Register {1}, +Asset Depreciation Schedule {0} for Asset {1} already exists.,TillgĆ„ng Avskrivning Schema {0} fƶr TillgĆ„ng {1} finns redan., +Asset Depreciation Schedule {0} for Asset {1} and Finance Book {2} already exists.,TillgĆ„ng Avskrivning Schema {0} fƶr TillgĆ„ng {1} och Finans Register {2} finns redan., +"Asset Depreciation Schedules created:
{0}

Please check, edit if needed, and submit the Asset.",TillgƄng Avskrivning Schema skapades:
{0}

Kontrollera och GodkƤnn TillgĆ„ng., +Asset ID,TillgĆ„ng ID, +Asset Quantity,TillgĆ„ng Antal, +Asset Repair Consumed Item,TillgĆ„ng Bokning Fƶrbrukad Post, +Asset Settings,TillgĆ„ng InstƤllningar, +Asset Shift Allocation,TillgĆ„ng Skift Tilldelning, +Asset Shift Factor,TillgĆ„ng Fƶrskjutning Faktor, +Asset Shift Factor {0} is set as default currently. Please change it first.,TillgĆ„ng Fƶrskjutning Faktor {0} Ƥr fƶr nƤrvarande angiven som standard. VƤnligen Ƥndra det fƶrst., +Asset cancelled,TillgĆ„ng Annullerad, +Asset capitalized after Asset Capitalization {0} was submitted,TillgĆ„ng kapitaliserad efter att TillgĆ„ng Kapitalisering {0} godkƤndes , +Asset created,TillgĆ„ng Skapad, +Asset created after Asset Capitalization {0} was submitted,TillgĆ„ng skapad efter att TillgĆ„ng Kapitalisering {0} godkƤndes, +Asset created after being split from Asset {0},TillgĆ„ng skapad efter att ha delats frĆ„n TillgĆ„ng {0}, +Asset decapitalized after Asset Capitalization {0} was submitted,TillgĆ„ng avkapitaliserad efter att TillgĆ„ng Kapitalisering {0} godkƤndes, +Asset deleted,TillgĆ„ng Borttagen, +Asset issued to Employee {0},TillgĆ„ng utfƤrdad till Personal {0}, +Asset out of order due to Asset Repair {0},TillgĆ„ng ur funktion pĆ„ grund av reparation av TillgĆ„ng {0}, +Asset received at Location {0} and issued to Employee {1},TillgĆ„ng mottagen pĆ„ plats {0} och utfƤrdad till Personal {1}, +Asset restored,TillgĆ„ng Ć„terstƤlld, +Asset restored after Asset Capitalization {0} was cancelled,TillgĆ„ng Ć„terstƤlld efter att TillgĆ„ng Kapitalisering {0} annullerats, +Asset returned,TillgĆ„ng Ć„terlƤmnad, +Asset scrapped,TillgĆ„ng skrotad, +Asset sold,TillgĆ„ng SĆ„ld, +Asset submitted,TillgĆ„ng GodkƤnd, +Asset transferred to Location {0},TillgĆ„ng ƶverfƶrd till Plats {0}, +Asset updated after being split into Asset {0},TillgĆ„ng uppdaterad efter att ha delats upp i TillgĆ„ng {0}, +Asset updated after cancellation of Asset Repair {0},TillgĆ„ng uppdaterad efter annullering av TillgĆ„ng Reparation {0}, +Asset updated after completion of Asset Repair {0},TillgĆ„ng uppdaterad efter slutfƶrande av TillgĆ„ng Reparation {0}, +Asset {0} cannot be received at a location and given to an employee in a single movement,TillgĆ„ng {0} kan inte tas emot pĆ„ plats och ges till Personal i en enda rƶrelse, +Asset {0} does not belong to Item {1},TillgĆ„ng {0} tillhƶr inte Post {1}, +Asset {0} does not exist,TillgĆ„ng {0} finns inte, +Asset {0} has been created. Please set the depreciation details if any and submit it.,TillgĆ„ng {0} skapad. Ange avskrivning detaljer och godkƤnn den., +Asset {0} has been updated. Please set the depreciation details if any and submit it.,TillgĆ„ng {0} uppdaterad. Ange avskrivning detaljer och godkƤnn den., +Asset's depreciation schedule updated after Asset Shift Allocation {0},TillgĆ„ng Avskrivning Schema uppdaterad efter TillgĆ„ng Fƶrskjutning Tilldelning {0}, +Asset's value adjusted after cancellation of Asset Value Adjustment {0},TillgĆ„ng VƤrde Justerat efter annullering av TillgĆ„ng VƤrde Justering {0}, +Asset's value adjusted after submission of Asset Value Adjustment {0},TillgĆ„ngens VƤrde Justerat efter godkƤnade av TillgĆ„ng VƤrde Justering {0}, +Assign Job to Employee,Tilldela jobb till Personal, +Assignment,Tilldelning, +Assignment Conditions,Tilldelning Villkor, +At Row #{0}: The picked quantity {1} for the item {2} is greater than available stock {3} for the batch {4} in the warehouse {5}.,PĆ„ rad #{0}: Plockad kvantitet {1} fƶr artikel {2} Ƥr stƶrre Ƥn tillgƤnglig kvantitet {3} fƶr parti {4} i lager {5}., +At Row #{0}: The picked quantity {1} for the item {2} is greater than available stock {3} in the warehouse {4}.,PĆ„ rad #{0}: Plockad kvantitet {1} fƶr artikel {2} Ƥr stƶrre Ƥn tillgƤnglig kvantitet {3} i lager {4}., +At row {0}: Batch No is mandatory for Item {1},Rad {0}: Parti Nummer erfordras fƶr Artikel {1}, +At row {0}: Parent Row No cannot be set for item {1},Rad {0}: Ɩverordnad rad nummer kan inte anges fƶr artikel {1}, +At row {0}: Qty is mandatory for the batch {1},Rad {0}: Kvantitet erfordras fƶr Artikel {1}, +At row {0}: Serial No is mandatory for Item {1},Rad {0}: Serie Nummer erfordras fƶr Artikel {1}, +At row {0}: Serial and Batch Bundle {1} has already created. Please remove the values from the serial no or batch no fields.,Rad {0}: Serie och Parti Paket {1} Ć„r redan skapad. Ta bort vƤrde frĆ„n serienummer eller parti nummer fƤlt., +At row {0}: set Parent Row No for item {1},Rad {0}: ange ƶverordnad rad nummer fƶr artikel {1}, +Attach CSV File,Bifoga CSV Fil, +Attendance & Leaves,NƤrvaro & Ledighet, +Attribute value: {0} must appear only once,Egenskap VƤrde: {0} fĆ„r endast visas en gĆ„ng, +Auto Create Exchange Rate Revaluation,Automatiskt Skapa Valuta VƤxling Kurs OmvƤrdering, +Auto Create Purchase Receipt,Automatiskt Skapa Inkƶp Fƶljesedel, +Auto Create Serial and Batch Bundle For Outward,Automatiskt Skapa Serie Nummer och Parti Paket fƶr FƶrsƤljning, +Auto Create Subcontracting Order, Automatiskt Skapa Underleverantƶr Order, +Auto Created Serial and Batch Bundle,Automatiskt Skapad Serie och Parti Paket, +Auto Creation of Contact,Automatiskt Skapa Kontakt, +Auto Email Report,Automatiskt E-post Rapport, +Auto Insert Item Price If Missing,Automatiskt Infoga Artikel Pris om det saknas, +Auto Name,Automatiskt, +Auto Reconcile,Automatiskt AvstƤmning, +Auto Reconcile Payments,Automatisk Betalning AvstƤmning, +Auto Reconciliation,Automatisk AvstƤmning, +Auto Reconciliation of Payments has been disabled. Enable it through {0},Automatisk AvstƤmning av Betalningar har inaktiverats. Aktivera genom {0}, +Auto Reserve Serial and Batch Nos,Automatisk Reservera Serie och Parti Nummer, +Auto Reserve Stock for Sales Order on Purchase,Automatisk Reservera Lager fƶr FƶrsƤljning Order vid Inkƶp, +Auto close Opportunity Replied after the no. of days mentioned above,Automatiskt StƤng Besvarad Mƶjlighet efter ovan angivet antal dagar, +Auto match and set the Party in Bank Transactions,Automatiskt avstƤm och ange Parti i Bank Transaktioner, +Auto write off precision loss while consolidation,Automatisk Avskrivning Precision av Fƶrlust under Konsolidering, +Automatically Add Filtered Item To Cart,Automatiskt LƤgg till Filtrerad Artikel till Kundkorg, +Automatically Fetch Payment Terms from Order,Automatikt HƤmta Betalning Villkor frĆ„n Order, +Automatically post balancing accounting entry,Automatiskt skapa balans bokfƶring post, +Available Batch Report,TillgƤnglig Parti Rapport, +Available Qty For Consumption,TillgƤnglig Kvantitet fƶr Fƶrbrukning, +Available Qty at Company,TillgƤnglig Kvantitet, +Available Qty at Target Warehouse,TillgƤnglig Kvantitet pĆ„ Till Lager, +Available Qty to Reserve,TillgƤngligt Kvantitet att Reservera, +Average Completion,Genomsnittlig Slutfƶrande, +Avg Rate,Genomsnittlig Pris, +Avg Rate (Balance Stock),Genomsnittlig Pris (Lager Saldo), +BIN Qty,Lager Kvantitet, +BOM Created,Stycklista Skapad, +BOM Creator,Stycklista Generator, +BOM Creator Item,Stycklista Generator Post, +BOM Info,Stycklista Information, +BOM Level,Stycklista NivĆ„, +BOM Tree,Stycklista TrƤd, +BOM UoM,Stycklista Enhet, +BOM Update Batch,Stycklista Uppdatera Parti , +BOM Update Initiated,Stycklista Uppdatering Initierad, +BOM Update Log,Stycklista Uppdatering Logg, +BOM Update Tool Log with job status maintained,Stycklista Uppdatering Verktyg Logg med jobb status upprƤtthĆ„llen, +BOM Updation already in progress. Please wait until {0} is complete.,Stycklista Uppdatering pĆ„gĆ„r. VƤnta tills {0} Ƥr klar., +BOM Updation is queued and may take a few minutes. Check {0} for progress.,Stycklista Uppdatering i kƶ och kan ta nĆ„gra minuter. Kontrollera {0} fƶr framsteg., +BOM and Production,Stycklista & Produktion, +BOM recursion: {1} cannot be parent or child of {0},Stycklista Rekursion: {1} kan inte vara ƶverordnad eller underordnad till {0}, +BOMs Updated,Stycklista Uppdaterad, +BOMs created successfully,Stycklista Skapad, +BOMs creation failed,Stycklista Skapande Misslyckades, +"BOMs creation has been enqueued, kindly check the status after some time",Skapandet av Stycklistor i Kƶ. VƤnligen kontrollera status efter en tid, +Balance Qty (Stock),Saldo Kvantitet (Lager), +Balance Sheet Summary,Balans Rapport Ɩversikt, +Balance Stock Value,Saldo Lager VƤrde, +Bank Reconciliation Tool,Bank AvstƤmning Verktyg, +Bank Statement Import,Bank AvstƤmning Import, +Bank Transaction {0} Matched,Bank Transaktion {0} avstƤmd, +Bank Transaction {0} added as Journal Entry,Bank Transaktion {0} har lagts till som Journal Post, +Bank Transaction {0} added as Payment Entry,Bank Transaktion {0} har lagts till som Betalning Post, +Bank Transaction {0} is already fully reconciled,Bank Transaktion {0} Ƥr redan helt avstƤmd, +Bank Transaction {0} updated,Bank Transaktion {0} uppdaterad, +Bank/Cash Account,Bank / Kassa Konto, +Bank/Cash Account {0} doesn't belong to company {1},Bank / Kassa Konto {0} tillhƶr inte bolag {1}, +Base Amount,Bas Belopp, +Base Cost Per Unit,Bas Kostnad per Enhet, +Base Rate,Bas Pris, +Base Tax Withholding Net Total,Bas Netto Totalt Ex Moms , +Base Total,Bas Totalt, +Base Total Billable Amount,Bas Totalt Fakturerbar Belopp, +Base Total Billed Amount,Bas Totalt Fakturerad Belopp, +Base Total Costing Amount,Bas Totalt Kostnad Belopp, +Based On Value,Baserad pĆ„ VƤrde, +"Based on your HR Policy, select your leave allocation period's end date",Baserat pĆ„ din Personal Princip vƤlj slutdatum fƶr din ledighet tilldelning, +"Based on your HR Policy, select your leave allocation period's start date",Baserat pĆ„ din Personal Princip vƤljstart datum fƶr ledighet period, +Batch Expiry Date,Parti Fƶrfallodatum, +Batch No is mandatory,Parti Nummer erfordras, +Batch No {0} does not exists,Parti Nummer {0} finns inte, +Batch No {0} is linked with Item {1} which has serial no. Please scan serial no instead.,Parti Nummer {0} Ƥr lƤnkat till Artikel {1} som har serie nummer. Skanna serie nummer istƤllet., +Batch No.,Parti Nummer, +Batch Nos,Parti Nummer, +Batch Nos are created successfully,Parti Nummer Skapade, +Batch Not Available for Return,Parti Ej TillgƤnglig fƶr Retur, +Batch Qty,Parti Kvantitet, +Batch and Serial No,Parti och Serie Nummer, +Batch not created for item {} since it does not have a batch series.,Parti Ƥr inte skapad fƶr Artikel {} eftersom den inte har Parti Nummer., +Batch {0} and Warehouse,Parti {0} och Lager, +Batch {0} is not available in warehouse {1},Parti {0} Ƥr inte tillgƤngligt i lager {1}, +Batchwise Valuation,VƤrdering per Parti, +Beginning of the current subscription period,Bƶrjan av aktuell prenumeration period, +Below Subscription Plans are of different currency to the party default billing currency/Company currency: {0},Nedan Prenumeration Planer Ƥr i annan valuta Ƥn Parti standard valuta/bolag valuta: {0}, +Bill for Rejected Quantity in Purchase Invoice,Faktura fƶr Avvisad Kvantitet i Inkƶp Faktura, +Billed Items To Be Received,Fakturerade Artiklar att Ta Emot, +"Billed, Received & Returned","Fakturerad,Mottagen & Returnerad", +Billing Address Details,Faktura Adress Detaljer, +Billing Interval in Subscription Plan must be Month to follow calendar months,Fakturering Intervall i Prenumeration Plan mĆ„ste vara MĆ„nad fƶr att fƶlja kalender mĆ„nader, +Bisect Accounting Statements,Halvera Bokfƶring Rapporter, +Bisect Left,Bisekt VƤnster, +Bisect Nodes,Halvera Noder, +Bisect Right,Halvera Hƶger, +Bisecting From,Halvera FrĆ„n, +Bisecting Left ...,Halverar VƤnster..., +Bisecting Right ...,Halverar Hƶger..., +Bisecting To,Halverar Till, +Blanket Order Allowance (%),Blankoavtal Order TillĆ„telse (%), +Bom No,Stycklista Nummer, +Book Advance Payments as Liability option is chosen. Paid From account changed from {0} to {1}.,Bokfƶr Fƶrskott Betalningar eftersom Skuld alternativ Ƥr vald. Betald frĆ„n konto har Ƥndrats frĆ„n {0} till {1}., +Book Advance Payments in Separate Party Account,Bokfƶr Fƶrskott Betalningar pĆ„ Separat Konto, +Book Tax Loss on Early Payment Discount,Bokfƶr Moms Bortfall vid Tidig Betalning Rabatt, +Book an appointment,Boka Mƶte, +Booking stock value across multiple accounts will make it harder to track stock and account value.,Bokfƶring av Lager VƤrde pĆ„ flera Konto gƶr det svĆ„rare att spĆ„ra lager och konto vƤrde., +Books have been closed till the period ending on {0},Bokfƶring Ƥr lĆ„st till {0}, +Both Payable Account: {0} and Advance Account: {1} must be of same currency for company: {2},BĆ„de Skuld Konto: {0} och Fƶrskott Konto: {1} mĆ„ste vara i samma valuta fƶr bolag: {2}, +Both Receivable Account: {0} and Advance Account: {1} must be of same currency for company: {2},BĆ„de Fordring Konto: {0} och Fƶrskott Konto: {1} mĆ„ste vara i samma valuta fƶr bolag: {2}, +Both {0} Account: {1} and Advance Account: {2} must be of same currency for company: {3},BĆ„de {0} Konto: {1} och Fƶrskott Konto: {2} mĆ„ste vara i samma valuta fƶr bolag: {3}, +Budget Exceeded,Budget Ɩverskriden, +Buildable Qty,Producerbart Kvantitet, +Bulk Transaction Log,Mass Transaktion Logg, +Bulk Transaction Log Detail,Mass Transaktion Logg Detaljer, +Bulk Update,Mass Uppdatera, +Bundle Items,Paketera Artiklar, +Buying & Selling Settings,Inkƶp & FƶrsƤljning InstƤllningar, +Buying and Selling,Inkƶp & FƶrsƤljning, +"By default, the Supplier Name is set as per the Supplier Name entered. If you want Suppliers to be named by a Naming Series choose the 'Naming Series' option.","Som standard Ƥr leverantƶr namn satt enligt angiven Leverantƶrs Namn. Om man vill att leverantƶrer ska namnges av Nummer Serie VƤlj 'Nummer Serie'", +Bypass credit check at Sales Order,Ignorera Kredit Kontroll vid FƶrsƤljning Order, +COGS By Item Group,Kostnad fƶr SĆ„lda Artiklar Efter Artikel Grupp, +COGS Debit,Kostnad fƶr SĆ„lda Artiklar Debet, +CRM Note,SƤljstƶd Anteckning, +CRM Settings,SƤljstƶd InstƤllningar, +Calculate Product Bundle Price based on Child Items' Rates,BerƤkna Artikel Paket Pris baserat pĆ„ Underordnade Artiklar priser, +Calculate daily depreciation using total days in depreciation period,BerƤkna daglig avskrivning med hjƤlp av totalt antal dagar i avskrivningsperiod, +Call Again,Ring Igen, +Call Ended,Samtal Avslutad, +Call Handling Schedule,Samtal Hantering Schema, +Call Received By,Samtal Mottagen Av, +Call Receiving Device,Samtal Mottagning Enhet, +Call Routing,Samtal Dirigering, +Call Schedule Row {0}: To time slot should always be ahead of From time slot.,Samtal Schema Rad {0}: Till tid bƶr alltid ligga fƶre FrĆ„n tid., +Call Type,Samtal Typ, +Callback,ƅteruppringning, +Campaign Item,Kampanj Artikel, +Can not close Work Order. Since {0} Job Cards are in Work In Progress state.,"Kan inte stƤnga Arbetsorder, eftersom {0} Jobbkort har Arbete pĆ„gĆ„r status.", +"Can not filter based on Child Account, if grouped by Account","Kan inte filtrera baserat pĆ„ Underordnad Konto, om grupperat efter konto", +"Can't change the valuation method, as there are transactions against some items which do not have its own valuation method","Kan inte Ƥndra vƤrdering sƤtt, eftersom det finns transaktioner mot vissa artiklar som inte har egen vƤrdering sƤtt", +Can't disable batch wise valuation for active batches.,Kan inte inaktivera partivis vƤrdering fƶr aktiva partier., +Can't disable batch wise valuation for items with FIFO valuation method.,Kan inte inaktivera partivis vƤrdering fƶr artiklar med FIFO vƤrdering metod., +Cannot Merge,Kan inte SlĆ„ Samman, +Cannot Resubmit Ledger entries for vouchers in Closed fiscal year.,Kan inte godkƤnna om Register Poster fƶr verifikationer under stƤngt BokfƶringsĆ„r., +"Cannot amend {0} {1}, please create a new one instead.","Kan inte Ƥndra {0} {1}, skapa ny istƤllet.", +Cannot apply TDS against multiple parties in one entry,Kan inte tillƤmpa TDS mot flera parter i en post, +Cannot cancel as processing of cancelled documents is pending.,Kan inte avbryta eftersom behandling av annullerade dokument vƤntar., +Cannot cancel the transaction. Reposting of item valuation on submission is not completed yet.,Kan inte annullera transaktion. Ompostering av artikel vƤrdering vid godkƤnnande Ƥr inte klar Ƥnnu., +Cannot change Reference Document Type.,Kan inte Ƥndra Referens Dokument Typ, +Cannot complete task {0} as its dependant task {1} are not completed / cancelled.,Kan inte slutfƶra uppgift {0} eftersom dess beroende uppgift {1} inte har slutfƶrts/avbrutits., +Cannot convert Task to non-group because the following child Tasks exist: {0}.,Kan inte konvertera uppgift till ej grupp eftersom fƶljande underordnade uppgifter finns: {0}., +Cannot convert to Group because Account Type is selected.,Kan inte konvertera till Grupp eftersom Konto Typ Ƥr vald., +Cannot create Stock Reservation Entries for future dated Purchase Receipts.,Kan inte skapa Lager Reservation Poster fƶr framtid daterade Inkƶp Fƶljesedlar., +Cannot create a pick list for Sales Order {0} because it has reserved stock. Please unreserve the stock in order to create a pick list.,Kan inte skapa plocklista fƶr FƶrsƤljning Order {0} eftersom den har reserverad lager. VƤnligen avboka lager fƶr att skapa plocklista., +Cannot create accounting entries against disabled accounts: {0},Kan inte skapa bokfƶring poster mot inaktiverade konto: {0}, +Cannot disable batch wise valuation for FIFO valuation method.,Kan inte inaktivera partivis vƤrdering fƶr FIFO vƤrdering metod., +Cannot enqueue multi docs for one company. {0} is already queued/running for company: {1},Kan inte stƤlla flera dokument i kƶ fƶr ett bolag. {0} Ƥr redan i kƶ/kƶrs fƶr bolag: {1}, +Cannot find a default warehouse for item {0}. Please set one in the Item Master or in Stock Settings.,Kan inte hitta standardlager fƶr artikel {0}. Ange det i ArtikelinstƤllningar eller i LagerinstƤllningar., +Cannot make any transactions until the deletion job is completed,Kan inte skapa transaktioner fƶrrƤn borttagning jobb Ƥr slutfƶrt, +Cannot produce more item for {0},Kan inte producera fler artiklar fƶr {0}, +Cannot produce more than {0} items for {1},Kan inte producera mer Ƥn {0} artiklar fƶr {1}, +Cannot receive from customer against negative outstanding,Kan inte ta emot frĆ„n kund mot negativt utestĆ„ende, +Cannot retrieve link token for update. Check Error Log for more information,Kan inte hƤmta lƤnk token fƶr uppdatering Kontrollera Fellogg fƶr mer information, +Cannot retrieve link token. Check Error Log for more information,Kan inte hƤmta lƤnk token. Se fellogg fƶr mer information, +Cannot {0} from {2} without any negative outstanding invoice,Kan inte {0} frĆ„n {2} utan nĆ„gon negativ utestĆ„ende faktura, +Capacity (Stock UOM),Kapacitet (Lager Enhet), +Capacity in Stock UOM,Kapacitet i Lager Enhet, +Capacity must be greater than 0,Kapacitet mĆ„ste vara hƶgre Ƥn 0, +Capitalization,Kapitalisering, +Capitalization Method,Kaptitalisering SƤtt, +Capitalize Asset,Kapitalisera TillgĆ„ng, +Capitalize Repair Cost,Kapitalisera Reparation Kostnad, +Capitalized,Kapitaliserad, +Carrier,Transportƶr, +Carrier Service,Transportƶr Service, +Carry Forward Communication and Comments,Vidarebefordra E-post och Kommentarer, +Category Details,Kategori Detaljer, +Caution: This might alter frozen accounts.,Varning: Detta kan Ƥndra lĆ„sta konto., +Change in Stock Value,FƶrƤndring i Lager VƤrde, +Changed customer name to '{}' as '{}' already exists.,Ƅndrade kund namn till '{}' eftersom '{}' redan finns., +Changes,Ƅndringar, +Charge of type 'Actual' in row {0} cannot be included in Item Rate or Paid Amount,"Debitering av typ ""Verklig"" i rad {0} kan inte inkluderas i Artikel Pris eller Betald Belopp", +Chart Of Accounts,Kontoplan, +Checked On,Kontrollerad, +Checking this will round off the tax amount to the nearest integer,Om vald avrundas moms belopp till nƤrmaste heltal, +Cheques and Deposits Incorrectly cleared,Checkar och Depositioner felaktigt avstƤmda, +Choose a WIP composite asset,VƤlj pĆ„gĆ„ende arbete Sammansatt TillgĆ„ng, +Clear Demo Data,Tar Bort Demo Data... , +Clear Notifications,Rensa Noteringar, +Clearing Demo Data...,Tar Bort Demo Data..., +Click on 'Get Finished Goods for Manufacture' to fetch the items from the above Sales Orders. Items only for which a BOM is present will be fetched.,"Klicka pĆ„ ""HƤmta FƤrdiga Artiklar fƶr Produktion"" fƶr att hƤmta artiklar frĆ„n ovanstĆ„ende FƶrsƤljning Ordrar. Endast artiklar fƶr vilka det finns stycklista kommer att hƤmtas.", +Click on Add to Holidays. This will populate the holidays table with all the dates that fall on the selected weekly off. Repeat the process for populating the dates for all your weekly holidays,Klicka pĆ„ 'LƤgg till Helgdagar'. Detta kommer att fylla helg tabell med alla datum som infaller pĆ„ valda veckovis ledighet. Upprepa processen fƶr att fylla i datum fƶr alla helgdagar, +Click on Get Sales Orders to fetch sales orders based on the above filters.,Klicka pĆ„ 'HƤmta FƶrsƤljning Order' fƶr att hƤmta FƶrsƤljning Ordrar baserade pĆ„ ovanstĆ„ende filter., +Click to add email / phone,Klicka pĆ„ att lƤgga till e-post / telefon, +Close Replied Opportunity After Days,StƤng Besvarad Mƶjlighet Efter Dagar, +Closed Work Order can not be stopped or Re-opened,StƤngd Arbetsorder kan inte stoppas eller ƶppnas igen, +Closing,StƤnger, +Closing Balance as per Bank Statement,StƤngning Saldo enligt Bank, +Closing Balance as per ERP, StƤngning Saldo enligt System, +Closing Stock Balance,StƤngning Lager Saldo, +Columns are not according to template. Please compare the uploaded file with standard template,Kolumner Ƥr inte enligt mall. JƤmfƶr uppladdad fil med standardmall, +Communication Channel,Kommunikation Kanal, +Company Address Display,Bolag Adress Visning, +Company Billing Address,Bolag Faktura Adress, +Company Details,Bolag Detaljer, +Company Shipping Address,Bolag Leverans Adress, +Company Tax ID,Org.Nr., +Company and Posting Date is mandatory,Bolag och Bokfƶring Datum erfordras, +Company is mandatory,Bolag Erfordras, +Company is mandatory for generating an invoice. Please set a default company in Global Defaults.,Bolag erfordras fƶr att skapa faktura. Ange standard bolag i Standard InstƤllningar., +Company which internal customer represents,Bolag som intern kund representerar, +Company which internal customer represents.,Bolag som intern kund representerar., +Company which internal supplier represents,Bolag som intern leverantƶr representerar, +Company {0} is added more than once,Bolag{0} har lagts till mer Ƥn en gĆ„ng, +Company {} does not exist yet. Taxes setup aborted.,Bolag {} finns inte Ƥnnu. Moms instƤllning avbrƶts., +Company {} does not match with POS Profile Company {},Bolag {} stƤmmer inte med Kassa Profil Bolag {}, +Competitor,Konkurrent, +Competitor Detail,Konkurrent Detaljer, +Competitor Name,Konkurrent Namn, +Competitors,Konkurrenter, +Complete Job,Slutfƶr Jobb, +Complete Order,Slutfƶr Order, +Completed On,Slutfƶrd, +Completed On cannot be greater than Today,Klar datum kan inte vara senare Ƥn idag, +Completed Tasks,Antal Klara Uppgifter, +Completed Time,Klar Tid, +Completion Date can not be before Failure Date. Please adjust the dates accordingly.,Slutfƶrande datum kan inte vara fƶre fel datum. Justera datum dƤrefter., +Conditional Rule,Villkor Regel, +Conditional Rule Examples,Villkor Regel Exempel, +Configure Product Assembly,Konfigurera Artikel Produktion, +Configure the action to stop the transaction or just warn if the same rate is not maintained.,Konfigurera Ć„tgƤrd att stoppa transaktion eller varna om Marginal inte bibehĆ„lls., +Connections,Anslutningar, +Consider Entire Party Ledger Amount,Inkludera Hela Partibok Belopp, +Consider Minimum Order Qty,Inkludera Minimum Order Kvantitet, +Consider Rejected Warehouses,Inkludera Avvisad Lager , +Considered In Paid Amount,Moms Inkluderad i Betald Belopp, +Consolidate Sales Order Items,Konsolidera FƶrsƤljning Order Artiklar, +Consolidate Sub Assembly Items,Konsolidera Delmontering Artiklar, +Consumed Asset Items is mandatory for Decapitalization,Fƶrbrukade TillgĆ„ng Artiklar erfordras fƶr Dekapitalisering, +Consumed Asset Total Value,Fƶrbrukad TillgĆ„ng Totalt VƤrde, +Consumed Assets,Fƶrbrukade TillgĆ„ngar, +Consumed Quantity,Fƶrbrukad Kvantitet, +Consumed Stock Items,Fƶrbrukade Lager Artiklar, +Consumed Stock Items or Consumed Asset Items are mandatory for creating new composite asset,Fƶrbrukade Lager Artiklar eller Fƶrbrukade TillgĆ„ng Artiklar erfordras fƶr att skapa ny sammansatt tillgĆ„ng, +"Consumed Stock Items, Consumed Asset Items or Consumed Service Items is mandatory for Capitalization","Fƶrbrukade Lager Artiklar, Fƶrbrukade TillgĆ„ng Artiklar eller Fƶrbrukade Service Artiklar erfordras fƶr Kapitalisering", +Consumed Stock Total Value,Fƶrbrukad Lager Totalt VƤrde, +Consumption Rate,Fƶrbrukning VƤrde, +Contact Details,Kontakt Uppgifter, +Contact Mobile,Kontakt Mobil, +Contact Us Settings,Kontakta Oss InstƤllningar, +Contacts,Kontakter, +Contract Template Help,Avtal Mall HjƤlp, +Contribution Qty,Bidrag Kvantitet, +Control Historical Stock Transactions,Kontroll Tidigare Lager Transaktioner, +Conversion factor for item {0} has been reset to 1.0 as the uom {1} is same as stock uom {2}.,"Konvertering faktor fƶr artikel {0} Ƥr Ć„terstƤlld till 1,0 eftersom enhet {1} Ƥr samma som lager enhet {2}.", +Convert Item Description to Clean HTML in Transactions,Konvertera Artikel Beskrivning till ren HTML i Transaktioner, +Convert to Group,Konvertera till Grupp,Warehouse +Convert to Item Based Reposting,Konvertera till Artikel Baserad Ompostering, +Convert to Ledger,Konvertera till Register,Warehouse +Core,System, +Corrective Job Card,Korrigerande Jobbkort, +Corrective Operation,Korrigerande ƅtgƤrd, +Corrective Operation Cost,Korrigerande ƅtgƤrd Kostnad, +Cost Center Allocation,Resultat Enhet Tilldelning, +Cost Center Allocation Percentage,Resultat Enhet Procentuell Tilldelning , +Cost Center Allocation Percentages,Resultat Enhet Procentuell Tilldelning, +Cost Center For Item with Item Code {0} has been Changed to {1},Resultat Enhet fƶr Artikel med Artikelkod {0} har Ƥndrats till {1}, +"Cost Center is a part of Cost Center Allocation, hence cannot be converted to a group",Resultat Enhet Ƥr del av Resultat Enhet Tilldelning och kan dƤrfƶr inte konverteras till grupp, +Cost Center with Allocation records can not be converted to a group,Resultat Enhet med tilldelning poster kan inte konverteras till grupp, +Cost Center {0} cannot be used for allocation as it is used as main cost center in other allocation record.,Resultat Enhet {0} kan inte anvƤndas fƶr tilldelning eftersom det anvƤnds som Huvud Resultat Enhet i annan tilldelning post., +Cost Center {} doesn't belong to Company {},Resultat Enhet {} tillhƶr inte bolag {}, +Cost Center {} is a group cost center and group cost centers cannot be used in transactions,Resultat Enhet {} Ƥr Grupp Resultat Enhet och Grupp Resultat Enhet kan inte anvƤndas i transaktioner, +Cost Configuration,Kostnad InstƤllning, +Cost Per Unit,Kostnad Per Enhet, +Cost of New Capitalized Asset,Kostnad fƶr ny Kapitaliserad TillgĆ„ng, +Cost of Poor Quality Report,Kostand fƶr DĆ„lig Kvalitet Rapport, +Cost to Company (CTC),Totalt Kostnad per ƅr, +Costing Details,Kostnad Detaljer, +Could Not Delete Demo Data,Kunde inte ta bort demodata, +Could not auto update shifts. Shift with shift factor {0} needed.,Kunde inte uppdatera fƶrskjutning automatiskt. Fƶrskjutning med fƶrskjutning faktor {0} behƶvs., +Could not detect the Company for updating Bank Accounts,Kunde inte identifiera bolag fƶr uppdatering av Bank Konto, +Could not find path for ,Kunde inte hitta sƶkvƤg fƶr, +Count,Antal, +Create Depreciation Entry,Skapa Avskrivning Post, +Create Employee records.,Skapa Personal Uppgifter, +Create Grouped Asset,Skapa Grupperad TillgĆ„ng, +Create Job Card based on Batch Size,Skapa Jobbkort baserad pĆ„ Parti Storlek, +Create Journal Entries,Skapa Journal Poster, +Create Ledger Entries for Change Amount,Skapa Register Poster fƶr VƤxel Belopp, +Create Link,Skapa LƤnk, +Create Multi-level BOM,Skapa FlernivĆ„ Stycklista, +Create New Customer,Skapa Ny Kund, +Create Opportunity,Skapa Mƶjlighet, +Create Prospect,Skapa Prospekt, +Create Reposting Entries,Skapa Ompostering Poster, +Create Reposting Entry,Skapa Ompostering Post, +Create Stock Entry,Skapa Lager Post, +Create Workstation,Skapa Arbetsplats, +Create a new composite asset,Skapa ny Sammansatt TillgĆ„ng, +Create a variant with the template image.,Skapa variant med Mall Bild., +Create in Draft Status,Skapa i Utkast Status, +Create {0} {1} ?,Skapa {0} {1} ?, +Created On,Skapad, +Created {0} scorecards for {1} between:,Skapade {0} Resultatkort fƶr {1} mellan:, +Creating Delivery Note ...,Skapar FƶrsƤljning Fƶljesedel ..., +Creating Journal Entries...,Skapar Journal Poster..., +Creating Packing Slip ...,Skapar Packsedel ..., +Creating Purchase Invoices ...,Skapar Inkƶp Ordrar ..., +Creating Purchase Receipt ...,Skapar Inkƶp Fƶljesedel ..., +Creating Sales Invoices ...,Skapa FƶrsƤljning Fakturor ..., +Creating Stock Entry,Skapar Lager Post...., +Creating Subcontracting Order ...,Skapar Underleverantƶr Order ..., +Creating Subcontracting Receipt ...,Skapar Underleverantƶr Fƶljesedel ..., +Creating User...,Skapar AnvƤndare..., +Creation,Skapande, +Creation of {1}(s) successful,Skapande av {1}(s) klar, +"Creation of {0} failed. + Check Bulk Transaction Log","Skapande av {0} misslyckad. + Kontrollera Mass Transaktion Logg", +"Creation of {0} partially successful. + Check Bulk Transaction Log","Skapande av {0} delvis klar. + Kontrollera Mass Transaktion Logg", +Credit (Transaction),Kredit (Transaktion), +Credit Amount in Transaction Currency,Kredit Belopp i Transaktion Valuta, +Credit Limit Crossed,Kredit GrƤns Ɩverskriden, +Credit Limit Settings,Kredit GrƤns InstƤllningar, +"Credit Note will update it's own outstanding amount, even if ""Return Against"" is specified.","Kredit Nota kommer att uppdatera sitt eget utestĆ„ende belopp, Ƥven om ""Retur Mot"" anges.", +Currency Exchange Settings Details,Valuta VƤxling InstƤllning Detaljer, +Currency Exchange Settings Result,Valuta VƤxling InstƤllning Resultat, +Current Asset,Aktuell TillgĆ„ng, +Current Index,Aktuellt Index, +Current Level,Aktuell NivĆ„, +Current Liability,Aktuell Skuld, +Current Node,Aktuell Nod, +Current Serial / Batch Bundle,Aktuell Serie / Parti Paket, +Custom,Anpassad, +Custom delimiters,Anpassade AvgrƤnsare, +Customer ,Kund, +Customer / Item / Item Group,Kund / Artikel / Artikel Grupp, +Customer Defaults,Kund Standard, +Customer Group Item,Kund Grupp Artikel, +Customer Group: {0} does not exist,Kund Grupp: {0} finns inte, +Customer Item,Kund Artikel, +Customer Name: ,Kund Namn:, +Customer Portal Users,PortalĀ  AnvƤndare, +Customer: ,Kund:, +Daily Time to send,Daglig Tid att Skicka, +Dashboard,Ɩversikt Panel, +Data Based On,Data Baserad PĆ„, +Date ,Datum , +Date must be between {0} and {1},Datum mĆ„ste vara mellan {0} och {1}, +Days before the current subscription period,Dagar fƶre aktuell prenumeration period, +DeLinked,Bortkoppplad, +Deal Owner,Ansvarig, +Debit (Transaction),Debet (Transaktion), +Debit Amount in Transaction Currency,Debet Belopp i Transaktion Valuta, +"Debit Note will update it's own outstanding amount, even if ""Return Against"" is specified.","Debet Nota kommer att uppdatera sitt eget utestĆ„ende belopp, Ƥven om ""Retur Mot"" anges.", +Debit-Credit Mismatch,Debet-Kredit ƶverensstƤmmer ej, +Debit-Credit mismatch,Debet-Kredit ƶverensstƤmmer ej, +Decapitalization,Dekapitalisering, +Decapitalized,Dekapitaliserad, +Default Advance Account,Standard Fƶrskƶtt Konto, +Default Advance Paid Account,Standard Fƶrskƶtt Skuld Konto, +Default Advance Received Account,Standard Fƶrskƶtt IntƤkt Konto, +Default BOM not found for FG Item {0},Standard Stycklista hittades inte fƶr FƤrdig Artikel {0}, +Default Discount Account,Standard Rabatt Konto, +Default In-Transit Warehouse,Standard I Transit Lager, +Default Operating Cost Account,Standard Driftskostnad Konto, +Default Payment Discount Account,Standard Rabatt Konto, +Default Provisional Account,Standard Provisorisk Konto, +Default Service Level Agreement for {0} already exists.,Standard Service NivĆ„ Avtal fƶr {0} finns redan., +Default Unit of Measure for Item {0} cannot be changed directly because you have already made some transaction(s) with another UOM. You need to either cancel the linked documents or create a new Item.,Standard Enhet fƶr Artikel {0} kan inte Ƥndras eftersom det finns nĆ„gra transaktion(er) med annan Enhet. Man mĆ„ste antingen annullera lƤnkade dokument eller skapa ny artikel., +Default settings for your stock-related transactions,Standard instƤllningar fƶr lager relaterade transaktioner, +"Default tax templates for sales, purchase and items are created.","Standard Moms Mallar fƶr FƶrsƤljning,Inkƶp och Artiklar Ƥr skapade. ", +Deferred Accounting,Uppskjuten Bokfƶring, +Deferred Accounting Defaults,Uppskjuten Bokfƶring Standard, +Deferred Revenue and Expense,Uppskjuten IntƤkt och Kostnad, +Deferred accounting failed for some invoices:,Uppskjuten Bokfƶring misslyckades fƶr vissa fakturor:, +Delay (In Days),Fƶrsening (I Dagar), +Delayed,Fƶrsenad, +Delayed Tasks Summary,Fƶrsenad Uppgifter Ɩversikt, +Delete Accounting and Stock Ledger Entries on deletion of Transaction,Ta bort Bokfƶring och Lager Register Poster vid radering av Transaktion, +Delete Bins,Ta bort frĆ„n Papperskorg, +Delete Cancelled Ledger Entries,Ta bort Annullerade Register Poster, +Delete Dimension,Ta bort Dimension, +Delete Leads and Addresses,Ta bort Prospekt och Adresser, +Delete Transactions,Ta bort Transaktioner, +Deleted Documents,Papperskorg, +Deletion in Progress!,Borttagning PĆ„gĆ„r!, +Delimiter options,AvgrƤnsning Alternativ, +Delivery Manager,Leverans Ansvarig, +Delivery Note Packed Item,FƶrsƤljning Fƶljesedel Packad Artikel, +Delivery Note(s) created for the Pick List,FƶrsƤljning Fƶljesedel skapad fƶr Plocklista, +Delivery User,Leverans AnvƤndare, +Delivery to,Leverera Till, +Demo Company,Demo Bolag, +Demo data cleared,Demo Data Borttagen, +Dependant SLE Voucher Detail No,Beroende SLE Verifikat Detalj Nummer, +Dependent Task {0} is not a Template Task,Beroende Uppgift {0} Ƥr inte Mall Uppgift, +Deposit,InsƤttning, +Depreciate based on daily pro-rata,Skriv av baserat pĆ„ dagliga proportioner, +Depreciate based on shifts,Skriv av baserat pĆ„ fƶrskjutningar, +Depreciation Details,Avskrivning Detaljer, +Depreciation Entry Posting Status,Avskrivning Post Bokfƶring Status, +Depreciation Expense Account should be an Income or Expense Account.,Kostnad Avskrivning Konto ska vara IntƤkt eller Kostnad Konto., +Depreciation Posting Date cannot be before Available-for-use Date,Avskrivning Bokfƶring Datum kan inte vara fƶre TillgƤnglig fƶr AnvƤndning Datum, +Depreciation Row {0}: Depreciation Posting Date cannot be before Available-for-use Date,Avskrivning Rad {0}: Avskrivning Bokfƶring Datum kan inte vara fƶre TillgƤnglig fƶr AnvƤndning Datum, +Depreciation Schedule View,Avskrivning Schema Vy, +Depreciation cannot be calculated for fully depreciated assets,Avskrivning kan inte berƤknas fƶr fullt avskrivna tillgĆ„ngar, +Description of Content,Beskrivning av InnehĆ„ll, +Desk User,Skrivbord AnvƤndare, +Difference In,Differens I, +Difference Posting Date,Differens Bokfƶring Datum, +Difference Qty,Differens Kvantitet, +Different 'Source Warehouse' and 'Target Warehouse' can be set for each row.,Olika 'FrĆ„n Lager' och 'Till Lager' kan anges fƶr varje rad., +Dimension Details,Dimension Detaljer, +Dimension Filter Help,Dimension Filter HjƤlp, +Dimension-wise Accounts Balance Report,Bokfƶring Saldo Rapport per Dimension, +Direct Expense,Direkta Kostnader, +Disable Last Purchase Rate,Inaktivera Senaste Inkƶp Pris, +Disable Serial No And Batch Selector,Inaktivera Serie Nummer och Parti VƤljare, +Disabled Account Selected,Inaktiverad Konto Vald, +Disabled Warehouse {0} cannot be used for this transaction.,Inaktiverad Lager {0} kan inte anvƤndas fƶr denna transaktion., +Disabled pricing rules since this {} is an internal transfer,Inaktiverade PrissƤttning Regler eftersom detta {} Ƥr intern ƶverfƶring, +Disabled tax included prices since this {} is an internal transfer,Inaktiverade Pris Inklusive Moms eftersom detta {} Ƥr intern ƶverfƶring, +Disables auto-fetching of existing quantity,Inaktiverar automatisk hƤmtning av befintlig kvantitet, +Disassemble,Demontera, +Disassemble Order,Demontering Order, +Discount Account,Rabatt Konto, +Discount Date,Rabatt Datum, +Discount Settings,Rabatt InstƤllningar, +Discount Validity,Rabatt Giltighet , +Discount Validity Based On,Rabatt Giltighet Baserad PĆ„, +Discount of {} applied as per Payment Term,Rabatt pĆ„ {} tillƤmpad enligt Betalning Villkor, +Discounted Amount,Rabatterad Belopp, +"Discounts to be applied in sequential ranges like buy 1 get 1, buy 2 get 2, buy 3 get 3 and so on","Rabatter som ska tillƤmpas i sekventiella intervall som kƶp 1 fĆ„ 1, kƶp 2 fĆ„ 2, kƶp 3 fĆ„ 3 och sĆ„ vidare", +Discrepancy between General and Payment Ledger,Avvikelse mellan Bƶkfƶring Register och Betalning Register, +Dispatch Address,Leverans Adress, +Dispatch Address Name,Leverans Adress Namn, +Distinct Item and Warehouse,Distinkt Artikel och Lager, +Distribute Additional Costs Based On ,Fƶrdela Extra Kostnader Baserat PĆ„, +Distribute Manually,Fƶrdela Manuellt, +Do Not Explode,Utvidga Ej, +Do Not Update Serial / Batch on Creation of Auto Bundle,Uppdatera inte Serie / Parti vid skapande av Automatiskt Paket, +Do Not Use Batch-wise Valuation,AnvƤnd inte partivis vƤrdering, +Do reposting for each Stock Transaction,Skapa ompostering fƶr varje Lager Transaktion, +Do you still want to enable negative inventory?,Vill du fortfarande aktivera negativ Lager?, +DocField,DocType FƤlt, +DocTypes should not be added manually to the 'Excluded DocTypes' table. You are only allowed to remove entries from it.,"DocTypes ska inte lƤggas till manuellt i ""Excluded DocTypes"" tabell . Man fĆ„r bara ta bort poster frĆ„n den.", +Document Type already used as a dimension,Dokument Typ anvƤnds redan som dimension, +Documents,Dokument , +Documents: {0} have deferred revenue/expense enabled for them. Cannot repost.,Dokument: {0} har uppskjutna intƤkter/kostnader aktiverat fƶr dem. Kan inte posta om., +Domain Settings,DomƤn InstƤllningar, +Don't Reserve Sales Order Qty on Sales Return,Reservera inte FƶrsƤljning Order Kvantitet vid FƶrsƤljning Retur, +Don't Send Emails,Skicka inte E-post, +Dont Recompute tax,RƤkna inte om Moms, +Download Backups,SƤkerhetskopior, +Download CSV Template,Ladda ner CSV Mall, +Download Materials Request Plan,Ladda ner Material BegƤran Plan, +Download Materials Request Plan Section,Ladda ner Material BegƤran Plan Sektion, +Dunning Amount (Company Currency),PĆ„minnelse Avgift (Bolag Valuta), +Dunning Level,PĆ„minnelse NivĆ„, +Duplicate Closing Stock Balance,Kopiera StƤngning Lager Saldo, +Duplicate Customer Group,Kopiera Kund Grupp, +Duplicate Finance Book,Kopiera Finans Register, +Duplicate Item Group,Kopiera Artikel Grupp, +Duplicate POS Invoices found,Kopiera Kassa Fakturor , +Dynamic Condition,Dynamiska Villkor, +Edit Capacity,Redigera Kapacitet, +Edit Cart,Ƅndra Kundkorg, +Edit Full Form,Ɩppna i Full FormulƤr, +Edit Note,Redigera Anteckning, +Editing {0} is not allowed as per POS Profile settings,Ej TillĆ„tet att Redigera {0} pga Kassa Profil InstƤllningar, +Either 'Selling' or 'Buying' must be selected,"""Inkƶp"" eller ""FƶrsƤljning"" mĆ„ste vƤljas", +Email / Notifications,E-post / Aviseringar, +Email Address (required),E-post Adress (erfordras), +"Email Address must be unique, it is already used in {0}","E-post Adress mĆ„ste vara unik, den anvƤnds redan i {0}", +Email Digest Recipient,E-post Utskick Mottagare, +Email Digest: {0},E-post Utskick: {0}, +Email Domain,E-post DomƤn, +Email or Phone/Mobile of the Contact are mandatory to continue.,E-post eller Telefon / Mobil fƶr Kontakt erfordras fƶr att fortsƤtta., +Email verification failed.,E-post verifiering misslyckades., +Employee User Id,AnvƤndare ID, +Enable Allow Partial Reservation in the Stock Settings to reserve partial stock.,Aktivera TillĆ„t Partiell Reservation i Lager InstƤllningar fƶr att reservera partiell lager., +Enable Automatic Party Matching,Aktivera Automatiskt Parti AvstƤmning, +Enable Common Party Accounting,Aktivera Gemensam Parti Bokfƶring, +Enable Discount Accounting for Selling,Aktivera Rabatt Bokfƶring fƶr FƶrsƤljning, +Enable Fuzzy Matching,Aktivera UngefƤrlig AvstƤmning, +Enable Health Monitor,Aktivera System Ɩvervakning, +Enable Immutable Ledger,Aktivera Ofƶrenderlig Bokfƶring, +Enable Provisional Accounting For Non Stock Items,Aktivera Provisorisk Bokfƶring fƶr ej Lager Artiklar, +Enable Stock Reservation,Aktivera Lager Reservation, +Enable it if users want to consider rejected materials to dispatch.,Aktivera om anvƤndare vill inkludera att avvisat material ska skickas., +Enable this checkbox even if you want to set the zero priority,Aktivera denna kryssruta Ƥven om nollprioritet ska anges, +"Enable this option to calculate daily depreciation by considering the total number of days in the entire depreciation period, (including leap years) while using daily pro-rata based depreciation",Aktivera detta alternativ fƶr att berƤkna daglig avskrivning genom att anvƤnda totalt antal dagar i hela avskrivningsperiod (inklusive skottĆ„r) vid anvƤndning av proportionell baserad avskrivning, +Enable to apply SLA on every {0},Aktivera fƶr att tillƤmpa Service NivĆ„ Avtal pĆ„ varje {0}, +Enabling this ensures each Purchase Invoice has a unique value in Supplier Invoice No. field within a particular fiscal year,Aktivera fƶr att sƤkerstƤlla att varje Inkƶp Faktura har unikt vƤrde i fƤlt Leverantƶr Faktura Nummer fƤlt per bokfƶring Ć„r, +Enabling this option will allow you to record -

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

2. Advances Paid in an Asset Account instead of the Liability Account,Aktivera fƶr att mƶjliggƶra bokfƶring av:

1. Mottagna Fƶrskott Betalningar bokfƶrs pƄ Skuld Konto istƤllet fƶr TillgƄng Konto

2. Betalade Fƶrskott Betalningar bokfƶrs pĆ„ TillgĆ„ng Konto istƤllet fƶr Skuld Konto, +Enabling this will allow creation of multi-currency invoices against single party account in company currency,Aktivera fƶr att tillĆ„ta skapande av fakturor i flera valutor mot enskilt konto i bolag valuta, +Enabling this will change the way how cancelled transactions are handled.,Aktivering av detta Ƥndrar hur avbrutna transaktioner hanteras., +End Transit,Avsluta Transit, +End of the current subscription period,Slut datum pĆ„ aktuell prenumeration period, +"Enter First and Last name of Employee, based on Which Full Name will be updated. IN transactions, it will be Full Name which will be fetched.","Ange Fƶr och Efternamn pĆ„ Personal, baserat pĆ„ vilket FullstƤndigt namn kommer att uppdateras. Vid transaktioner kommer det att vara FullstƤndigt namn som kommer att hƤmtas.", +Enter Manually,Ange Manuellt, +Enter Serial Nos,Ange Serie Nummer, +Enter Visit Details,Ange Besƶk Detaljer, +Enter a name for Routing.,Ange namn fƶr ƅtgƤrd Fƶljd., +"Enter a name for the Operation, for example, Cutting.","Ange namn fƶr ƅtgƤrd, till exempel SkƤrning.", +Enter a name for this Holiday List.,Ange namn fƶr denna Helg Lista., +"Enter an Item Code, the name will be auto-filled the same as Item Code on clicking inside the Item Name field.","Ange Artikel Kod, namn kommer att automatiskt hƤmtas pĆ„ samma sƤtt som Artikel Kod nƤr man klickar i Artikel Namn fƤlt .", +Enter each serial no in a new line,Ange varje Serie Nummer pĆ„ ny rad, +"Enter the Operation, the table will fetch the Operation details like Hourly Rate, Workstation automatically. + + After that, set the Operation Time in minutes and the table will calculate the Operation Costs based on the Hourly Rate and Operation Time.","Ange ƅtgƤrd, detaljer hƤmtas automatiskt som timpris, arbetsstation . + + EfterĆ„t, anges ƅtgƤrd tid i minuter och system berƤknar ƅtgƤrd Kostnad baserat pĆ„ timpris och tid.", +Enter the opening stock units.,Ange Ɩppning Lager Enheter., +Enter the quantity of the Item that will be manufactured from this Bill of Materials.,Ange kvantitet fƶr Artikel som ska produceras frĆ„n denna Stycklista., +Enter the quantity to manufacture. Raw material Items will be fetched only when this is set.,Ange kvantitet som ska produceras. RĆ„material Artiklar hƤmtas endast nƤr detta Ƥr angivet., +Error during caller information update,Fel uppstod under uppdatering av samtalsinformation, +Error while posting depreciation entries,Fel uppstod vid bokfƶring av avskrivning poster, +Error while processing deferred accounting for {0},Fel uppstod nƤr uppskjuten bokfƶring fƶr {0} bearbetades, +Error while reposting item valuation,Fel uppstod vid ombokning av artikel vƤrdering , +"Error: This asset already has {0} depreciation periods booked. + The `depreciation start` date must be at least {1} periods after the `available for use` date. + Please correct the dates accordingly.","Fel: Denna tillgĆ„ng har redan {0} avskrivning perioder bokade. + Start datum fƶr ""avskrivning"" mĆ„ste vara minst {1} perioder efter ""tillgƤnglig fƶr anvƤndning"" datum. + Korrigera datum enligt detta.", +Errors Notification,Fel Avisering, +Even invoices with apply tax withholding unchecked will be considered for checking cumulative threshold breach,Ƅven fakturor med tillƤmpa moms undanhĆ„llning omarkerad kommer att betraktas fƶr att kontrollera kumulativ trƶskel ƶvertrƤdelse, +Example URL,Exempel URL, +Example of a linked document: {0},Exempel pĆ„ lƤnkad dokument: {0}, +"Example: ABCD.##### +If series is set and Serial No is not mentioned in transactions, then automatic serial number will be created based on this series. If you always want to explicitly mention Serial Nos for this item. leave this blank.","Exempel:. ABCD ##### Om serie Ƥr angiven och Serie Nummer inte anges i transaktioner, skapas Serie Nummer automatiskt utifrĆ„n denna serie. Om man alltid vill ange serie nummer fƶr denna artikel lƤmna det tomt.", +Example: Serial No {0} reserved in {1}.,Exempel: Serie Nummer {0} reserverad i {1}., +Excess Materials Consumed,Ɩverskott Material Fƶrbrukad, +Excess Transfer,Ɩverskott Ɩverfƶring, +Exchange Gain Or Loss,ValutavƤxling Resultat, +Exchange Gain/Loss amount has been booked through {0},ValutavƤxling Resultat Belopp har bokfƶrts genom {0}, +Exchange Rate Revaluation Settings,ValutavƤxling Kurs OmvƤrdering InstƤllningar, +Excluded DocTypes,Exkluderade DocTyper, +Exempt Supplies,Undantagna Leveranser, +Expected,FƶrvƤntad, +Expected Balance Qty,FƶrvƤntad Saldo Kvantitet, +Expected End Date should be less than or equal to parent task's Expected End Date {0}.,FƶrvƤntat Slut Datum ska vara tidigare Ƥn eller lika med Ɩverordnade Uppgifters fƶrvƤntade Slut Datum {0}., +Expected Stock Value,FƶrvƤntad Lager VƤrde, +Expected Time Required (In Mins),FƶrvƤntad Tid (I Minuter), +Expiry,BESTFƖRE, +Export Data,Data Export, +Export Errored Rows,Exportera Felaktiga Rader, +Export Import Log,Exportera Import Logg, +Extra Consumed Qty,Extra Fƶrbrukad Kvantitet, +Extra Job Card Quantity,Extra Jobbkort Kvantitet, +FIFO Queue vs Qty After Transaction Comparison,FIFO Kƶ mot Kvantitet Efter Transaktion JƤmfƶrelse, +"FIFO Stock Queue (qty, rate)","FIFO Lager Kƶ (kvantitet, pris)", +FIFO/LIFO Queue,FIFO / LIFO Kƶ, +Failed Entries,Misslyckade Poster, +"Failed to erase demo data, please delete the demo company manually.","Misslyckades att ta bort demodata, vƤnligen radera demo bolag manuellt.", +Failed to post depreciation entries,Kunde inte bokfƶra avskrivning poster, +Failed to setup defaults for country {0}. Please contact support.,Misslyckades att ange standard instƤllningar fƶr {0}. Kontakta support., +Failure,Fel, +Failure Description,Fel Beskrivning, +Fetch Based On,HƤmta Baserad PĆ„, +Fetch Overdue Payments,HƤmta Fƶrfallna Fakturor, +Fetch Timesheet,HƤmta Tidrapport, +Fetch Value From,HƤmta VƤrde FrĆ„n, +Fetching exchange rates ...,HƤmtar vƤxel kurs ..., +Filter by Reference Date,Filtrera efter Referens Datum, +Filter on Invoice,Filtrera Faktura, +Filter on Payment,Filtrera Betalning, +Filters missing,Filter saknas, +Final Product,FƤrdig Artikel, +Financial Ratios,Finansiell Nyckeltal, +Financial Reports,Rapporter, +Financial Year Begins On,BokfƶringsĆ„r Start Datum, +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) ,Finansiella Rapporter kommer att genereras med hjƤlp av Bokfƶring Poster (ska vara aktiverat om Period LĆ„sning Verifikat inte publiceras fƶr alla Ć„r i fƶljd eller saknas), +Finished Good BOM,FƤrdig Stycklista, +Finished Good Item,FƤrdig Artikel, +Finished Good Item Qty,FƤrdig Artikel Kvantitet, +Finished Good Item Quantity,FƤrdig Artikel Kvantitet, +Finished Good Item is not specified for service item {0},FƤrdig Artikel Ƥr inte specificerad fƶr service artikel {0}, +Finished Good Item {0} Qty can not be zero,FƤrdig Artikel {0} kvantitet kan inte vara noll, +Finished Good Item {0} must be a sub-contracted item,FƤrdig Artikel {0} mĆ„ste vara underleverantƶr artikel, +Finished Good Qty,FƤrdig Artikel Kvantitet, +Finished Good Quantity ,FƤrdig Artikel Kvantitet, +Finished Good UOM,FƤrdig Artikel Enhet, +Finished Good {0} does not have a default BOM.,FƤrdig Artikel {0} har ingen standard Stycklista., +Finished Good {0} is disabled.,FƤrdig Artikel {0} Ƥr inaktiverad., +Finished Good {0} must be a stock item.,FƤrdig Artikel {0} mĆ„ste vara lager artikel., +Finished Good {0} must be a sub-contracted item.,FƤrdig Artikel {0} mĆ„ste vara underleverantƶr artikel., +Finished Goods Based Operating Cost,FƤrdiga Artiklar Baserade Driftskostnader, +Finished Goods Item,FƤrdig Artikel Post, +Finished Goods Reference,FƤrdig Artikel Referens, +Finished Goods Value,FƤrdig Artikel VƤrde, +Finished Goods based Operating Cost,FƤrdiga Artiklar Baserade Driftskostnader, +Finished Item {0} does not match with Work Order {1},FƤrdig Artikel {0} stƤmmer inte med Arbetsorder {1}, +First Response Due,Fƶrsta Respons, +First Response SLA Failed by {},Fƶrsta Respons Service NivĆ„ Avtal misslyckades efter {}, +Fixed Asset Defaults,Fasta TillgĆ„ngar, +Fixed Time,Fast Tid, +Floor,Yta, +Floor Name,Yta Namn, +For Item,Fƶr Artikel, +For Item {0} cannot be received more than {1} qty against the {2} {3},Fƶr Artikel {0} kan inte tas emot mer Ƥn {1} i kvantitet mot {2} {3}, +For Job Card,Fƶr Jobbkort, +For Operation,Fƶr ƅtgƤrd, +"For Return Invoices with Stock effect, '0' qty Items are not allowed. Following rows are affected: {0}","Fƶr Retur Fakturor med Lager pĆ„verkan, '0' kvantitet artiklar Ƥr inte tillĆ„tna. Fƶljande rader pĆ„verkas: {0}", +For Work Order,Fƶr Arbetsorder, +For dunning fee and interest,Fƶr pĆ„minnelseavgift och rƤnta, +"For item {0}, rate must be a positive number. To Allow negative rates, enable {1} in {2}","Fƶr Artikel {0} pris mĆ„ste vara positiv tal. Att tillĆ„ta negativa priser, aktivera {1} i {2}", +For quantity {0} should not be greater than allowed quantity {1},Fƶr Kvantitet {0} ska inte vara hƶgre Ƥn tillĆ„ten kvantitet {1}, +"For the item {0}, the quantity should be {1} according to the BOM {2}.",Fƶr artikel {0} ska kvantitet vara {1} enligt stycklista {2}., +"For the {0}, no stock is available for the return in the warehouse {1}.",Fƶr {0} finns inget kvantitet tillgƤngligt fƶr retur i lager {1}., +"For the {0}, the quantity is required to make the return entry",Fƶr {0} erfordras kvantitet fƶr att skapa retur post, +Force-Fetch Subscription Updates,HƤmta Prenumeration Uppdateringar, +Forecasting,Prognos, +Formula Based Criteria,Formel Baserade Kriterier, +Free Item Rate,Gratis Artikel Pris, +From Corrective Job Card,FrĆ„n Korrigerande Jobbkort, +From Date and To Date are mandatory,FrĆ„n Datum och Till Datum Erfodras, +From Date is mandatory,FrĆ„n Datum Erfordras, +From Date: {0} cannot be greater than To date: {1},FrĆ„n Datum: {0} kan inte vara senare Ƥn Till Datum: {1}, +From Delivery Date,FrĆ„n Leverans Datum, +From Doctype,FrĆ„n DocType, +From Due Date,FrĆ„n Fƶrfallo Datum, +From Opportunity,FrĆ„n Mƶjlighet, +From Payment Date,FrĆ„n Betalning Datum, +From Prospect,FrĆ„n Prospekt, +From Reference Date,FrĆ„n Referens Datum, +From Voucher Detail No,FrĆ„n Verifikat Detalj Nummer, +From Voucher No,FrĆ„n Verifikat Nummer, +From Voucher Type,FrĆ„n Verifikat Typ, +From and To dates are required,FrĆ„n och Till Datum Erfodras, +Full and Final Statement,AvgĆ„ng Avtal, +GL Balance,Bokfƶring Register Saldo, +GL Entry Processing Status,Bokfƶring Register Bearbetning Status, +GL reposting index,Bokfƶring Register Ompostering Index, +Gain/Loss accumulated in foreign currency account. Accounts with '0' balance in either Base or Account currency,Resultat ackumulerad pĆ„ konto i utlƤndsk valuta. Konto med '0' saldo i antingen Bas eller Konto valuta, +Gain/Loss already booked,Resultat Bokfƶrd, +Gain/Loss from Revaluation,OmvƤrdering Resultat, +General Ledger,Bokfƶring Register,Warehouse +General and Payment Ledger Comparison,Bokfƶring och Betalning Register JƤmfƶrelse, +General and Payment Ledger mismatch,Bokfƶring och Betalning Register JƤmfƶrelse , +Generate Closing Stock Balance,Skapa StƤngning Lager Saldo, +Generate Demo Data for Exploration,Skapa Demo Data fƶr att Utforska, +Generate E-Invoice,Skapa E-Faktura, +Generate Invoice At,Skapa Faktura , +Generated,Skapad, +Generating Preview,Skapar Fƶrhandsvisning, +Get Allocations,HƤmta Tilldelningar, +Get Customer Group Details,HƤmta Kund Grupp Detaljer, +Get Finished Goods for Manufacture,HƤmta Artiklar fƶr Produktion, +Get Outstanding Orders,HƤmta UtestĆ„ende Ordrar, +Get Raw Materials Cost from Consumption Entry,HƤmta RĆ„material Kostnad frĆ„n Fƶrbrukning Post, +Get Raw Materials for Purchase,HƤmta RĆ„material fƶr Inkƶp, +Get Raw Materials for Transfer,HƤmta RĆ„material fƶr Ɩverfƶring, +Get Scrap Items,HƤmta Rest Artiklar, +Get Stock,HƤmta Lager, +Get Sub Assembly Items,HƤmta Delmontering Artiklar, +Get Supplier Group Details,HƤmta Leverantƶr Grupp Detaljer, +Get Timesheets,HƤmta Tidrapporter, +Get stops from,HƤmta Leverans Stopp frĆ„n, +Getting Scrap Items,HƤmta Rest Artiklar, +Give free item for every N quantity,LƤmna gratis artikel fƶr varje N kvantitet, +Go back,Tillbaka, +Go to {0} List,Till {0} Lista, +Goals,MĆ„l, +Goods,Gods, +Grant Commission,Bevilja Provision, +Greeting Message,HƤlsning Meddelande, +Gross Profit Percent,Brutto Resultat %, +Gross Purchase Amount Too Low: {0} cannot be depreciated over {1} cycles with a frequency of {2} depreciations.,Brutto Inkƶp Belopp fƶr lĆ„gt: {0} kan inte skrivas av ƶver {1} cykler med en frekvens pĆ„ {2} avskrivningar., +Gross Purchase Amount should be equal to purchase amount of one single Asset.,Brutto Inkƶp Belopp ska vara lika med inkƶp belopp fƶr enskild TillgĆ„ng., +Group Same Items,Sammanfoga lika Artikelrader, +Growth View,TillvƤxt Vy, +Half-yearly,HalvĆ„r, +Handle Employee Advances,Hantera Personal Fƶrskott, +Has Alternative Item,Har Alternativ Artikel, +Has Item Scanned,Har Artikel Skannad, +Has Priority,Har Prioritet, +Have Default Naming Series for Batch ID?,Har Standard Nummer Serie fƶr Parti?, +Heatmap,VƤrme Karta, +Height (cm),Hƶjd (cm), +"Hello,","Hej,", +Helps you distribute the Budget/Target across months if you have seasonality in your business.,HjƤlper vid fƶrdelning av Budget/ MĆ„l ƶver mĆ„nader om bolag har sƤsongsvariationer., +Here are the error logs for the aforementioned failed depreciation entries: {0},HƤr Ƥr felloggar fƶr ovannƤmnda misslyckade avskrivning poster: {0}, +Here are the options to proceed:,HƤr Ƥr alternativ fƶr att fortsƤtta:, +"Here, you can select a senior of this Employee. Based on this, Organization Chart will be populated.",HƤr kan du vƤlja ƶverordnad fƶr Personal. Baserat pĆ„ detta kommer organisation diagam att fyllas i., +"Here, your weekly offs are pre-populated based on the previous selections. You can add more rows to also add public and national holidays individually.",HƤr Ƥr veckovisa ledigheter angivna i fƶrvƤg baserat pĆ„ de tidigare val. Man kan lƤgga till fler rader fƶr att Ƥven lƤgga till allmƤnna och nationella helgdagar individuellt., +"Hi,","Hej,", +Hide Images,Dƶlj Bilder, +Holiday Date {0} added multiple times,Helgdag {0} har lagts till flera gĆ„nger, +Hours Spent,Fƶrbrukade Timmar, +How often should Project be updated of Total Purchase Cost ?,Hur ofta ska Projekt uppdateras baserat pĆ„ Totalt Inkƶp Kostnad?, +Idle,Overksam, +"If Enabled - Reconciliation happens on the Advance Payment posting date
+If Disabled - Reconciliation happens on oldest of 2 Dates: Invoice Date or the Advance Payment posting date
+","Om Aktiverad - AvstƤmning sker pƄ Fƶrskott Betalning datum
+Om Inaktiverad - AvstƤmning sker pƄ Ƥldsta av 2 datum: Faktura datum eller Fƶrskott Betalning datum
+", +"If an operation is divided into sub operations, they can be added here.",Om Ć„tgƤrd Ƥr uppdelad i underĆ„tgƤrder kan de lƤggas till hƤr., +"If checked, Rejected Quantity will be included while making Purchase Invoice from Purchase Receipt.","Om vald, kommer avvisad kvantitet att inkluderas nƤr Inkƶp Faktura skapas frĆ„n Inkƶp Fƶljesedel.", +"If checked, Stock will be reserved on Submit","Om vald, kommer Lager Reservation att skapas vid GodkƤnn", +"If checked, picked qty won't automatically be fulfilled on submit of pick list.","Om vald, kommer plockad kvantitet inte automatiskt att uppfyllas nƤr plocklista godkƤnns.", +"If checked, the tax amount will be considered as already included in the Paid Amount in Payment Entry","Om vald, kommer moms belopp anses vara inkluderad i Betald Belopp i Betalning Post", +"If checked, we will create demo data for you to explore the system. This demo data can be erased later.","Om vald,kommer demo data skapas sĆ„ att man kan utforska system. Dessa demo data kan raderas senare.", +If enabled then system will manufacture Sub-assembly against the Job Card (operation).,Om aktiverad kommer system att producera Underenheter mot Jobbkort (ƅtgƤrd)., +If enabled then system won't apply the pricing rule on the delivery note which will be create from the pick list,Om aktiverat kommer systemet inte att tillƤmpa prisregel pĆ„ fƶljesedel som kommer att skapas frĆ„n plocklista, +If enabled then system won't override the picked qty / batches / serial numbers.,Om aktiverad kommer system inte Ć„sidosƤtta plockad kvantitet / partier / serie nummer., +"If enabled, a print of this document will be attached to each email","Om aktiverad, kommer utskrift av detta dokument att bifogas till varje e-post meddelande", +"If enabled, additional ledger entries will be made for discounts in a separate Discount Account","Om aktiverad, extra bokfƶring poster kommer att skapas fƶr rabatter pĆ„ separat rabatt konto", +"If enabled, all files attached to this document will be attached to each email","Om aktiverad, kommer alla filer som bifogas detta dokument att bifogas till varje e-post meddelande", +"If enabled, do not update serial / batch values in the stock transactions on creation of auto Serial + / Batch Bundle. ","Om aktiverad, uppdatera inte serie nummer /parti vƤrde i lager transaktioner vid skapande av automatiskt Serie Nummer + / Parti Paket. ", +"If enabled, ledger entries will be posted for change amount in POS transactions","Om aktiverad,bokfƶring poster kommer att bokfƶras fƶr VƤxel Belopp i Kassa Transaktioner", +"If enabled, the consolidated invoices will have rounded total disabled",Om aktiverad kommer Konsoliderad Faktura att ha avrundad totalt inaktiverad, +"If enabled, the item rate won't adjust to the valuation rate during internal transfers, but accounting will still use the valuation rate.","Om aktiverad, kommer artikel pris inte att sƤttas till grund pris vid interna ƶverfƶringar, men bokfƶring kommer fortfarande att anvƤnda grund pris.", +"If enabled, the system will create material requests even if the stock exists in the 'Raw Materials Warehouse'.","Om aktiverad, kommer system att skapa material begƤran Ƥven om det finns pĆ„ 'RĆ„material Lager'.", +"If enabled, the system will use the moving average valuation method to calculate the valuation rate for the batched items and will not consider the individual batch-wise incoming rate.","aaOm aktiverad, kommer system att anvƤnda Ma vƤrdering sƤtt fƶr att berƤkna vƤrdering fƶr artikel partier och kommer inte att beakta individuell per parti pris.", +"If enabled, then system will only validate the pricing rule and not apply automatically. User has to manually set the discount percentage / margin / free items to validate the pricing rule","Om aktiverad, kommer system bara att validera prissƤttning regel och inte tillƤmpas automatiskt. AnvƤndare mĆ„ste manuellt ange rabatt procent / marginal / gratis artikel fƶr att validera prissƤttning regel", +"If mentioned, the system will allow only the users with this Role to create or modify any stock transaction earlier than the latest stock transaction for a specific item and warehouse. If set as blank, it allows all users to create/edit back-dated transactions.","Om angiven kommer system att tillĆ„ta AnvƤndare med denna roll att skapa eller Ƥndra lager transaktioner tidigare Ƥn senaste lager transaktion fƶr specifik artikel och lager. Om angiven som tomt, tillĆ„ter det alla AnvƤndare att skapa/redigera backdaterade transaktioner.", +"If not, you can Cancel / Submit this entry",Om inte kan man Annullera/GodkƤnna denna post, +"If rate is zero then item will be treated as ""Free Item""","Om pris Ƥr noll kommer artikel att behandlas som ""Gratis Artikel""", +"If the BOM results in Scrap material, the Scrap Warehouse needs to be selected.",Om Stycklista har Rest Material mĆ„ste Rest Lager vƤljas., +"If the selected BOM has Operations mentioned in it, the system will fetch all Operations from BOM, these values can be changed.","Om vald Stycklista har angivna ƅtgƤrder kommer system att hƤmta alla ƅtgƤrder frĆ„n Stycklista, dessa vƤrden kan Ƥndras.", +"If this checkbox is enabled, then the system won’t run the MRP for the available sub-assembly items.",Om vald kommer systemet inte att kƶra Material Resurs Planering fƶr tillgƤngliga undermontering artiklar., +If this is undesirable please cancel the corresponding Payment Entry.,Om detta inte Ƥr ƶnskvƤrt annullera motsvarande betalning post., +"If yes, then this warehouse will be used to store rejected materials","Om ja, kommer detta lager att anvƤndas fƶr att lagra avvisat material", +"If you are maintaining stock of this Item in your Inventory, ERPNext will make a stock ledger entry for each transaction of this item.","Om man har denna artikel i Lager, kommer System att lagerbokfƶra varje transaktion av denna artikel.", +"If you need to reconcile particular transactions against each other, then please select accordingly. If not, all the transactions will be allocated in FIFO order.","Om man behƶver stƤmma av specifika transaktioner mot varandra, vƤlj dƤrefter. Om inte, kommer alla transaktioner att tilldelas i FIFO ordning.", +"If you still want to proceed, please disable 'Skip Available Sub Assembly Items' checkbox.","Om du fortfarande vill fortsƤtta, avmarkera ""Hoppa ƶver tillgƤngliga undermontering artiklar"".", +"If you still want to proceed, please enable {0}.","Fƶr att fortsƤtta, aktivera {0}.", +"If your CSV uses a different delimiter, add that character here, ensuring no spaces or additional characters are included.","Om CSV anvƤnder en annan avgrƤnsare, lƤgg till det tecknet hƤr, se till att inga mellanslag eller ytterligare tecken inkluderas.", +Ignore Account Closing Balance,Ignorera Bokfƶring StƤngning Saldo, +Ignore Available Stock,Ignorera TillgƤngligt Lager, +Ignore Closing Balance,Ignorera StƤngning Saldo, +Ignore Default Payment Terms Template,Ignorera Standard Betalning Villkor Mall , +Ignore Empty Stock,Ignorera Tom Lager, +Ignore Exchange Rate Revaluation Journals,Ignorera VƤxelkurs OmvƤrdering Journaler , +Ignore Pricing Rule is enabled. Cannot apply coupon code.,Ignorera att PrissƤttning Regel Ƥr aktiverad. Det gĆ„r inte att anvƤnda kupong kod., +Ignore System Generated Credit / Debit Notes,Ignorera System Skapade Kredit / Debet Notor, +Ignore Voucher Type filter and Select Vouchers Manually,Ignorera Verifikat Typ filter och VƤlj Verifikat Manuellt, +Impairment,Nedskrivningar, +Import File,Importera Fil, +Import File Errors and Warnings,Importera Fil Fel och Varningar, +Import Log Preview,Importera Logg Fƶrhandsvisning, +Import Preview,Importera Fƶrhandsvisning, +Import Progress,Import Framsteg, +Import Type,Import Typ, +Import Using CSV file,Importera med hjƤlp av CSV fil, +Import Warnings,Import Varningar, +Import from Google Sheets,Importera frĆ„n Google Sheets, +"Importing {0} of {1}, {2}","Importerar {0} av {1}, {2}", +In House,Intern, +In Minutes,I Minuter, +In Party Currency,I Parti Valuta, +In Transit Transfer,I Transit Ɩverfƶring, +In Transit Warehouse,I Transit Lager, +In mins,I Minuter, +"In row {0} of Appointment Booking Slots: ""To Time"" must be later than ""From Time"".","PĆ„ rad {0} av Mƶte Bokning Tider: ""Till Tid"" mĆ„ste vara senare Ƥn ""FrĆ„n Tid"".", +"In the case of 'Use Multi-Level BOM' in a work order, if the user wishes to add sub-assembly costs to Finished Goods items without using a job card as well the scrap items, then this option needs to be enable.","I fall att 'AnvƤnd FlernivĆ„ Stycklista' anvƤnds i arbetsorder, om anvƤndare vill lƤgga till undermontering kostnader till fƤrdiga artiklar utan att anvƤnda jobbkort samt rest artiklar, mĆ„ste detta alternativ vara aktiverad.", +"In this section, you can define Company-wide transaction-related defaults for this Item. Eg. Default Warehouse, Default Price List, Supplier, etc.","I detta sektion kan man definiera bolagsomfattande transaktion relaterade standard instƤllningar fƶr denna artikel. T.ex. Standard Lager, Standard Prislista, Leverantƶr, osv.", +Inactive Status,Inaktiv Status, +Include Account Currency,Inkludera Konto Valuta, +Include Closed Orders,Inkludera StƤngda Ordrar, +Include Default FB Assets,Inkludera Standard Finans Register TillgĆ„ngar, +Include Disabled,Inkludera Inaktiverad, +Include Expired Batches,Inkludera UtgĆ„ngna Partier, +Include Safety Stock in Required Qty Calculation,Inkludera SƤkerhet Lager i BegƤrd Kvantitet BerƤkning, +Include Timesheets in Draft Status,Inkludera Tidrapporter i Utkast Status, +Include Zero Stock Items,Inkludera artiklar ej tillgƤngliga i lager, +Incoming Call Handling Schedule,Inkommande Samtalshantering Schema, +Incoming Call Settings,Inkommande Samtal InstƤllningar, +Incoming Rate (Costing),Inkƶp Pris (Kostnad), +Incorrect Balance Qty After Transaction,Felaktig Saldo Kvantitet Efter Transaktion, +Incorrect Batch Consumed,Felaktig Parti Fƶrbrukad, +Incorrect Check in (group) Warehouse for Reorder,Felaktig vald (grupp) Lager fƶr OmbestƤllning, +Incorrect Component Quantity,Felaktig Komponent Kvantitet, +Incorrect Invoice,Felaktig Faktura, +Incorrect Movement Purpose,Felaktig Fƶrflytning Syfte, +Incorrect Payment Type,Felaktig Betalning Typ, +Incorrect Reference Document (Purchase Receipt Item),Felaktig Referens Dokument (Inkƶp Fƶljesedel Artikel), +Incorrect Serial No Valuation,Felaktig Serie Nummer VƤrdering, +Incorrect Serial Number Consumed,Felaktig Serie Nummer Fƶrbrukad, +Incorrect Stock Value Report,Felaktig Lager VƤrde Rapport, +Incorrect Type of Transaction,Felaktig Typ av Transaktion, +Increase In Asset Life(Months),Utƶkning av TillgĆ„ng LivslƤngd (MĆ„nader), +Indent,Rekvisition, +Indirect Expense,Indirekt Kostnad, +Individual GL Entry cannot be cancelled.,Enskild Bokfƶring Post kan inte avbokas., +Individual Stock Ledger Entry cannot be cancelled.,Enskild Lager Register Post kan inte avbokas., +Initialize Summary Table,Initiera Ɩversikt Tabell, +Insert New Records,Infoga Nya Poster, +Inspection Rejected,Kontroll Avvisad, +Inspection Submission,Kontroll GodkƤnnande, +Instruction,Instruktion, +Insufficient Capacity,OtillrƤcklig Kapacitet, +Insufficient Stock for Batch,OtillrƤcklig Lager fƶr Parti, +Inter Transfer Reference,Intern Ɩverfƶring Referens, +Interest and/or dunning fee,RƤnta och/eller PĆ„minnelseavgift, +Internal,Intern, +Internal Customer,Intern Kund, +Internal Customer for company {0} already exists,Intern Kund fƶr Bolag {0} finns redan, +Internal Sale or Delivery Reference missing.,Intern FƶrsƤljning eller Leverans Referens saknas., +Internal Sales Reference Missing,Intern FƶrsƤljning Referens saknas, +Internal Supplier,Intern Leverantƶr, +Internal Supplier for company {0} already exists,Intern Leverantƶr fƶr Bolag {0} finns redan, +Internal Transfer Reference Missing,Intern Ɩverfƶring Referens saknas, +Internal Transfers,Interna Ɩverfƶringar, +Internal transfers can only be done in company's default currency,Interna Ɩverfƶringar kan endast gƶras i bolag standard valuta, +Invalid,Ogiltig, +Invalid Allocated Amount,Ogiltig Tilldelad Belopp, +Invalid Amount,Ogiltig Belopp, +Invalid Auto Repeat Date,Ogiltig ƅterkommande Datum, +Invalid Cost Center,Ogiltig Resultat Enhet, +Invalid Delivery Date,Ogiltig Leverans Datum, +Invalid Document,Ogiltig Dokument, +Invalid Document Type,Ogiltig Dokument Typ, +Invalid Formula,Ogiltig Formel, +Invalid Group By,Ogiltig Gruppera Efter, +Invalid Item Defaults,Ogiltig Artikel Standard, +Invalid Ledger Entries,Ogiltiga Register Poster, +Invalid Primary Role,Ogiltig PrimƤr Roll, +Invalid Priority,Ogiltig Prioritet, +Invalid Process Loss Configuration,Ogiltig Process Fƶrlust Konfiguration, +Invalid Purchase Invoice,Ogiltig Inkƶp Faktura, +Invalid Qty,Ogiltig Kvantitet, +Invalid Schedule,Ogiltig Schema, +Invalid Serial and Batch Bundle,Felaktig Serie och Parti Paket, +Invalid Warehouse,Ogiltig Lager, +Invalid result key. Response:,Ogiltig resultat nyckel. Svar:, +Invalid value {0} for {1} against account {2},Ogiltigt vƤrde {0} fƶr {1} mot konto {2}, +Inventory Dimension,Lager Dimension, +Inventory Dimension Negative Stock,Lager Dimension Negativ Lager, +Inventory Settings,Lager InstƤllningar, +Invoice Cancellation,Faktura Annullering, +Invoice Limit,Faktura GrƤns, +Invoice Portion (%),Faktura Andel (%), +Invoice and Billing,Faktura & Fakturering, +Invoiced Qty,Fakturerad Kvantitet, +Invoices and Payments have been Fetched and Allocated,Fakturor och Betalningar Ƥr HƤmtade och Tilldelade, +Invoicing Features,Fakturering Funktioner, +Is Adjustment Entry,Ƅr Justering Post, +Is Alternative,Ƅr Alternativ, +Is Cash or Non Trade Discount,Ƅr Kontant eller Ej FƶrsƤljning Rabatt, +Is Composite Asset,Ƅr Sammansatt TillgĆ„ng, +Is Corrective Job Card,Ƅr Korrigerande Jobbkort, +Is Corrective Operation,Ƅr Korrigerande ƅtgƤrd, +Is Expandable,Ƅr Utvidningsbar, +Is Finished Item,Ƅr FƤrdig Artikel, +Is Fully Depreciated,Ƅr Helt Avskriven, +Is Group Warehouse,Ƅr Grupp Lager, +Is Old Subcontracting Flow,Ƅr Gammal Underleverantƶr Flƶde, +Is Outward,Ƅr UtgĆ„ende, +Is Period Closing Voucher Entry,Ƅr Period StƤngning Verifikat Post, +Is Rate Adjustment Entry (Debit Note),Ƅr Pris Justering Post (Debet Nota), +Is Recursive,Ƅr Rekursiv, +Is Rejected,Ƅr Avvisad, +Is Rejected Warehouse,Ƅr Avvisad Lager, +Is Scrap Item,Ƅr Rest Artikel, +Is Short Year, Ƅr Kort ƅr, +Is Standard,Ƅr Standard, +Is Stock Item,Ƅr Lager Artikel, +Is System Generated,Ƅr System Skapad, +Is Tax Withholding Account,Ƅr Moms Avdrag Konto, +Is Template,Ƅr Mall, +Issue Analytics,Ƅrende Analys, +Issue Summary,Ƅrende Ɩversikt, +Issue a debit note with 0 qty against an existing Sales Invoice,UtfƤrda Debet Nota med 0 i Kvantitet mot befintlig FƶrsƤljning Faktura, +Issuing cannot be done to a location. Please enter employee to issue the Asset {0} to,UtfƤrdande kan inte gƶras till plats. Ange personal att utfƤrda TillgĆ„ng {0} , +It can take upto few hours for accurate stock values to be visible after merging items.,Det kan ta upp till nĆ„gra timmar fƶr korrekta lagervƤrden att vara synliga efter sammanslagning av artiklar., +"It's not possible to distribute charges equally when total amount is zero, please set 'Distribute Charges Based On' as 'Quantity'","Det Ƥr inte mƶjligt att fƶrdela avgifter lika nƤr det totala beloppet Ƥr noll, vƤnligen ange ""Distribuera Avgifter Baserat PĆ„"" som ""Kvantitet""", +Item Code (Final Product),Artikel Kod (FƤrdig Artikel), +Item Group wise Discount,Rabatt per Artikel Grupp, +Item Price Settings,Artikel Pris InstƤllningar, +"Item Price appears multiple times based on Price List, Supplier/Customer, Currency, Item, Batch, UOM, Qty, and Dates.","Artikel Pris visas flera gĆ„nger baserat pĆ„ Prislista, Leverantƶr/Kund, Valuta, Artikel, Parti, Enhet, Kvantitet och Datum.", +Item Reference,Artikel Referens, +Item Warehouse based reposting has been enabled.,Artikel Lager baserad omlƤggning Ƥr aktiverad., +Item and Warehouse,Artikel och Lager, +Item is removed since no serial / batch no selected.,Artikel tas bort eftersom ingen serie nummer/parti nummer Ƥr vald., +Item qty can not be updated as raw materials are already processed.,Artikel kvantitet kan inte uppdateras eftersom rĆ„material redan Ƥr bearbetad., +Item rate has been updated to zero as Allow Zero Valuation Rate is checked for item {0},Artikel pris har Ƥndrats till noll eftersom TillĆ„t Noll Grund Pris Ƥr vald fƶr artikel {0}, +Item valuation reposting in progress. Report might show incorrect item valuation.,Artikel vƤrdering omlƤggning pĆ„gĆ„r. Rapport kan visa felaktig artikelvƤrde., +Item {0} cannot be added as a sub-assembly of itself,Artikel {0} kan inte lƤggas till som underenhet av sig sjƤlv, +Item {0} cannot be ordered more than {1} against Blanket Order {2}.,Artikel {0} kan inte skapas order fƶr mer Ƥn {1} mot Blankoavtal Order {2}., +Item {0} does not exist.,Artikel {0} finns inte., +Item {0} entered multiple times.,Artikel {0} Ƥr angiven flera gĆ„nger., +Item {0} is already reserved/delivered against Sales Order {1}.,Artikel {0} Ƥr redan reserverad/levererad mot FƶrsƤljning Order {1}., +Item {0} must be a Non-Stock Item,Artikel {0} mĆ„ste vara Ej Lager Artikel, +Item {0} not found in 'Raw Materials Supplied' table in {1} {2},"Artikel {0} hittades inte i ""RĆ„material Levererad"" tabell i {1} {2}", +Item {0} not found.,Artikel {0} hittades inte., +Item {} does not exist.,Artikel {} finns inte., +Items & Pricing,Artiklar & Priser, +Items Catalogue,Artikel Katalog, +Items cannot be updated as Subcontracting Order is created against the Purchase Order {0}.,Artiklar kan inte uppdateras eftersom underleverantƶr order Ƥr skapad mot Inkƶp Order {0}., +Items rate has been updated to zero as Allow Zero Valuation Rate is checked for the following items: {0},Artikel Pris har Ƥndrats till noll eftersom TillĆ„t Noll Grund Pris Ƥr vald fƶr fƶljande artiklar: {0}, +Items to Be Repost,Artikel som ska LƤggas om, +Items to Order and Receive,Inkƶp Artiklar, +Items to Reserve,Artiklar att Reservera, +Items {0} do not exist in the Item master.,Artikel {0} saknas i Artikel Register., +Job Capacity,Arbetskapacitet, +Job Card Operation,Jobbkort ƅtgƤrd, +Job Card Scheduled Time,Jobbkort Schemalagd Tid, +Job Card Scrap Item,Jobbkort Rest Artikel, +Job Card and Capacity Planning,Jobbkort & Kapacitet Planering, +Job Cards,Jobbkort , +Job Paused,Jobb Pausad, +Job Worker,Jobb Ansvarig, +Job Worker Address,Jobb Ansvarig Adress, +Job Worker Address Details,Jobb Ansvarig Adress Detaljer, +Job Worker Contact,Jobb Ansvarig Kontakt, +Job Worker Delivery Note,Jobb Ansvarig Fƶljesedel, +Job Worker Name,Jobb Ansvarig Namn, +Job Worker Warehouse,Jobb Ansvarig Lager, +Job: {0} has been triggered for processing failed transactions,Jobb: {0} Ƥr utlƶst fƶr bearbetning av misslyckade transaktioner, +Joining,AnstƤllning, +Journal Entries,Journal Poster, +Journal Entry for Asset scrapping cannot be cancelled. Please restore the Asset.,Journal Post fƶr TillgĆ„ng avskrivning kan inte annulleras. VƤnligen Ć„terstƤll TillgĆ„ng., +Journal Entry type should be set as Depreciation Entry for asset depreciation,Journal Post Typ ska anges som Avskrivning Post fƶr tillgĆ„ng avskrivning, +Journal entries have been created,Journal Poster Ƥr skapade, +Journals,Journaler, +Key,Nyckel, +Kindly cancel the Manufacturing Entries first against the work order {0}.,VƤnligen annullera Produktion Poster fƶrst mot Arbetsorder {0}., +"Last Name, Email or Phone/Mobile of the user are mandatory to continue.","Efternamn, E-post eller Telefon/Mobil erfodras fƶr anvƤndare att fortsƤtta.", +Lead -> Prospect,Potentiell Kund -> Prospekt, +Lead Conversion Time,Potentiell Kund Samtal Tid, +Lead Owner cannot be same as the Lead Email Address,Potentiell Kund Ansvarig kan inte vara samma som Potentiell Kund E-post Adress, +Lead {0} has been added to prospect {1}.,Potentiell Kund {0} Ƥr lagd till Prospekt {1}., +Leaderboard,Topplista, +Leads,Potentiella Kunder, +Learn Accounting,LƤr dig om Bokfƶring, +Learn Inventory Management,LƤr dig om Lager Hantering, +Learn Manufacturing,LƤr dig om Produktion, +Learn Procurement,LƤr dig om Inkƶp, +Learn Project Management,LƤr dig om Projekt Hantering, +Learn Sales Management,LƤr dig om FƶrsƤljning, +"Learn about Common Party","LƤr dig om Gemensam Parti", +"Leave blank for home. +This is relative to site URL, for example ""about"" will redirect to ""https://yoursitename.com/about""","LƤmna tom fƶr Hem. Detta Ƥr relativt till webbadress, till exempel 'Om' kommer att omdirigera till 'https://yoursitename.com/about'", +Ledger Health,Register Status, +Ledger Health Monitor,Register Status Ɩvervakning, +Ledger Health Monitor Company,Register Status Ɩvervakning Bolag, +Ledger Merge,Bokgƶring Register Sammanslagning, +Ledger Merge Accounts,Register Sammanslagning Konton, +Ledgers,Register, +Left Child,VƤnster Underordnad, +Legend,Fƶrklaring, +Length,LƤngd, +Length (cm),LƤngd (cm), +Less than 12 months.,Kortare Ƥn tolv mĆ„nader., +Level (BOM),NivĆ„ (Stycklista), +Limit timeslot for Stock Reposting,BegrƤnsa tid fƶr Lager OmlƤggning, +Limits don't apply on,GrƤns gƤller inte, +Link a new bank account,LƤnka ny Bank Konto, +Link with Customer,LƤnka med Kund, +Link with Supplier,LƤnka med Leverantƶr, +Linked with submitted documents,LƤnkad med godkƤnda dokument, +Linking Failed,LƤnkning Misslyckad, +Linking to Customer Failed. Please try again.,LƤnkning med Kund Misslyckades. Var god fƶrsƶk igen., +Linking to Supplier Failed. Please try again.,LƤnkning med Leverantƶr Misslyckades. Var god fƶrsƶk igen., +Links,LƤnkar, +Loading import file...,Laddar import fil..., +Locked,LĆ„st, +Log Entries,Logg Poster, +Log the selling and buying rate of an Item,Logga fƶrsƤljning och inkƶp pris fƶr Artikel, +Lost Quotations,Fƶrlorade FƶrsƤljning Offerter, +Lost Quotations %,Fƶrlorade FƶrsƤljning Offerter (%), +Lost Reasons are required in case opportunity is Lost.,Fƶrlorad Anledning erfordras om Mƶjlighet Ƥr Fƶrlorad., +Lost Value,Fƶrlorad VƤrde, +Lost Value %,Fƶrlorad VƤrde %, +Machine Type,Maskin Typ, +Main Cost Center,Standard Resultat Enhet, +Main Cost Center {0} cannot be entered in the child table,Huvud Resultat Enhet {0} kan inte anges i underordnad tabell, +Maintain Asset,UnderhĆ„ll TillgĆ„ng, +Maintenance Details,Service Detaljer, +Make ,MƤrke , +Make Asset Movement,Skapa TillgĆ„ng Fƶrflyttning, +Make Quotation,Skapa Offert, +Make Return Entry,Skapa Retur Post, +Make Serial No / Batch from Work Order,Skapa Serie / Parti Nummer frĆ„n Arbetsorder, +Make {0} Variant,Skapa {0} Variant, +Make {0} Variants,Skapa {0} Varianter, +Making Journal Entries against advance accounts: {0} is not recommended. These Journals won't be available for Reconciliation.,Skapa Journal Poster mot fƶrskott konton: {0} rekommenderas inte. Dessa journaler kommer inte att vara tillgƤngliga fƶr avstƤmning., +Manage,Hantera, +Mandatory Accounting Dimension,Erfodrad Bokfƶring Dimension, +Mandatory Depends On,Erfordrad Beroende Av, +Mandatory Field,Erfodrad FƤlt, +Mandatory Section,Erfodrad Sektion, +Manual Inspection,Manuell Kontroll, +Manufacture Sub-assembly in Operation,Producera Underenhet i ƅtgƤrd, +Manufacturing Type,Produktion Typ, +Mapping Purchase Receipt ...,Mappar Inkƶp Fƶljesedel..., +Mapping Subcontracting Order ...,Mappar Underleverantƶr Order ..., +Mapping {0} ...,Mappar {0} ..., +Margin View,Marginal Vy, +Mark As Closed,Ange som StƤngd , +Material Returned from WIP,Material Retur frĆ„n Arbete PĆ„gĆ„r, +Material Transfer (In Transit),Material Ɩverfƶring (I Transit), +Materials are already received against the {0} {1},Material mottagen mot {0} {1}, +Materials needs to be transferred to the work in progress warehouse for the job card {0},Material mĆ„ste ƶverfƶras till Arbete PĆ„gĆ„r Lager fƶr Jobbkort {0}, +Max Qty (As Per Stock UOM),Maximum Kvantitet (per Lager Enhet), +Maximum Net Rate,Maximum Netto Pris, +Maximum Payment Amount,Maximum Betalning Belopp, +Maximum Value,Maximum VƤrde, +Maximum quantity scanned for item {0}.,Maximum kvantitet skannad fƶr artikel {0}., +Meeting,Mƶte, +Mention if non-standard Receivable account,Ange om ej Standard Fordring Konto, +Merge Invoices Based On,SlĆ„ Samman Faktura Baserad PĆ„, +Merge Progress,Sammanslagning Framsteg, +Merge Similar Account Heads,SlĆ„ Samman Liknande Konto Poster, +Merge taxes from multiple documents,SlĆ„ Samman Moms frĆ„n flera dokument, +Merged,Sammanslagen, +"Merging is only possible if following properties are same in both records. Is Group, Root Type, Company and Account Currency","Sammanslagning Ƥr endast mƶjlig om fƶljande egenskaper Ƥr lika i bĆ„da poster. Ƅr Grupp, Konto Klass, Bolag och Konto Valuta", +Merging {0} of {1},SlĆ„r Samman {0} av {1}, +Min Qty (As Per Stock UOM),Minimum Kvantitet (per Lager Enhet), +Min Qty should be greater than Recurse Over Qty,Minimum Kvantitet ska vara hƶgre Ƥn Rekurs ƶver kvantitet, +Minimum Net Rate,Minimum Netto Pris, +Minimum Payment Amount,Minimum Betalning Belopp, +Minimum Value,Mimimum VƤrde, +Mismatch,FelavstƤmd, +Missing,Saknas, +Missing Asset,TillgĆ„ng Saknas, +Missing Cost Center,Resultat Enhet Saknas, +Missing Finance Book,Finans Register Saknas, +Missing Finished Good,FƤrdig Artikel Saknas, +Missing Formula,Formel Saknas, +Missing Items,Artiklar Saknas, +Missing Payments App,Betalning App Saknas, +Missing Serial No Bundle,Serie Nummer Paket Saknas, +Missing value,VƤrde Saknas, +Modified By,Modifierad Av, +Modified On,Modifierad, +Module Settings,Modul InstƤllningar, +Monitor for Last 'X' days,"Ɩvervakning fƶr senaste ""X"" dagar", +Move Stock,Flytta Lager, +Move to Cart,Flytta till Kundkorg, +Movement,Fƶrflyttning, +Moving up in tree ...,Flytta upp i trƤd..., +Multi-level BOM Creator,Fler NivĆ„ Stycklista Generator, +Multiple Loyalty Programs found for Customer {}. Please select manually.,Flera Lojalitet Program hittades fƶr Kund {}. VƤlj manuellt., +Multiple Warehouse Accounts,Flera Lager Konton, +Multiple items cannot be marked as finished item,Flera artiklar kan inte vƤljas som fƤrdiga artiklar, +Must be a publicly accessible Google Sheets URL and adding Bank Account column is necessary for importing via Google Sheets,MĆ„ste vara publik tillgƤnglig URL fƶr Google Sheets och det Ƥr nƶdvƤndigt att lƤgga till Bank Konto Kolumn att importera via Google Sheets, +Named Place,Namngiven Plats, +Naming Series and Price Defaults,Nummer Serie & Pris Standard, +Net total calculation precision loss,Netto Total BerƤkning Precision Fƶrlust, +New Balance In Account Currency,Ny Saldo i Konto Valuta, +New Event,Ny HƤndelse, +New Note,Ny Anteckning, +New Task,Ny Uppgift, +New Version,Ny Version, +Newsletter,Nyhetsbrev, +No Answer,Ingen Svar, +No Customers found with selected options.,Inga Kunder hittades med valda alternativ., +No Items selected for transfer.,Inga Artiklar har valts fƶr ƶverfƶring., +No Matching Bank Transactions Found,Inga matchande banktransaktioner hittades, +No Notes,Inga Anteckningar, +No Outstanding Invoices found for this party,Inga UtestĆ„ende Fakturor hittades fƶr denna parti, +No POS Profile found. Please create a New POS Profile first,Ingen Kassa Profil hittad. Skapa ny Kassa Profil, +No Records for these settings.,Inga Poster fƶr dessa instƤllningar., +No Serial / Batches are available for return,Inga Serie Nummer/Partier Ƥr tillgƤngliga fƶr retur, +No Stock Available Currently,Ingen Lager TillgƤnglig fƶr nƤrvarande, +No Summary,Ingen Ɩversikt, +No Tax Withholding data found for the current posting date.,Ingen Moms Avdrag data hittades fƶr aktuell bokfƶring datum., +No Terms,Inga Villkor, +No Unreconciled Invoices and Payments found for this party and account,Inga OavstƤmda Fakturor och Betalningar hittades fƶr denna parti och konto, +No Unreconciled Payments found for this party,Inga OavstƤmda Betalningar hittades fƶr denna parti, +No Work Orders were created,Inga Arbetsordrar skapades, +No additional fields available,Inga extra fƤlt tillgƤngliga, +No billing email found for customer: {0},Ingen faktura e-post hittades fƶr kund: {0}, +No data found. Seems like you uploaded a blank file,Ingen data hittades. Det verkar som om tom fil laddats upp, +No employee was scheduled for call popup,Ingen personal var schemalagd fƶr ovƤntad samtal, +No failed logs,Inga Misslyckade Logg, +No item available for transfer.,Ingen artikel tillgƤnglig fƶr ƶverfƶring., +No items are available in sales orders {0} for production,Inga artiklar Ƥr tillgƤngliga i FƶrsƤljning Order {0} fƶr produktion, +No items are available in the sales order {0} for production,Inga artiklar Ƥr tillgƤngliga i FƶrsƤljning Order {0} fƶr produktion, +No items in cart,Antal Artiklar i Kundkorg, +No matches occurred via auto reconciliation,Inga avstƤmningar uppstod via automatisk avstƤmning, +No more children on Left,Inga fler underordnade till VƤnster, +No more children on Right,Inga fler underordnade till Hƶger, +No of Docs,Antal Dokument , +No of Employees,Personal Antal, +No of Months (Expense),Antal MĆ„nader, +No of Months (Revenue),Antal MĆ„nader, +No open event,Inga ƶppna HƤndelse, +No open task,Inga ƶppna Uppgifter, +No outstanding {0} found for the {1} {2} which qualify the filters you have specified.,Inga utestĆ„ende {0} hittades fƶr {1} {2} som uppfyller angiven filter., +No primary email found for customer: {0},Ingen primƤr e-post adress hittades fƶr kund: {0}, +No records found in Allocation table,Inga poster hittades i Tilldelning tabell, +No records found in the Invoices table,Inga poster hittades i Faktura Tabell, +No records found in the Payments table,Inga poster hittades i Betalning Tabell, +No stock transactions can be created or modified before this date.,Inga lager transaktioner kan skapas eller Ƥndras fƶre detta datum., +No {0} Accounts found for this company.,Inga {0} konto hittades fƶr detta bolag., +No.,Nr., +No. of Employees,Personal Antal, +No. of parallel job cards which can be allowed on this workstation. Example: 2 would mean this workstation can process production for two Work Orders at a time.,Antal samtidiga Jobbkort som kan tillĆ„tas pĆ„ denna arbetsstation. Exempel: 2 skulle innebƤra att denna arbetsstation kan hantera tvĆ„ Arbetsordrar Ć„t gĆ„ngen., +Note: Automatic log deletion only applies to logs of type Update Cost,Obs: Automatisk logg radering gƤller endast loggar av typ Uppdatera Kostnad, +"Note: To merge the items, create a separate Stock Reconciliation for the old item {0}",Obs: Fƶr att slĆ„ samman artiklar skapar separat lager avstƤmning fƶr gamla artikel {0}, +Notes HTML,Anteckningar HTML, +Notification,Aviseringar, +Notification Settings,Avisering InstƤllningar, +Notify Reposting Error to Role,Avisera omlƤggning fel till roll, +Number of Days,Antal Dagar, +Numeric,Numerisk, +Numeric Inspection,Numerisk Kontroll, +Off,Av, +Offsetting Account,AvrƤkning Konto, +Offsetting for Accounting Dimension,AvrƤkning fƶr Bokfƶring Dimension, +On Paid Amount,PĆ„ Betald Belopp, +On This Date,Datum, +On Track,PĆ„ Bana, +On enabling this cancellation entries will be posted on the actual cancellation date and reports will consider cancelled entries as well,NƤr du aktiverar denna annullering kommer poster att publiceras pĆ„ faktisk annullering datum och rapporter kommer ocksĆ„ att inkludera annullerade poster, +"On expanding a row in the Items to Manufacture table, you'll see an option to 'Include Exploded Items'. Ticking this includes raw materials of the sub-assembly items in the production process.","Vid utƶkning av rad i Artiklar att Producera Tabell,kommer du att se alternativ ""Inkludera Utvidgade Artiklar"". Genom att vƤljadetta ingĆ„r rĆ„varor frĆ„n delkomponenter i produktion process.", +"On submission of the stock transaction, system will auto create the Serial and Batch Bundle based on the Serial No / Batch fields.",Vid godkƤnnade av lager transaktion kommer system att automatiskt skapa Serie och Parti Paket baserat pĆ„ Serienummer / Parti fƤlt., +Once the Work Order is Closed. It can't be resumed.,NƤr Arbetsorder Ƥr StƤngd kan den inte Ć„terupptas., +Only 'Payment Entries' made against this advance account are supported.,"Endast ""Betalning Poster"" som skapas mot detta fƶrskott konto stƶds.", +Only CSV and Excel files can be used to for importing data. Please check the file format you are trying to upload,Endast CSV och Excel filer kan anvƤndas fƶr data import. Kontrollera filformat du fƶrsƶker ladda upp, +Only Deduct Tax On Excess Amount ,Endast Dra av Skatt pĆ„ Ɩverskjutande Belopp, +Only Include Allocated Payments,Endast Inkludera allokerade betalningar, +Only Parent can be of type {0},Endast Ɩverordnad kan vara av typ {0}, +Only existing assets,Endast Befintliga TillgĆ„ngar, +"Only one Subcontracting Order can be created against a Purchase Order, cancel the existing Subcontracting Order to create a new one.","Endast en Underleverantƶr Order kan skapas mot en Inkƶp Order, annullera befintlig Underleverantƶr Order fƶr att skapa ny.", +Only one {0} entry can be created against the Work Order {1},Endast en {0} post kan skapas mot Arbetsorder {1}, +"Only values between [0,1) are allowed. Like {0.00, 0.04, 0.09, ...} +Ex: If allowance is set at 0.07, accounts that have balance of 0.07 in either of the currencies will be considered as zero balance account","Endast vƤrden mellan [0,1) Ƥr tillĆ„tna, t.ex.{0.00, 0.04, 0.09, ...} +Exempel: Om tillĆ„telse Ƥr angiven till 0,07, kommer konton som har saldo pĆ„ 0,07 i nĆ„gon av valutorna att betraktas som noll saldo konto.", +Only {0} are supported,Endast {0} stƶds, +Open Activities HTML,Ɩppna Aktiviteter HTML, +Open Call Log,Ɩppna Samtal Logg, +Open Event,Ɩppna HƤndelse, +Open Events,Ɩppna HƤndelser, +Open Sales Orders,Ɩppna FƶrsƤljning Ordrar, +Open Task,Ɩppna Uppgift, +Open Tasks,Ɩppna Uppgifter, +Open Work Order {0},Ɩppna Arbetsorder {0}, +Opening & Closing,Ɩppning & StƤngning, +Opening Accumulated Depreciation must be less than or equal to {0},Ɩppning Ackumulerad Avskrivning mĆ„ste vara mindre Ƥn eller lika med {0}, +Opening Entry can not be created after Period Closing Voucher is created.,Ɩppning Post kan inte skapas efter att Period StƤngning Verifikat Ƥr skapad., +Opening Number of Booked Depreciations,Ɩppning Nummer fƶr Bokfƶrda Avskrivningar, +Opening Purchase Invoices have been created.,Ɩppning Inkƶp Fakturor Ƥr skapade., +Opening Sales Invoices have been created.,Ɩppning FƶrsƤljning Fakturor Ƥr skapade., +Operating Cost Per BOM Quantity,Drift Kostnad per Stycklista Kvantitet, +Operation time does not depend on quantity to produce,ƅtgƤrd Tid beror inte pĆ„ kvantitet som ska produceras, +Opportunity Amount (Company Currency),Mƶjlighet Belopp (Bolag Valuta), +Opportunity Owner,Mƶjlighet Ansvarig, +Opportunity Source,Mƶjlighet KƤlla, +Opportunity Summary by Sales Stage,Mƶjlighet Ɩversikt efter FƶrsƤljning Stadium, +Opportunity Summary by Sales Stage ,Mƶjlighet Ɩversikt efter FƶrsƤljning Stadium, +Opportunity Value,Mƶjlighet VƤrde, +Other Info,Ɩvrig Information, +Out of stock,Ej pĆ„ Lager, +Over Picking Allowance,Ɩver Plock TillĆ„telse, +Over Receipt,Ɩver Fƶljesedel, +Over Receipt/Delivery of {0} {1} ignored for item {2} because you have {3} role.,Ɩver Fƶljesedel/Leverans av {0} {1} ignoreras fƶr artikel {2} eftersom du har {3} roll., +Over Transfer Allowance,Ɩver Ɩverfƶring TillĆ„telse, +Overbilling of {0} {1} ignored for item {2} because you have {3} role.,Ɩverfakturering av {0} {1} ignoreras fƶr artikel {2} eftersom du har {3} roll., +Overbilling of {} ignored because you have {} role.,Ɩverfakturering av {} ignoreras eftersom du har {} roll., +Overdue Payment,Fƶrfallna Fakturor, +Overdue Payments,Fƶrfallna Fakturor, +Overdue Tasks,Fƶrsenade Uppgifter, +Overview,Recension , +PDF Name,PDF Namn, +POS Closing Failed,Kassa StƤngning Misslyckad, +POS Closing failed while running in a background process. You can resolve the {0} and retry the process again.,Kassa StƤngning misslyckades medan den kƶrdes i bakgrund process. Du kan lƶsa {0} och fƶrsƶka igen., +POS Invoice is already consolidated,Kassa Faktura Ƥr redan konsoliderad, +POS Invoice is not submitted,Kassa Faktura Ƥr inte godkƤnd, +POS Invoice should have the field {0} checked.,Kassa Faktura ska ha {} fƤlt vald., +POS Invoices will be consolidated in a background process,Kassa Fakturor kommer att konsolideras i bakgrund process, +POS Invoices will be unconsolidated in a background process,Kassa Fakturor kommer att okonsolideras i bakgrund process, +POS Profile doesn't match {},Kassa Profil matchar inte {}, +POS Profile {} contains Mode of Payment {}. Please remove them to disable this mode.,Kassa Profil {} innehĆ„ller Betalning SƤtt {}. Ta bort Betalning SƤtt fƶr att inaktivera detta lƤge., +POS Search Fields,Kassa Sƶk FƤlt, +POS Setting,Kassa InstƤllningar, +Package No(s) already in use. Try from Package No {0},Paket Nummer anvƤnds redan. Prova frĆ„n Paket Nummer {0} , +Packaging Slip From Delivery Note,Packsedel frĆ„n FƶrsƤljning Fƶljesedel, +Packed Items cannot be transferred internally,Packade artiklar kan inte ƶverfƶras internt, +Packed Qty,Fƶrpackad Kvantitet, +Page Break After Each SoA,Sidbrytning efter varje SoA, +Paid Amount After Tax,Betald Belopp efter Moms, +Paid Amount After Tax (Company Currency),Betald Belopp efter Moms (Bolag Valuta), +Paid From Account Type,Betald frĆ„n Konto Typ, +Paid To Account Type,Betald till Konto Typ, +Pallets,Pall, +Parameter Group,Parameter Grupp, +Parameter Group Name,Parameter Grupp Namn, +Parcel Template,Leverans Paket Mall, +Parcel Template Name,Leverans Paket Mall Namn, +Parcel weight cannot be 0,Leverans Paket Vikt fĆ„r inte vara 0, +Parcels,Paket, +Parent Account Missing,Ɩverordnad Konto Saknas, +Parent Document,Ɩverordnad Dokument, +Parent Item {0} must not be a Fixed Asset,Ɩverordnad Artikel {0} fĆ„r inte vara TillgĆ„ng, +Parent Row No,Ɩverordnad Rad Nummer, +Parent Task {0} is not a Template Task,Ɩverordnad Uppgift {0} Ƥr inte Mall Uppgift, +Partial Material Transferred,Delvis Material Ɩverfƶrd, +Partial Stock Reservation,Delvis Lager Reservation, +Partial Success,Delvis Klar, +"Partial stock can be reserved. For example, If you have a Sales Order of 100 units and the Available Stock is 90 units then a Stock Reservation Entry will be created for 90 units. ","Delvis Lager kan reserveras. Till exempel, om du har en fƶrsƤljning order pĆ„ 100 enheter och det tillgƤngliga lagret Ƥr 90 enheter kommer lager reservation post att skapas fƶr 90", +Partially Delivered,Delvis Levererad, +Partially Reconciled,Delvis AvstƤmd, +Partially Reserved,Delvis Reserverad, +Partly Paid,Delvis Betald, +Partly Paid and Discounted,Delvis Betald och Rabatterad, +Partnership,Partner, +Party Account No. (Bank Statement),Parti Konto Nummer (Kontoutdrag), +Party Account {0} currency ({1}) and document currency ({2}) should be same,Parti Konto {0} valuta ({1}) och dokument valuta ({2}) ska vara samma, +Party IBAN (Bank Statement),Parti IBAN (Kontoutdrag), +Party Item Code,Parti Artikel Kod, +Party Link,Parti LƤnk, +Party Name/Account Holder (Bank Statement),Parti Namn/Kontoinnehavare (Kontoutdrag), +Party Specific Item,Parti Specifik Artikel, +Party Type and Party is required for Receivable / Payable account {0},Parti Typ och Parti erfodras fƶr Fordring / Skuld konto {0}, +Party can only be one of {0},Parti kan endast vara en av {0}, +Passport Details,Passport Detaljer, +Pause Job,Pausa Jobb, +Paused,Pausad, +Payment Amount (Company Currency),Faktura Belopp (Bolag Valuta), +"Payment Entry {0} is linked against Order {1}, check if it should be pulled as advance in this invoice.","Betalning Post {0} Ƥr lƤnkad till Order {1}, kontrollera om den ska hƤmtas som fƶrskott pĆ„ denna faktura.", +Payment Ledger,Betalning Register, +Payment Ledger Balance,Betalning Register Saldo, +Payment Ledger Entry,Betalning Register Post, +Payment Limit,Betalning GrƤns, +Payment Reconciliation Allocation,Betalning AvstƤmning Tilldelning , +Payment Reconciliation Job: {0} is running for this party. Can't reconcile now.,Betalning AvstƤmning Jobb: {0} kƶrs fƶr denna parti. Kan inte avstƤmas nu., +Payment Reconciliations,Betalning AvstƤmningar, +Payment Request Outstanding,Betalning BegƤran UtestĆ„ende Belopp, +Payment Request created from Sales Order or Purchase Order will be in Draft status. When disabled document will be in unsaved state.,Betalning BegƤran som skapas frĆ„n FƶrsƤljning Order eller Inkƶp Order kommer att fĆ„ Utkast status. NƤr inaktiverad kommer dokument att vara i osparat tillstĆ„nd., +Payment Request is already created,Betalning BegƤran Ƥr redan skapad, +Payment Request took too long to respond. Please try requesting for payment again.,Betalning BegƤran tog fƶr lĆ„ng tid att svara. Fƶrsƶk att begƤra betalning igen., +Payment Requests cannot be created against: {0},Betalning BegƤran kan inte skapas mot: {0}, +Payment Term Outstanding,Betalningsvillkor UtestĆ„ende Belopp, +Payment Terms Status for Sales Order,Betalning Villkor Status fƶr FƶrsƤljning Order, +Payment Terms from orders will be fetched into the invoices as is,Betalning Villkor frĆ„n Order hƤmtas till Faktura som den Ƥr, +Payment Unlink Error,Betalning Bortkoppling Fel, +Payment of {0} received successfully.,Betalning pĆ„ {0} mottagen., +Payment of {0} received successfully. Waiting for other requests to complete...,Betalning pĆ„ {0} mottagen. VƤntar pĆ„ att andra begƤran ska slutfƶras..., +Payment request failed,Betalning BegƤran Misslyckades, +Payment term {0} not used in {1},Betalning Villkor {0} anvƤnds inte i {1}, +Pending processing,VƤntar pĆ„ bearbetning, +Per Received,Per Mottagen, +Percentage (%),Procent (%), +Percentage you are allowed to order beyond the Blanket Order quantity.,Procentandel tillĆ„ten fƶr order utƶver Blankoavtal Order Kvantitet., +Percentage you are allowed to sell beyond the Blanket Order quantity.,Procentandel tillĆ„ten fƶr fƶrsƤljning utƶver Blankoavtal Order Kvantitet., +Period Closed,Period StƤngd, +Period Closing Entry For Current Period,Period LĆ„sning Post fƶr Aktuell Period, +Period Closing Settings,Period LĆ„sning InstƤllningar, +Period Details,Period Detaljer, +Period To Date,Period Till Datum, +Period_from_date,Period FrĆ„n Datum, +Phone Ext.,Telefon Anknytning, +Pick List Incomplete,Plocklista OfullstƤndig, +Pick Manually,Plocka Manuellt, +Pick Serial / Batch Based On,VƤlj Serie / Parti Baserad PĆ„, +Pick Serial / Batch No,VƤlj Serie / Parti Nummer, +Picked Qty (in Stock UOM),Plockad Kvantitet (Lager Enhet), +Pickup,HƤmtning, +Pickup Contact Person,HƤmtning Adress Kontakt Person, +Pickup Date,HƤmtning Datum, +Pickup Date cannot be before this day,HƤmtning Datum kan inte infalla fƶre denna dag, +Pickup From,HƤmtning FrĆ„n, +Pickup To time should be greater than Pickup From time,HƤmtning Till Tid ska vara senare Ƥn HƤmtning FrĆ„n Tid, +Pickup Type,HƤmtning Typ, +Pickup from,HƤmtning FrĆ„n, +Pickup to,HƤmtning Till, +Pipeline By,Tratt Efter, +Plaid Link Failed,Plaid LƤnk Misslyckades, +Plaid Link Refresh Required,Plaid LƤnk Uppdatering erfordras, +Plaid Link Updated,Plaid LƤnk Uppdaterad, +Plan to Request Qty,Planerad Kvantitet, +Plant Dashboard,Fabrik Ɩversikt Panel, +Plant Floor,Produktion Yta, +Please Set Priority,Ange Prioritet, +Please Specify Account,Specificera Konto, +Please add 'Supplier' role to user {0}.,"LƤgg till Roll ""Leverantƶr"" till anvƤndare {0}.", +Please add Request for Quotation to the sidebar in Portal Settings.,LƤgg till Offert FƶrfrĆ„ga i sidofƤlt i Portal InstƤllningar., +Please add Root Account for - {0},LƤgg till Ɩverordnad Konto fƶr - {0}, +Please add atleast one Serial No / Batch No,LƤgg till minst en Serie Nr / Parti Nr, +Please add the Bank Account column,LƤgg till Bank Konto kolumn, +Please add the account to root level Company - {0},LƤgg till Konto till Ɩverordnad Bolag - {0}, +Please add {1} role to user {0}.,LƤgg till roll {1} till anvƤndare {0}., +Please adjust the qty or edit {0} to proceed.,Justera kvantitet eller redigera {0} fƶr att fortsƤtta., +Please attach CSV file,Bifoga CSV Fil, +Please cancel and amend the Payment Entry,Annullera och Ƥndra Betalning Post, +Please cancel payment entry manually first,Annullera Betalning Post manuellt, +Please cancel related transaction.,Annullera relaterad transaktion., +Please check Process Deferred Accounting {0} and submit manually after resolving errors.,VƤlj Bearbeta Uppskjuten Bokfƶring {0} och godkƤnn manuellt efter att ha lƶst fel., +Please check either with operations or FG Based Operating Cost.,VƤlj antingen Med ƅtgƤrder eller FƤrdig Artikel Baserad ƅtgƤrd Kostnad., +Please check the error message and take necessary actions to fix the error and then restart the reposting again.,Kontrollera felmeddelande och vidta nƶdvƤndiga Ć„tgƤrder fƶr att Ć„tgƤrda fel och starta sedan ompostning igen., +Please check your email to confirm the appointment,Kontrollera din E-post fƶr att bekrƤfta Mƶte, +Please contact any of the following users to extend the credit limits for {0}: {1},Kontakta nĆ„gon av fƶljande anvƤndare fƶr att utƶka kredit grƤnser fƶr {0}: {1}, +Please contact any of the following users to {} this transaction.,Kontakta nĆ„gon av fƶljande anvƤndare fƶr att {} denna transaktion., +Please contact your administrator to extend the credit limits for {0}.,Kontakta administratƶr fƶr att utƶka kredit grƤnser fƶr {0}., +Please create Landed Cost Vouchers against Invoices that have 'Update Stock' enabled.,"Skapa Landad Kostnad Verifikat mot fakturor som har ""Uppdatera Lager"" aktiverad.", +Please create a new Accounting Dimension if required.,Skapa Bokfƶring Dimension vid behov., +Please create purchase from internal sale or delivery document itself,Skapa Inkƶp frĆ„n intern FƶrsƤljning eller Fƶljesedel, +"Please delete Product Bundle {0}, before merging {1} into {2}",Ta bort Artikel Paket {0} innan sammanslagning av {1} med {2}, +Please do not book expense of multiple assets against one single Asset.,Bokfƶr inte kostnader fƶr flera TillgĆ„ngar mot enskild TillgĆ„ng., +Please enable Use Old Serial / Batch Fields to make_bundle,Aktivera AnvƤnd gamla Serie / Parti FƤlt fƶr att skapa paket, +Please enable only if the understand the effects of enabling this.,Aktivera endast om du fƶrstĆ„r effekterna av att aktivera detta., +Please enable {0} in the {1}.,Aktivera {0} i {1}., +Please enable {} in {} to allow same item in multiple rows,Aktivera {} i {} fƶr att tillĆ„ta samma Artikel i flera rader, +Please ensure that the {0} account is a Balance Sheet account. You can change the parent account to a Balance Sheet account or select a different account.,Kontrollera att {0} konto Ƥr Balans Rapport Konto. Ƅndra Ɩverordnad Konto till Balans Rapport Konto eller vƤlj annat konto., +Please ensure that the {0} account {1} is a Payable account. You can change the account type to Payable or select a different account.,Kontrollera att {0} konto {1} Ƥr Skuld Konto. Ƅndra Konto Typ till Skuld Konto Typ eller vƤlj ett annat konto., +Please ensure {} account is a Balance Sheet account.,Kontrollera att {} konto Ƥr Balans Rapport konto., +Please ensure {} account {} is a Receivable account.,Kontrollera att {} konto {} Ƥr fordring konto., +Please enter Root Type for account- {0},Ange Konto Klass fƶr konto {0}, +Please enter Serial Nos,Ange Serie Nummer, +Please enter Shipment Parcel information,Ange Leverans Paket information, +Please enter Stock Items consumed during the Repair.,Ange Artiklar fƶrbrukade under reparation., +Please enter mobile number first.,Ange Mobil Nummer, +Please enter quantity for item {0},Ange Kvantitet fƶr artikel {0}, +Please enter serial nos,Ange Serie Nummer, +"Please first set Last Name, Email and Phone for the user","Ange Efternamn, E-post och Telefon fƶr anvƤndare", +Please fix overlapping time slots for {0},ƅtgƤrda ƶverlappande tider fƶr {0}, +Please fix overlapping time slots for {0}.,ƅtgƤrda ƶverlappande tider fƶr {0}, +Please import accounts against parent company or enable {} in company master.,Importera konton mot moderbolag eller aktivera {} i bolag instƤllningar., +"Please keep one Applicable Charges, when 'Distribute Charges Based On' is 'Distribute Manually'. For more charges, please create another Landed Cost Voucher.","BehĆ„ll en TillƤmplig Avgift nƤr ""Distribuera avgifter Baserat pĆ„"" Ƥr ""Distribuera Manuellt"". Fƶr fler avgifter, skapa annan Landad Kostnad Verifikat", +Please make sure the file you are using has 'Parent Account' column present in the header.,"Kontrollera att fil har kolumn ""Ɩverordnad Konto"" i rubrik.", +Please mention 'Weight UOM' along with Weight.,"Ange ""Vikt Enhet"" tillsammans med Vikt.", +Please mention the Current and New BOM for replacement.,Ange Aktuell och Ny Stycklista fƶr ersƤttning., +Please rectify and try again.,RƤtta till och fƶrsƶk igen., +Please refresh or reset the Plaid linking of the Bank {}.,Uppdatera eller Ć„terstƤll Plaid LƤnk fƶr Bank {}., +Please save before proceeding.,Spara innan du fortsƤtter., +Please select Bank Account,VƤlj Bank Konto, +Please select Finished Good Item for Service Item {0},VƤlj FƤrdig Artikel fƶr Service Artikel {0}, +Please select Serial/Batch Nos to reserve or change Reservation Based On to Qty.,VƤlj Serie / Parti Nummer att reservera eller Ƥndra Reservation Baserad PĆ„ Kvantitet., +Please select Subcontracting Order instead of Purchase Order {0},VƤlj Underleverantƶr Order istƤllet fƶr Inkƶp Order {0}, +Please select Unrealized Profit / Loss account or add default Unrealized Profit / Loss account account for company {0},VƤlj Orealiserad Resultat Konto eller ange standard konto fƶr Orealiserad Resultat Konto fƶr Bolag {0}, +Please select a Subcontracting Purchase Order.,VƤlj Underleverantƶrer Inkƶp Order, +Please select a Warehouse,VƤlj Lager, +Please select a Work Order first.,VƤlj Arbetsorder, +Please select a country,VƤlj Land, +Please select a customer for fetching payments.,VƤlj Kund fƶr att hƤmta betalningar., +Please select a date,VƤlj Datum, +Please select a date and time,VƤlj Tid och Datum, +Please select a row to create a Reposting Entry,VƤlj rad att skapa Ompostering Post, +Please select a supplier for fetching payments.,VƤlj Leverantƶr fƶr att hƤmta betalningar., +Please select a valid Purchase Order that has Service Items.,VƤlj giltig Inkƶp Order med Service Artiklar., +Please select a valid Purchase Order that is configured for Subcontracting.,VƤlj giltig Inkƶp Order som Ƥr konfigurerad fƶr Underleverantƶr., +Please select an item code before setting the warehouse.,VƤlj Artikel Kod innan du anger Lager., +Please select either the Item or Warehouse or Warehouse Type filter to generate the report.,"VƤlj antingen Artikel,Lager eller Lager Typ filter att skapa rapport.", +Please select items,VƤlj Artiklar, +Please select items to reserve.,VƤlj Artiklar att reservera, +Please select items to unreserve.,VƤlj Artiklar att reservera, +Please select only one row to create a Reposting Entry,VƤnligen vƤlj endast en rad fƶr att skapa Ompostering Post, +Please select rows to create Reposting Entries,VƤlj rader fƶr att skapa Ompostering Poster, +Please select the required filters,VƤlj de filter som krƤvs, +Please select valid document type.,VƤlj giltig dokument typ., +Please set Account,Ange Konto, +Please set Accounting Dimension {} in {},Ange Bokfƶring Dimension {} i {}, +Please set Email/Phone for the contact,Ange E-post/Telefon fƶr Kontakt, +Please set Fiscal Code for the customer '%s',"Ange Org.Nr. fƶr Kund ""%s""", +Please set Fiscal Code for the public administration '%s',"Ange Org.Nr. fƶr Offentlig Fƶrvaltning ""%s""", +Please set Fixed Asset Account in {} against {}.,Ange TillgĆ„ng Konto i {} mot {}., +Please set Opening Number of Booked Depreciations,Ange Ɩppning Nummer fƶr Bokfƶrda Avskrivningar, +Please set Parent Row No for item {0},Ange Ɩverordnad Rad Nummer fƶr artikel {0}, +Please set Root Type,Ange Konto Klass, +Please set Tax ID for the customer '%s',Ange Org.Nr. for Kund '%s', +Please set VAT Accounts in {0},Ange Moms Konton i {0}, +"Please set Vat Accounts for Company: ""{0}"" in UAE VAT Settings","Ange Moms Konton fƶr Bolag: ""{0}"" i moms instƤllningarna i Fƶrenade Arabemirater", +Please set a Cost Center for the Asset or set an Asset Depreciation Cost Center for the Company {},Ange Resultat Enhet fƶr TillgĆ„ng eller ange Resultat Enhet fƶr TillgĆ„ng Avskrivningar fƶr Bolag {}, +Please set a default Holiday List for Company {0},Ange standard Helg Lista fƶr Bolag {0}, +Please set an Address on the Company '%s',Ange adress fƶr Bolag '%s', +Please set an Expense Account in the Items table,Ange Kostnad konto i Artikel tabell, +Please set default Exchange Gain/Loss Account in Company {},Ange Standard ValutavƤxling Resultat Konto fƶr Bolag {}, +Please set default Expense Account in Company {0},Ange Standard Konstnad Konto fƶr Bolag {0}, +Please set default cost of goods sold account in company {0} for booking rounding gain and loss during stock transfer,Ange Standard Kostnad fƶr sĆ„lda artiklar i bolag {0} fƶr bokning av avrundning av vinst och fƶrlust under lager ƶverfƶring, +Please set either the Tax ID or Fiscal Code on Company '%s',"Ange antingen Skatt eller Moms Kod fƶr Bolag ""%s""", +Please set filters,Ange Filter, +Please set one of the following:,Ange nĆ„got av fƶljande:, +Please set the cost center field in {0} or setup a default Cost Center for the Company.,Ange Resultat Enhet i {0} eller ange Standard Resultat Enhet fƶr Bolag., +Please set {0} in BOM Creator {1},Ange {0} i Stycklista Generator {1}, +Please setup and enable a group account with the Account Type - {0} for the company {1},Konfigurera och aktivera Kontoplan Grupp med Kontoklass {0} fƶr bolag {1}, +Please share this email with your support team so that they can find and fix the issue.,Dela detta e-post meddelande med support sĆ„ att de kan hitta och Ć„tgƤrda problem. , +Please specify a {0},Ange {0}, +Please try again in an hour.,Fƶrsƶk igen om en timme., +Please update Repair Status.,Uppdatera Reparation Status., +Portal User,Portal AnvƤndare, +Portal Users,Portal AnvƤndare, +Posting Datetime,Bokfƶring Datum och Tid, +Powered by {0},TillhandahĆ„lls av {0}, +"Previous Year is not closed, please close it first","FƶregĆ„ende ƅr Ƥr inte stƤngd, vƤnligen stƤng det", +Price ({0}),Pris ({0}), +Price List Defaults,Prislista Standard, +Price Per Unit ({0}),Pris Per Enhet ({0}), +Primary Address and Contact,PrimƤr Adress & Kontakt, +Primary Contact,PrimƤr Kontakt, +Primary Party,PrimƤr Parti, +Primary Role,PrimƤr Roll, +Print Format Builder,Utskrift Format Redigerare, +Print Style,Utskrift Stil, +Printing,Utskrift, +Priority cannot be lesser than 1.,Prioritet fĆ„r inte vara mindre Ƥn 1., +Priority is mandatory,Parti Erfodras , +Probability,Sannolikhet, +Process Loss,Process Fƶrlust, +Process Loss Percentage cannot be greater than 100,Process Fƶrlust i Procent fĆ„r inte vara stƶrre Ƥn 100 , +Process Loss Qty,Process Fƶrlust Kvantitet, +Process Loss Report,Process Fƶrlust Rapport, +Process Loss Value,Process Fƶrlust VƤrde, +Process Payment Reconciliation,Betalning AvstƤmning Bearbetning, +Process Payment Reconciliation Log,Betalning AvstƤmning Bearbetning Logg, +Process Payment Reconciliation Log Allocations,Betalning AvstƤmning Bearbetning Logg Tilldelningar, +Process Subscription,Behandla Prenumeration, +Process in Single Transaction,Process i Singel Transaktion, +Processed BOMs,Behandlade Stycklistor, +Processing Sales! Please Wait...,Behandlar FƶrsƤljning!VƤnlige VƤnta..., +Produced / Received Qty,Producerad / Mottagen Kvantitet, +Product Price ID,Artikel Pris, +Production Plan Already Submitted,Produktion Plan Redan GodkƤnd, +Production Plan Item Reference,Produktion Plan Post Referens, +Production Plan Qty,Produktion Plan Kvantitet, +Production Plan Sub Assembly Item,Produktion Plan Delmontering Artikel, +Production Plan Sub-assembly Item,Produktion Plan Delmontering Artikel, +Production Plan Summary,Produktion Plan Ɩversikt, +Profile,Profil, +Profit and Loss Summary,Resultat Rapport Ɩversikt, +Progress,Framsteg, +Progress (%),Framsteg(%), +Project Progress:,Projekt Framsteg:, +Prompt Qty,FrĆ„ga efter Kvantitet, +Prospect,Prospekt, +Prospect Lead,Prospekt Potentiell Kund, +Prospect Opportunity,Prospekt Mƶjlighet, +Prospect Owner,Prospekt Ansvarig, +Prospect {0} already exists,Prospekt {0} finns redan, +Provisional Account,Provisoriskt Konto, +Provisional Expense Account,Provisorisk Kostnad Konto, +Purchase Order Item reference is missing in Subcontracting Receipt {0},Inkƶp Order Artikel Referens saknas pĆ„ Underleverantƶr Fƶljesedel {0}, +Purchase Orders {0} are un-linked,Inkƶp Ordrar {0} Ƥr inte lƤnkade, +Purchase Receipt (Draft) will be auto-created on submission of Subcontracting Receipt.,Inkƶp Fƶljesedel (utkast) kommer att skapas automatiskt vid godkƤnnande av Underleverantƶr Fƶljesedel., +Purchase Receipt {0} created.,Inkƶp Fƶljesedel {0} skapad, +Purchase Value,Inkƶp VƤrde, +Purchases,Inkƶp, +Purposes Required,Anledning Erfodras, +Putaway Rule,LƤgg Undan Regel, +Putaway Rule already exists for Item {0} in Warehouse {1}.,LƤgg Undan Regel finns redan fƶr Artikel {0} i Lager {1}., +Qty ,Kvantitet, +Qty After Transaction,Kvantitet efter Transaktion, +Qty As Per BOM,Kvantitet per Stycklista, +Qty Change,Kvantitet FƶrƤndring, +Qty In Stock,Kvantitet i Lager, +Qty Per Unit,Kvantitet per Enhet, +"Qty To Manufacture ({0}) cannot be a fraction for the UOM {2}. To allow this, disable '{1}' in the UOM {2}.","Kvantitet att Producera ({0}) kan inte vara brĆ„kdel fƶr enhet {2}. Fƶr att tillĆ„ta detta, inaktivera '{1}' i enhet {2}.", +Qty To Produce,Kvantitet att Producera, +Qty and Rate,Kvantitet och Pris, +Qty as Per Stock UOM,Kvantitet per Lager Enhet, +Qty for which recursion isn't applicable.,Kvantitet fƶr vilket rekursion inte Ƥr tillƤmplig., +Qty in Stock UOM,Kvantitet i Lager Enhet, +Qty of Finished Goods Item should be greater than 0.,Kvantitet FƤrdiga Artiklar ska vara hƶgre Ƥn 0., +Qty to Be Consumed,Kvantitet att Fƶrbruka, +Qty to Build,Kvantitet att Producera, +Qty to Fetch,Kvantitet att HƤmta, +Qty to Produce,Kvantitet att Producera, +Qualification Status,Kvalificering Status, +Qualified,Kvalificerad, +Qualified By,Kvalificerad Av, +Qualified on,Kvalificerad(Datum), +Quality Inspection Parameter,Kvalitet Kontroll Parameter, +Quality Inspection Parameter Group,Kvalitet Kontroll Parametrar Grupp, +Quality Inspection Settings,Kvalitet Kontroll InstƤllningar, +Quality Inspection(s),Kvalitet Kontroll, +Quantity is required,Kvantitet erfodras, +"Quantity must be greater than zero, and less or equal to {0}",Kvantitet mĆ„ste vara stƶrre Ƥn noll och mindre eller lika med {0}, +Quantity to Produce should be greater than zero.,Kvantitet att producera ska vara hƶgre Ƥn noll., +Quantity to Scan,Kvantitet att Skanna, +Quarter {0} {1},Kvartal {0} {1}, +Quotation Number,FƶrsƤljning Offert Nummer, +Quoted Amount,Offererad Belopp, +Rate Difference with Purchase Invoice,Pris Differens mot Inkƶp Faktura, +Rate Section,Pris, +Rate of Stock UOM,Pris av Lager Enhet, +Ratios,FƶrhĆ„llanden, +Raw Material Cost Per Qty,RĆ„material Kostnad per Kvantitet, +Raw Material Item,RĆ„material Artikel, +Raw Material Value,RĆ„material VƤrde, +Raw Materials Actions,RĆ„material ƅtgƤrder, +Raw Materials Consumption ,RĆ„material Fƶrbrukning , +Raw Materials Warehouse,RĆ„material Lager, +Reached Root,NĆ„tt Rot, +Reading Value,AvlƤst VƤrde, +Reason for hold:,Anledning fƶr SpƤrr:, +Rebuild Tree,Bygg om TrƤd, +Rebuilding BTree for period ...,Bygger om BTree fƶr period ..., +Recalculate Incoming/Outgoing Rate,RƤkna om In/Ut Pris, +Recalculating Purchase Cost against this Project...,RƤkna om Inkƶp Kostnad mot detta Projekt..., +Receivable/Payable Account,Fordring / Skuld Konto, +Receivable/Payable Account: {0} doesn't belong to company {1},Fordring / Skuld Konto: {0} tillhƶr inte bolag {1}, +Received Amount After Tax,Mottaget Belopp Efter Moms, +Received Amount After Tax (Company Currency),Mottaget Belopp Efter Moms (Bolag Valuta), +Received Amount cannot be greater than Paid Amount,Mottaget Belopp kan inte vara hƶgre Ƥn Betald Belopp, +Received Qty in Stock UOM,Mottagen Kvantitet (per Lager Enhet), +Recent Orders,Senaste Ordrar, +Recent Transactions,Senaste Transaktioner, +Reconcile All Serial Nos / Batches,StƤm av alla Serie Nummer / Partier, +Reconcile on Advance Payment Date,StƤm av pĆ„ Fƶrskott Betalning Datum, +Reconcile the Bank Transaction,AvstƤm Bank Transaktion, +Reconciled Entries,AvstƤmda Poster, +Reconciliation Error Log,AvstƤmning Fel Logg, +Reconciliation Logs,AvstƤmning Logg, +Reconciliation Progress,AvstƤmning Framsteg, +Recording HTML,Inspelning HTML, +Recoverable Standard Rated expenses should not be set when Reverse Charge Applicable is Y,ƅterstƤllbara Standard Klassade Kostnader ska inte anges nƤr OmvƤnd Debitering Ƥr Ja, +Recurse Every (As Per Transaction UOM),Rekurs Varje (per Transaktion Enhet), +Recurse Over Qty cannot be less than 0,Rekurs Ɩver Kvantitet fĆ„r inte vara mindre Ƥn 0, +Recursive Discounts with Mixed condition is not supported by the system,Rekursiva Rabatter med Blandat Villkor stƶds inte av system, +Reference Date for Early Payment Discount,Referens Datum fƶr Tidig Betalning Rabatt, +Reference Detail,Referens Detalj, +Reference DocType,Referens DocType, +Reference Exchange Rate,Referens VƤxel Kurs, +Reference No,Referens Nummer. , +Reference number of the invoice from the previous system,Referens Nummer pĆ„ Faktura frĆ„n tidigare system, +References to Sales Invoices are Incomplete,Referenser till FƶrsƤljning Fakturor Ƥr ofullstƤndiga, +References to Sales Orders are Incomplete,Referenser till FƶrsƤljning Ordrar Ƥr ofullstƤndiga, +References {0} of type {1} had no outstanding amount left before submitting the Payment Entry. Now they have a negative outstanding amount.,Referenser {0} av typ {1} hade inget utestĆ„ende belopp kvar innan godkƤnnande av Betalning Post. Nu har de negativ utestĆ„ende belopp., +Refresh Google Sheet,Uppdatera Google Sheet, +Refresh Plaid Link,Uppdatera Plaid LƤnk, +Regenerate Closing Stock Balance,ƅterskapa StƤngning Lager Saldo, +Rejected Serial and Batch Bundle,Avvisad Serie och Parti Paket, +Rejected Warehouse and Accepted Warehouse cannot be same.,Avvisad lager och Accepterad lager kan inte vara samma., +Remarks Column Length,AnmƤrkningar Kolumn Bredd, +Remove Parent Row No in Items Table,Ta bort Ɩverordnad Radnummer i Artikel Tabell, +Repair,Reparera, +Repair Asset,Reparera TillgĆ„ng, +Repair Details,Reparation Detaljer, +"Replace a particular BOM in all other BOMs where it is used. It will replace the old BOM link, update cost and regenerate ""BOM Explosion Item"" table as per new BOM. +It also updates latest price in all the BOMs.","ErsƤtt stycklista i alla andra stycklistor dƤr den anvƤnds. Den kommer att ersƤtta gamla stycklista lƤnk, uppdatera kostnaden och regenerera tabell ""Stycklista Utvidgad Artikel"" enligt ny stycklista. +Den uppdaterar ocksĆ„ senaste pris i alla stycklistor.", +Report Error,Rapport Fel, +Report Filters,Rapport Sortering, +Report View,Rapport Vy, +Repost Accounting Ledger,Posta om Bokfƶring Register, +Repost Accounting Ledger Items,Posta om Bokfƶring Register Poster, +Repost Accounting Ledger Settings,Posta om Bokfƶring Register Poster InstƤllningar, +Repost Allowed Types,Posta om TillĆ„tna Typer, +Repost Error Log,Posta om Fel Logg, +Repost Item Valuation,Posta om Artikel VƤrdering, +Repost Payment Ledger,Posta om Betalning Register, +Repost Payment Ledger Items,Posta om Betalning Register Poster, +Repost has started in the background,Repost startad i bakgrund, +Repost in background,Repost i bakgrund, +Repost started in the background,Repost startad i bakgrund, +Reposting Completed {0}%,Repostering Klar {0}%, +Reposting Data File,Repostering av Data Fil, +Reposting Info,Repostering Info, +Reposting Progress,Repostering Framsteg, +Reposting entries created: {0},Repostering Poster skapade: {0}, +Reposting has been started in the background.,Repostering startad i bakgrund, +Reposting in the background.,Repostering i bakgrund, +Represents a Financial Year. All accounting entries and other major transactions are tracked against the Fiscal Year.,Representerar ett bokfƶringsĆ„r. Alla bokfƶringsposter och andra stƶrre transaktioner spĆ„ras mot bokfƶringsĆ„r., +Request Parameters,BegƤran Parametrar, +Request Timeout,BegƤran Lƶpte Ut, +Reservation Based On,Reservation Baserad PĆ„, +Reserve,Reservera, +Reserve Stock,Reservera, +"Reserved Qty ({0}) cannot be a fraction. To allow this, disable '{1}' in UOM {3}.","Reserverat Kvantitet ({0}) kan inte vara brĆ„kdel. Fƶr att tillĆ„ta detta, inaktivera '{1}' i Enhet {3}.", +Reserved Qty for Production Plan,Reserverad Kvantitet fƶr Produktion Plan, +Reserved Qty for Subcontract,Reserverad Kvantitet fƶr Underleverantƶr, +Reserved Qty should be greater than Delivered Qty.,Reserverad Kvantitet ska vara hƶgre Ƥn Levererad Kvantitet., +Reserved Serial No.,Reserverad Serie Nummer, +Reserved Stock,Reserverad, +Reserved Stock for Batch,Reserverad fƶr Parti, +Reserved for POS Transactions,Reserverad fƶr Kassa, +Reserved for Production,Reserverad fƶr Produktion, +Reserved for Production Plan,Reserverad fƶr Produktion Plan, +Reserved for Sub Contracting,Reserverad fƶr Underleverantƶr, +Reserving Stock...,Reserverar...., +Reset Company Default Values,ƅterstƤll Bolag Standard VƤrde, +Reset Plaid Link,ƅterstƤll Plaid LƤnk, +Reset Raw Materials Table,ƅterstƤll RĆ„material Tabell, +Resolution Due,Beslut om, +Response and Resolution,Svar och Lƶsning, +Restart,Starta om, +Restore Asset,ƅterstƤll TillgĆ„ng, +Restrict,BegrƤnsa, +Restrict Items Based On,BegrƤnsa Artiklar Baserat PĆ„, +Result Key,Resultat Nyckel, +Resume Job,ƅteruppta Jobb, +Retried,Fƶrsƶkte igen, +Retry,Fƶrsƶk igen, +Retry Failed Transactions,Fƶrsƶk igen med Misslyckade Transaktioner, +Return Against,Retur Mot, +Return Against Subcontracting Receipt,Retur mot Underleverantƶr Fƶljesedel, +Return Components,Returnera Komponenter, +Return Issued,Retur UtfƤrdad, +Return Qty,Retur Kvantitet, +Return Qty from Rejected Warehouse,Retur Kvantitet frĆ„n Avvisad Lager, +Return of Components,Retur av Komponenter, +Returned,Retur, +Returned Against,Returnerad Mot, +Returned Qty ,Retur Kvantitet, +Returned Qty in Stock UOM,Retur Kvantitet (per Lager Enhet), +Returned exchange rate is neither integer not float.,Returnerad vƤxelkurs Ƥr varken heltal eller flyttal., +Revaluation Journals,OmvƤrdering Journaler, +Revaluation Surplus,OmvƤrdering Ɩverskott, +Revenue,IntƤkt, +Reversal Of,ƅterfƶring Av, +Right Child,Hƶger Underordnad, +Role Allowed to Create/Edit Back-dated Transactions,Roll GodkƤnd att Skapa/Redigera Bakdaterade Transaktioner, +Role Allowed to Over Bill ,Roll GodkƤnd att Ɩverfakturera, +Role Allowed to Over Deliver/Receive,Roll GodkƤnd att Ɩver Leverera/Ta Emot, +Role Allowed to Override Stop Action,Roll GodkƤnd att ƅsidosƤtta Stopp ƅtgƤrd, +Role allowed to bypass Credit Limit,Roll GodkƤnd att ƅsidosƤtta Kredit GrƤns, +Root,Klass, +"Root Type for {0} must be one of the Asset, Liability, Income, Expense and Equity","Konto Klass fƶr {0} mĆ„ste vara en av fƶljande klasser: TillgĆ„ng, Skuld, IntƤkt, Kostnad och Eget Kapital", +Round Free Qty,Avrunda Gratis Kvantitet, +Round Off Tax Amount,Avrunda Moms Belopp, +Round Tax Amount Row-wise,Avrunda Moms Belopp per Artikelrad, +Rounding Loss Allowance,Avrundning Fƶrlust TillĆ„telse, +Rounding Loss Allowance should be between 0 and 1,Avrundning Fƶrlust TillĆ„telse ska vara mellan 0 och 1, +Rounding gain/loss Entry for Stock Transfer,Avrundning Resultat Post fƶr Lager Ɩverfƶring, +Row #,Rad #, +Row # {0}:,Rad # {0}:, +Row #{0}: A reorder entry already exists for warehouse {1} with reorder type {2}.,Rad # {0}: ƅterbestƤllning Post finns redan fƶr lager {1} med Ć„terbestƤllning typ {2}., +Row #{0}: Acceptance Criteria Formula is incorrect.,Rad # {0}: GodkƤnnande Villkor Formel Ƥr felaktig., +Row #{0}: Acceptance Criteria Formula is required.,Rad # {0}: GodkƤnnande Villkor Formel erfodras., +Row #{0}: Accepted Warehouse and Rejected Warehouse cannot be same,Rad # {0}: GodkƤnd Lager och Avvisat Lager kan inte vara samma, +Row #{0}: Accepted Warehouse is mandatory for the accepted Item {1},Rad # {0}: GodkƤnd Lager erfodras fƶr godkƤnd Artikel {1}, +Row #{0}: Allocated Amount cannot be greater than Outstanding Amount of Payment Request {1},Rad #{0}: Tilldelad belopp kan inte vara hƶgre Ƥn utestĆ„ende belopp fƶr betalning begƤran {1}, +Row #{0}: Allocated amount:{1} is greater than outstanding amount:{2} for Payment Term {3},Rad # {0}: Tilldela belopp:{1} Ƥr hƶgre Ƥn utestĆ„ende belopp:{2} fƶr Betalning Villkor {3}, +Row #{0}: Amount must be a positive number,Rad # {0}: Belopp mĆ„ste vara positiv tal, +Row #{0}: BOM is not specified for subcontracting item {0},Rad # {0}: Stycklista Ƥr inte specificerad fƶr Underleverantƶr Artikel {0}, +Row #{0}: Batch No {1} is already selected.,Rad # {0}: Parti Nummer {1} Ƥr redan vald., +Row #{0}: Cannot allocate more than {1} against payment term {2},Rad # {0}: Kan inte tilldela mer Ƥn {1} mot betalning villkor {2}, +Row #{0}: Cannot transfer more than Required Qty {1} for Item {2} against Job Card {3},Rad # {0}: Kan inte ƶverfƶra mer Ƥn Erforderlig Kvantitet {1} fƶr Artikel {2} mot Jobbkort {3}, +Row #{0}: Consumed Asset {1} cannot be Draft,Rad # {0}: Fƶrbrukad TillgĆ„ng {1} kan inte vara Utkast, +Row #{0}: Consumed Asset {1} cannot be cancelled,Rad # {0}: Fƶrbrukad tillgĆ„ng {1} kan inte annulleras, +Row #{0}: Consumed Asset {1} cannot be the same as the Target Asset,Rad # {0}: Fƶrbrukad TillgĆ„ng {1} kan inte vara samma som MĆ„l TillgĆ„ng, +Row #{0}: Consumed Asset {1} cannot be {2},Rad # {0}: Fƶrbrukad TillgĆ„ng {1} kan inte vara {2}, +Row #{0}: Consumed Asset {1} does not belong to company {2},Rad # {0}: Fƶrbrukad TillgĆ„ng {1} tillhƶr inte Bolag {2}, +Row #{0}: Cumulative threshold cannot be less than Single Transaction threshold,Rad # {0}: Kumulativ trƶskel fĆ„r inte vara lƤgre Ƥn Enskild Transaktion trƶskel, +Row #{0}: Dates overlapping with other row,Rad # {0}: Datum ƶverlappar andra rad , +Row #{0}: Default BOM not found for FG Item {1},Rad # {0}: Standard Stycklista hittades inte fƶr FƤrdig Artikel {1} , +Row #{0}: Expense Account not set for the Item {1}. {2},Rad # {0}: Kostnad Konto inte angiven fƶr Artikel {1}. {2}, +Row #{0}: Finished Good Item Qty can not be zero,Rad # {0}: FƤrdig Artikel Kvantitet kan inte vara noll, +Row #{0}: Finished Good Item is not specified for service item {1},Rad # {0}: FƤrdig Artikel Ƥr inte specificerad fƶr Service Artikel {1} , +Row #{0}: Finished Good Item {1} must be a sub-contracted item,Rad # {0}: FƤrdig Artikel {1} mĆ„ste vara Underleverantƶr Artikel , +Row #{0}: Finished Good reference is mandatory for Scrap Item {1}.,Rad # {0}: FƤrdig Artikel referens erfodras fƶr Rest Artikel {1}. , +Row #{0}: For {1} Clearance date {2} cannot be before Cheque Date {3},Rad #{0}: Fƶr {1} AvstƤmning datum {2} kan inte vara fƶre Check Datum {3}, +"Row #{0}: For {1}, you can select reference document only if account gets credited",Rad # {0}: Fƶr {1} kan du vƤlja referens dokument endast om konto krediteras, +"Row #{0}: For {1}, you can select reference document only if account gets debited",Rad # {0}: Fƶr {1} kan du vƤlja referens dokument endast om konto debiteras, +Row #{0}: From Date cannot be before To Date,Rad # {0}: FrĆ„n Datum kan inte vara fƶre Till Datum, +Row #{0}: Item {1} does not exist,Rad # {0}: Artikel {1} finns inte, +"Row #{0}: Item {1} has been picked, please reserve stock from the Pick List.","Rad # {0}: Artikel {1} Ƥr plockad, reservera lager frĆ„n Plocklista. ", +Row #{0}: Item {1} is not a service item,Rad # {0}: Artikel {1} Ƥr inte service artikel, +Row #{0}: Item {1} is not a stock item,Rad # {0}: Artikel {1} Ƥr inte service artikel, +Row #{0}: Only {1} available to reserve for the Item {2},Rad # {0}: Endast {1} tillgƤnglig att reservera fƶr artikel {2} , +Row #{0}: Please select Item Code in Assembly Items,Rad # {0}: VƤlj Artikel Kod fƶr Montering Artiklar, +Row #{0}: Please select the BOM No in Assembly Items,Rad # {0}: VƤlj Stycklista Nummer fƶr Montering Artiklar, +Row #{0}: Please select the Sub Assembly Warehouse,Rad # {0}: VƤlj Undermontering Lager, +Row #{0}: Please update deferred revenue/expense account in item row or default account in company master,Rad # {0}: Uppdatera konto fƶr uppskjutna intƤkter/kostnader i artikel rad eller standard konto i bolag, +Row #{0}: Qty increased by {1},Rad # {0}: Kvantitet ƶkade med {1}, +Row #{0}: Qty must be a positive number,Rad # {0}: Kvantitet mĆ„ste vara psitivt tal, +Row #{0}: Qty should be less than or equal to Available Qty to Reserve (Actual Qty - Reserved Qty) {1} for Iem {2} against Batch {3} in Warehouse {4}.,Rad # {0}: Kvantitet ska vara mindre Ƥn eller lika med tillgƤnglig kvantitet att reservera (verklig antal - reserverad antal) {1} fƶr artikel {2} mot parti {3} i lager {4}., +Row #{0}: Quantity to reserve for the Item {1} should be greater than 0.,Rad # {0}: Kvantitet att reservera fƶr Artikel {1} ska vara hƶgre Ƥn 0., +Row #{0}: Rate must be same as {1}: {2} ({3} / {4}),Rad #{0}: Pris mĆ„ste vara samma som {1}: {2} ({3} / {4}) , +Row #{0}: Received Qty must be equal to Accepted + Rejected Qty for Item {1},Rad #{0}: Mottaget Kvantitet mĆ„ste vara lika med GodkƤnd + Avvisad Kvantitet fƶr Artikel {1}, +Row #{0}: Rejected Qty cannot be set for Scrap Item {1}.,Rad # {0}: Avvisad Kvantitet kan inte anges fƶr Rest Artikel {1}., +Row #{0}: Rejected Warehouse is mandatory for the rejected Item {1},Rad # {0}: Avvisad Lager erfodras fƶr avvisad Artikel {1}, +Row #{0}: Scrap Item Qty cannot be zero,Rad # {0}: Rest Artikel Kvantitet kan inte vara noll, +"Row #{0}: Selling rate for item {1} is lower than its {2}. + Selling {3} should be atleast {4}.

Alternatively, + you can disable selling price validation in {5} to bypass + this validation.","Rad # {0}: FƶrsƤljning Pris fƶr artikel {1} Ƥr lƤgre Ƥn {2}. + FƶrsƤljning Pris {3} ska vara minst {4}.

Alternativt, + kan man inaktivera validering av fƶrsƤljning pris i {5} fƶr att kringgĆ„ + denna validering.", +Row #{0}: Serial No {1} for Item {2} is not available in {3} {4} or might be reserved in another {5}.,Rad # {0}: Serie Nummer {1} fƶr artikel {2} Ƥr inte tillgƤnglig i {3} {4} eller kan vara reserverad i annan {5}., +Row #{0}: Serial No {1} is already selected.,Rad # {0}: Serie Nummer {1} Ƥr redan vald., +Row #{0}: Start Time and End Time are required,Rad # {0}: FrĆ„n Tid och till Tid erfodras. , +Row #{0}: Start Time must be before End Time,Rad # {0}: FrĆ„n Tid mĆ„ste vara fƶre till Tid , +Row #{0}: Status is mandatory,Rad # {0}: Status erfodras, +Row #{0}: Stock cannot be reserved for Item {1} against a disabled Batch {2}.,Rad # {0}: Lager kan inte reserveras fƶr artikel {1} mot inaktiverad Parti {2}., +Row #{0}: Stock cannot be reserved for a non-stock Item {1},Rad # {0}: Lager kan inte reserveras fƶr artikel som inte finns i lager {1}, +Row #{0}: Stock cannot be reserved in group warehouse {1}.,Rad # {0}: Lager kan inte reserveras i Grupp Lager {1}., +Row #{0}: Stock is already reserved for the Item {1}.,Rad # {0}: Lager Ƥr redan reserverad fƶr artikel {1}., +Row #{0}: Stock is reserved for item {1} in warehouse {2}.,Rad # {0}: Lager Ƥr reserverad fƶr artikel {1} i lager {2}., +Row #{0}: Stock not available to reserve for Item {1} against Batch {2} in Warehouse {3}.,Rad # {0}: Lager Ƥr inte tillgƤnglig att reservera fƶr artikel {1} mot Parti {2} i Lager {3}., +Row #{0}: Stock not available to reserve for the Item {1} in Warehouse {2}.,Rad # {0}: Kvantitet ej tillgƤnglig fƶr reservation fƶr Artikel {1} pĆ„ {2} Lager., +Row #{0}: The warehouse {1} is not a child warehouse of a group warehouse {2},Rad # {0}: Lager {1} Ƥr inte underordnad till grupp lager {2}, +Row #{0}: You cannot use the inventory dimension '{1}' in Stock Reconciliation to modify the quantity or valuation rate. Stock reconciliation with inventory dimensions is intended solely for performing opening entries.,Rad # {0}: Man kan inte anvƤnda Lager Dimension '{1}' i Lager AvstƤmning fƶr att Ƥndra kvantitet eller Grund Pris. Lager AvstƤmning med Lager Dimensioner Ƥr endast avsedd fƶr att utfƶra ƶppningsposter., +Row #{0}: You must select an Asset for Item {1}.,Rad # {0}: Du mĆ„ste vƤlja TillgĆ„ng fƶr Artikel {1}., +Row #{0}: {1} is not a valid reading field. Please refer to the field description.,Rad #{0}: {1} Ƥr inte giltigt lƤsfƤlt. Se fƤlt beskrivning., +Row #{0}: {1} of {2} should be {3}. Please update the {1} or select a different account.,Rad # {0}: {1} av {2} ska vara {3}. Uppdatera {1} eller vƤlj ett annat konto., +Row #{1}: Warehouse is mandatory for stock Item {0},Rad # {1}: Lager erfodras fƶr lager artikel {0} , +Row #{}: Finance Book should not be empty since you're using multiple.,Rad # {}: Finans Register ska inte vara tom eftersom du anvƤnder flera., +Row #{}: Please use a different Finance Book.,Rad # {}: AnvƤnd annan Finans Register., +Row #{}: The original Invoice {} of return invoice {} is not consolidated.,Rad #{}: Ursprunglig Faktura {} fƶr Retur Faktura {} Ƥr inte konsoliderad., +Row #{}: item {} has been picked already.,Rad # {}: Artikel {} Ƥr redan plockad., +Row #{}: {} {} doesn't belong to Company {}. Please select valid {}.,Rad # {}: {} {} tillhƶr inte bolag {}. VƤlj giltig {}., +Row No {0}: Warehouse is required. Please set a Default Warehouse for Item {1} and Company {2},Rad # {0}: Lager erfodras. Ange Standard Lager fƶr Artikel {1} och Bolag {2}, +Row Number,Rad Nummer, +Row {0},Rad {0}, +"Row {0} picked quantity is less than the required quantity, additional {1} {2} required.","Rad # {0}: Plockad kvantitet Ƥr lƤgre Ƥn erfodrad kvantitet, ytterligare {1} {2} erfodras.", +Row {0}# Item {1} cannot be transferred more than {2} against {3} {4},Rad # {0}: Artikel {1} kan inte ƶverfƶras mer Ƥn {2} mot {3} {4}, +Row {0}# Item {1} not found in 'Raw Materials Supplied' table in {2} {3},"Rad # {0}: Artikel {1} hittades inte i tabellen ""RĆ„material Levererad"" i {2} {3}", +Row {0}: Accepted Qty and Rejected Qty can't be zero at the same time.,Rad # {0}: GodkƤnd Kvantitet och Avvisad Kvantitet kan inte vara noll samtidigt., +Row {0}: Account {1} and Party Type {2} have different account types,Rad # {0}: Konto {1} och Parti Typ {2} har olika konto typer, +Row {0}: Allocated amount {1} must be less than or equal to invoice outstanding amount {2},Rad # {0}: Tilldelad belopp {1} mĆ„ste vara lƤgre Ƥn eller lika med utestĆ„ende faktura belopp {2}, +Row {0}: Allocated amount {1} must be less than or equal to remaining payment amount {2},Rad # {0}: Tilldelad belopp {1} mĆ„ste vara lƤgre Ƥn eller lika med Ć„terstĆ„ende betalning belopp {2}, +"Row {0}: As {1} is enabled, raw materials cannot be added to {2} entry. Use {3} entry to consume raw materials.",Rad {0}: Eftersom {1} Ƥr aktiverat kan rĆ„material inte lƤggas till {2} post. AnvƤnd {3} post fƶr att fƶrbruka rĆ„material., +Row {0}: Both Debit and Credit values cannot be zero,Rad # {0}: BĆ„de debet och kredit vƤrdena kan inte vara noll, +Row {0}: Cost Center {1} does not belong to Company {2},Rad # {0}: Resultat Enhet {1} tillhƶr inte Bolag {2}, +Row {0}: Either Delivery Note Item or Packed Item reference is mandatory.,Rad # {0}: Antingen Fƶljesedel eller Packad Artikel Referens erfordras, +Row {0}: Expense Head changed to {1} as no Purchase Receipt is created against Item {2}.,Rad # {0}: Kostnad har Ƥndrats till {1} eftersom inget Inkƶp Fƶljesedel Ƥr skapad mot Artikel {2}., +Row {0}: Expense Head changed to {1} because account {2} is not linked to warehouse {3} or it is not the default inventory account,Rad # {0}: Kostnad har Ƥndrats till {1} eftersom konto {2} inte Ƥr lƤnkat till lager {3} eller det inte Ƥr standard konto fƶr lager, +Row {0}: Expense Head changed to {1} because expense is booked against this account in Purchase Receipt {2},Rad # {0}: Kostnad har Ƥndrats till {1} eftersom kostnad bokfƶrs mot detta konto i Inkƶp Fƶljesedel {2}, +Row {0}: From Warehouse is mandatory for internal transfers,Rad # {0}: FrĆ„n Lager erfodras fƶr interna ƶverfƶringar, +Row {0}: Item Tax template updated as per validity and rate applied,Rad # {0}: Artikel Moms Mall uppdaterad enligt giltighet och tillƤmpad moms sats, +Row {0}: Item rate has been updated as per valuation rate since its an internal stock transfer,Rad # {0}: Artikel Pris Ƥr uppdaterad enligt Grund Pris eftersom det Ƥr intern lager ƶverfƶring, +Row {0}: Item {1} must be a stock item.,Rad # {0}: Artikel {1} mĆ„ste vara lager artikel., +Row {0}: Item {1} must be a subcontracted item.,Rad # {0}: Artikel {1} mĆ„ste vara Underleverantƶr Artikel., +Row {0}: Packed Qty must be equal to {1} Qty.,Rad # {0}: Packad Kvantitet mĆ„ste vara lika med {1} Kvantitet., +Row {0}: Packing Slip is already created for Item {1}.,Rad # {0}: Packsedel Ƥr redan skapad fƶr Artikel {1}., +Row {0}: Payment Term is mandatory,Rad # {0}: Betalning Villkor Erfodras, +Row {0}: Please provide a valid Delivery Note Item or Packed Item reference.,Rad # {0}: Ange giltig referens fƶr FƶrsƤljning Fƶljesedel eller Packsedel., +Row {0}: Please select a BOM for Item {1}.,Rad # {0}: VƤlj Stycklista fƶr Artikel {1}., +Row {0}: Please select an active BOM for Item {1}.,Rad # {0}: VƤlj aktiv Stycklista fƶr Artikel {1}., +Row {0}: Please select an valid BOM for Item {1}.,Rad # {0}: VƤlj giltig Stycklista fƶr Artikel {1}, +Row {0}: Project must be same as the one set in the Timesheet: {1}.,Rad # {0}: Projekt mĆ„ste vara samma som Ƥr angiven i tidrapport: {1}., +Row {0}: Purchase Invoice {1} has no stock impact.,Rad # {0}: Inkƶp Faktura {1} har ingen efekt pĆ„ lager., +Row {0}: Qty cannot be greater than {1} for the Item {2}.,Rad # {0}: Kvantitet fĆ„r inte vara hƶgre Ƥn {1} fƶr Artikel {2}., +Row {0}: Qty in Stock UOM can not be zero.,Rad # {0}: Kvantitet i Lager Enhet kan inte vara noll., +Row {0}: Qty must be greater than 0.,Rad # {0}: Kvantitet mĆ„ste vara hƶgre Ƥn 0., +Row {0}: Shift cannot be changed since the depreciation has already been processed,Rad # {0}: Fƶrskjutning inte Ƥndras eftersom avskrivning redan Ƥr behandlad, +Row {0}: Target Warehouse is mandatory for internal transfers,Rad # {0}: Till Lager erfodras fƶr interna ƶverfƶringar, +"Row {0}: To set {1} periodicity, difference between from and to date must be greater than or equal to {2}",Rad # {0}: Fƶr att ange periodicitet fƶr {1} mĆ„ste skillnaden mellan frĆ„n och till datum vara stƶrre Ƥn eller lika med {2}, +Row {0}: Total Number of Depreciations cannot be less than or equal to Opening Number of Booked Depreciations,Rad # {0}: Totalt Antal Avskrivningar fĆ„r inte vara mindre Ƥn eller lika med antal bokfƶrda avskrivningar, +Row {0}: {1} account already applied for Accounting Dimension {2},Rad # {0}: {1} konto Ƥr redan tillƤmpad fƶr Bokfƶring Dimension {2}, +Row {0}: {1} {2} cannot be same as {3} (Party Account) {4},Rad # {0}: {1} {2} kan inte vara samma som {3} (Parti Konto) {4}, +Row {0}: {2} Item {1} does not exist in {2} {3},Rad # {0}: {2} Artikel {1} finns inte i {2} {3}, +Row({0}): Outstanding Amount cannot be greater than actual Outstanding Amount {1} in {2},Rad # ({0}): UtestĆ„ende belopp kan inte vara hƶgre Ƥn verklig utestĆ„ende belopp {1} i {2}, +Rows with Same Account heads will be merged on Ledger,Rader med samma Konto Poster kommer slĆ„s samman i Bokfƶring Register, +Rows: {0} have 'Payment Entry' as reference_type. This should not be set manually.,"Rader: {0} har ""Betalning Post"" som referens typ. Detta ska inte anges manuellt.", +Rows: {0} in {1} section are Invalid. Reference Name should point to a valid Payment Entry or Journal Entry.,Rader: {0} i sektion {1} Ƥr ogiltiga. Referens namn ska peka pĆ„ giltig Betalning Post eller Journal Post, +Run parallel job cards in a workstation,Kƶr parallella jobbkort pĆ„ arbetsplats, +Running,Behandlar, +SCO Supplied Item,Underleverantƶr Levererad Artikel, +SLA Fulfilled On,Service NivĆ„ Avtal Uppfylld , +SLA Fulfilled On Status,Service NivĆ„ Avtal Uppfylld Status, +SLA Paused On,Service NivĆ„ Avtal Pausad, +SLA will be applied if {1} is set as {2}{3},"Service NivĆ„ Avtal kommer att tillƤmpas om {1} Ƥr angiven som {2}{3} +​", +SLA will be applied on every {0},Service NivĆ„ Avtal kommer att tillƤmpas varje {0}, +SMS Settings,SMS InstƤllningar, +SO Total Qty,FƶrsƤljning Order Totalt Kvantitet, +Salary Currency,Lƶn Valuta, +Sales Incoming Rate,Inkommande FƶrsƤljning Pris, +Sales Invoice {0} must be deleted before cancelling this Sales Order,FƶrsƤljning Faktura {0} mĆ„ste annulleras innan annullering av denna FƶrsƤljning Order, +Sales Order Packed Item,FƶrsƤljning Order Packad Artikel, +Sales Order Reference,FƶrsƤljning Order Referens, +Sales Order Status,FƶrsƤljning Order Status, +"Sales Order {0} already exists against Customer's Purchase Order {1}. To allow multiple Sales Orders, Enable {2} in {3}","FƶrsƤljning Order {0} finns redan mot Kund Inkƶp Order {1}. Fƶr att tillĆ„ta flera FƶrsƤljning Ordrar, aktivera {2} i {3}", +Sales Partner ,FƶrsƤljning Partner, +Sales Partner Item,FƶrsƤljning Partner Artikel, +Sales Partner Target Variance Based On Item Group,Artikel Grupp MĆ„l Avvikelse per FƶrsƤljning Partner, +Sales Pipeline Analytics,FƶrsƤljning Analys, +Sales Update Frequency in Company and Project,FƶrsƤljning Uppdatering Intervall, +Sales Value,FƶrsƤljning VƤrde, +Salvage Value Percentage,Procentuellt Rest VƤrde, +Same item and warehouse combination already entered.,Samma artikel och lager kombination Ƥr redan angivna., +Savings,Besparingar, +Scan Batch No,Skanna Parti Nummer, +Scan Mode,Skanning LƤge, +Scan Serial No,Skanna Serie Nummer, +Scan barcode for item {0},Skanna Streckkod fƶr artikel {0}, +"Scan mode enabled, existing quantity will not be fetched.","Skanning LƤge aktiverad, befintlig kvantitet kommer inte att hƤmtas.", +Scanned Quantity,Skannad Kvantitet, +Scheduled Time Logs,Schemalagda Tidsloggar, +Scheduler is Inactive. Can't trigger job now.,SchemalƤggare Ƥr inaktiv. Kan inte starta jobb nu., +Scheduler is Inactive. Can't trigger jobs now.,SchemalƤggare Ƥr inaktiv. Kan inte starta jobb nu., +Scheduler is inactive. Cannot enqueue job.,SchemalƤggare Ƥr inaktiv. Kan inte placera jobb i kƶ., +Scheduler is inactive. Cannot merge accounts.,SchemalƤggare Ƥr inaktiv. Kan inte slĆ„ samman konton., +Scheduling,SchemalƤggning, +"Scorecard variables can be used, as well as: +{total_score} (the total score from that period), +{period_number} (the number of periods to present day) +","Resultatkort variabler kan anvƤndas, sĆ„vƤl som: +{total_score} (totalt resultat frĆ„n detta period), +{period_number} (antal intervall tills nu) +", +Scrap & Process Loss,Rest & Bearbetning Fƶrlust, +Scrap Asset,Rest TillgĆ„ng, +Scrap Cost Per Qty,Rest Kostnad per Kvantitet, +Scrap Item Code,Rest Artikel Kod, +Scrap Item Name,Rest Artikel Namn, +"Search by item code, serial number or barcode","Sƶk efter Artikel Kod, Serie Nummer eller Streck/QR Kod", +Secondary Party,SekundƤr Parti, +Secondary Role,SekundƤr Roll, +Segregate Serial / Batch Bundle,Skilj Serie / Parti Paket, +Select Accounting Dimension.,VƤlj Bokfƶring Dimension, +Select Alternative Items for Sales Order,VƤlj Alternativ Artikel fƶr FƶrsƤljning Order, +Select Batch No,VƤlj Parti Nummer, +Select Corrective Operation,VƤlj Korrigerande ƅtgƤrd, +Select Date of Birth. This will validate Employees age and prevent hiring of under-age staff.,VƤlj Fƶdelsedag. Detta kommer att validera personal Ć„lder och fƶrhindra anstƤllning av minderĆ„rig personal., +"Select Date of joining. It will have impact on the first salary calculation, Leave allocation on pro-rata bases.","VƤlj AnstƤllning Datum. Detta kommer att pĆ„verka fƶrsta lƶn, Ledighet tilldelning pĆ„ proportionell bas.", +Select Dimension,VƤlj Dimension, +Select Finished Good,VƤlj FƤrdig Artikel, +Select Items for Quality Inspection, VƤlj Artiklar fƶr Kvalitet Kontroll, +Select Job Worker Address,VƤlj Jobb Ansvarig Adress, +Select Serial No,VƤlj Serie Nummer, +Select Serial and Batch,VƤlj Serie Nummer och Parti Nummer, +Select Time,VƤlj Tid, +Select View,VƤlj Vy, +Select Vouchers to Match,VƤlj Verifikat, +Select Warehouses to get Stock for Materials Planning,VƤlj Lager fƶr att hƤmta Lager Kvantitet fƶr Material Planering, +Select a Company this Employee belongs to.,VƤlj Bolag som detta Personal tillhƶr till, +Select a Customer,VƤlj Kund, +Select an Item Group.,VƤlj Artikel Grupp, +Select an invoice to load summary data,VƤlj faktura fƶr att ladda ƶversikt data, +Select an item from each set to be used in the Sales Order.,VƤlj artikel frĆ„n varje uppsƤttning som ska anvƤndas i FƶrsƤljning Order., +Select the Default Workstation where the Operation will be performed. This will be fetched in BOMs and Work Orders.,VƤlj Standard Arbetsstation dƤr ƅtgƤrd ska utfƶras. Detta kommer att lƤggas till Stycklistor och Arbetsordrar., +Select the Item to be manufactured.,VƤlj Artikel som ska produceras., +"Select the Item to be manufactured. The Item name, UoM, Company, and Currency will be fetched automatically.","VƤlj Artikel som ska produceras. Artikel Namn, Enhet, Bolag och Valuta kommer att hƤmtas automatiskt.", +Select the Warehouse,VƤlj Lager, +Select the date and your timezone,VƤlj Datum och Tidzon, +Select the raw materials (Items) required to manufacture the Item,VƤlj RĆ„material (Artiklar) som erfodras fƶr att producera artikel, +"Select whether to get items from a Sales Order or a Material Request. For now select Sales Order. + A Production Plan can also be created manually where you can select the Items to manufacture.","VƤlj att fĆ„ artiklar frĆ„n FƶrsƤljning Order eller Material BegƤran. Fƶr TillfƤllet VƤlj FƶrsƤljning Order. + Produktion Plan kan ocksĆ„ skapas manuellt dƤr man kan vƤlja vilka artiklar som ska produceras.", +Select your weekly off day,VƤlj ledig dag i veckan, +Selected Vouchers,Valda Verifikat, +Selected date is,Vald Datum, +Selected document must be in submitted state,Vald dokument mĆ„ste ha godkƤnd status, +Self delivery,Egen Leverans, +Sell Asset,SƤlj TillgĆ„ng, +Send Attached Files,Skicka Bifogade Filer, +Send Document Print,Skicka Utskrifter, +Send Emails,Skicka E-post, +Sequential,Sekventiell, +Serial & Batch Item,Serie Nummer & Parti, +Serial & Batch Item Settings,Serie Nummer & Parti InstƤllningar, +Serial / Batch Bundle,Serie / Parti Paket, +Serial / Batch Bundle Missing,Serie / Parti Paket Saknas, +Serial / Batch No,Serie / Parti Nummer, +Serial / Batch Nos,Serie / Parti Nummer, +Serial No Ledger,Serie Nummer Register, +Serial No Range,Serienummer Intervall, +Serial No and Batch Selector cannot be use when Use Serial / Batch Fields is enabled.,Serie Nummer och Parti VƤljare kan inte anvƤndas nƤr AnvƤnd Serie Nummer / Parti FƤlt Ƥr aktiverad., +Serial No and Batch for Finished Good,Serie Nummer och Parti fƶr FƤrdig Artikel, +Serial No is mandatory,Serie Nummer erfodras, +Serial No {0} already exists,Serie Nummer {0} finns redan, +Serial No {0} already scanned,Serie Nummer {0} Ƥr redan skannad, +Serial No {0} does not exists,Serie Nummer {0} finns inte , +Serial No {0} is already added,Serie Nummer {0} har redan lagts till, +Serial Nos,Serie Nummer., +Serial Nos / Batch Nos,Serie Nummer. / Parti Nummer., +Serial Nos Mismatch,Serie Nummer stƤmmer inte, +Serial Nos are created successfully,Serie Nummer skapade, +"Serial Nos are reserved in Stock Reservation Entries, you need to unreserve them before proceeding.","Serie Nmmer Ƥr reserverade iLagerreservationsinlƤgg, du mĆ„ste avboka dem innan du fortsƤtter.", +Serial and Batch,Serie Nummer och Parti , +Serial and Batch Bundle,Serie och Parti Paket, +Serial and Batch Bundle created,Serie och Parti Paket skapad, +Serial and Batch Bundle updated,Serie och Parti Paket uppdaterad, +Serial and Batch Bundle {0} is already used in {1} {2}.,Serie och Parti Paket {0} anvƤnds redan i {1} {2}., +Serial and Batch Details,Serie och Parti Detaljer, +Serial and Batch Entry,Serie och Parti Post, +Serial and Batch No,Serie och Parti Nummer, +Serial and Batch Nos,Serie och Parti Nummer, +Serial and Batch Nos will be auto-reserved based on Pick Serial / Batch Based On,Serie och Parti Nummer kommer att reserveras automatiskt baserat pĆ„ VƤlj Serie/Parti baserat pĆ„, +Serial and Batch Reservation,Serie Nummer och Parti Reservation, +Serial and Batch Summary,Serie- och batchnummer kommer att reserveras automatiskt baserat pĆ„ VƤlj serie/batch baserat pĆ„, +Service Cost Per Qty,Service Kostnad Per Kvantitet, +Service Expense Total Amount,Service Kostnad Totalt Belopp, +Service Expenses,Service Kostnader, +Service Item,Service Artikel, +Service Item Qty,Service Artikel Kvantitet, +Service Item Qty / Finished Good Qty,Service Artikel Kvantitet / FƤrdig Artikel Kvantitet, +Service Item UOM,Service Artikel Enhet, +Service Item {0} is disabled.,Service Artikel Ƥr inaktiverad, +Service Item {0} must be a non-stock item.,Service Artikel {0} fĆ„r inte vara Lager Artikel., +Service Items,Service Artikel, +Service Level Agreement for {0} {1} already exists.,Service NivĆ„ Avtal fƶr {0} {1} finns redan., +Service Level Name,Service NivĆ„ Namn, +Service Provider,Service Leverantƶr, +Set Default Supplier,Ange Standard Leverantƶr, +Set From Warehouse,Ange FrĆ„n Lager, +Set Landed Cost Based on Purchase Invoice Rate,Ange Landad Kostnad baserat pĆ„ Inkƶp Faktura Pris, +Set Loyalty Program,Ange Bonus Program, +Set Operating Cost / Scrape Items From Sub-assemblies,Ange Driftskostnad / Rest Artiklar frĆ„n Underenheter, +Set Operating Cost Based On BOM Quantity,Ange ƅtgƤrd Kostnad baserad pĆ„ Stycklista, +Set Parent Row No in Items Table,Ange Ɩverordnad Radnummer i Artikel Tabell, +Set Process Loss Item Quantity,Ange Process Fƶrlust Artikel Kvantitet, +Set Project Status,Ange Projekt Status, +Set Quantity,Ange Kvantitet, +Set Response Time for Priority {0} in row {1}.,Ange Svarstid fƶr Prioritet {0} i rad {1}., +Set Valuation Rate Based on Source Warehouse,Ange Grund Pris Baserad pĆ„ FrĆ„n Lager, +Set Warehouse,VƤlj Lager, +Set default {0} account for non stock items,Ange Standard {0} konto fƶr Ej Lager Artiklar, +Set fieldname from which you want to fetch the data from the parent form.,Ange fƤltnamn frĆ„n vilket data ska hƤmtas frĆ„n ƶverordnad formulƤr., +Set quantity of process loss item:,Ange kvantitet fƶr Process Fƶrlust Artikel:, +Set the Planned Start Date (an Estimated Date at which you want the Production to begin),Ange Planerad Start Datum, +Set the status manually.,Ange status manuellt., +Set {0} in asset category {1} for company {2},Ange {0} i TillgĆ„ng Kategori {1} fƶr Bolag {2}, +Sets 'Accepted Warehouse' in each row of the Items table.,Ange 'GodkƤnd Lager' i varje rad i Artiklar Tabell, +Sets 'Rejected Warehouse' in each row of the Items table.,Ange 'Avvisad Lager' i varje rad i Artikel Tabell, +Sets 'Reserve Warehouse' in each row of the Supplied Items table.,Anger 'Reserv Lager' i varje rad i Levererad Artikel Tabell., +Setting Item Locations...,Anger Artikelplatser..., +Setting the account as a Company Account is necessary for Bank Reconciliation,Ange konto som Bolag Konto fƶr Bank AvstƤmmning, +Setting {} is required,Konfigurera {} erfodras, +Setup your organization,Bolag InstƤllningar, +Shelf Life in Days,HĆ„llbarhet i Dagar, +Shift,Skift, +Shift Factor,Fƶrskjutning Faktor, +Shift Name,Fƶrskjutning Namn, +Shipment,Leverans, +Shipment Amount,Leverans Belopp, +Shipment Delivery Note,Leverans FƶrsƤljning Fƶljesedel, +Shipment ID,Leverans ID, +Shipment Information,Leverans Information, +Shipment Parcel,Leverans Paket, +Shipment Parcel Template,Leverans Paket Mall, +Shipment Type,Leverans Typ, +Shipment details,Leverans Detaljer, +Shipping Address Details,Leverans Adress Detaljer, +Shipping Address Template,Leverans Adress Mall, +Show Balances in Chart Of Accounts,Visa Saldo i Kontoplan, +Show Barcode Field in Stock Transactions,Visa Streck/QR Kod FƤlt i Lager Transaktioner, +Show Dimension Wise Stock,Visa Lager per Dimension, +Show Disabled Warehouses,Visa Inaktiverade Lager, +Show Failed Logs,Visa Misslyckade Logg, +Show GL Balance,Visa Bokfƶring Register Saldo, +Show Item Name,Visa Artikel Namn, +Show Ledger View,Visa Register Vy, +Show Net Values in Party Account,Visa NettovƤrde i Parti Konto, +Show Pay Button in Purchase Order Portal,Visa Betala Knapp i Portal, +Show Preview,Fƶrhandsgranska, +Show Remarks,Visa AnmƤrkningar, +Show Taxes as Table in Print,Visa Moms Belopp som Kolumn, +Show net values in opening and closing columns,Visa NettovƤrde i Ɩppning och StƤngning kolumner, +Show only the Immediate Upcoming Term,Visa endast Omedelbart Kommande Villkor, +Show pending entries,Visa vƤntande poster, +Show with upcoming revenue/expense,Visa med kommande IntƤkter/Kostnader, +"Simple Python Expression, Example: doc.status == 'Open' and doc.issue_type == 'Bug'","Enkelt Python uttryck, exempel: doc.status == 'Open' och doc.issue_type == 'Bug'", +"Simple Python formula applied on Reading fields.
Numeric eg. 1: reading_1 > 0.2 and reading_1 < 0.5
+Numeric eg. 2: mean > 3.5 (mean of populated fields)
+Value based eg.: reading_value in (""A"", ""B"", ""C"")","Enkel Python formel tillƤmpad pƄ lƤsfƤlt.
Numerisk t.ex. 1: reading_1 > 0.2 and reading_1 < 0.5
+Numerisk t.ex. 2: mean > 3.5 (mean of populated fields)
+VƤrde baserad t.ex.: reading_value in (""A"", ""B"", ""C"")", +Simultaneous,Samtidig, +Skip Available Sub Assembly Items,Hoppa ƶver tillgƤngliga Delmontering Artiklar, +Skipped,Hoppade ƶver, +Skipping Tax Withholding Category {0} as there is no associated account set for Company {1} in it.,Hoppar ƶver Moms Undantag Kategori {0} eftersom det inte finns nĆ„got konto associerat med Bolag {1}., +"Skipping {0} of {1}, {2}","Hoppa ƶver {0} av {1}, {2}", +Sold by,FƶrsƤljare, +Something went wrong please try again,NĆ„got gick snett! Fƶrsƶk igen., +Source Exchange Rate,KƤll VƤxelkurs, +Source Fieldname,KƤll FƤltnamn, +Source Warehouse Address Link,FrĆ„n Lager Adress LƤnk, +South Africa VAT Account,Sydafrika Moms Konto, +South Africa VAT Settings,Sydafrika Moms Konto InstƤllningar, +Spacer,Mellanrum, +Split Asset,Dela TillgĆ„ng, +Split Early Payment Discount Loss into Income and Tax Loss,Dela Tidig Betalning Rabatt Bortfall i IntƤkt och Moms Bortfall, +Split From,Dela FrĆ„n, +Split Qty,Dela Kvantitet, +Split qty cannot be grater than or equal to asset qty,Delad kvantitet fĆ„r inte vara stƶrre Ƥn eller lika med tillgĆ„ng kvantitet, +Splitting {0} {1} into {2} rows as per Payment Terms,Delar {0} {1} i {2} rader enligt Betalning Villkor, +Stage,Stadie, +Stale Days should start from 1.,Inaktuella Dagar ska bƶrja frĆ„n 1., +Standard Description,Standard Beskrivning, +Standard Rated Expenses,Standard Klassade Kostnader, +"Standard Terms and Conditions that can be added to Sales and Purchases. Examples: Validity of the offer, Payment Terms, Safety and Usage, etc.","Standard Villkor som kan lƤggas till FƶrsƤljning och Inkƶp. Exempel: Erbjudande Giltighet, Betalningsvillkor, SƤkerhet,AnvƤndning, etc.", +Standard rated supplies in {0},Standard Klassade Fƶrbrukning Artiklar i {0}, +"Standard tax template that can be applied to all Purchase Transactions. This template can contain a list of tax heads and also other expense heads like ""Shipping"", ""Insurance"", ""Handling"", etc.","Standard Moms Mall som kan tillƤmpas pĆ„ alla Inkƶp Transaktioner. Mallen kan innehĆ„lla lista ƶver Moms Konto och Ƥven andra kostnad konto som ""Leverans"", ""FƶrsƤkring"", ""Hantering"" osv.", +"Standard tax template that can be applied to all Sales Transactions. This template can contain a list of tax heads and also other expense/income heads like ""Shipping"", ""Insurance"", ""Handling"" etc.","Standard Moms Mall som kan tillƤmpas pĆ„ alla FƶrsƤljning Transaktioner. Mallen kan innehĆ„lla lista ƶver Moms Konto och Ƥven andra kostnad konto som ""Leverans"", ""FƶrsƤkring"", ""Hantering"" osv.", +Start / Resume,Starta / ƅteruppta, +Start Date should be lower than End Date,Startdatum ska vara fƶre Slutdatum, +Start Deletion,Starta Borttagning, +Start Import,Starta Import, +Start Job,Starta Jobb, +Start Merge,Starta Sammanslagning, +Start Reposting,Starta Ompostning, +Start Time can't be greater than or equal to End Time for {0}.,Start Tid fĆ„r inte vara senare Ƥn eller lika med Slut Tid fƶr {0}., +Started a background job to create {1} {0},Startade bakgrundsjobb fƶr att skapa {1} {0}, +Status Details,Status Detaljer, +Status Illustration,Statusbild, +Status set to rejected as there are one or more rejected readings.,Status satt till avvisad eftersom det finns en eller flera avvisade avlƤsningar., +Stock Closing,Lager LĆ„sning, +Stock Consumed During Repair,Lager Fƶrbrukad under Reparation, +Stock Consumption Details,Lager Fƶrbrukning Detaljer, +Stock Entries already created for Work Order {0}: {1},Lager Poster redan skapade fƶr Arbetsorder {0}: {1}, +Stock Ledger Invariant Check,Lager Register OfƶrƤnderlig Kontroll, +Stock Ledger Variance,Lager Register Avvikelse, +Stock Movement,Lager Hantering, +Stock Planning,Lager Planering, +Stock Reposting Settings,Lager Ompostering InstƤllningar, +Stock Reservation,Lager Reservation, +Stock Reservation Entries Cancelled,Lager Reservation Poster Annullerade, +Stock Reservation Entries Created,Lager Reservation Poster Skapade, +Stock Reservation Entry,Lager Reservation Post, +Stock Reservation Entry cannot be updated as it has been delivered.,Lager Reservation Post kan inte uppdateras eftersom den Ƥr levererad. , +"Stock Reservation Entry created against a Pick List cannot be updated. If you need to make changes, we recommend canceling the existing entry and creating a new one.",Lager Reservation Post skapad mot Plocklista kan inte uppdateras. Om man behƶver gƶra Ƥndringar rekommenderas att man anullerar befintlig post och skapar ny. , +Stock Reservation Warehouse Mismatch,Lager Reservation fƶr Lager stƤmmer inte, +Stock Reservation can only be created against {0}.,Lager Reservation kan endast skapas mot {0}., +Stock Reserved Qty (in Stock UOM),Lager Reserverad Kvantitet (Lager Enhet), +Stock Transactions Settings,Lager Transaktion InstƤllningar, +Stock UOM Quantity,Lager Kvantitet, +Stock Unreservation,Lager Reservation Annullering, +Stock Validations,Lager Validering, +Stock and Manufacturing,Lager & Produktion, +Stock cannot be reserved in group warehouse {0}.,Lager kan inte reserveras i grupp lager {0}., +Stock cannot be reserved in the group warehouse {0}.,Lager kan inte reserveras i grupp lager {0}., +Stock not available for Item {0} in Warehouse {1}.,Lager ej tillgƤngligt fƶr Artikel {0} i Lager {1}., +Stock quantity not enough for Item Code: {0} under warehouse {1}. Available quantity {2} {3}.,Lager Kvantitet ej tillgƤnglig fƶr Artikel Kod: {0} pĆ„ lager {1}. TillgƤnglig kvantitet {2} {3}., +Stock transactions that are older than the mentioned days cannot be modified.,Lager Transaktioner som Ƥr Ƥldre Ƥn angiven antal dagar kan inte Ƥndras., +Stock will be reserved on submission of Purchase Receipt created against Material Request for Sales Order.,Lager kommer att reserveras vid godkƤnnade av Inkƶp Fƶljesedel skapat mot Material BegƤran fƶr FƶrsƤljning Order., +Stock/Accounts can not be frozen as processing of backdated entries is going on. Please try again later.,Lager/Konton kan inte lĆ„sas eftersom bearbetning av bakdaterade poster pĆ„gĆ„r. Fƶrsƶk igen senare., +Sub Assemblies & Raw Materials,Delmonteringar & RĆ„material, +Sub Assembly Item,Delmontering Artikel, +Sub Assembly Item Code,Delmontering Artikel Kod, +Sub Assembly Items,Delmontering Artiklar, +Sub Assembly Warehouse,Delmontering Lager, +Sub Operation,UnderĆ„tgƤrd, +Sub Operations,UnderĆ„tgƤrder, +Subcontract BOM,Underleverantƶr Stycklista, +Subcontract Order,Underleverantƶr Order, +Subcontract Order Summary,Underleverantƶr Order Ɩversikt, +Subcontract Return,Underleverantƶr Retur, +Subcontracting BOM,Underleverantƶr Stycklista, +Subcontracting Order,Underleverantƶr Order, +Subcontracting Order (Draft) will be auto-created on submission of Purchase Order.,Underleverantƶr Order (Utkast) kommer att skapas automatiskt vid godkƤnnande av Inkƶp Order., +Subcontracting Order Item,Underleverantƶr Order Artikel, +Subcontracting Order Service Item,Underleverantƶr Order Service Artikel, +Subcontracting Order Supplied Item,Underleverantƶr Order Levererad Artikel, +Subcontracting Order {0} created.,Underleverantƶr Order {0} skapad, +Subcontracting Purchase Order,Underleverantƶr Inkƶp Order, +Subcontracting Receipt,Underleverantƶr Fƶljesedel/Kvitto, +Subcontracting Receipt Item,Underleverantƶr Fƶljesedel/Kvitto Artikel, +Subcontracting Receipt Supplied Item,Underleverantƶr Fƶljesedel/Kvitto Levererad Artikel, +Subcontracting Settings,Underleverantƶr InstƤllningar, +Subdivision,Underavdelning, +Submit Action Failed,GodkƤnnande Misslyckades, +Submit After Import,GodkƤnn efter Import, +Submit ERR Journals?,GodkƤnn ERR Journaler ?, +Submit Generated Invoices,GodkƤnn Skapade Fakturor, +Subscription for Future dates cannot be processed.,Prenumeration fƶr framtida datum kan inte behandlas., +Succeeded,Klar, +Succeeded Entries,Klara Poster, +"Successfully changed Stock UOM, please redefine conversion factors for new UOM.","Lager Enhet Ƥndrad, Ƥndra konvertering faktor fƶr ny enhet.", +Successfully imported {0},Importerade {0}, +"Successfully imported {0} record out of {1}. Click on Export Errored Rows, fix the errors and import again.","Importerade {0} post av {1}. Klicka pĆ„ Exportera felaktiga rader, Ć„tgƤrda fel och importera igen.", +Successfully imported {0} record.,Importerade {0} post., +"Successfully imported {0} records out of {1}. Click on Export Errored Rows, fix the errors and import again.","Importerade {0} poster av {1}. Klicka pĆ„ Exportera felaktiga rader, Ć„tgƤrda fel och importera igen.", +Successfully imported {0} records.,Importerade {0} poster., +Successfully linked to Customer,LƤnkad till Kund, +Successfully linked to Supplier,LƤnkad till Leverantƶr, +Successfully merged {0} out of {1}.,Slog samman {0} av {1}., +Successfully updated {0},Uppdaterade {0}, +"Successfully updated {0} record out of {1}. Click on Export Errored Rows, fix the errors and import again.","Uppdaterade {0} post av {1}. Klicka pĆ„ Exportera felaktiga rader, Ć„tgƤrda fel och importera igen.", +Successfully updated {0} record.,Uppdaterade {0} post., +"Successfully updated {0} records out of {1}. Click on Export Errored Rows, fix the errors and import again.","Uppdaterade {0} poster av {1}. Klicka pĆ„ Exportera felaktiga rader, Ć„tgƤrda fel och importera igen.", +Successfully updated {0} records.,Uppdaterade {0} poster., +Sum of Repair Cost and Value of Consumed Stock Items.,Summa fƶr reparation kostnad och vƤrde fƶr fƶrbrukade lager artiklar., +Supplied Item,Levererad Artikel, +Supplier Address Details,Leverantƶr Adress Detaljer, +Supplier Contact,Leverantƶr Kontakt, +Supplier Group Item,Leverantƶr Grupp Artikel, +Supplier Info,Leverantƶr Info, +Supplier Invoice,Leverantƶr Faktura, +Supplier Item,Leverantƶr Artikel, +Supplier Portal Users,LeverantƶrĀ  Portal AnvƤndare, +Supplier Primary Address,Leverantƶr PrimƤr Adress, +Supplier Primary Contact,Leverantƶr PrimƤr Kontakt, +Supplier Warehouse mandatory for sub-contracted {0},Leverantƶr Lager erfodras fƶr underleverantƶr {0}, +Supplies subject to the reverse charge provision,Leveranser som omfattas av omvƤnd betalning provision, +Sync Now,Synkronisera Nu, +Sync Started,Synkronisering Startad, +System Settings,System InstƤllningar, +System will automatically create the serial numbers / batch for the Finished Good on submission of work order,System kommer automatiskt att skapa serienummer/parti fƶr fƤrdig artikel vid godkƤnnade av arbetsorder, +System will not check over billing since amount for Item {0} in {1} is zero,System kontrollerar inte ƶverfakturering eftersom belopp fƶr Artikel {0} i {1} Ƥr noll, +TCS Amount,TCS Belopp, +TCS Rate %,TDS Sats %, +TDS Amount,TDS Belopp, +TDS Payable,TDS Betalbar, +Target Asset,TillgĆ„ng, +Target Asset Location,TillgĆ„ng Plats, +Target Asset {0} cannot be cancelled,TillgĆ„ng {0} kan inte annulleras, +Target Asset {0} cannot be submitted,TillgĆ„ng {0} kan inte godkƤnnas, +Target Asset {0} cannot be {1},TillgĆ„ng {0} kan inte bli {1}, +Target Asset {0} does not belong to company {1},TillgĆ„ng {0} tillhƶr inte bolag {1}, +Target Asset {0} needs to be composite asset,TillgĆ„ng {0} mĆ„ste vara sammansatt tillgĆ„ng, +Target Batch No,Parti Nummer, +Target Exchange Rate,VƤxelkurs, +Target Fieldname (Stock Ledger Entry),FƤltnamn (Lager Register Post), +Target Fixed Asset Account,TillgĆ„ng Konto, +Target Has Batch No,Har Parti Nummer, +Target Has Serial No,Har Serie Nummer, +Target Incoming Rate,In Pris, +Target Is Fixed Asset,Ƅr TillgĆ„ng, +Target Item Code,Artikel Kod, +Target Item Name,Artikel Namn, +Target Item {0} is neither a Fixed Asset nor a Stock Item,Artikel {0} Ƥr varken TillgĆ„ng eller Lager Artikel, +Target Item {0} must be a Fixed Asset item,Artikel {0} mĆ„ste vara TillgĆ„ng, +Target Item {0} must be a Stock Item,Artikel {0} mĆ„ste vara Lager Artikel, +Target Qty must be a positive number,Kvantitet mĆ„ste vara positivt tal, +Target Serial No,Serie Nummer, +Target Warehouse Address Link,Till Lager Adress LƤnk, +Target Warehouse is mandatory for Decapitalization,Till Lager erfodras fƶr Dekapitalisering, +Target Warehouse is set for some items but the customer is not an internal customer.,Till Lager angiven fƶr vissa artiklar men kund Ƥr inte intern kund., +Task {0} depends on Task {1}. Please add Task {1} to the Tasks list.,Uppgift {0} beror pĆ„ Uppgift {1}. LƤgg till Uppgift {1} i Uppgift Lista., +Tax Amount,Momspliktig Belopp, +Tax Amount will be rounded on a row(items) level,Moms Belopp kommer att avrundas per Artikelrad, +Tax Masters,Moms InstƤllningar, +Tax Refunds provided to Tourists under the Tax Refunds for Tourists Scheme,Moms ƅterbƤring till turister enligt momsĆ„terbƤring fƶr turister, +Tax Settings,Moms InstƤllningar, +Tax Withheld Vouchers,Moms Avdrag Verifikat, +Tax Withholding,Moms Avdrag, +Tax Withholding Category {} against Company {} for Customer {} should have Cumulative Threshold value.,Moms Avdrag Kategori {} mot bolag {} fƶr kund {} ska ha Kumulativ Trƶskel VƤrde., +Tax Withholding Details,Moms Avdrag Kategori, +Tax Withholding Net Total,Moms Avdrag Netto Totalt, +"Tax detail table fetched from item master as a string and stored in this field. +Used for Taxes and Charges","Moms Tabell hƤmtad frĆ„n Artikel Tabell som strƤng och lagrad i detta fƤlt. +AnvƤnds fƶr Moms och Avgifter", +Tax will be withheld only for amount exceeding the cumulative threshold,Moms kommer att dras av bara fƶr belopp som ƶverstiger kumulativ trƶskel, +Taxes row #{0}: {1} cannot be smaller than {2},Momsrad #{0}: {1} kan inte vara lƤgre Ƥn {2}, +Telephony Call Type,Telefoni Typ, +Template Item Selected,Mall Artikel Vald, +Template Options,Mall Alternativ, +Template Task,Mall Upgift, +Template Warnings,Mall Varningar, +Terms & Conditions,Regler & Villkor, +Terms Template,Villkor Mall, +Territory Item,Distrikt Artikel, +Territory Wise Sales,FƶrsƤljning per Distrikt, +The Condition '{0}' is invalid,Villkor '{0}' Ƥr ogiltig, +The Document Type {0} must have a Status field to configure Service Level Agreement,Dokument Typ {0} mĆ„ste ha StatusfƤlt fƶr att konfigurera Service NivĆ„ Avtal, +"The GL Entries will be cancelled in the background, it can take a few minutes.","Bokfƶring Register Poster kommer att annulleras i bakgrunden, det kan ta nĆ„gra minuter.", +"The Payment Request {0} is already paid, cannot process payment twice","Betalning BegƤran {0} Ƥr redan betald, kan inte behandla betalning tvĆ„ gĆ„nger", +"The Pick List having Stock Reservation Entries cannot be updated. If you need to make changes, we recommend canceling the existing Stock Reservation Entries before updating the Pick List.",Plocklista med Lager Reservation kan inte uppdateras. Om Ƥndringar behƶver gƶras rekommenderas annullering av befintlig Lager Reservation innan uppdatering av Plocklista., +The Process Loss Qty has reset as per job cards Process Loss Qty,Process Fƶrlust Kvantitet Ƥr Ć„terstƤlld enligt Jobbkort Process Fƶrlust Kvantitet, +The Serial No at Row #{0}: {1} is not available in warehouse {2}.,Serie Nummer pĆ„ rad #{0}: {1} Ƥr inte tillgƤnglig i lager {2}., +The Serial and Batch Bundle {0} is not valid for this transaction. The 'Type of Transaction' should be 'Outward' instead of 'Inward' in Serial and Batch Bundle {0},"Serie och Parti Paket {0} Ƥr inte giltigt fƶr denna transaktion. ""Typ av Transaktion"" ska vara ""UtgĆ„ende"" istƤllet fƶr ""IngĆ„ende"" i Serie och Parti Paket {0}", +The Work Order is mandatory for Disassembly Order,Arbetsorder erfordras fƶr Demontering Order, +The allocated amount is greater than the outstanding amount of Payment Request {0},Tilldelad Belopp Ƥr hƶgre Ƥn utestĆ„ende belopp fƶr Betalning BegƤran {0}, +The currency of invoice {} ({}) is different from the currency of this dunning ({}).,Faktura valuta {} ({}) Ƥr annan Ƥn valuta fƶr denna pĆ„minnelse ({})., +The default BOM for that item will be fetched by the system. You can also change the BOM.,Standard Stycklista fƶr artikel kommer att hƤmtas av system. Man kan ocksĆ„ Ƥndra Stycklista., +The field {0} in row {1} is not set,FƤlt {0} i rad {1} Ƥr inte angiven, +"The following Items, having Putaway Rules, could not be accomodated:","Fƶljande Artiklar, med LƤgg undan regler, kunde inte tillgodoses:", +The following assets have failed to automatically post depreciation entries: {0},Fƶljande tillgĆ„ngar kunde inte bokfƶra avskrivning poster automatiskt: {0}, +The following invalid Pricing Rules are deleted:,Fƶljande ogiltiga prissƤttningsregler tas bort:, +The items {0} and {1} are present in the following {2} :,Artiklar {0} och {1} finns i fƶljande {2}:, +The operation {0} can not add multiple times,ƅtgƤrd {0} kan inte lƤggas till flera gĆ„nger, +The operation {0} can not be the sub operation,ƅtgƤrd {0} kan inte vara underĆ„tgƤrd, +The original invoice should be consolidated before or along with the return invoice.,Original Faktura ska konsolideras fƶre eller tillsammans med retur faktura., +The percentage you are allowed to pick more items in the pick list than the ordered quantity.,Procentandel artiklar du fĆ„r plocka utƶver artiklar i plocklista., +"The percentage you are allowed to transfer more against the quantity ordered. For example, if you have ordered 100 units, and your Allowance is 10%, then you are allowed transfer 110 units.","Procentandel man fĆ„r ƶverfƶra mer mot order kvantitet. Till exempel, om man har order pĆ„ 100 enheter och tillĆ„telse Ƥr 10%, fĆ„r man ƶverfƶra upp till 110 enheter.", +The reserved stock will be released when you update items. Are you certain you wish to proceed?,Lager Reservation kommer att slƤppas nƤr artiklar uppdaterats. FortsƤtt?, +The reserved stock will be released. Are you certain you wish to proceed?,Lager Reservation kommer att slƤppas. FortsƤtt?, +The selected {0} does not contain the selected Asset Item.,Vald {0} innehĆ„ller inte vald TillgĆ„ng Post., +"The stock for the item {0} in the {1} warehouse was negative on the {2}. You should create a positive entry {3} before the date {4} and time {5} to post the correct valuation rate. For more details, please read the documentation.","Lager fƶr artikel {0} i {1} lager var negativt {2}. Skapa positiv post {3} fƶre {4} och {5} fƶr att bokfƶra rƤtt grund pris. Fƶr mer information, lƤs dokumentation .", +"The stock has been reserved for the following Items and Warehouses, un-reserve the same to {0} the Stock Reconciliation:

{1}","Lager Ƥr reserverad fƶr fƶljande Artiklar och Lager, ta bort reservation till {0} Lager Inventering :

{1}", +"The sync has started in the background, please check the {0} list for new records.",Synkronisering startad i bakgrunden. Kolla {0} lista fƶr nya poster., +The task has been enqueued as a background job.,Uppgift Ƥr i kƶ som bakgrund jobb., +"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",Uppgift Ƥr i kƶ som ett bakgrund jobb. Om det finns nĆ„gra problem med bearbetning i bakgrund kommer system att lƤgga till kommentar om fel pĆ„ denna Lager Inventering och Ć„tergĆ„ till GodkƤnd status, +The total Issue / Transfer quantity {0} in Material Request {1} cannot be greater than allowed requested quantity {2} for Item {3},Totalt UtfƤrdad / Ɩverfƶring Kvantitet {0} i Material BegƤran {1} kan inte vara hƶgre Ƥn tillĆ„ten begƤrd kvantitet {2} fƶr artikel {3}, +The total Issue / Transfer quantity {0} in Material Request {1} cannot be greater than requested quantity {2} for Item {3},Totalt UtfƤrdad / Ɩverfƶring Kvantitet {0} i Material BegƤran {1} kan inte vara hƶgre Ƥn begƤrd kvantitet {2} fƶr artikel {3}, +"The users with this Role are allowed to create/modify a stock transaction, even though the transaction is frozen.","AnvƤndare med denna roll fĆ„r skapa/Ƥndra lager transaktion, Ƥven om transaktion Ƥr lĆ„st.", +The warehouse where you store finished Items before they are shipped.,Lager dƤr fƤrdiga artiklar lagras innan de levereras., +"The warehouse where you store your raw materials. Each required item can have a separate source warehouse. Group warehouse also can be selected as source warehouse. On submission of the Work Order, the raw materials will be reserved in these warehouses for production usage.",Lager dƤr rĆ„material lagras. Varje erfodrad artikel kan ha separat frĆ„n lager. Grupp lager kan ocksĆ„ vƤljas som frĆ„n lager. Vid godkƤnnade av arbetsorder kommer rĆ„material att reserveras i dessa lager fƶr produktion., +The warehouse where your Items will be transferred when you begin production. Group Warehouse can also be selected as a Work in Progress warehouse.,Lager dƤr artiklar kommer att ƶverfƶras nƤr produktion pĆ„bƶrjas. Grupp Lager kan ocksĆ„ vƤljas som Arbete PĆ„gĆ„r lager., +The {0} {1} is used to calculate the valuation cost for the finished good {2}.,{0} {1} anvƤnds fƶr att berƤkna grund kostnad fƶr fƤrdig artikel {2}., +There are ledger entries against this account. Changing {0} to non-{1} in live system will cause incorrect output in 'Accounts {2}' report,"Det finns bokfƶring register aposter mot detta konto. Om du Ƥndrar {0} till icke-{1} i system kommer det att orsaka felaktig utdata i ""Konton {2}"" rapporten ", +There are no Failed transactions,Det finns inga misslyckade transaktioner, +There are no active Fiscal Years for which Demo Data can be generated.,Det finns inga aktiva bokfƶringsĆ„r fƶr vilka demo data kan genereras., +There are no slots available on this date,Det finns inga lediga tider fƶr detta datum, +There are only {0} asset created or linked to {1}. Please create or link {2} Assets with respective document.,Det finns bara {0} tillgĆ„ngar skapade eller lƤnkade till {1}. Skapa eller lƤnka {2} TillgĆ„ngar med respektive dokument., +"There are two options to maintain valuation of stock. FIFO (first in - first out) and Moving Average. To understand this topic in detail please visit
Item Valuation, FIFO and Moving Average.","Det finns tvĆ„ alternativ fƶr att upprƤtthĆ„lla Lager vƤrdering. FIFO (fƶrst in - fƶrst ut) och MedelvƤrde. Fƶr att fƶrstĆ„ detta Ƥmne i detalj besƶk ArtikelvƤrdering, FIFO och MedelvƤrde.", +There aren't any item variants for the selected item,Det finns inga artikelvarianter fƶr vald artikel, +There is already a valid Lower Deduction Certificate {0} for Supplier {1} against category {2} for this time period.,Det finns redan giltigt LƤgre Avdrag Certifikat {0} fƶr Leverantƶr {1} mot kategori {2} fƶr denna tidsperiod., +There is already an active Subcontracting BOM {0} for the Finished Good {1}.,Det finns redan aktiv Underleverantƶr Stycklista {0} fƶr fƤrdig artikel {1}., +There must be atleast 1 Finished Good in this Stock Entry,Det mĆ„ste finnas minst en fƤrdig artikel i denna Lager Post, +There was an error creating Bank Account while linking with Plaid.,Det uppstod fel nƤr Bank Konto skulle skapas vid lƤnkning med Plaid., +There was an error syncing transactions.,Det uppstod fel med synkronisering av transaktioner., +There was an error updating Bank Account {} while linking with Plaid.,Det uppstod fel nƤr Bank Konto {} skulle uppdateras vid lƤnkning med Plaid., +There was an issue connecting to Plaid's authentication server. Check browser console for more information,Det uppstod fel vid anslutning till Plaid autentisering server. Kontrollera webblƤsare konsol fƶr mer information, +There were issues unlinking payment entry {0}.,Det uppstod fel med borttagning av lƤnk till Betalning Post {0}., +This Account has '0' balance in either Base Currency or Account Currency,"Konto har ""0"" Saldo i antingen Standard Valuta eller Konto Valuta", +This field is used to set the 'Customer'.,Detta fƤlt anvƤnds fƶr att ange 'Kund'., +This filter will be applied to Journal Entry.,Detta filter kommer att tillƤmpas pĆ„ Journal Post, +This is a Template BOM and will be used to make the work order for {0} of the item {1},Detta Ƥr Stycklista Mall och kommer att anvƤndas fƶr att skapa arbetsorder fƶr {0} av artikel {1}, +This is considered dangerous from accounting point of view.,Detta anses vara farligt ur bokfƶring synpunkt., +"This is enabled by default. If you want to plan materials for sub-assemblies of the Item you're manufacturing leave this enabled. If you plan and manufacture the sub-assemblies separately, you can disable this checkbox.","Detta Ƥr aktiverat som standard. Planeras material fƶr underenheter fƶr artikel som produceras, lƤmna detta aktiverat. Planeras och produceras underenheterna separat kan den inaktiveras.", +"This is for raw material Items that'll be used to create finished goods. If the Item is an additional service like 'washing' that'll be used in the BOM, keep this unchecked.","Detta Ƥr fƶr rĆ„material artiklar som kommer att anvƤndas fƶr att skapa fƤrdiga artiklar. Om artikel Ƥr tillƤgg service som ""tvƤtt"" som kommer att anvƤndas i stycklista, lĆ„t den vara inaktiverad", +This item filter has already been applied for the {0},Detta artikel filter har redan tillƤmpats fƶr {0}, +This option can be checked to edit the 'Posting Date' and 'Posting Time' fields.,Detta alternativ kan vƤljas fƶr att redigera fƤlt 'Post Datum' och 'Post Tid'., +This schedule was created when Asset {0} was adjusted through Asset Value Adjustment {1}.,Detta schema skapades nƤr TillgĆ„ng {0} justerades genom TillgĆ„ng VƤrde Justering {1}., +This schedule was created when Asset {0} was consumed through Asset Capitalization {1}.,Detta schema skapades nƤr TillgĆ„ng {0} fƶrbrukades genom TillgĆ„ng Kapitalisering {1}., +This schedule was created when Asset {0} was repaired through Asset Repair {1}.,Detta schema skapades nƤr TillgĆ„ng {0} reparerades genom TillgĆ„ng Reparation {1}., +This schedule was created when Asset {0} was restored on Asset Capitalization {1}'s cancellation.,Detta schema skapades nƤr TillgĆ„ng {0} Ć„terstƤlldes vid annullering av TillgĆ„ng Kapitalisering {1}., +This schedule was created when Asset {0} was restored.,Detta schema skapades nƤr TillgĆ„ng {0} Ć„terstƤlldes., +This schedule was created when Asset {0} was returned through Sales Invoice {1}.,Detta schema skapades nƤr TillgĆ„ng {0} returnerades via FƶrsƤljning Faktura {1}., +This schedule was created when Asset {0} was scrapped.,Detta schema skapades nƤr TillgĆ„ng {0} skrevs av., +This schedule was created when Asset {0} was sold through Sales Invoice {1}.,Detta schema skapades nƤr TillgĆ„ng {0} sĆ„ldes via FƶrsƤljning Faktura {1}., +This schedule was created when Asset {0} was updated after being split into new Asset {1}.,Detta schema skapades nƤr TillgĆ„ng {0} uppdaterades efter att ha delats upp i ny TillgĆ„ng {1}., +This schedule was created when Asset {0}'s Asset Repair {1} was cancelled.,Detta schema skapades nƤr TillgĆ„ng Reparation {0} TillgĆ„ng Reparation {1} annullerades., +This schedule was created when Asset {0}'s Asset Value Adjustment {1} was cancelled.,Detta schema skapades nƤr TillgĆ„ng {0} TillgĆ„ng VƤrde Justering {1} annullerades., +This schedule was created when Asset {0}'s shifts were adjusted through Asset Shift Allocation {1}.,Detta schema skapades nƤr TillgĆ„ng {0} fƶrskjutning justerades genom TillgĆ„ng Fƶrskjutning Tilldelning {1}., +This schedule was created when new Asset {0} was split from Asset {1}.,Detta schema skapades nƤr ny TillgĆ„ng {0} delades frĆ„n TillgĆ„ng {1}., +"This table is used to set details about the 'Item', 'Qty', 'Basic Rate', etc.","Denna tabell anvƤnds fƶr att ange detaljer om 'Artikel', 'Kvantitet', 'Bas Pris', etc.", +This {} will be treated as material transfer.,Denna {} kommer att behandlas som material ƶverfƶring., +Threshold for Suggestion (In Percentage),Trƶskel fƶr Fƶrslag (%), +Time Taken to Deliver,Tid som tagits att Leverera, +Time in mins,Tid i minuter, +Time in mins.,Tid i minuter, +Time slot is not available,Tid Ƥr inte tillgƤnglig, +To Date is mandatory,Till Datum Erfordras, +To Delivery Date,Till Leverans Datum, +To Doctype,Till DocType, +To Due Date,Till Fƶrfallo Datum, +To Payment Date,Till Betalning Datum, +To Reference Date,Till Referens Datum, +To add Operations tick the 'With Operations' checkbox.,Att lƤgga till ƅtgƤrder kryssa i rutan 'Med ƅtgƤrder'., +To add subcontracted Item's raw materials if include exploded items is disabled.,Att lƤgga till Underleverantƶr Artikel rĆ„material om Inkludera Utvidgade Artiklar Ƥr inaktiverad., +To apply condition on parent field use parent.field_name and to apply condition on child table use doc.field_name. Here field_name could be based on the actual column name of the respective field.,"Att tillƤmpa villkor pĆ„ ƶverordnad fƤlt, anvƤnd parent.field_name och fƶr att tillƤmpa villkor pĆ„ underordnad tabell anvƤnd doc.field_name. HƤr kan fƤltnamn baseras pĆ„ verklig kolumn namn fƶr respektive fƤlt.", +To be Delivered to Customer,Levereras till Kund, +To cancel a {} you need to cancel the POS Closing Entry {}.,Att annullera {} mĆ„ste du annullera Kassa StƤngning Post {}., +"To enable Capital Work in Progress Accounting,",Att aktivera PĆ„gĆ„ende Kapitalarbete Bokfƶring, +To include non-stock items in the material request planning. i.e. Items for which 'Maintain Stock' checkbox is unticked.,Att inkludera ej lagerartiklar i material begƤran planering. dvs Artiklar fƶr vilka ruta 'Lager Hantera' Ƥr inaktiverad., +To submit the invoice without purchase order please set {0} as {1} in {2},"Att godkƤnna faktura utan inkƶp order, ange {0} som {1} i {2}", +To submit the invoice without purchase receipt please set {0} as {1} in {2},Att godkƤnna faktura utan inkƶp fƶljesedel ange {0} som {1} i {2}, +"To use a different finance book, please uncheck 'Include Default FB Assets'","Att anvƤnda annan finans register, inaktivera ""Inkludera Standard Finans Register TillgĆ„ngar""", +"To use a different finance book, please uncheck 'Include Default FB Entries'","Att anvƤnda annan finans register, inaktivera ""Inkludera Standard Finans Register TillgĆ„ngar""", +Total Active Items,Totalt Aktiva Artiklar, +Total Allocations,Totala Tilldelningar, +Total Asset,Totalt TillgĆ„ng, +Total Asset Cost,Totalt TillgĆ„ng Kostnad, +Total Billing Hours,Totalt Fakturerbara Timmar, +Total Contribution Amount Against Invoices: {0},Totalt Bidrag Belopp Mot Fakturor: {0}, +Total Contribution Amount Against Orders: {0},Totalt Bidrag Belopp Mot Ordrar: {0}, +Total Equity,Totalt Eget Kapital, +Total Incoming Bills,Inkommande Fakturor, +Total Incoming Payment,Inkommande Betalningar, +Total Incoming Value (Receipt),Totalt IngĆ„ende VƤrde (Faktura), +Total Interest,Totalt RƤnta, +Total Issues,Totalt FrĆ„gor, +Total Items,Totalt Artiklar, +Total Liability,Totalt Skuld, +Total Number of Booked Depreciations ,Totalt Antal Bokfƶrda Avskrivningar , +Total Operation Time,Totalt Drift Tid, +Total Other Charges,Totalt Ɩvriga Avgifter, +Total Outgoing Bills,UtgĆ„ende Fakturor, +Total Outgoing Payment,UtgĆ„ende Betalningar, +Total Outgoing Value (Consumption),UtgĆ„ende VƤrde (Fƶrbrukning), +Total Picked Quantity {0} is more than ordered qty {1}. You can set the Over Picking Allowance in Stock Settings.,Totalt plockad kvantitet {0} Ƥr mer Ƥn order kvantitet {1}. Du kan ange ƶverplock tillĆ„telse i Lager InstƤllningar., +Total Purchase Amount,Totalt Inkƶp Belopp, +Total Purchase Cost has been updated,Totalt Inkƶp Kostnad uppdaterad, +Total Repair Cost,Totalt Reparation Kostnad, +Total Reposting Count,Totalt Ompostering Antal, +Total Sales Amount,Totalt FƶrsƤljning Belopp, +Total Stock Value,Totalt Lager VƤrde, +Total Supplied Qty,Totalt Levererad Kvantitet, +Total Time (in Mins),Totalt Tid i Minuter, +Total Value,Totalt VƤrde, +Total Value Difference (Incoming - Outgoing),VƤrde Differens (Inkommande - UtgĆ„ende), +Total Views,Totalt Visningar, +Total Warehouses,Totalt Antal Lager, +Total percentage against cost centers should be 100,Totalt procentsats mot resultat enhet ska vara 100%, +Tracking Status,SpĆ„rning Status, +Tracking Status Info,SpĆ„rning Status Info, +Tracking URL,SpĆ„rning URL, +Transaction Deletion Document: {0} is running for this Company. {1},Transaktion Borttagning Dokument: {0} kƶrs redan fƶr {1}, +Transaction Deletion Record,Transaktion Borttagning Post, +Transaction Deletion Record Details,Transaktion Borttagning Post Detaljer, +Transaction Deletion Record Item,Transaktion Borttagning Post Artikel, +Transaction Exchange Rate,Transaktion VƤxelkurs, +Transaction Settings,Transaktion InstƤllningar, +Transaction currency: {0} cannot be different from Bank Account({1}) currency: {2},Transaktion valuta: {0} kan inte skilja sig frĆ„n Bank Konto ({1}) valuta: {2}, +Transactions against the Company already exist! Chart of Accounts can only be imported for a Company with no transactions.,Transaktioner mot bolag finns redan! Kontoplan kan endast importeras fƶr bolag utan transaktioner., +Transfer Asset,Ɩverfƶr TillgĆ„ng, +Transfer From Warehouses,Ɩverfƶring FrĆ„n Lager, +Transferring cannot be done to an Employee. Please enter location where Asset {0} has to be transferred,Ɩverfƶring kan inte skapas fƶr Personal. Ange plats dƤr TillgĆ„ng {0} ska ƶverfƶras, +Transit Entry,Transit Post, +Truncates 'Remarks' column to set character length,"Trunkerar kolumn ""AnmƤrkningar"" fƶr att ange teckenlƤngd", +Type Of Call,Typ av Samtal, +Type of Transaction,Typ av Transaktion, +UAE VAT Account,UAE VAT Konto, +UAE VAT Accounts,UAE VAT Konton, +UAE VAT Settings,UAE VAT InstƤllningar, +UOM conversion factor required for UOM: {0} in Item: {1},Enhet Konvertering Faktor erfodras fƶr Enhet: {0} fƶr Artikel: {1}, +UnReconcile,ƅngra, +Unable to find the time slot in the next {0} days for the operation {1}. Please increase the 'Capacity Planning For (Days)' in the {2}.,"Kunde inte att hitta tider under de kommande {0} dagarna fƶr Ć„tgƤrd {1}. Ɩka ""Kapacitet Planering fƶr (Dagar)"" i {2}.", +Unable to find variable:,Kan inte hitta variabel:, +Unassigned Qty,Ej Tilldelat Kvantitet, +"Under Working Hours table, you can add start and end times for a Workstation. For example, a Workstation may be active from 9 am to 1 pm, then 2 pm to 5 pm. You can also specify the working hours based on shifts. While scheduling a Work Order, the system will check for the availability of the Workstation based on the working hours specified.","Under Arbetstid tabell kan man lƤgga till start och slut tider fƶr arbetsstation. Till exempel kan arbetsstation vara aktiv frĆ„n 9.00 till 12.00, sedan 1300 till 17.00. Du kan Ƥven ange arbetstid utifrĆ„n skift. Under schemalƤggning av arbetsorder kommer system att kontrollera tillgƤnglighet fƶr arbetsstation baserat pĆ„ angiven arbetstid.", +Unit of Measure (UOM),Enhet, +Unlinked,Bortkopplad, +Unqualified,Okvalificerad, +Unrealized Profit / Loss Account,Orealiserad Resultat Konto, +Unrealized Profit / Loss account for intra-company transfers,Orealiserad Resultat konto fƶr koncern ƶverfƶringar, +Unrealized Profit/Loss account for intra-company transfers,Orealiserad Resultat konto fƶr koncern ƶverfƶringar, +Unreconcile Payment,ƅngra Betalning AvstƤmning, +Unreconcile Payment Entries,ƅngra Betalning AvstƤmning Post, +Unreconcile Transaction,ƅngra Transaktion, +Unreconciled Amount,ƅngrad Betalning AvstƤmning Belopp, +Unreconciled Entries,ƅngrad Betalning AvstƤmning Post, +Unreserve,ƅngra Reservation, +Unreserve Stock,ƅngra Lager Reservation, +Unreserving Stock...,ƅngrar Lager Reservation ..., +Unset Matched Payment Request,ƅngra AvstƤmd Betalning BegƤran, +Up,Upp, +Update Billed Amount in Delivery Note,Uppdatera Fakturerad Belopp i FƶrsƤljning Fƶljesedel, +Update Billed Amount in Purchase Order,Uppdatera Fakturerad Belopp i Inkƶp Order, +Update Billed Amount in Purchase Receipt,Uppdatera Fakturerad Belopp i Inkƶp Fƶljesedel, +Update Existing Price List Rate,Uppdatera Befintlig Prislista Pris, +Update Existing Records,Uppdatera Befintliga Poster, +Update Outstanding for Self,Uppdatera UtestĆ„ende fƶr sig sjƤlv, +Update Rate as per Last Purchase,Uppdatera Pris per Senaste Inkƶp, +Update Total Purchase Cost,Uppdatera Total Inkƶp Kostnad, +Update frequency of Project,Projekt Uppdatering Intervall, +Update stock must be enabled for the purchase invoice {0},Uppdatera Lager mĆ„ste vara aktiverat fƶr Inkƶp Faktura {0}, +Updated via 'Time Log' (In Minutes),Uppdaterad via 'Tid Logg' (i Minuter), +Updating Work Order status,Uppdaterar Arbetsorder status, +"Updating {0} of {1}, {2}","Uppdaterar {0} av {1}, {2}", +Upload Bank Statement,Importera Bank AvstƤmning, +Use 'Repost in background' button to trigger background job. Job can only be triggered when document is in Queued or Failed status.,"AnvƤnd knapp ""Posta om i Backgrund"" fƶr att utlƶsa bakgrund jobb. Jobb kan bara utlƶsas nƤr dokument har status 'I Kƶ' eller 'Misslyckad'.", +Use Batch-wise Valuation,AnvƤnd VƤrdering per Parti, +Use Company Default Round Off Cost Center,AnvƤnd Bolag Standard Avrundning Resultat Enhet, +Use Company default Cost Center for Round off,AnvƤnd Bolag standard Resultat Enhet fƶr Avrundning, +Use HTTP Protocol,AnvƤnd HTTP Protokoll, +Use Item based reposting,AnvƤnd Artikel baserad Ompostering , +Use Serial / Batch Fields,AnvƤnd Serie / Parti Nummer FƤlt, +Use Serial No / Batch Fields,AnvƤnd Serie / Parti Nummer FƤlt, +Use Transaction Date Exchange Rate,AnvƤnd Transaktion Datum VƤxelkurs, +User {0}: Removed Employee Self Service role as there is no mapped employee.,AnvƤndare {0}: Borttagen Personal SjƤlvbetjƤning roll eftersom det inte finns nĆ„gon mappad personal., +User {0}: Removed Employee role as there is no mapped employee.,AnvƤndare {0}: Borttagen Personal roll eftersom det inte finns mappad personal., +Users can enable the checkbox If they want to adjust the incoming rate (set using purchase receipt) based on the purchase invoice rate.,AnvƤndare kan kryssa i rut Om de vill justera inkƶp pris (anges med inkƶp fƶljesedel) baserat pĆ„ inkƶp faktura pris., +Users with this role are allowed to over bill above the allowance percentage,AnvƤndare med denna roll tillĆ„ts att ƶverfakturera ƶver tillĆ„ten procentsats, +Users with this role are allowed to over deliver/receive against orders above the allowance percentage,AnvƤndare med denna roll tillĆ„ts att ƶverleverera/ta emot ordrar ƶver tillĆ„ten procentsats, +Using negative stock disables FIFO/Moving average valuation when inventory is negative.,AnvƤndning av negativ lager inaktiverar FIFO/MA vƤrdering sƤtt nƤr lager vƤrde Ƥr negativ., +VAT Accounts,Moms Konton, +VAT Amount (AED),Moms Belopp (AED), +VAT Audit Report,Moms Revision Rapport, +VAT on Expenses and All Other Inputs,Moms pĆ„ Utgifter och Alla Andra intƤkter, +VAT on Sales and All Other Outputs,Moms pĆ„ FƶrsƤljning och Alla Andra utgifter, +Valid From must be after {0} as last GL Entry against the cost center {1} posted on this date,Giltig FrĆ„n Datum mĆ„ste vara efter {0} eftersom senaste Bokfƶring Register Post mot resultat enhet {1} postad detta datum, +Validate Components Quantities Per BOM,Validera Komponent Kvantiteter per Stycklista, +Validate Negative Stock,Validera Negativ Lager, +Validate Pricing Rule,Validera PrissƤttning Regel, +Validate Stock on Save,Validera Lager pĆ„ Spara, +Valuation Field Type,VƤrdering FƤlt Typ, +Valuation Rate (In / Out),Grund Pris (In/Ut), +Valuation rate for customer provided items has been set to zero.,Grund Pris fƶr Kund Fƶrsedda Artiklar angavs till noll., +Valuation rate for the item as per Sales Invoice (Only for Internal Transfers),Grund Pris fƶr artikel enligt FƶrsƤljning Faktura (endast fƶr Interna Ɩverfƶringar), +Value Based Inspection,VƤrde Baserad Kontroll, +Value Change,VƤrde FƶrƤndring, +Value Details,VƤrde Detaljer, +Value of Goods,Gods VƤrde, +Value of goods cannot be 0,GodsvƤrde kan inte vara 0, +Verification failed please check the link,"Verifiering misslyckades, kontrollera lƤnk", +Via Landed Cost Voucher,Genom Landad Kostnad Verifikat, +View BOM Update Log,Visa Stycklista Uppdatering Logg, +View Exchange Gain/Loss Journals,Visa VƤxelkurs Resultat Journaler, +View General Ledger,Visa Bokfƶring Register, +View Ledgers,Visa Register, +Visits,Besƶk, +Voice Call Settings,Rƶst Samtal InstƤllningar, +Voucher,Verifikat, +Voucher Name,Verifikat Namn, +Voucher No is mandatory,Verifikat Nummer Erfodras, +Voucher Qty,Verifikat Kvantitet, +Voucher Subtype,Verifikat Undertyp, +Voucher {0} is over-allocated by {1},Verifikat {0} Ƥr ƶvertilldelad av {1}, +Voucher {0} value is broken: {1},Verifikat {0} vƤrde bruten: {1}, +Voucher-wise Balance,Saldo per Verifikat, +"WARNING: Exotel app has been separated from ERPNext, please install the app to continue using Exotel integration.","OBS: Exotel app har separerats frĆ„n System, installera app fƶr att fortsƤtta anvƤnda Exotelintegration.", +WIP Composite Asset,PĆ„gĆ„ende Arbete Sammansatt TillgĆ„ng, +Waiting for payment...,VƤntar pĆ„ betalning..., +Warehouse Capacity for Item '{0}' must be greater than the existing stock level of {1} {2}.,Lager Kapacitet fƶr Artikel '{0}' mĆ„ste vara hƶgre Ƥn befintlig lager nivĆ„ pĆ„ {1} {2}. , +Warehouse Details,Lager Detaljer, +Warehouse Disabled?,Lager Inaktiverad?, +Warehouse Settings,Lager InstƤllningar, +Warehouse Wise Stock Balance,Lager Saldo per Lager, +Warehouse wise Stock Value,Lager VƤrde per Lager, +Warehouse {0} does not belong to Company {1}.,Lager {0} tillhƶr inte Bolag {1}., +"Warehouse {0} is not linked to any account, please mention the account in the warehouse record or set default inventory account in company {1}.",Lager {0} Ƥr inte lƤnkad till nĆ„got konto. Ange konto i lager post eller ange standard konto fƶr lager i bolag {1}., +Warehouse's Stock Value has already been booked in the following accounts:,Lagrets Lager VƤrde Ƥr redan bokfƶrd pĆ„ fƶljande konton:, +Warning - Row {0}: Billing Hours are more than Actual Hours,Varning - Rad # {0}: Fakturerbara timmar Ƥr fler Ƥn verkliga timmar, +Warning on Negative Stock,Varna vid Negativt Lager, +Warning!,Varning!, +Watch Video,Visa Video, +"We can see {0} is made against {1}. If you want {1}'s outstanding to be updated, uncheck '{2}' checkbox.

Or you can use {3} tool to reconcile against {1} later.","Vi kan se att {0} skapades mot {1}. Om du vill att {1}s utestƄende ska uppdateras, avmarkera '{2}'kryssruta .

Eller sĆ„ kan du anvƤnda {3} verktyg fƶr att stƤmma av mot {1} senare.", +Website Script,Webbplats Skript, +Website Theme,Webbplats Tema, +Week {0} {1},Vecka {0} {1}, +Weekly Time to send,Veckotid att skicka, +Weight (kg),Vikt (kg), +"When a parent warehouse is chosen, the system conducts stock checks against the associated child warehouses",NƤrƶverordnat lager valts utfƶr system lagerkontroller mot tillhƶrande underordnade lager, +"When creating an Item, entering a value for this field will automatically create an Item Price at the backend.","Vid skapande av Artikel, om man anger vƤrde fƶr detta fƤlt, skapas automatiskt artikelpris i bakgrund.", +"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.","Vid skapande avĀ Inkƶp Faktura frĆ„n Inkƶp Order, anvƤnd Inkƶp Faktura transaktion datum fƶrĀ vƤxelkurs istƤllet fƶr att Ƥrva den frĆ„n Inkƶp Order. GƤller endast Inkƶp Faktura.", +Width (cm),Bredd (cm), +With Period Closing Entry For Opening Balances,Med PeriodStƤngning Post fƶr Ɩppning Saldon, +Withdrawal,Uttag, +Work Order / Subcontract PO,Arbetsorder / Underleverantƶr Inkƶp Order, +Work Order Consumed Materials,Arbetsorder Fƶrbrukat Material, +Workflow,Arbetsflƶde, +Workflow Action,Arbetsflƶde ƅtgƤrd, +Workflow State,Arbetsflƶde TillstĆ„nd, +Workstation Dashboard,Arbetsplats Ɩversikt Panel, +Workstation Status,Arbetsplats Status, +Workstation Type,Arbetsplats Typ, +Workstations,Arbetsplatser, +Write Off Limit,Avskrivning GrƤns, +Wrong Company,Fel Bolag, +Wrong Template,Fel Mall, +You are not authorized to make/edit Stock Transactions for Item {0} under warehouse {1} before this time.,Du Ƥr inte behƶrig att skapa/redigera lager transaktioner fƶr artikel {0} under lager {1} fƶre denna tidpunkt., +You are picking more than required quantity for the item {0}. Check if there is any other pick list created for the sales order {1}.,Du vƤljer mer Ƥn vad som krƤvs fƶr artikel {0}. Kontrollera om det finns nĆ„gon annan plocklista skapad fƶr fƶrsƤljning order {1}., +You can add the original invoice {} manually to proceed.,LƤgg till original faktura {} manuellt fƶr att fortsƤtta., +"You can set it as a machine name or operation type. For example, stiching machine 12",Du kan ange den som maskin namn eller Ć„tgƤrd typ. Till exempel sy maskin 12, +You can't make any changes to Job Card since Work Order is closed.,Du kan inte gƶra nĆ„gra Ƥndringar i Jobbkort eftersom Arbetsorder Ƥr stƤngd., +You can't redeem Loyalty Points having more value than the Rounded Total.,Det gĆ„r inte att lƶsa in Lojalitet PoƤng som har mer vƤrde Ƥn Avrundad Belopp., +You cannot change the rate if BOM is mentioned against any Item.,Du kan inte Ƥndra pris om Stycklista Ƥr angiven mot nĆ„gon artikel., +You cannot create a {0} within the closed Accounting Period {1},Du kan inte skapa {0} inom stƤngd bokfƶring period {1}, +You cannot create/amend any accounting entries till this date.,Du kan inte skapa/Ƥndra nĆ„gra bokfƶring poster fram till detta datum., +You cannot repost item valuation before {},Du kan inte lƤgga om artikel vƤrdering fƶre {}, +You have entered a duplicate Delivery Note on Row,Du har angett dubblett FƶrsƤljning Fƶljesedel pĆ„ Rad, +You haven't created a {0} yet,Ingen {0} skapad Ƥn, +You need to cancel POS Closing Entry {} to be able to cancel this document.,Annullera Kassa StƤngning Post {} fƶr att annullera detta dokument., +Your Name (required),Ditt Namn, +Your email has been verified and your appointment has been scheduled,Din E-post Ƥr verifierad och ditt mƶte Ƥr bokad, +Zero Balance,Noll Saldo, +Zero Rated,Noll Pris, +Zero quantity,Noll Kvantitet, +`Allow Negative rates for Items`,"""TillĆ„t Negativa Priser fƶr Artiklar"".", +as a percentage of finished item quantity,som procentsats av fƤrdig artikel kvantitet, +at,kl., +dated {0},daterad {0}, +description,Beskrivning, +discount applied,Rabatt TillƤmpad, +fieldname,FƤlt Namn , +is already,Ƥr redan, +must be between 0 and 100,mĆ„ste vara mellan 0 och 100, +or its descendants,eller dess underordnad, +out of 5,av 5 mƶjliga, +payments app is not installed. Please install it from {0} or {1},payment app Ƥr inte installerad. Installera det frĆ„n {0} eller {1}, +payments app is not installed. Please install it from {} or {},payment app Ƥr inte installerad. Installera det frĆ„n {0} eller {1}, +performing either one below:,utfƶr nĆ„gon av dem nedan:, +product bundle item row's name in sales order. Also indicates that picked item is to be used for a product bundle,Artikel paket artikel rad namn i fƶrsƤljning order. Indikerar ocksĆ„ att plockad artikel ska anvƤndas fƶr artikel paket, +quotation_item,FƶrsƤljning Offert Artikel, +ratings,Bedƶmningar, +subscription is already cancelled.,prenumeration Ƥr redan annullerad., +to unallocate the amount of this Return Invoice before cancelling it.,att ta bort belopp fƶr denna Retur Faktura innan annullering., +variance,avvikelse, +via BOM Update Tool,via ErsƤtt Stycklista Verktyg, +will be,kommer vara, +{0} {1} has submitted Assets. Remove Item {2} from table to continue.,{0} {1} har godkƤnt tillgĆ„ngar. Ta bort Artikel {2} frĆ„n tabell fƶr att fortsƤtta., +{0} Account not found against Customer {1}.,{0} Konto hittades inte mot Kund {1}., +{0} Account: {1} ({2}) must be in either customer billing currency: {3} or Company default currency: {4},{0} Konto: {1} ({2}) mĆ„ste vara antingen i kundens faktura valuta: {3} eller bolag standard valuta: {4}, +{0} Budget for Account {1} against {2} {3} is {4}. It {5} exceed by {6},{0} Budget fƶr Konto {1} mot {2} {3} Ƥr {4}. Det {5} ƶverstiger med {6}, +{0} Transaction(s) Reconciled,{0} Transaktion(er) AvstƤmda, +{0} account is not of type {1},{0} konto Ƥr inte av typ {1}, +{0} account not found while submitting purchase receipt,{0} konto hittades inte nƤr vid godkƤnande av Inkƶp Fƶljesedel, +{0} and {1},{0} och {1}, +{0} cannot be used as a Main Cost Center because it has been used as child in Cost Center Allocation {1},{0} kan inte anvƤndas som Ɩverordnad Resultat Enhet eftersom det har anvƤnts som underordnad i Resultat Enhet Tilldelning {1}, +{0} cannot be zero,{0} kan inte vara noll, +{0} currency must be same as company's default currency. Please select another account.,{0} valuta mĆ„ste vara samma som bolag standard valuta. VƤlj ett annat konto., +{0} entered twice {1} in Item Taxes,{0} angiven tvĆ„ gĆ„nger {1} under Artikel Moms, +{0} has Payment Term based allocation enabled. Select a Payment Term for Row #{1} in Payment References section,{0} har Betalning Villkor baserad tilldelning aktiverad. VƤlj Betalning Villkor fƶr Rad #{1} i Betalning Referenser, +{0} is a mandatory Accounting Dimension.
Please set a value for {0} in Accounting Dimensions section.,{0} Ƥr erfordrad Bokfƶring Dimension.
Ange vƤrde fƶr {0} Bokfƶring Dimensioner., +{0} is added multiple times on rows: {1},{0} lƤggs till flera gĆ„nger pĆ„ rader: {1}, +{0} is already running for {1}, {0} kƶrs redan fƶr {1}, +{0} is mandatory for account {1},{0} Ƥr erfodrad fƶr konto {1}, +{0} is not running. Cannot trigger events for this Document,{0} kƶrs inte. Kan inte utlƶsa hƤndelser fƶr detta Dokument, +{0} qty of Item {1} is being received into Warehouse {2} with capacity {3}.,{0} kvantitet av artikel {1} tas emot i Lager {2} med kapacitet {3}., +"{0} units are reserved for Item {1} in Warehouse {2}, please un-reserve the same to {3} the Stock Reconciliation.","{0} enheter Ƥr reserverade fƶr Artikel {1} i Lager {2}, ta bort reservation fƶr {3} Lager Inventering.", +{0} units of Item {1} is not available in any of the warehouses.,{0} enheter av Artikel {1} Ƥr inte tillgƤngliga pĆ„ Lager., +{0} units of Item {1} is picked in another Pick List.,{0} enheter av Artikel {1} Ƥr vald i en annan Plocklista., +"{0} units of {1} are required in {2}{3}, on {4} {5} for {6} to complete the transaction.","{0} enheter av {1} erfordras i {2}{3}, den {4} {5} fƶr att {6} ska kunna slutfƶra transaktion.", +{0} units of {1} needed in {2} on {3} {4} to complete this transaction.,{0} enheter av {1} behƶvs i {2} den {3} {4} fƶr att slutfƶra denna transaktion., +{0} will be given as discount.,{0} kommer att ges som rabatt., +{0} {1} Manually,{0} {1} Manuellt, +{0} {1} Partially Reconciled,{0} {1} Delvis AvstƤmd, +"{0} {1} cannot be updated. If you need to make changes, we recommend canceling the existing entry and creating a new one.",{0} {1} kan inte uppdateras. Om du behƶver gƶra Ƥndringar rekommenderar vi att du annullerar befintlig post och skapar ny., +{0} {1} has already been fully paid.,{0} {1} Ƥr redan betalad till fullo., +{0} {1} has already been partly paid. Please use the 'Get Outstanding Invoice' or the 'Get Outstanding Orders' button to get the latest outstanding amounts.,"{0} {1} Ƥr redan delvis betald. AnvƤnd knapp ""HƤmta UtestĆ„ende Faktura"" eller ""HƤmta UtestĆ„ende Ordrar"" knapp fƶr att hƤmta senaste utestĆ„ende belopp.", +{0} {1} is allocated twice in this Bank Transaction,{0} {1} Ƥr tilldelad tvĆ„ gĆ„nger i denna Bank Transaktion, +{0} {1} is not in any active Fiscal Year,{0} {1} Ƥr inte under nĆ„got aktivt BokfƶringsĆ„r, +{0} {1} is on hold,{0} {1} Ƥr parkerad, +{0} {1} not allowed to be reposted. Modify {2} to enable reposting.,{0} {1} fĆ„r inte lƤggas upp igen. Ƅndra {2} fƶr att aktivera omlƤggning., +{0} {1} via CSV File,{0} {1} via CSV fil, +{0} {1}: Account {2} is a Group Account and group accounts cannot be used in transactions,{0} {1}: Konto {2} Ƥr ett grupp konto och grupp konton kan inte anvƤndas i transaktioner, +{0} {1}: Cost Center is required for 'Profit and Loss' account {2}.,"{0} {1}: Resultat Enhet erfordras fƶr ""Resultat"" konto {2}.", +{0} {1}: Cost Center {2} is a group cost center and group cost centers cannot be used in transactions,{0} {1}: Resultat Enhet {2} Ƥr grupp resultat enhet och grupp resultat enhet kan inte anvƤndas i transaktioner, +{0}'s {1} cannot be after {2}'s Expected End Date.,{0}s {1} kan inte vara efter fƶrvƤntad slut datum fƶr {2}, +{item_name}'s Sample Size ({sample_size}) cannot be greater than the Accepted Quantity ({accepted_quantity}),{item_name} Prov Kvantitet ({sample_size}) kan inte vara hƶgre Ƥn accepterad kvantitete ({accepted_quantity}), +{} Available,{} TillgƤngliga, +{} To Deliver,{} Att Leverera, +{} To Receive,{} Att Ta Emot, +{} Assigned,{} Tilldelade, +{} Available,{} TillgƤngliga, +{} Open,{} Ɩppen, +{} Pending,{}PĆ„gĆ„ende, +{} To Bill,{} Att Fakturera, +{} is a child company.,{} Ƥr dotter bolag., +{} {} is already linked with another {},{} {} Ƥr redan lƤnkad till annan {}, +{} {} is already linked with {} {},{} {} Ƥr redan lƤnkad till {} {}, From 19db526fddf1661c8c68571f00b111e4c90ca476 Mon Sep 17 00:00:00 2001 From: ljain112 Date: Fri, 25 Oct 2024 17:11:27 +0530 Subject: [PATCH 407/734] refactor: query for expense_account moved to setup hook in purchase invoice (cherry picked from commit a9ac0cc2238d420c518fdcbf69e8ed1defac7f2e) --- .../doctype/purchase_invoice/purchase_invoice.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index c78db5b86d6..2bb02f2e041 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -31,6 +31,13 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying. }, }; }); + + this.frm.set_query("expense_account", "items", function () { + return { + query: "erpnext.controllers.queries.get_expense_account", + filters: { company: doc.company }, + }; + }); } onload() { @@ -506,13 +513,6 @@ cur_frm.fields_dict["select_print_heading"].get_query = function (doc, cdt, cdn) }; }; -cur_frm.set_query("expense_account", "items", function (doc) { - return { - query: "erpnext.controllers.queries.get_expense_account", - filters: { company: doc.company }, - }; -}); - cur_frm.set_query("wip_composite_asset", "items", function () { return { filters: { is_composite_asset: 1, docstatus: 0 }, From 9a5604c5bb4f2c8d99fa59bdfb9bc16565aa6ffa Mon Sep 17 00:00:00 2001 From: ljain112 Date: Tue, 15 Oct 2024 18:38:42 +0530 Subject: [PATCH 408/734] fix: do not check for payment terms details for return invoices. (cherry picked from commit 6703b7d1aea159d02c4eae6f45e486b27138e29c) --- .../accounts/report/accounts_receivable/accounts_receivable.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index 8d4a8579ae3..984d77dbad2 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -522,7 +522,8 @@ class ReceivablePayableReport: from `tab{row.voucher_type}` si, `tabPayment Schedule` ps where si.name = ps.parent and - si.name = %s + si.name = %s and + si.is_return = 0 order by ps.paid_amount desc, due_date """, row.voucher_no, From a826a894f4d9ef2be1c59c4e97f0ceb475b569b4 Mon Sep 17 00:00:00 2001 From: ljain112 Date: Fri, 25 Oct 2024 18:09:01 +0530 Subject: [PATCH 409/734] fix: do not set payment terms for return invoices (cherry picked from commit 8b700eadc746698c02573f5af209a962d32be5d6) --- .../accounts/doctype/purchase_invoice/purchase_invoice.js | 4 +++- erpnext/accounts/doctype/sales_invoice/sales_invoice.js | 3 +++ erpnext/controllers/accounts_controller.py | 7 ++++++- erpnext/public/js/controllers/transaction.js | 2 +- 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index 2bb02f2e041..be429689b5a 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -342,7 +342,9 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying. party_type: "Supplier", account: this.frm.doc.credit_to, price_list: this.frm.doc.buying_price_list, - fetch_payment_terms_template: cint(!this.frm.doc.ignore_default_payment_terms_template), + fetch_payment_terms_template: cint( + (this.frm.doc.is_return == 0) & !this.frm.doc.ignore_default_payment_terms_template + ), }, function () { me.apply_pricing_rule(); diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 87b71cd793f..5e248c8b235 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -339,6 +339,9 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends ( account: this.frm.doc.debit_to, price_list: this.frm.doc.selling_price_list, pos_profile: pos_profile, + fetch_payment_terms_template: cint( + (this.frm.doc.is_return == 0) & !this.frm.doc.ignore_default_payment_terms_template + ), }, function () { me.apply_pricing_rule(); diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 188cbf0587c..e6fb5d0d153 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -451,6 +451,11 @@ class AccountsController(TransactionBase): ) def validate_invoice_documents_schedule(self): + if self.is_return: + self.payment_terms_template = "" + self.payment_schedule = [] + return + self.validate_payment_schedule_dates() self.set_due_date() self.set_payment_schedule() @@ -465,7 +470,7 @@ class AccountsController(TransactionBase): self.validate_payment_schedule_amount() def validate_all_documents_schedule(self): - if self.doctype in ("Sales Invoice", "Purchase Invoice") and not self.is_return: + if self.doctype in ("Sales Invoice", "Purchase Invoice"): self.validate_invoice_documents_schedule() elif self.doctype in ("Quotation", "Purchase Order", "Sales Order"): self.validate_non_invoice_documents_schedule() diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 92e9e559ada..f9b78a3b8bc 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -2438,7 +2438,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe payment_terms_template() { var me = this; const doc = this.frm.doc; - if(doc.payment_terms_template && doc.doctype !== 'Delivery Note') { + if(doc.payment_terms_template && doc.doctype !== 'Delivery Note' && doc.is_return == 0) { var posting_date = doc.posting_date || doc.transaction_date; frappe.call({ method: "erpnext.controllers.accounts_controller.get_payment_terms", From 29aa5d64683d8af8864996c4f09214c17b58905d Mon Sep 17 00:00:00 2001 From: ljain112 Date: Fri, 25 Oct 2024 18:14:07 +0530 Subject: [PATCH 410/734] fix: hide payment terms for return and paid purchase invoices (cherry picked from commit 912e1e3f3d3a3e3d8b15fee9dfa1dda7c1922e37) # Conflicts: # erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json --- .../accounts/doctype/purchase_invoice/purchase_invoice.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index c2ee4166ed1..b6205af0fdf 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -1134,12 +1134,14 @@ "label": "Payment Terms" }, { + "depends_on": "eval:(!doc.is_paid && !doc.is_return)", "fieldname": "payment_terms_template", "fieldtype": "Link", "label": "Payment Terms Template", "options": "Payment Terms Template" }, { + "depends_on": "eval:(!doc.is_paid && !doc.is_return)", "fieldname": "payment_schedule", "fieldtype": "Table", "label": "Payment Schedule", @@ -1631,7 +1633,11 @@ "idx": 204, "is_submittable": 1, "links": [], +<<<<<<< HEAD "modified": "2024-09-11 12:59:19.130593", +======= + "modified": "2024-10-25 18:13:01.944477", +>>>>>>> 912e1e3f3d (fix: hide payment terms for return and paid purchase invoices) "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice", From 27108874ea6969734286d1636762c129f5f7d10e Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 28 Oct 2024 11:50:55 +0530 Subject: [PATCH 411/734] chore: replace whitespace with tab --- .../payment_request/payment_request.py | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index 5a206ee433c..7bbf422375a 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -300,19 +300,19 @@ class PaymentRequest(Document): if hasattr(controller, "validate_minimum_transaction_amount"): controller.validate_minimum_transaction_amount(self.currency, self.grand_total) - return controller.get_payment_url( - **{ - "amount": flt(self.grand_total, self.precision("grand_total")), - "title": data.company, - "description": self.subject, - "reference_doctype": "Payment Request", - "reference_docname": self.name, - "payer_email": self.email_to or frappe.session.user, - "payer_name": data.customer_name, - "order_id": self.name, - "currency": self.currency, - } - ) + return controller.get_payment_url( + **{ + "amount": flt(self.grand_total, self.precision("grand_total")), + "title": data.company, + "description": self.subject, + "reference_doctype": "Payment Request", + "reference_docname": self.name, + "payer_email": self.email_to or frappe.session.user, + "payer_name": data.customer_name, + "order_id": self.name, + "currency": self.currency, + } + ) def set_as_paid(self): if self.payment_channel == "Phone": From 303ae8321b271aef96f5de61d7bc0ff8b3ee6d80 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 28 Oct 2024 13:33:24 +0530 Subject: [PATCH 412/734] chore: resolve conflict --- .../accounts/doctype/purchase_invoice/purchase_invoice.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index b6205af0fdf..6b21ec5b678 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -1633,11 +1633,7 @@ "idx": 204, "is_submittable": 1, "links": [], -<<<<<<< HEAD - "modified": "2024-09-11 12:59:19.130593", -======= "modified": "2024-10-25 18:13:01.944477", ->>>>>>> 912e1e3f3d (fix: hide payment terms for return and paid purchase invoices) "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice", From 935f2e11e859002548c26061319ab29678ef85ed Mon Sep 17 00:00:00 2001 From: RAVIBHARATHI P C <131471282+ravibharathi656@users.noreply.github.com> Date: Wed, 23 Oct 2024 14:27:13 +0530 Subject: [PATCH 413/734] feat: add party name in payment request (cherry picked from commit 0acb609d97286128f5521506e08e6e899f7feff3) # Conflicts: # erpnext/accounts/doctype/payment_request/payment_request.py --- .../doctype/payment_request/payment_request.json | 11 +++++++++-- .../doctype/payment_request/payment_request.py | 14 ++++++++++++++ erpnext/public/js/controllers/transaction.js | 3 ++- 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/payment_request/payment_request.json b/erpnext/accounts/doctype/payment_request/payment_request.json index b7af8412810..2eef429cd3a 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.json +++ b/erpnext/accounts/doctype/payment_request/payment_request.json @@ -14,6 +14,7 @@ "party_details", "party_type", "party", + "party_name", "column_break_4", "reference_doctype", "reference_name", @@ -422,13 +423,19 @@ "label": "Party Account Currency", "options": "Currency", "read_only": 1 + }, + { + "fieldname": "party_name", + "fieldtype": "Data", + "label": "Party Name", + "read_only": 1 } ], "in_create": 1, "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2024-09-16 17:50:54.440090", + "modified": "2024-10-23 12:23:40.117336", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Request", @@ -467,4 +474,4 @@ "sort_field": "modified", "sort_order": "DESC", "states": [] -} +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index 7bbf422375a..e479b3c7a0c 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -71,6 +71,7 @@ class PaymentRequest(Document): outstanding_amount: DF.Currency party: DF.DynamicLink | None party_account_currency: DF.Link | None + party_name: DF.Data | None party_type: DF.Link | None payment_account: DF.ReadOnly | None payment_channel: DF.Literal["", "Email", "Phone", "Other"] @@ -609,6 +610,19 @@ def make_payment_request(**args): "party_type": party_type, "party": args.get("party") or ref_doc.get("customer"), "bank_account": bank_account, +<<<<<<< HEAD +======= + "party_name": args.get("party_name") or ref_doc.get("customer_name"), + "make_sales_invoice": ( + args.make_sales_invoice # new standard + or args.order_type == "Shopping Cart" # compat for webshop app + ), + "mute_email": ( + args.mute_email # new standard + or args.order_type == "Shopping Cart" # compat for webshop app + or gateway_account.get("payment_channel", "Email") != "Email" + ), +>>>>>>> 0acb609d97 (feat: add party name in payment request) } ) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index f9b78a3b8bc..adb72818fe8 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -375,7 +375,8 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe recipient_id: me.frm.doc.contact_email, payment_request_type: payment_request_type, party_type: payment_request_type == 'Outward' ? "Supplier" : "Customer", - party: payment_request_type == 'Outward' ? me.frm.doc.supplier : me.frm.doc.customer + party: payment_request_type == 'Outward' ? me.frm.doc.supplier : me.frm.doc.customer, + party_name:payment_request_type == 'Outward' ? me.frm.doc.supplier_name : me.frm.doc.customer_name }, callback: function(r) { if(!r.exc){ From a937e32989ab7dfddd31a99335ab2e232def3746 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 29 Oct 2024 11:55:50 +0530 Subject: [PATCH 414/734] chore: resolve conflict --- .../doctype/payment_request/payment_request.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index e479b3c7a0c..2d4a64c317b 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -610,19 +610,7 @@ def make_payment_request(**args): "party_type": party_type, "party": args.get("party") or ref_doc.get("customer"), "bank_account": bank_account, -<<<<<<< HEAD -======= "party_name": args.get("party_name") or ref_doc.get("customer_name"), - "make_sales_invoice": ( - args.make_sales_invoice # new standard - or args.order_type == "Shopping Cart" # compat for webshop app - ), - "mute_email": ( - args.mute_email # new standard - or args.order_type == "Shopping Cart" # compat for webshop app - or gateway_account.get("payment_channel", "Email") != "Email" - ), ->>>>>>> 0acb609d97 (feat: add party name in payment request) } ) From 8bfc212e262654ee9397fc435e2f08ccd286a520 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 29 Oct 2024 12:08:24 +0530 Subject: [PATCH 415/734] =?UTF-8?q?fix:=20use=20period=20closing=20voucher?= =?UTF-8?q?=20object=20to=20call=20get=5Faccount=5Fclosing=5Fba=E2=80=A6?= =?UTF-8?q?=20(backport=20#43880)=20(#43883)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix: use period closing voucher object to call get_account_closing_ba… (#43880) fix: use period closing voucher object to call get_account_closing_balances method (cherry picked from commit 99d1c5f34221d3f450aa74825339209c7595bcb9) Co-authored-by: Venkatesh <47534423+venkat102@users.noreply.github.com> --- erpnext/patches/v14_0/update_closing_balances.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/patches/v14_0/update_closing_balances.py b/erpnext/patches/v14_0/update_closing_balances.py index a2670717ee9..73204b0d6c4 100644 --- a/erpnext/patches/v14_0/update_closing_balances.py +++ b/erpnext/patches/v14_0/update_closing_balances.py @@ -24,8 +24,9 @@ def execute(): for pcv in get_period_closing_vouchers(company): company_wise_order.setdefault(pcv.company, []) if pcv.period_end_date not in company_wise_order[pcv.company]: - pcv.pl_accounts_reverse_gle = get_pcv_gl_entries(pcv, gle_fields) - closing_entries = pcv.get_account_closing_balances() + pcv_doc = frappe.get_doc("Period Closing Voucher", pcv.name) + pcv_doc.pl_accounts_reverse_gle = get_pcv_gl_entries(pcv, gle_fields) + closing_entries = pcv_doc.get_account_closing_balances() if closing_entries: make_closing_entries(closing_entries, pcv.name, pcv.company, pcv.period_end_date) From c70282663c69955d49ce41c772056cfe02bae975 Mon Sep 17 00:00:00 2001 From: ljain112 Date: Wed, 23 Oct 2024 18:13:42 +0530 Subject: [PATCH 416/734] fix: scrub "-" from fieldname in accounting dimension (cherry picked from commit 023b7b9a606483de52caf21bb9dad9f1d18c65bd) --- .../doctype/accounting_dimension/accounting_dimension.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js index 4e45dede1d5..6f4f9f8d782 100644 --- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js +++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js @@ -58,7 +58,7 @@ frappe.ui.form.on("Accounting Dimension", { }, label: function (frm) { - frm.set_value("fieldname", frappe.model.scrub(frm.doc.label)); + frm.set_value("fieldname", frm.doc.label.replace(/ /g, "_").replace(/-/g, "_").toLowerCase()); }, document_type: function (frm) { From b21abf4d9056496f016c9aedf0182bb8dff7688d Mon Sep 17 00:00:00 2001 From: ljain112 Date: Wed, 23 Oct 2024 18:14:30 +0530 Subject: [PATCH 417/734] fix: validate fieldname (cherry picked from commit ca31a19eb7da1fa7fb9295911662fb55c14754a2) --- .../doctype/accounting_dimension/accounting_dimension.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py index db99bcd223b..8fc22dd7650 100644 --- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py +++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py @@ -7,6 +7,7 @@ import json import frappe from frappe import _, scrub from frappe.custom.doctype.custom_field.custom_field import create_custom_field +from frappe.database.schema import validate_column_name from frappe.model import core_doctypes_list from frappe.model.document import Document from frappe.utils import cstr @@ -60,6 +61,7 @@ class AccountingDimension(Document): if not self.is_new(): self.validate_document_type_change() + validate_column_name(self.fieldname) self.validate_dimension_defaults() def validate_document_type_change(self): From 13557a2c1035c849dc76157691446641f3c288ce Mon Sep 17 00:00:00 2001 From: "lukas.brandhoff" Date: Wed, 16 Oct 2024 13:24:57 +0000 Subject: [PATCH 418/734] refactor: include 'Project Name' in Project summary report Keep name field for backwards compatibility (cherry picked from commit 736d1a1105009c272c6b2ba3cec6b6741c39b62d) --- .../projects/report/project_summary/project_summary.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/erpnext/projects/report/project_summary/project_summary.py b/erpnext/projects/report/project_summary/project_summary.py index 7a35fd236a0..261517f02c6 100644 --- a/erpnext/projects/report/project_summary/project_summary.py +++ b/erpnext/projects/report/project_summary/project_summary.py @@ -15,6 +15,7 @@ def execute(filters=None): filters=filters, fields=[ "name", + "project_name", "status", "percent_complete", "expected_start_date", @@ -48,6 +49,11 @@ def get_columns(): "options": "Project", "width": 200, }, + { + "fieldname": "project_name", + "label": _("Project Name"), + "width": 200, + }, { "fieldname": "project_type", "label": _("Type"), @@ -82,7 +88,7 @@ def get_chart_data(data): overdue = [] for project in data: - labels.append(project.name) + labels.append(project.project_name) total.append(project.total_tasks) completed.append(project.completed_tasks) overdue.append(project.overdue_tasks) From 603d2cf77dd8ca6bae612dc9b406a37416c3fb72 Mon Sep 17 00:00:00 2001 From: Imesha Sudasingha Date: Mon, 28 Oct 2024 03:43:56 +0000 Subject: [PATCH 419/734] fix: add parenttype clause to invoice tax query in sales_register report (cherry picked from commit e30ab141f4bc51b1378eb80d1bf3885453afbcfe) --- erpnext/accounts/report/sales_register/sales_register.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/report/sales_register/sales_register.py b/erpnext/accounts/report/sales_register/sales_register.py index 6c0bf91e3f8..34d53238f50 100644 --- a/erpnext/accounts/report/sales_register/sales_register.py +++ b/erpnext/accounts/report/sales_register/sales_register.py @@ -526,7 +526,8 @@ def get_invoice_tax_map(invoice_list, invoice_income_map, income_accounts, inclu tax_details = frappe.db.sql( """select parent, account_head, sum(base_tax_amount_after_discount_amount) as tax_amount - from `tabSales Taxes and Charges` where parent in (%s) group by parent, account_head""" + from `tabSales Taxes and Charges` where parent in (%s) and parenttype = 'Sales Invoice' + group by parent, account_head""" % ", ".join(["%s"] * len(invoice_list)), tuple(inv.name for inv in invoice_list), as_dict=1, From d06831ea9404852bcf730b6b17de6059ff5b1227 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 29 Oct 2024 15:48:40 +0530 Subject: [PATCH 420/734] fix: do not copy serial numbers from DN to SI (backport #43885) (#43893) fix: do not copy serial numbers from DN to SI (#43885) (cherry picked from commit 0c93bc31a5f3fdecfb2f9e053341eb8b90ce1851) Co-authored-by: rohitwaghchaure --- erpnext/stock/doctype/delivery_note/delivery_note.py | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index cb006bb3e99..68eb07d3a77 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -1020,7 +1020,6 @@ def make_sales_invoice(source_name, target_doc=None, args=None): "parent": "delivery_note", "so_detail": "so_detail", "against_sales_order": "sales_order", - "serial_no": "serial_no", "cost_center": "cost_center", }, "postprocess": update_item, From e22d0a3406932956598ac4e7024cdee68bf713d8 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 29 Oct 2024 17:25:40 +0530 Subject: [PATCH 421/734] fix: post account closing balance against pcv closing account (backport #43887) (#43898) fix: post account closing balance against pcv closing account (#43887) (cherry picked from commit 34295d0344573a296d5fb76e6cc2c819a93354b2) Co-authored-by: Nabin Hait --- .../period_closing_voucher.py | 13 ++++++++++--- erpnext/patches/v14_0/update_closing_balances.py | 3 +-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py index 99b42089501..6ffa6346702 100644 --- a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py +++ b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py @@ -354,7 +354,8 @@ class PeriodClosingVoucher(AccountsController): def get_account_closing_balances(self): pl_closing_entries = self.get_closing_entries_for_pl_accounts() bs_closing_entries = self.get_closing_entries_for_balance_sheet_accounts() - closing_entries = pl_closing_entries + bs_closing_entries + closing_entries_for_closing_account = self.get_closing_entries_for_closing_account() + closing_entries = pl_closing_entries + bs_closing_entries + closing_entries_for_closing_account return closing_entries def get_closing_entries_for_pl_accounts(self): @@ -404,6 +405,13 @@ class PeriodClosingVoucher(AccountsController): self.update_default_dimensions(closing_entry, dimensions) return closing_entry + def get_closing_entries_for_closing_account(self): + closing_entries = copy.deepcopy(self.closing_account_gle) + for d in closing_entries: + d.period_closing_voucher = self.name + + return closing_entries + def is_first_period_closing_voucher(self): return not frappe.db.exists( "Period Closing Voucher", @@ -442,8 +450,7 @@ def process_gl_and_closing_entries(doc): make_gl_entries(gl_entries, merge_entries=False) closing_entries = doc.get_account_closing_balances() - if closing_entries: - make_closing_entries(closing_entries, doc.name, doc.company, doc.period_end_date) + make_closing_entries(closing_entries, doc.name, doc.company, doc.period_end_date) frappe.db.set_value(doc.doctype, doc.name, "gle_processing_status", "Completed") except Exception as e: diff --git a/erpnext/patches/v14_0/update_closing_balances.py b/erpnext/patches/v14_0/update_closing_balances.py index 73204b0d6c4..f216f209d4f 100644 --- a/erpnext/patches/v14_0/update_closing_balances.py +++ b/erpnext/patches/v14_0/update_closing_balances.py @@ -27,8 +27,7 @@ def execute(): pcv_doc = frappe.get_doc("Period Closing Voucher", pcv.name) pcv_doc.pl_accounts_reverse_gle = get_pcv_gl_entries(pcv, gle_fields) closing_entries = pcv_doc.get_account_closing_balances() - if closing_entries: - make_closing_entries(closing_entries, pcv.name, pcv.company, pcv.period_end_date) + make_closing_entries(closing_entries, pcv.name, pcv.company, pcv.period_end_date) company_wise_order[pcv.company].append(pcv.period_end_date) i += 1 From cd4746ad2a1cac13c97a798a07bb4abee322f29f Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 29 Oct 2024 17:58:40 +0530 Subject: [PATCH 422/734] =?UTF-8?q?fix:=20incorrect=20value=20of=20availab?= =?UTF-8?q?le=5Fqty=5Ffor=5Fconsumption=20in=20subcontracti=E2=80=A6=20(ba?= =?UTF-8?q?ckport=20#43836)=20(#43861)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix: incorrect value of available_qty_for_consumption in subcontracti… (#43836) fix: incorrect value of available_qty_for_consumption in subcontracting receipt (cherry picked from commit ad6ce09b865786d99daca38194bdeeea53aa11e6) Co-authored-by: rohitwaghchaure --- .../doctype/subcontracting_receipt/subcontracting_receipt.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py index 50ce270c166..a99f7ea1615 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py @@ -331,9 +331,12 @@ class SubcontractingReceipt(SubcontractingController): supplied_items_details[item.name] = {} for supplied_item in supplied_items: + if supplied_item.rm_item_code not in supplied_items_details[item.name]: + supplied_items_details[item.name][supplied_item.rm_item_code] = 0.0 + supplied_items_details[item.name][ supplied_item.rm_item_code - ] = supplied_item.available_qty + ] += supplied_item.available_qty else: for item in self.get("supplied_items"): item.available_qty_for_consumption = supplied_items_details.get(item.reference_name, {}).get( From 94a03c6e17f797dd4ffb533ce9ff0af2f3d5d6ff Mon Sep 17 00:00:00 2001 From: Venkatesh <47534423+venkat102@users.noreply.github.com> Date: Tue, 29 Oct 2024 11:47:40 +0530 Subject: [PATCH 423/734] =?UTF-8?q?fix:=20use=20period=20closing=20voucher?= =?UTF-8?q?=20object=20to=20call=20get=5Faccount=5Fclosing=5Fba=E2=80=A6?= =?UTF-8?q?=20(#43880)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix: use period closing voucher object to call get_account_closing_balances method (cherry picked from commit 99d1c5f34221d3f450aa74825339209c7595bcb9) --- erpnext/patches/v14_0/update_closing_balances.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/patches/v14_0/update_closing_balances.py b/erpnext/patches/v14_0/update_closing_balances.py index a2670717ee9..73204b0d6c4 100644 --- a/erpnext/patches/v14_0/update_closing_balances.py +++ b/erpnext/patches/v14_0/update_closing_balances.py @@ -24,8 +24,9 @@ def execute(): for pcv in get_period_closing_vouchers(company): company_wise_order.setdefault(pcv.company, []) if pcv.period_end_date not in company_wise_order[pcv.company]: - pcv.pl_accounts_reverse_gle = get_pcv_gl_entries(pcv, gle_fields) - closing_entries = pcv.get_account_closing_balances() + pcv_doc = frappe.get_doc("Period Closing Voucher", pcv.name) + pcv_doc.pl_accounts_reverse_gle = get_pcv_gl_entries(pcv, gle_fields) + closing_entries = pcv_doc.get_account_closing_balances() if closing_entries: make_closing_entries(closing_entries, pcv.name, pcv.company, pcv.period_end_date) From be6cd6adc3d00cf925f91db5c7cf31157af534fa Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 29 Oct 2024 15:34:46 +0000 Subject: [PATCH 424/734] chore(release): Bumped to Version 15.39.4 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## [15.39.4](https://github.com/frappe/erpnext/compare/v15.39.3...v15.39.4) (2024-10-29) ### Bug Fixes * use period closing voucher object to call get_account_closing_ba… ([#43880](https://github.com/frappe/erpnext/issues/43880)) ([94a03c6](https://github.com/frappe/erpnext/commit/94a03c6e17f797dd4ffb533ce9ff0af2f3d5d6ff)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 645337290fb..d8473c0d258 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.39.3" +__version__ = "15.39.4" def get_default_company(user=None): From becfd980b2f4219aa15ec9dc7b94f7ae037196d7 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 29 Oct 2024 16:53:11 +0530 Subject: [PATCH 425/734] fix: post account closing balance against pcv closing account (#43887) (cherry picked from commit 34295d0344573a296d5fb76e6cc2c819a93354b2) --- .../period_closing_voucher.py | 13 ++++++++++--- erpnext/patches/v14_0/update_closing_balances.py | 3 +-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py index 99b42089501..6ffa6346702 100644 --- a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py +++ b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py @@ -354,7 +354,8 @@ class PeriodClosingVoucher(AccountsController): def get_account_closing_balances(self): pl_closing_entries = self.get_closing_entries_for_pl_accounts() bs_closing_entries = self.get_closing_entries_for_balance_sheet_accounts() - closing_entries = pl_closing_entries + bs_closing_entries + closing_entries_for_closing_account = self.get_closing_entries_for_closing_account() + closing_entries = pl_closing_entries + bs_closing_entries + closing_entries_for_closing_account return closing_entries def get_closing_entries_for_pl_accounts(self): @@ -404,6 +405,13 @@ class PeriodClosingVoucher(AccountsController): self.update_default_dimensions(closing_entry, dimensions) return closing_entry + def get_closing_entries_for_closing_account(self): + closing_entries = copy.deepcopy(self.closing_account_gle) + for d in closing_entries: + d.period_closing_voucher = self.name + + return closing_entries + def is_first_period_closing_voucher(self): return not frappe.db.exists( "Period Closing Voucher", @@ -442,8 +450,7 @@ def process_gl_and_closing_entries(doc): make_gl_entries(gl_entries, merge_entries=False) closing_entries = doc.get_account_closing_balances() - if closing_entries: - make_closing_entries(closing_entries, doc.name, doc.company, doc.period_end_date) + make_closing_entries(closing_entries, doc.name, doc.company, doc.period_end_date) frappe.db.set_value(doc.doctype, doc.name, "gle_processing_status", "Completed") except Exception as e: diff --git a/erpnext/patches/v14_0/update_closing_balances.py b/erpnext/patches/v14_0/update_closing_balances.py index 73204b0d6c4..f216f209d4f 100644 --- a/erpnext/patches/v14_0/update_closing_balances.py +++ b/erpnext/patches/v14_0/update_closing_balances.py @@ -27,8 +27,7 @@ def execute(): pcv_doc = frappe.get_doc("Period Closing Voucher", pcv.name) pcv_doc.pl_accounts_reverse_gle = get_pcv_gl_entries(pcv, gle_fields) closing_entries = pcv_doc.get_account_closing_balances() - if closing_entries: - make_closing_entries(closing_entries, pcv.name, pcv.company, pcv.period_end_date) + make_closing_entries(closing_entries, pcv.name, pcv.company, pcv.period_end_date) company_wise_order[pcv.company].append(pcv.period_end_date) i += 1 From 50db0aca61c31b1f32b5d96baaff63ddc9c20e1c Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 29 Oct 2024 15:39:40 +0000 Subject: [PATCH 426/734] chore(release): Bumped to Version 15.39.5 ## [15.39.5](https://github.com/frappe/erpnext/compare/v15.39.4...v15.39.5) (2024-10-29) ### Bug Fixes * post account closing balance against pcv closing account ([#43887](https://github.com/frappe/erpnext/issues/43887)) ([becfd98](https://github.com/frappe/erpnext/commit/becfd980b2f4219aa15ec9dc7b94f7ae037196d7)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index d8473c0d258..3b36669ae7c 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.39.4" +__version__ = "15.39.5" def get_default_company(user=None): From 93d0db29100735cca01228ccf943a5abeb772bf9 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 29 Oct 2024 22:36:39 +0530 Subject: [PATCH 427/734] fix: Calculate gross margin on update of project costing from invoices (backport #43876) (#43900) * fix: Calculate gross margin on update of project costing from invoices (#43876) * fix: Calculate gross margin on update of project costing from invoices * chore: linter issues (cherry picked from commit 0bba6442c0f64bbf36f79d0df27bb922275d96af) # Conflicts: # erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json * fix: merge conflict --------- Co-authored-by: Nabin Hait --- .../purchase_invoice/purchase_invoice.py | 6 ++- .../purchase_invoice_item.json | 5 ++- .../doctype/sales_invoice/sales_invoice.py | 6 ++- .../sales_invoice_item.json | 5 ++- erpnext/projects/doctype/project/project.py | 42 +++++-------------- 5 files changed, 26 insertions(+), 38 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index c9f3b220cc6..b46fb9e9b30 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -1593,7 +1593,11 @@ class PurchaseInvoice(BuyingController): for proj, value in projects.items(): res = frappe.qb.from_(pj).select(pj.total_purchase_cost).where(pj.name == proj).for_update().run() current_purchase_cost = res and res[0][0] or 0 - frappe.db.set_value("Project", proj, "total_purchase_cost", current_purchase_cost + value) + # frappe.db.set_value("Project", proj, "total_purchase_cost", current_purchase_cost + value) + project_doc = frappe.get_doc("Project", proj) + project_doc.total_purchase_cost = current_purchase_cost + value + project_doc.calculate_gross_margin() + project_doc.db_update() def validate_supplier_invoice(self): if self.bill_date: 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 8a2ba36cf62..258cc10d4ec 100644 --- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json +++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json @@ -505,7 +505,8 @@ "fieldtype": "Link", "label": "Project", "options": "Project", - "print_hide": 1 + "print_hide": 1, + "search_index": 1 }, { "allow_on_submit": 1, @@ -974,7 +975,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2024-07-19 12:12:42.449298", + "modified": "2024-10-28 15:06:19.246141", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice Item", diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index ebdbb01fdc2..eb43de47a54 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1734,9 +1734,11 @@ class SalesInvoice(SellingController): ) def update_project(self): - if self.project: - project = frappe.get_doc("Project", self.project) + unique_projects = list(set([d.project for d in self.get("items") if d.project])) + for p in unique_projects: + project = frappe.get_doc("Project", p) project.update_billed_amount() + project.calculate_gross_margin() project.db_update() def verify_payment_amount_is_positive(self): 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 932bc8e49d4..8b93f56a4c2 100644 --- a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json +++ b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json @@ -812,7 +812,8 @@ "fieldname": "project", "fieldtype": "Link", "label": "Project", - "options": "Project" + "options": "Project", + "search_index": 1 }, { "depends_on": "eval:parent.update_stock == 1", @@ -927,7 +928,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2024-05-23 16:36:18.970862", + "modified": "2024-10-28 15:06:40.980995", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice Item", diff --git a/erpnext/projects/doctype/project/project.py b/erpnext/projects/doctype/project/project.py index 7870a2ace73..f1512501e76 100644 --- a/erpnext/projects/doctype/project/project.py +++ b/erpnext/projects/doctype/project/project.py @@ -93,14 +93,14 @@ class Project(Document): def validate(self): if not self.is_new(): - self.copy_from_template() + self.copy_from_template() # nosemgrep self.send_welcome_email() self.update_costing() self.update_percent_complete() self.validate_from_to_dates("expected_start_date", "expected_end_date") self.validate_from_to_dates("actual_start_date", "actual_end_date") - def copy_from_template(self): + def copy_from_template(self): # nosemgrep """ Copy tasks from template """ @@ -205,7 +205,7 @@ class Project(Document): self.db_update() def after_insert(self): - self.copy_from_template() + self.copy_from_template() # nosemgrep if self.sales_order: frappe.db.set_value("Sales Order", self.sales_order, "project", self.name) @@ -324,9 +324,13 @@ class Project(Document): self.total_sales_amount = total_sales_amount and total_sales_amount[0][0] or 0 def update_billed_amount(self): + # nosemgrep total_billed_amount = frappe.db.sql( - """select sum(base_net_total) - from `tabSales Invoice` where project = %s and docstatus=1""", + """select sum(base_net_amount) + from `tabSales Invoice Item` si_item, `tabSales Invoice` si + where si_item.parent = si.name + and if(si_item.project, si_item.project, si.project) = %s + and si.docstatus=1""", self.name, ) @@ -676,31 +680,8 @@ def update_project_sales_billing(): return # Else simply fallback to Daily - exists_query = "(SELECT 1 from `tab{doctype}` where docstatus = 1 and project = `tabProject`.name)" - project_map = {} - for project_details in frappe.db.sql( - """ - SELECT name, 1 as order_exists, null as invoice_exists from `tabProject` where - exists {order_exists} - union - SELECT name, null as order_exists, 1 as invoice_exists from `tabProject` where - exists {invoice_exists} - """.format( - order_exists=exists_query.format(doctype="Sales Order"), - invoice_exists=exists_query.format(doctype="Sales Invoice"), - ), - as_dict=True, - ): - project = project_map.setdefault( - project_details.name, frappe.get_doc("Project", project_details.name) - ) - if project_details.order_exists: - project.update_sales_amount() - if project_details.invoice_exists: - project.update_billed_amount() - - for project in project_map.values(): - project.save() + for project in frappe.get_all("Project", filters={"status": ["!=", "Cancelled"]}): + frappe.get_doc("Project", project.name).save() @frappe.whitelist() @@ -751,7 +732,6 @@ def get_users_email(doc): def calculate_total_purchase_cost(project: str | None = None): if project: pitem = qb.DocType("Purchase Invoice Item") - frappe.qb.DocType("Purchase Invoice Item") total_purchase_cost = ( qb.from_(pitem) .select(Sum(pitem.base_net_amount)) From 7189daba193127350d74f5397147ab27a5525f60 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 29 Oct 2024 22:37:26 +0530 Subject: [PATCH 428/734] fix: work order finish button not showing (backport #43875) (backport #43877) (#43904) fix: work order finish button not showing (backport #43875) (#43877) * fix: work order finish button not showing (#43875) (cherry picked from commit 0a70be5b993895dcc888c7418bbd675224554d2b) # Conflicts: # erpnext/manufacturing/doctype/job_card/job_card.js # erpnext/manufacturing/doctype/job_card/job_card.py * chore: fix conflicts * chore: fix conflicts * chore: fix conflicts --------- Co-authored-by: rohitwaghchaure (cherry picked from commit 76530de786d9a220ca9e59720184b5d5b9328979) Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- .../doctype/job_card/job_card.py | 23 +++++++------------ 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py index de8116b296c..58637109077 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.py +++ b/erpnext/manufacturing/doctype/job_card/job_card.py @@ -941,26 +941,19 @@ class JobCard(Document): qty = 0 if self.work_order: - doc = frappe.get_doc("Work Order", self.work_order) if doc.transfer_material_against == "Job Card" and not doc.skip_transfer: - completed = True + min_qty = [] for d in doc.operations: - if d.status != "Completed": - completed = False + if d.completed_qty: + min_qty.append(d.completed_qty) + else: + min_qty = [] break - if completed: - job_cards = frappe.get_all( - "Job Card", - filters={"work_order": self.work_order, "docstatus": ("!=", 2)}, - fields="sum(transferred_qty) as qty", - group_by="operation_id", - ) + if min_qty: + qty = min(min_qty) - if job_cards: - qty = min(d.qty for d in job_cards) - - doc.db_set("material_transferred_for_manufacturing", qty) + doc.db_set("material_transferred_for_manufacturing", qty) self.set_status(update_status) From 8a72845ee6fa7f954e6915272ea722f487907171 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 29 Oct 2024 22:38:41 +0530 Subject: [PATCH 429/734] fix: Handle None value for item description in customer portal invoice view (backport #43823) (#43889) fix: Handle None value for item description in customer portal invoice view (cherry picked from commit ceb449c75b830c1c18adffd9a6e68557507b76c4) Co-authored-by: ljain112 --- erpnext/templates/pages/order.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/templates/pages/order.html b/erpnext/templates/pages/order.html index 6c59a9688dc..388feb9eba9 100644 --- a/erpnext/templates/pages/order.html +++ b/erpnext/templates/pages/order.html @@ -173,11 +173,11 @@
{{ d.item_code }}
- {{ html2text(d.description) | truncate(140) }} + {{ html2text(d.description or "") | truncate(140) }}
{{ _("Qty ") }}({{ d.get_formatted("qty") }})
-{% endmacro %} \ No newline at end of file +{% endmacro %} From c2eb771c4d228458a95db99dbbb2f96c4a00db02 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 29 Oct 2024 23:07:58 +0530 Subject: [PATCH 430/734] fix: Patch for reposting account closing balance (backport #43905) (#43909) fix: Patch for reposting account closing balance (#43905) (cherry picked from commit 3a0d27b393a6224e9213d23c022f9b45687d7508) Co-authored-by: Nabin Hait --- erpnext/patches.txt | 2 +- .../patches/v14_0/update_closing_balances.py | 21 ++++++++++++++++--- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 515a299504a..f48dc00fc8e 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -315,7 +315,7 @@ erpnext.patches.v15_0.update_asset_value_for_manual_depr_entries erpnext.patches.v15_0.update_gpa_and_ndb_for_assdeprsch erpnext.patches.v14_0.create_accounting_dimensions_for_closing_balance erpnext.patches.v14_0.set_period_start_end_date_in_pcv -erpnext.patches.v14_0.update_closing_balances #14-07-2023 +erpnext.patches.v14_0.update_closing_balances #29-10-2024 execute:frappe.db.set_single_value("Accounts Settings", "merge_similar_account_heads", 0) erpnext.patches.v14_0.update_reference_type_in_journal_entry_accounts erpnext.patches.v14_0.update_subscription_details diff --git a/erpnext/patches/v14_0/update_closing_balances.py b/erpnext/patches/v14_0/update_closing_balances.py index f216f209d4f..31c7e85d875 100644 --- a/erpnext/patches/v14_0/update_closing_balances.py +++ b/erpnext/patches/v14_0/update_closing_balances.py @@ -25,7 +25,8 @@ def execute(): company_wise_order.setdefault(pcv.company, []) if pcv.period_end_date not in company_wise_order[pcv.company]: pcv_doc = frappe.get_doc("Period Closing Voucher", pcv.name) - pcv_doc.pl_accounts_reverse_gle = get_pcv_gl_entries(pcv, gle_fields) + pcv_doc.pl_accounts_reverse_gle = get_pcv_gl_entries_for_pl_accounts(pcv, gle_fields) + pcv_doc.closing_account_gle = get_pcv_gl_entries_for_closing_accounts(pcv, gle_fields) closing_entries = pcv_doc.get_account_closing_balances() make_closing_entries(closing_entries, pcv.name, pcv.company, pcv.period_end_date) @@ -38,6 +39,7 @@ def get_gle_fields(): accounting_dimension_fields = get_accounting_dimensions() gle_fields = [ "name", + "company", "posting_date", "account", "account_currency", @@ -61,14 +63,27 @@ def get_period_closing_vouchers(company): ) -def get_pcv_gl_entries(pcv, gle_fields): +def get_pcv_gl_entries_for_pl_accounts(pcv, gle_fields): + return get_gl_entries(pcv, gle_fields, {"account": ["!=", pcv.closing_account_head]}) + + +def get_pcv_gl_entries_for_closing_accounts(pcv, gle_fields): + return get_gl_entries(pcv, gle_fields, {"account": pcv.closing_account_head}) + + +def get_gl_entries(pcv, gle_fields, accounts_filter=None): + filters = {"voucher_no": pcv.name, "is_cancelled": 0} + if accounts_filter: + filters.update(accounts_filter) + gl_entries = frappe.db.get_all( "GL Entry", - filters={"voucher_no": pcv.name, "account": ["!=", pcv.closing_account_head], "is_cancelled": 0}, + filters=filters, fields=gle_fields, ) for entry in gl_entries: entry["is_period_closing_voucher_entry"] = 1 entry["closing_date"] = pcv.period_end_date entry["period_closing_voucher"] = pcv.name + return gl_entries From d8e1a21bdc34d88c77c33b2f21d53561fe81b1b1 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 29 Oct 2024 17:39:33 +0000 Subject: [PATCH 431/734] chore(release): Bumped to Version 15.39.6 ## [15.39.6](https://github.com/frappe/erpnext/compare/v15.39.5...v15.39.6) (2024-10-29) ### Bug Fixes * Patch for reposting account closing balance (backport [#43905](https://github.com/frappe/erpnext/issues/43905)) ([#43909](https://github.com/frappe/erpnext/issues/43909)) ([c2eb771](https://github.com/frappe/erpnext/commit/c2eb771c4d228458a95db99dbbb2f96c4a00db02)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 3b36669ae7c..b05af8dad95 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.39.5" +__version__ = "15.39.6" def get_default_company(user=None): From ab162070a6dd08bc2a7217242dc5085d190b60ff Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 29 Oct 2024 23:22:29 +0530 Subject: [PATCH 432/734] fix: Patch for reposting account closing balance (backport #43905) (#43910) fix: Patch for reposting account closing balance (#43905) (cherry picked from commit 3a0d27b393a6224e9213d23c022f9b45687d7508) Co-authored-by: Nabin Hait --- erpnext/patches.txt | 2 +- .../patches/v14_0/update_closing_balances.py | 21 ++++++++++++++++--- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 515a299504a..f48dc00fc8e 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -315,7 +315,7 @@ erpnext.patches.v15_0.update_asset_value_for_manual_depr_entries erpnext.patches.v15_0.update_gpa_and_ndb_for_assdeprsch erpnext.patches.v14_0.create_accounting_dimensions_for_closing_balance erpnext.patches.v14_0.set_period_start_end_date_in_pcv -erpnext.patches.v14_0.update_closing_balances #14-07-2023 +erpnext.patches.v14_0.update_closing_balances #29-10-2024 execute:frappe.db.set_single_value("Accounts Settings", "merge_similar_account_heads", 0) erpnext.patches.v14_0.update_reference_type_in_journal_entry_accounts erpnext.patches.v14_0.update_subscription_details diff --git a/erpnext/patches/v14_0/update_closing_balances.py b/erpnext/patches/v14_0/update_closing_balances.py index f216f209d4f..31c7e85d875 100644 --- a/erpnext/patches/v14_0/update_closing_balances.py +++ b/erpnext/patches/v14_0/update_closing_balances.py @@ -25,7 +25,8 @@ def execute(): company_wise_order.setdefault(pcv.company, []) if pcv.period_end_date not in company_wise_order[pcv.company]: pcv_doc = frappe.get_doc("Period Closing Voucher", pcv.name) - pcv_doc.pl_accounts_reverse_gle = get_pcv_gl_entries(pcv, gle_fields) + pcv_doc.pl_accounts_reverse_gle = get_pcv_gl_entries_for_pl_accounts(pcv, gle_fields) + pcv_doc.closing_account_gle = get_pcv_gl_entries_for_closing_accounts(pcv, gle_fields) closing_entries = pcv_doc.get_account_closing_balances() make_closing_entries(closing_entries, pcv.name, pcv.company, pcv.period_end_date) @@ -38,6 +39,7 @@ def get_gle_fields(): accounting_dimension_fields = get_accounting_dimensions() gle_fields = [ "name", + "company", "posting_date", "account", "account_currency", @@ -61,14 +63,27 @@ def get_period_closing_vouchers(company): ) -def get_pcv_gl_entries(pcv, gle_fields): +def get_pcv_gl_entries_for_pl_accounts(pcv, gle_fields): + return get_gl_entries(pcv, gle_fields, {"account": ["!=", pcv.closing_account_head]}) + + +def get_pcv_gl_entries_for_closing_accounts(pcv, gle_fields): + return get_gl_entries(pcv, gle_fields, {"account": pcv.closing_account_head}) + + +def get_gl_entries(pcv, gle_fields, accounts_filter=None): + filters = {"voucher_no": pcv.name, "is_cancelled": 0} + if accounts_filter: + filters.update(accounts_filter) + gl_entries = frappe.db.get_all( "GL Entry", - filters={"voucher_no": pcv.name, "account": ["!=", pcv.closing_account_head], "is_cancelled": 0}, + filters=filters, fields=gle_fields, ) for entry in gl_entries: entry["is_period_closing_voucher_entry"] = 1 entry["closing_date"] = pcv.period_end_date entry["period_closing_voucher"] = pcv.name + return gl_entries From 1af2326a52e969013745a390d2dbbcc54733512f Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Tue, 29 Oct 2024 17:47:48 +0530 Subject: [PATCH 433/734] fix: consider opening asset values while calculating asset depreciation rate (cherry picked from commit 9d0fe7aa56e48cd53a41cb1d88bcb74ae741c8ec) --- erpnext/assets/doctype/asset/asset.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 6c77a1b9b0e..6bb3ff255cf 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -790,14 +790,19 @@ class Asset(AccountsController): args.get("value_after_depreciation") ) else: - value = flt(args.get("expected_value_after_useful_life")) / flt(self.gross_purchase_amount) + value = flt(args.get("expected_value_after_useful_life")) / ( + flt(self.gross_purchase_amount) - flt(self.opening_accumulated_depreciation) + ) depreciation_rate = math.pow( value, 1.0 / ( ( - flt(args.get("total_number_of_depreciations"), 2) + ( + flt(args.get("total_number_of_depreciations"), 2) + - flt(self.opening_number_of_booked_depreciations) + ) * flt(args.get("frequency_of_depreciation")) ) / 12 From 63668eb855252cd2b0c39356c21660871463eae5 Mon Sep 17 00:00:00 2001 From: ljain112 Date: Tue, 29 Oct 2024 12:15:59 +0530 Subject: [PATCH 434/734] fix: recalculate outstanding after save on checkout for POS Invoice (cherry picked from commit 9ce2184c6628075116f7cb93df709a91680ed553) --- erpnext/selling/page/point_of_sale/pos_payment.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/erpnext/selling/page/point_of_sale/pos_payment.js b/erpnext/selling/page/point_of_sale/pos_payment.js index 232b6a02123..9f389dfa81f 100644 --- a/erpnext/selling/page/point_of_sale/pos_payment.js +++ b/erpnext/selling/page/point_of_sale/pos_payment.js @@ -350,6 +350,11 @@ erpnext.PointOfSale.Payment = class { } checkout() { + const frm = this.events.get_frm(); + frm.cscript.calculate_outstanding_amount(); + frm.refresh_field("outstanding_amount"); + frm.refresh_field("paid_amount"); + frm.refresh_field("base_paid_amount"); this.events.toggle_other_sections(true); this.toggle_component(true); From 870709079b0434d810b792d17c93863c9dc008fb Mon Sep 17 00:00:00 2001 From: Ahmed Shareef Date: Tue, 29 Oct 2024 11:07:58 +0400 Subject: [PATCH 435/734] fix: remarks field in payment reconciliation (cherry picked from commit 2d5b079949ce13a162686780c2b4cde5c7fb1071) # Conflicts: # erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json --- .../payment_reconciliation.py | 1 + .../payment_reconciliation_payment.json | 18 +++++++++++------- .../payment_reconciliation_payment.py | 2 +- erpnext/accounts/utils.py | 2 ++ 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index 8481fd8ffa4..35d268accac 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -323,6 +323,7 @@ class PaymentReconciliation(Document): "posting_date": inv.posting_date, "currency": inv.currency, "cost_center": inv.cost_center, + "remarks": inv.remarks, } ) ) diff --git a/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json b/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json index d199236ae99..8e3ff2288aa 100644 --- a/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json +++ b/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json @@ -14,7 +14,7 @@ "amount", "difference_amount", "sec_break1", - "remark", + "remarks", "currency", "exchange_rate", "cost_center" @@ -74,12 +74,6 @@ "fieldname": "sec_break1", "fieldtype": "Section Break" }, - { - "fieldname": "remark", - "fieldtype": "Small Text", - "label": "Remark", - "read_only": 1 - }, { "fieldname": "currency", "fieldtype": "Link", @@ -105,12 +99,22 @@ "fieldtype": "Link", "label": "Cost Center", "options": "Cost Center" + }, + { + "fieldname": "remarks", + "fieldtype": "Small Text", + "label": "Remarks", + "read_only": 1 } ], "is_virtual": 1, "istable": 1, "links": [], +<<<<<<< HEAD "modified": "2023-11-17 17:33:34.818530", +======= + "modified": "2024-10-29 16:24:43.021230", +>>>>>>> 2d5b079949 (fix: remarks field in payment reconciliation) "modified_by": "Administrator", "module": "Accounts", "name": "Payment Reconciliation Payment", diff --git a/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.py b/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.py index 4ab80ecaafe..49c17eae41b 100644 --- a/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.py +++ b/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.py @@ -27,7 +27,7 @@ class PaymentReconciliationPayment(Document): reference_name: DF.DynamicLink | None reference_row: DF.Data | None reference_type: DF.Link | None - remark: DF.SmallText | None + remarks: DF.SmallText | None # end: auto-generated types @staticmethod diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 91904059824..37dbaef51a8 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -1969,6 +1969,7 @@ class QueryPaymentLedger: ple.cost_center.as_("cost_center"), Sum(ple.amount).as_("amount"), Sum(ple.amount_in_account_currency).as_("amount_in_account_currency"), + ple.remarks, ) .where(ple.delinked == 0) .where(Criterion.all(filter_on_voucher_no)) @@ -2031,6 +2032,7 @@ class QueryPaymentLedger: Table("vouchers").due_date, Table("vouchers").currency, Table("vouchers").cost_center.as_("cost_center"), + Table("vouchers").remarks, ) .where(Criterion.all(filter_on_outstanding_amount)) ) From ac121dd4e3af3092c3fe86ae2205a6c24f91c127 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 30 Oct 2024 10:23:41 +0530 Subject: [PATCH 436/734] chore: resolve conflict --- .../payment_reconciliation_payment.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json b/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json index 8e3ff2288aa..010e93558cf 100644 --- a/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json +++ b/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json @@ -110,11 +110,7 @@ "is_virtual": 1, "istable": 1, "links": [], -<<<<<<< HEAD - "modified": "2023-11-17 17:33:34.818530", -======= "modified": "2024-10-29 16:24:43.021230", ->>>>>>> 2d5b079949 (fix: remarks field in payment reconciliation) "modified_by": "Administrator", "module": "Accounts", "name": "Payment Reconciliation Payment", From 8218ca990c1c1da4210935eefc7cd1138724d6bc Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 30 Oct 2024 00:25:18 +0530 Subject: [PATCH 437/734] fix: find first PCV to consider opening entries (cherry picked from commit 2201fc62a290f15c3570043c832663d63d4ec6de) --- .../period_closing_voucher/period_closing_voucher.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py index 6ffa6346702..2a9b624a47a 100644 --- a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py +++ b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py @@ -413,11 +413,16 @@ class PeriodClosingVoucher(AccountsController): return closing_entries def is_first_period_closing_voucher(self): - return not frappe.db.exists( + first_pcv = frappe.db.get_value( "Period Closing Voucher", - {"company": self.company, "docstatus": 1, "name": ("!=", self.name)}, + {"company": self.company, "docstatus": 1}, + "name", + order_by="period_end_date", ) + if not first_pcv or first_pcv == self.name: + return True + def cancel_gl_entries(self): if self.get_gle_count_against_current_pcv() > 5000: frappe.enqueue( From 9ac87bd3b1547ba1d0c70cbacf26addbd4a3f637 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 30 Oct 2024 11:25:08 +0530 Subject: [PATCH 438/734] fix: rounding issue of required qty in subcontracting order (#43908) * fix: rounding issue of required qty in subcontracting order * fix: uom issue in test case * fix: test case * fix: restored report tests for manufacturing --- .../controllers/subcontracting_controller.py | 12 +++++----- .../tests/test_subcontracting_controller.py | 3 +++ .../test_subcontracting_order.py | 23 +++++++++++++++++++ 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/erpnext/controllers/subcontracting_controller.py b/erpnext/controllers/subcontracting_controller.py index 3c7f8b0ccca..5fb1ee468cd 100644 --- a/erpnext/controllers/subcontracting_controller.py +++ b/erpnext/controllers/subcontracting_controller.py @@ -561,11 +561,11 @@ class SubcontractingController(StockController): use_serial_batch_fields = frappe.db.get_single_value("Stock Settings", "use_serial_batch_fields") if self.doctype == self.subcontract_data.order_doctype: - rm_obj.required_qty = qty - rm_obj.amount = rm_obj.required_qty * rm_obj.rate + rm_obj.required_qty = flt(qty, rm_obj.precision("required_qty")) + rm_obj.amount = flt(rm_obj.required_qty * rm_obj.rate, rm_obj.precision("amount")) else: - rm_obj.consumed_qty = qty - rm_obj.required_qty = bom_item.required_qty or qty + rm_obj.consumed_qty = flt(qty, rm_obj.precision("consumed_qty")) + rm_obj.required_qty = flt(bom_item.required_qty or qty, rm_obj.precision("required_qty")) rm_obj.serial_and_batch_bundle = None setattr( rm_obj, self.subcontract_data.order_field, item_row.get(self.subcontract_data.order_field) @@ -664,8 +664,8 @@ class SubcontractingController(StockController): self.__set_serial_nos(item_row, rm_obj) def __set_consumed_qty(self, rm_obj, consumed_qty, required_qty=0): - rm_obj.required_qty = required_qty - rm_obj.consumed_qty = consumed_qty + rm_obj.required_qty = flt(required_qty, rm_obj.precision("required_qty")) + rm_obj.consumed_qty = flt(consumed_qty, rm_obj.precision("consumed_qty")) def __set_serial_nos(self, item_row, rm_obj): key = (rm_obj.rm_item_code, item_row.item_code, item_row.get(self.subcontract_data.order_field)) diff --git a/erpnext/controllers/tests/test_subcontracting_controller.py b/erpnext/controllers/tests/test_subcontracting_controller.py index dfd4351dcad..84326bafef2 100644 --- a/erpnext/controllers/tests/test_subcontracting_controller.py +++ b/erpnext/controllers/tests/test_subcontracting_controller.py @@ -1234,6 +1234,7 @@ def make_subcontracted_items(): "Subcontracted Item SA6": {}, "Subcontracted Item SA7": {}, "Subcontracted Item SA8": {}, + "Subcontracted Item SA9": {"stock_uom": "Litre"}, } for item, properties in sub_contracted_items.items(): @@ -1254,6 +1255,7 @@ def make_raw_materials(): "Subcontracted SRM Item 4": {"has_serial_no": 1, "serial_no_series": "SRII.####"}, "Subcontracted SRM Item 5": {"has_serial_no": 1, "serial_no_series": "SRIID.####"}, "Subcontracted SRM Item 8": {}, + "Subcontracted SRM Item 9": {"stock_uom": "Litre"}, } for item, properties in raw_materials.items(): @@ -1280,6 +1282,7 @@ def make_service_items(): "Subcontracted Service Item 6": {}, "Subcontracted Service Item 7": {}, "Subcontracted Service Item 8": {}, + "Subcontracted Service Item 9": {}, } for item, properties in service_items.items(): diff --git a/erpnext/subcontracting/doctype/subcontracting_order/test_subcontracting_order.py b/erpnext/subcontracting/doctype/subcontracting_order/test_subcontracting_order.py index 0ae07993be8..ac87239e73e 100644 --- a/erpnext/subcontracting/doctype/subcontracting_order/test_subcontracting_order.py +++ b/erpnext/subcontracting/doctype/subcontracting_order/test_subcontracting_order.py @@ -25,6 +25,7 @@ from erpnext.controllers.tests.test_subcontracting_controller import ( make_subcontracted_items, set_backflush_based_on, ) +from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom from erpnext.stock.doctype.item.test_item import make_item from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry from erpnext.subcontracting.doctype.subcontracting_order.subcontracting_order import ( @@ -683,6 +684,28 @@ class TestSubcontractingOrder(FrappeTestCase): self.assertEqual(requested_qty, new_requested_qty) + def test_subcontracting_order_rm_required_items_for_precision(self): + item_code = "Subcontracted Item SA9" + raw_materials = ["Subcontracted SRM Item 9"] + if not frappe.db.exists("BOM", {"item": item_code}): + make_bom(item=item_code, raw_materials=raw_materials, rate=100, rm_qty=1.04) + + service_items = [ + { + "warehouse": "_Test Warehouse - _TC", + "item_code": "Subcontracted Service Item 9", + "qty": 1, # 202.0656, + "rate": 100, + "fg_item": "Subcontracted Item SA9", + "fg_item_qty": 202.0656, + }, + ] + + sco = get_subcontracting_order(service_items=service_items) + sco.reload() + + self.assertEqual(sco.supplied_items[0].required_qty, 210.149) + def create_subcontracting_order(**args): args = frappe._dict(args) From 9a526611e020a4bd1cbbbd56b1db67367d5b9fc6 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 30 Oct 2024 11:25:56 +0530 Subject: [PATCH 439/734] fix: calculate tds with net amount when invoice exceeds single threshold amount (backport #43869) (#43920) * fix: calculate tds with net amount when invoice exceeds single threshold amount (cherry picked from commit ef694a40a1945fb4bd1f56a962a9245a6e76706b) * test: add unit test to validate purchase invoice exceeding single threshold value (cherry picked from commit 94badb464db4e598fd6fec72d3ee4d914d02d87d) --------- Co-authored-by: venkat102 --- .../tax_withholding_category.py | 20 +++++----- .../test_tax_withholding_category.py | 40 +++++++++++++++++++ 2 files changed, 50 insertions(+), 10 deletions(-) diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py index a56a7f045c8..46a0916c79f 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -514,7 +514,7 @@ def get_tds_amount(ldc, parties, inv, tax_details, vouchers): payment_entry_filters.pop("apply_tax_withholding_amount", None) payment_entry_filters.pop("tax_withholding_category", None) - supp_credit_amt = frappe.db.get_value("Purchase Invoice", invoice_filters, field) or 0.0 + supp_inv_credit_amt = frappe.db.get_value("Purchase Invoice", invoice_filters, field) or 0.0 supp_jv_credit_amt = ( frappe.db.get_value( @@ -538,7 +538,7 @@ def get_tds_amount(ldc, parties, inv, tax_details, vouchers): group_by="payment_type", ) - supp_credit_amt += supp_jv_credit_amt + supp_credit_amt = supp_jv_credit_amt supp_credit_amt += inv.tax_withholding_net_total for type in payment_entry_amounts: @@ -556,18 +556,18 @@ def get_tds_amount(ldc, parties, inv, tax_details, vouchers): tax_withholding_net_total = inv.tax_withholding_net_total if (threshold and tax_withholding_net_total >= threshold) or ( - cumulative_threshold and supp_credit_amt >= cumulative_threshold + cumulative_threshold and (supp_credit_amt + supp_inv_credit_amt) >= cumulative_threshold ): + # Get net total again as TDS is calculated on net total + # Grand is used to just check for threshold breach + net_total = ( + frappe.db.get_value("Purchase Invoice", invoice_filters, "sum(tax_withholding_net_total)") or 0.0 + ) + supp_credit_amt += net_total + if (cumulative_threshold and supp_credit_amt >= cumulative_threshold) and cint( tax_details.tax_on_excess_amount ): - # Get net total again as TDS is calculated on net total - # Grand is used to just check for threshold breach - net_total = ( - frappe.db.get_value("Purchase Invoice", invoice_filters, "sum(tax_withholding_net_total)") - or 0.0 - ) - net_total += inv.tax_withholding_net_total supp_credit_amt = net_total - cumulative_threshold if ldc and is_valid_certificate(ldc, inv.get("posting_date") or inv.get("transaction_date"), 0): diff --git a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py index 24c9265eecd..c8893a13e86 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py @@ -121,6 +121,46 @@ class TestTaxWithholdingCategory(FrappeTestCase): for d in reversed(invoices): d.cancel() + def test_cumulative_threshold_with_party_ledger_amount_on_net_total(self): + invoices = [] + frappe.db.set_value( + "Supplier", "Test TDS Supplier3", "tax_withholding_category", "Advance TDS Category" + ) + + # Invoice with tax and without exceeding single and cumulative thresholds + for _ in range(2): + pi = create_purchase_invoice(supplier="Test TDS Supplier3", rate=1000, do_not_save=True) + pi.apply_tds = 1 + pi.append( + "taxes", + { + "category": "Total", + "charge_type": "Actual", + "account_head": "_Test Account VAT - _TC", + "cost_center": "Main - _TC", + "tax_amount": 500, + "description": "Test", + "add_deduct_tax": "Add", + }, + ) + pi.save() + pi.submit() + invoices.append(pi) + + # Third Invoice exceeds single threshold and not exceeding cumulative threshold + pi1 = create_purchase_invoice(supplier="Test TDS Supplier3", rate=6000) + pi1.apply_tds = 1 + pi1.save() + pi1.submit() + invoices.append(pi1) + + # Cumulative threshold is 10,000 + # Threshold calculation should be only on the third invoice + self.assertEqual(pi1.taxes[0].tax_amount, 800) + + for d in reversed(invoices): + d.cancel() + def test_cumulative_threshold_tcs(self): frappe.db.set_value( "Customer", "Test TCS Customer", "tax_withholding_category", "Cumulative Threshold TCS" From db3be4195c3690e835355f0154bc29b602301ab8 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 30 Oct 2024 12:17:46 +0530 Subject: [PATCH 440/734] fix: purchase return validation issue (backport #43871) (backport #43874) (#43879) fix: purchase return validation issue (backport #43871) (#43874) fix: purchase return validation issue (#43871) (cherry picked from commit a7cc7b28c075388b22501d6f68faac3da5a1a20d) Co-authored-by: rohitwaghchaure (cherry picked from commit b4d4c4a736299b96d4ef2b1a9aad4c0fe9289d7a) Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- .../controllers/sales_and_purchase_return.py | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py index 3755506b6f5..6da23834b61 100644 --- a/erpnext/controllers/sales_and_purchase_return.py +++ b/erpnext/controllers/sales_and_purchase_return.py @@ -79,6 +79,9 @@ def validate_returned_items(doc): if doc.doctype in ["Purchase Invoice", "Purchase Receipt", "Subcontracting Receipt"]: select_fields += ",rejected_qty, received_qty" + if doc.doctype in ["Purchase Receipt", "Purchase Invoice"]: + select_fields += ",name" + for d in frappe.db.sql( f"""select {select_fields} from `tab{doc.doctype} Item` where parent = %s""", doc.return_against, @@ -104,15 +107,24 @@ def validate_returned_items(doc): items_returned = False for d in doc.get("items"): + key = d.item_code + raise_exception = False + if doc.doctype in ["Purchase Receipt", "Purchase Invoice"]: + field = frappe.scrub(doc.doctype) + "_item" + if d.get(field): + key = (d.item_code, d.get(field)) + raise_exception = True + if d.item_code and (flt(d.qty) < 0 or flt(d.get("received_qty")) < 0): - if d.item_code not in valid_items: - frappe.throw( + if key not in valid_items: + frappe.msgprint( _("Row # {0}: Returned Item {1} does not exist in {2} {3}").format( d.idx, d.item_code, doc.doctype, doc.return_against - ) + ), + raise_exception=raise_exception, ) else: - ref = valid_items.get(d.item_code, frappe._dict()) + ref = valid_items.get(key, frappe._dict()) validate_quantity(doc, d, ref, valid_items, already_returned_items) if ( @@ -193,8 +205,12 @@ def validate_quantity(doc, args, ref, valid_items, already_returned_items): def get_ref_item_dict(valid_items, ref_item_row): from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos + key = ref_item_row.item_code + if ref_item_row.get("name"): + key = (ref_item_row.item_code, ref_item_row.name) + valid_items.setdefault( - ref_item_row.item_code, + key, frappe._dict( { "qty": 0, @@ -208,7 +224,7 @@ def get_ref_item_dict(valid_items, ref_item_row): } ), ) - item_dict = valid_items[ref_item_row.item_code] + item_dict = valid_items[key] item_dict["qty"] += ref_item_row.qty item_dict["stock_qty"] += ref_item_row.get("stock_qty", 0) if ref_item_row.get("rate", 0) > item_dict["rate"]: From 4dcaf42bc5a78c5a707c82161f80e05e9ad99369 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Wed, 30 Oct 2024 12:04:17 +0530 Subject: [PATCH 441/734] fix: cannot create opp from lead without prospect (cherry picked from commit 603383bca7d017eca97a93c90a3dbd70bb69c9b2) --- erpnext/crm/doctype/lead/lead.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/crm/doctype/lead/lead.js b/erpnext/crm/doctype/lead/lead.js index 022d1906e84..325ee3d8d6e 100644 --- a/erpnext/crm/doctype/lead/lead.js +++ b/erpnext/crm/doctype/lead/lead.js @@ -127,8 +127,8 @@ erpnext.LeadController = class LeadController extends frappe.ui.form.Controller fieldname: "prospect_name", fieldtype: "Data", default: frm.doc.company_name, - reqd: 1, depends_on: "create_prospect", + mandatory_depends_on: "create_prospect", } ); } From 3648f3816f95b2aa4ac4425eea3cc51f63b2151d Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 30 Oct 2024 09:55:19 +0000 Subject: [PATCH 442/734] chore(release): Bumped to Version 15.40.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # [15.40.0](https://github.com/frappe/erpnext/compare/v15.39.6...v15.40.0) (2024-10-30) ### Bug Fixes * add company filter for project ([33fa1e4](https://github.com/frappe/erpnext/commit/33fa1e45ad27e41654335e65ca579a3099c5519d)) * add parenttype clause to invoice tax query in sales_register report ([603d2cf](https://github.com/frappe/erpnext/commit/603d2cf77dd8ca6bae612dc9b406a37416c3fb72)) * backport translations from develop ([#43849](https://github.com/frappe/erpnext/issues/43849)) ([11dd196](https://github.com/frappe/erpnext/commit/11dd1968c78ea1608edd8850854f7dcd4f998648)) * basic rate not editable in Stock Entry Detail (backport [#43837](https://github.com/frappe/erpnext/issues/43837)) ([#43838](https://github.com/frappe/erpnext/issues/43838)) ([20478b6](https://github.com/frappe/erpnext/commit/20478b632f82bd9d44bed28e37359b2dd2c7d989)) * better implementation, handle missing purchase order ([41db040](https://github.com/frappe/erpnext/commit/41db040a60c88afb8d61b52327227ad282488c3b)) * Calculate gross margin on update of project costing from invoices (backport [#43876](https://github.com/frappe/erpnext/issues/43876)) ([#43900](https://github.com/frappe/erpnext/issues/43900)) ([93d0db2](https://github.com/frappe/erpnext/commit/93d0db29100735cca01228ccf943a5abeb772bf9)) * calculate tds with net amount when invoice exceeds single threshold amount (backport [#43869](https://github.com/frappe/erpnext/issues/43869)) ([#43920](https://github.com/frappe/erpnext/issues/43920)) ([9a52661](https://github.com/frappe/erpnext/commit/9a526611e020a4bd1cbbbd56b1db67367d5b9fc6)) * cannot create opp from lead without prospect ([4dcaf42](https://github.com/frappe/erpnext/commit/4dcaf42bc5a78c5a707c82161f80e05e9ad99369)) * consider gle based on balances in company currency ([#43805](https://github.com/frappe/erpnext/issues/43805)) ([2fb4417](https://github.com/frappe/erpnext/commit/2fb441763a74bd4c5233e77f39611ee0c95b7efa)) * consider opening asset values while calculating asset depreciation rate ([1af2326](https://github.com/frappe/erpnext/commit/1af2326a52e969013745a390d2dbbcc54733512f)) * correct garbage value on Razorpay Payments Page ([2c21df2](https://github.com/frappe/erpnext/commit/2c21df2ad96f4eb2fee044ecd9b4828a3dad0ee3)) * do not check for payment terms details for return invoices. ([9a5604c](https://github.com/frappe/erpnext/commit/9a5604c5bb4f2c8d99fa59bdfb9bc16565aa6ffa)) * do not copy serial numbers from DN to SI (backport [#43885](https://github.com/frappe/erpnext/issues/43885)) ([#43893](https://github.com/frappe/erpnext/issues/43893)) ([d06831e](https://github.com/frappe/erpnext/commit/d06831ea9404852bcf730b6b17de6059ff5b1227)) * do not set payment terms for return invoices ([a826a89](https://github.com/frappe/erpnext/commit/a826a894f4d9ef2be1c59c4e97f0ceb475b569b4)) * find first PCV to consider opening entries ([8218ca9](https://github.com/frappe/erpnext/commit/8218ca990c1c1da4210935eefc7cd1138724d6bc)) * Handle None value for item description in customer portal invoice view (backport [#43823](https://github.com/frappe/erpnext/issues/43823)) ([#43889](https://github.com/frappe/erpnext/issues/43889)) ([8a72845](https://github.com/frappe/erpnext/commit/8a72845ee6fa7f954e6915272ea722f487907171)) * hide payment terms for return and paid purchase invoices ([29aa5d6](https://github.com/frappe/erpnext/commit/29aa5d64683d8af8864996c4f09214c17b58905d)) * incorrect value of available_qty_for_consumption in subcontracti… (backport [#43836](https://github.com/frappe/erpnext/issues/43836)) ([#43861](https://github.com/frappe/erpnext/issues/43861)) ([cd4746a](https://github.com/frappe/erpnext/commit/cd4746ad2a1cac13c97a798a07bb4abee322f29f)) * map doc from purchase order ([58a3ef7](https://github.com/frappe/erpnext/commit/58a3ef7aa6b5d446460bb25ba7cb0188cbdab66b)) * Patch for reposting account closing balance (backport [#43905](https://github.com/frappe/erpnext/issues/43905)) ([#43910](https://github.com/frappe/erpnext/issues/43910)) ([ab16207](https://github.com/frappe/erpnext/commit/ab162070a6dd08bc2a7217242dc5085d190b60ff)) * post account closing balance against pcv closing account (backport [#43887](https://github.com/frappe/erpnext/issues/43887)) ([#43898](https://github.com/frappe/erpnext/issues/43898)) ([e22d0a3](https://github.com/frappe/erpnext/commit/e22d0a3406932956598ac4e7024cdee68bf713d8)) * purchase return validation issue (backport [#43871](https://github.com/frappe/erpnext/issues/43871)) (backport [#43874](https://github.com/frappe/erpnext/issues/43874)) ([#43879](https://github.com/frappe/erpnext/issues/43879)) ([db3be41](https://github.com/frappe/erpnext/commit/db3be4195c3690e835355f0154bc29b602301ab8)) * recalculate outstanding after save on checkout for POS Invoice ([63668eb](https://github.com/frappe/erpnext/commit/63668eb855252cd2b0c39356c21660871463eae5)) * remarks field in payment reconciliation ([8707090](https://github.com/frappe/erpnext/commit/870709079b0434d810b792d17c93863c9dc008fb)) * **RFQ:** make strings translatable (backport [#43843](https://github.com/frappe/erpnext/issues/43843)) ([#43848](https://github.com/frappe/erpnext/issues/43848)) ([07aaef2](https://github.com/frappe/erpnext/commit/07aaef2af2413ff6c39b85514659d572096a6c73)) * rounding issue of required qty in subcontracting order ([#43908](https://github.com/frappe/erpnext/issues/43908)) ([9ac87bd](https://github.com/frappe/erpnext/commit/9ac87bd3b1547ba1d0c70cbacf26addbd4a3f637)) * scrub "-" from fieldname in accounting dimension ([c702826](https://github.com/frappe/erpnext/commit/c70282663c69955d49ce41c772056cfe02bae975)) * set bill_no before `against_voucher` gets concatenated ([81297ce](https://github.com/frappe/erpnext/commit/81297ce1683ec413a8f9b2fe153124452b7d1883)) * set default warehouse for pos invoice ([b80a5f2](https://github.com/frappe/erpnext/commit/b80a5f27a904be30572742ec446feea335a20a28)) * set proper currency format ([9f97018](https://github.com/frappe/erpnext/commit/9f970189feb9193fb80ac9564bb6d23bd3d05f66)) * Unnecessary validation for non deferred sales invoices ([#43816](https://github.com/frappe/erpnext/issues/43816)) ([bf4fb53](https://github.com/frappe/erpnext/commit/bf4fb53575be594e2e13e0c53cd4218fe4215ac5)) * use period closing voucher object to call get_account_closing_ba… (backport [#43880](https://github.com/frappe/erpnext/issues/43880)) ([#43883](https://github.com/frappe/erpnext/issues/43883)) ([8bfc212](https://github.com/frappe/erpnext/commit/8bfc212e262654ee9397fc435e2f08ccd286a520)) * validate fieldname ([b21abf4](https://github.com/frappe/erpnext/commit/b21abf4d9056496f016c9aedf0182bb8dff7688d)) * work order finish button not showing (backport [#43875](https://github.com/frappe/erpnext/issues/43875)) (backport [#43877](https://github.com/frappe/erpnext/issues/43877)) ([#43904](https://github.com/frappe/erpnext/issues/43904)) ([7189dab](https://github.com/frappe/erpnext/commit/7189daba193127350d74f5397147ab27a5525f60)) ### Features * add party name in payment request ([935f2e1](https://github.com/frappe/erpnext/commit/935f2e11e859002548c26061319ab29678ef85ed)) ### Performance Improvements * performance optimizations for accounting reports by refactoring account closing balance and period closing voucher ([#43798](https://github.com/frappe/erpnext/issues/43798)) ([ced76ca](https://github.com/frappe/erpnext/commit/ced76ca5c0b2e66e13781353ed71f1d95368bf87)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index b05af8dad95..1bf4cf4a827 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.39.6" +__version__ = "15.40.0" def get_default_company(user=None): From 4a749cec72c060f98662191f3dcaf801496d0ab9 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 30 Oct 2024 15:49:26 +0100 Subject: [PATCH 443/734] feat: remove Payroll Entry from Bank Account dashboard (backport #43931) (#43933) Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com> --- erpnext/accounts/doctype/bank_account/bank_account.json | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/erpnext/accounts/doctype/bank_account/bank_account.json b/erpnext/accounts/doctype/bank_account/bank_account.json index a5a7691eb76..962551b2417 100644 --- a/erpnext/accounts/doctype/bank_account/bank_account.json +++ b/erpnext/accounts/doctype/bank_account/bank_account.json @@ -224,11 +224,6 @@ "link_doctype": "Bank Guarantee", "link_fieldname": "bank_account" }, - { - "group": "Transactions", - "link_doctype": "Payroll Entry", - "link_fieldname": "bank_account" - }, { "group": "Transactions", "link_doctype": "Bank Transaction", @@ -255,7 +250,7 @@ "link_fieldname": "default_bank_account" } ], - "modified": "2024-09-24 06:57:41.292970", + "modified": "2024-10-30 09:41:14.113414", "modified_by": "Administrator", "module": "Accounts", "name": "Bank Account", From b343334f694b79a92faabbba03984dca086ca8c0 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 16 Oct 2024 17:01:47 +0530 Subject: [PATCH 444/734] feat: advance payment ledger doctype (cherry picked from commit 2d6efd7cc83e97186e07457567b1439b14b1fb57) --- .../advance_payment_ledger_entry/__init__.py | 0 .../advance_payment_ledger_entry.js | 8 ++ .../advance_payment_ledger_entry.json | 89 +++++++++++++++++++ .../advance_payment_ledger_entry.py | 27 ++++++ .../test_advance_payment_ledger_entry.py | 29 ++++++ 5 files changed, 153 insertions(+) create mode 100644 erpnext/accounts/doctype/advance_payment_ledger_entry/__init__.py create mode 100644 erpnext/accounts/doctype/advance_payment_ledger_entry/advance_payment_ledger_entry.js create mode 100644 erpnext/accounts/doctype/advance_payment_ledger_entry/advance_payment_ledger_entry.json create mode 100644 erpnext/accounts/doctype/advance_payment_ledger_entry/advance_payment_ledger_entry.py create mode 100644 erpnext/accounts/doctype/advance_payment_ledger_entry/test_advance_payment_ledger_entry.py diff --git a/erpnext/accounts/doctype/advance_payment_ledger_entry/__init__.py b/erpnext/accounts/doctype/advance_payment_ledger_entry/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/accounts/doctype/advance_payment_ledger_entry/advance_payment_ledger_entry.js b/erpnext/accounts/doctype/advance_payment_ledger_entry/advance_payment_ledger_entry.js new file mode 100644 index 00000000000..1a0dc1e7272 --- /dev/null +++ b/erpnext/accounts/doctype/advance_payment_ledger_entry/advance_payment_ledger_entry.js @@ -0,0 +1,8 @@ +// Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +// frappe.ui.form.on("Advance Payment Ledger Entry", { +// refresh(frm) { + +// }, +// }); diff --git a/erpnext/accounts/doctype/advance_payment_ledger_entry/advance_payment_ledger_entry.json b/erpnext/accounts/doctype/advance_payment_ledger_entry/advance_payment_ledger_entry.json new file mode 100644 index 00000000000..6f4792543d8 --- /dev/null +++ b/erpnext/accounts/doctype/advance_payment_ledger_entry/advance_payment_ledger_entry.json @@ -0,0 +1,89 @@ +{ + "actions": [], + "allow_rename": 1, + "creation": "2024-10-16 16:57:12.085072", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "company", + "voucher_type", + "voucher_no", + "against_voucher_type", + "against_voucher_no", + "amount", + "currency", + "event" + ], + "fields": [ + { + "fieldname": "voucher_type", + "fieldtype": "Link", + "label": "Voucher Type", + "options": "DocType" + }, + { + "fieldname": "voucher_no", + "fieldtype": "Dynamic Link", + "label": "Voucher No", + "options": "voucher_type" + }, + { + "fieldname": "against_voucher_type", + "fieldtype": "Link", + "label": "Against Voucher Type", + "options": "DocType" + }, + { + "fieldname": "against_voucher_no", + "fieldtype": "Dynamic Link", + "label": "Against Voucher No", + "options": "against_voucher_type" + }, + { + "fieldname": "amount", + "fieldtype": "Currency", + "label": "Amount" + }, + { + "fieldname": "currency", + "fieldtype": "Link", + "label": "Currency", + "options": "Currency" + }, + { + "fieldname": "event", + "fieldtype": "Data", + "label": "Event" + }, + { + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company" + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2024-10-16 17:08:09.334330", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Advance Payment Ledger Entry", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "creation", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/advance_payment_ledger_entry/advance_payment_ledger_entry.py b/erpnext/accounts/doctype/advance_payment_ledger_entry/advance_payment_ledger_entry.py new file mode 100644 index 00000000000..0ec2d411761 --- /dev/null +++ b/erpnext/accounts/doctype/advance_payment_ledger_entry/advance_payment_ledger_entry.py @@ -0,0 +1,27 @@ +# Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class AdvancePaymentLedgerEntry(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from frappe.types import DF + + against_voucher_no: DF.DynamicLink | None + against_voucher_type: DF.Link | None + amount: DF.Currency + company: DF.Link | None + currency: DF.Link | None + event: DF.Data | None + voucher_no: DF.DynamicLink | None + voucher_type: DF.Link | None + # end: auto-generated types + + pass diff --git a/erpnext/accounts/doctype/advance_payment_ledger_entry/test_advance_payment_ledger_entry.py b/erpnext/accounts/doctype/advance_payment_ledger_entry/test_advance_payment_ledger_entry.py new file mode 100644 index 00000000000..ef938062399 --- /dev/null +++ b/erpnext/accounts/doctype/advance_payment_ledger_entry/test_advance_payment_ledger_entry.py @@ -0,0 +1,29 @@ +# Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +# import frappe +from frappe.tests import IntegrationTestCase, UnitTestCase + +# On IntegrationTestCase, the doctype test records and all +# link-field test record depdendencies are recursively loaded +# Use these module variables to add/remove to/from that list +EXTRA_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"] +IGNORE_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"] + + +class TestAdvancePaymentLedgerEntry(UnitTestCase): + """ + Unit tests for AdvancePaymentLedgerEntry. + Use this class for testing individual functions and methods. + """ + + pass + + +class TestAdvancePaymentLedgerEntry(IntegrationTestCase): + """ + Integration tests for AdvancePaymentLedgerEntry. + Use this class for testing interactions between multiple components. + """ + + pass From 0d02f8b5d127b6f2dfcf8bf36a2bd6d1bb19aa0d Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 16 Oct 2024 17:11:50 +0530 Subject: [PATCH 445/734] refactor: make all fields readonly (cherry picked from commit f176a82198b894bfc289fcf8e506ce0d2c918573) --- .../advance_payment_ledger_entry.json | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/erpnext/accounts/doctype/advance_payment_ledger_entry/advance_payment_ledger_entry.json b/erpnext/accounts/doctype/advance_payment_ledger_entry/advance_payment_ledger_entry.json index 6f4792543d8..1d0a5b42a31 100644 --- a/erpnext/accounts/doctype/advance_payment_ledger_entry/advance_payment_ledger_entry.json +++ b/erpnext/accounts/doctype/advance_payment_ledger_entry/advance_payment_ledger_entry.json @@ -19,52 +19,60 @@ "fieldname": "voucher_type", "fieldtype": "Link", "label": "Voucher Type", - "options": "DocType" + "options": "DocType", + "read_only": 1 }, { "fieldname": "voucher_no", "fieldtype": "Dynamic Link", "label": "Voucher No", - "options": "voucher_type" + "options": "voucher_type", + "read_only": 1 }, { "fieldname": "against_voucher_type", "fieldtype": "Link", "label": "Against Voucher Type", - "options": "DocType" + "options": "DocType", + "read_only": 1 }, { "fieldname": "against_voucher_no", "fieldtype": "Dynamic Link", "label": "Against Voucher No", - "options": "against_voucher_type" + "options": "against_voucher_type", + "read_only": 1 }, { "fieldname": "amount", "fieldtype": "Currency", - "label": "Amount" + "label": "Amount", + "read_only": 1 }, { "fieldname": "currency", "fieldtype": "Link", "label": "Currency", - "options": "Currency" + "options": "Currency", + "read_only": 1 }, { "fieldname": "event", "fieldtype": "Data", - "label": "Event" + "label": "Event", + "read_only": 1 }, { "fieldname": "company", "fieldtype": "Link", "label": "Company", - "options": "Company" + "options": "Company", + "read_only": 1 } ], "index_web_pages_for_search": 1, "links": [], - "modified": "2024-10-16 17:08:09.334330", + "modified": "2024-10-16 17:11:28.143979", "modified_by": "Administrator", "module": "Accounts", "name": "Advance Payment Ledger Entry", From bf0b74bcbdb396ac5d1438034b4beb11f796cf78 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 16 Oct 2024 17:16:31 +0530 Subject: [PATCH 446/734] refactor: create advance ledger entries on submit and cancel (cherry picked from commit 575ca5b90048ea96bbb59c4e0d968349a29211aa) --- .../doctype/payment_entry/payment_entry.py | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index b645d92cb64..28ad4454596 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -110,6 +110,7 @@ class PaymentEntry(AccountsController): self.update_outstanding_amounts() self.update_payment_schedule() self.update_payment_requests() + self.make_advance_payment_ledger_entries() self.update_advance_paid() # advance_paid_status depends on the payment request amount self.set_status() @@ -197,6 +198,7 @@ class PaymentEntry(AccountsController): self.delink_advance_entry_references() self.update_payment_schedule(cancel=1) self.update_payment_requests(cancel=True) + self.make_advance_payment_ledger_entries() self.update_advance_paid() # advance_paid_status depends on the payment request amount self.set_status() @@ -1849,6 +1851,26 @@ class PaymentEntry(AccountsController): allocated_negative_outstanding, ) + def make_advance_payment_ledger_entries(self): + if self.docstatus == 1 or self.docstatus == 2: + advance_payment_doctypes = frappe.get_hooks( + "advance_payment_receivable_doctypes" + ) + frappe.get_hooks("advance_payment_payable_doctypes") + + advance_doctype_references = [ + x for x in self.references if x.reference_doctype in advance_payment_doctypes + ] + for x in advance_doctype_references: + doc = frappe.new_doc("Advance Payment Ledger Entry") + doc.company = self.company + doc.voucher_type = self.doctype + doc.voucher_no = self.name + doc.against_voucher_type = x.reference_doctype + doc.against_voucher_no = x.reference_name + doc.amount = x.allocated_amount if self.docstatus == 1 else -1 * x.allocated_amount + doc.event = "Submit" if self.docstatus == 1 else "Cancel" + doc.save() + @frappe.whitelist() def set_matched_payment_requests(self, matched_payment_requests): """ From 54f758c327d4e852ae10fc20570ddc59671efeae Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 16 Oct 2024 17:21:56 +0530 Subject: [PATCH 447/734] refactor: calculate advance from advance ledger (cherry picked from commit 2b2360bf7bedb771ca90d0a0a99f90eae693e179) # Conflicts: # erpnext/controllers/accounts_controller.py --- erpnext/controllers/accounts_controller.py | 25 ++++++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index e6fb5d0d153..22d59f428f2 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1935,22 +1935,33 @@ class AccountsController(TransactionBase): return stock_items +<<<<<<< HEAD def set_total_advance_paid(self): ple = frappe.qb.DocType("Payment Ledger Entry") party = self.customer if self.doctype == "Sales Order" else self.supplier +======= + def calculate_total_advance_from_ledger(self): + adv = frappe.qb.DocType("Advance Payment Ledger Entry") +>>>>>>> 2b2360bf7b (refactor: calculate advance from advance ledger) advance = ( - frappe.qb.from_(ple) - .select(ple.account_currency, Abs(Sum(ple.amount_in_account_currency)).as_("amount")) + frappe.qb.from_(adv) + .select(adv.currency, Abs(Sum(adv.amount)).as_("amount")) .where( - (ple.against_voucher_type == self.doctype) - & (ple.against_voucher_no == self.name) - & (ple.party == party) - & (ple.delinked == 0) - & (ple.company == self.company) + (adv.against_voucher_type == self.doctype) + & (adv.against_voucher_no == self.name) + & (adv.company == self.company) ) .run(as_dict=True) ) + return advance +<<<<<<< HEAD +======= + def set_total_advance_paid(self): + advance = self.calculate_total_advance_from_ledger() + advance_paid, order_total = None, None + +>>>>>>> 2b2360bf7b (refactor: calculate advance from advance ledger) if advance: advance = advance[0] From a6c26874c784b7ee5abea4916ee99f7344f15ab5 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 16 Oct 2024 17:26:20 +0530 Subject: [PATCH 448/734] refactor: remove advance payment ledgers on document deletion (cherry picked from commit 3c53b92f0561907ed71a41d82403ca77a088d3ed) --- erpnext/accounts/doctype/payment_entry/payment_entry.py | 1 + erpnext/controllers/accounts_controller.py | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 28ad4454596..abd6ea37e6d 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -191,6 +191,7 @@ class PaymentEntry(AccountsController): "Repost Accounting Ledger Items", "Unreconcile Payment", "Unreconcile Payment Entries", + "Advance Payment Ledger Entry", ) super().on_cancel() self.make_gl_entries(cancel=1) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 22d59f428f2..ffe28119041 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -345,9 +345,14 @@ class AccountsController(TransactionBase): repost_doc.flags.ignore_links = True repost_doc.save(ignore_permissions=True) + def _remove_advance_payment_ledger_entries(self): + adv = qb.DocType("Advance Payment Ledger Entry") + qb.from_(adv).delete().where(adv.voucher_type.eq(self.doctype) & adv.voucher_no.eq(self.name)).run() + def on_trash(self): from erpnext.accounts.utils import delete_exchange_gain_loss_journal + self._remove_advance_payment_ledger_entries() self._remove_references_in_repost_doctypes() self._remove_references_in_unreconcile() self.remove_serial_and_batch_bundle() From df25d33735ad79dca49c17a0daae8b0cc6fe39e7 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 17 Oct 2024 13:49:46 +0530 Subject: [PATCH 449/734] chore: remove duplicate test class (cherry picked from commit e2891a60d5a48205c544585032a0dae78975ac45) --- .../test_advance_payment_ledger_entry.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/erpnext/accounts/doctype/advance_payment_ledger_entry/test_advance_payment_ledger_entry.py b/erpnext/accounts/doctype/advance_payment_ledger_entry/test_advance_payment_ledger_entry.py index ef938062399..750a658102d 100644 --- a/erpnext/accounts/doctype/advance_payment_ledger_entry/test_advance_payment_ledger_entry.py +++ b/erpnext/accounts/doctype/advance_payment_ledger_entry/test_advance_payment_ledger_entry.py @@ -11,15 +11,6 @@ EXTRA_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"] IGNORE_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"] -class TestAdvancePaymentLedgerEntry(UnitTestCase): - """ - Unit tests for AdvancePaymentLedgerEntry. - Use this class for testing individual functions and methods. - """ - - pass - - class TestAdvancePaymentLedgerEntry(IntegrationTestCase): """ Integration tests for AdvancePaymentLedgerEntry. From a12df122a96d0e0313a475bec2b503707767b106 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 17 Oct 2024 13:56:24 +0530 Subject: [PATCH 450/734] refactor(test): advance_paid stays after reconciliation (cherry picked from commit c4197c3f31336ebca8ec2b81ee882ff2a43baccf) --- erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 7ac0d34e671..d05da0dbf19 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -1995,7 +1995,7 @@ class TestSalesInvoice(FrappeTestCase): # Check if SO is unlinked/replaced by SI in PE & if SO advance paid is 0 self.assertEqual(pe.references[0].reference_name, si.name) - self.assertEqual(sales_order.advance_paid, 0.0) + self.assertEqual(sales_order.advance_paid, 300.0) # check outstanding after advance allocation self.assertEqual( From d84a3c4f29df46156d94cef8ad195eaf59424e06 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 17 Oct 2024 14:31:18 +0530 Subject: [PATCH 451/734] fix: deleting SO/PO will remove its advance payment ledger entry (cherry picked from commit 14357bccba2c5a6e21091fc99239b91dcc7b647f) --- erpnext/controllers/accounts_controller.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index ffe28119041..23498ad6883 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -349,6 +349,14 @@ class AccountsController(TransactionBase): adv = qb.DocType("Advance Payment Ledger Entry") qb.from_(adv).delete().where(adv.voucher_type.eq(self.doctype) & adv.voucher_no.eq(self.name)).run() + advance_payment_doctypes = frappe.get_hooks("advance_payment_receivable_doctypes") + frappe.get_hooks( + "advance_payment_payable_doctypes" + ) + if self.doctype in advance_payment_doctypes: + qb.from_(adv).delete().where( + adv.against_voucher_type.eq(self.doctype) & adv.against_voucher_no.eq(self.name) + ).run() + def on_trash(self): from erpnext.accounts.utils import delete_exchange_gain_loss_journal From ffd426d43db54349cd4c969c66fee84e5ad54301 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 17 Oct 2024 16:51:16 +0530 Subject: [PATCH 452/734] refactor: link journal entry to advance payment ledger (cherry picked from commit fca5e952484af634f0f1268258dd0bf8c56bb73a) --- .../doctype/journal_entry/journal_entry.py | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index 47626492e84..d11cc27ccb7 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -188,6 +188,7 @@ class JournalEntry(AccountsController): self.validate_cheque_info() self.check_credit_limit() self.make_gl_entries() + self.make_advance_payment_ledger_entries() self.update_advance_paid() self.update_asset_value() self.update_inter_company_jv() @@ -220,6 +221,7 @@ class JournalEntry(AccountsController): "Unreconcile Payment Entries", ) self.make_gl_entries(1) + self.make_advance_payment_ledger_entries() self.update_advance_paid() self.unlink_advance_entry_reference() self.unlink_asset_reference() @@ -231,6 +233,32 @@ class JournalEntry(AccountsController): def get_title(self): return self.pay_to_recd_from or self.accounts[0].account + def make_advance_payment_ledger_entries(self): + if self.docstatus == 1 or self.docstatus == 2: + advance_payment_doctypes = frappe.get_hooks( + "advance_payment_receivable_doctypes" + ) + frappe.get_hooks("advance_payment_payable_doctypes") + + advance_doctype_references = [ + x for x in self.accounts if x.reference_type in advance_payment_doctypes + ] + + for x in advance_doctype_references: + # Looking for payments + dr_or_cr = "credit" if x.account_type == "Receivable" else "debit" + + amount = x.get(dr_or_cr) + if amount > 0: + doc = frappe.new_doc("Advance Payment Ledger Entry") + doc.company = self.company + doc.voucher_type = self.doctype + doc.voucher_no = self.name + doc.against_voucher_type = x.reference_type + doc.against_voucher_no = x.reference_name + doc.amount = amount if self.docstatus == 1 else -1 * amount + doc.event = "Submit" if self.docstatus == 1 else "Cancel" + doc.save() + def update_advance_paid(self): advance_paid = frappe._dict() for d in self.get("accounts"): From cb36dcb3823a3d50907e3084819db33271a52345 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 18 Oct 2024 13:32:28 +0530 Subject: [PATCH 453/734] refactor(test): reconciliation shouldn't affect advance paid (cherry picked from commit 35a8a187283efe4a9ce325017278d1e3b0bbdfbc) --- .../unreconcile_payment/test_unreconcile_payment.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/unreconcile_payment/test_unreconcile_payment.py b/erpnext/accounts/doctype/unreconcile_payment/test_unreconcile_payment.py index 13e5294aa78..3d222b22ff8 100644 --- a/erpnext/accounts/doctype/unreconcile_payment/test_unreconcile_payment.py +++ b/erpnext/accounts/doctype/unreconcile_payment/test_unreconcile_payment.py @@ -362,10 +362,14 @@ class TestUnreconcilePayment(AccountsTestMixin, FrappeTestCase): # Assert 'Advance Paid' so.reload() pe.reload() - self.assertEqual(so.advance_paid, 0) + self.assertEqual(so.advance_paid, 100) self.assertEqual(len(pe.references), 0) self.assertEqual(pe.unallocated_amount, 100) + pe.cancel() + so.reload() + self.assertEqual(so.advance_paid, 100) + def test_06_unreconcile_advance_from_payment_entry(self): self.enable_advance_as_liability() so1 = self.create_sales_order() @@ -411,7 +415,7 @@ class TestUnreconcilePayment(AccountsTestMixin, FrappeTestCase): so2.reload() pe.reload() self.assertEqual(so1.advance_paid, 150) - self.assertEqual(so2.advance_paid, 0) + self.assertEqual(so2.advance_paid, 110) self.assertEqual(len(pe.references), 1) self.assertEqual(pe.unallocated_amount, 110) @@ -459,6 +463,6 @@ class TestUnreconcilePayment(AccountsTestMixin, FrappeTestCase): # Assert 'Advance Paid' so.reload() - self.assertEqual(so.advance_paid, 0) + self.assertEqual(so.advance_paid, 1000) self.disable_advance_as_liability() From 085e0455d80a853dddb01fe34123f613b51b1693 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 18 Oct 2024 13:48:15 +0530 Subject: [PATCH 454/734] refactor: patch to migrating old SO / PO to advance ledger (cherry picked from commit b927f2f4a0744db8680dcd9d664ec4c9ab9453f0) --- .../create_advance_payment_ledger_records.py | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 erpnext/patches/v15_0/create_advance_payment_ledger_records.py diff --git a/erpnext/patches/v15_0/create_advance_payment_ledger_records.py b/erpnext/patches/v15_0/create_advance_payment_ledger_records.py new file mode 100644 index 00000000000..64d8e3c0f3c --- /dev/null +++ b/erpnext/patches/v15_0/create_advance_payment_ledger_records.py @@ -0,0 +1,71 @@ +import frappe +from frappe import qb +from frappe.query_builder.custom import ConstantColumn + + +def get_advance_doctypes() -> list: + return frappe.get_hooks("advance_payment_receivable_doctypes") + frappe.get_hooks( + "advance_payment_payable_doctypes" + ) + + +def get_payments_with_so_po_reference() -> list: + advance_doctypes = get_advance_doctypes() + per = qb.DocType("Payment Entry Reference") + payments_with_reference = ( + qb.from_(per) + .select(per.parent) + .distinct() + .where(per.reference_doctype.isin(advance_doctypes) & per.docstatus.eq(1)) + .run() + ) + pe = qb.DocType("Payment Entry") + advance_payment_entries = ( + qb.from_(pe) + .select(ConstantColumn("Payment Entry").as_("doctype")) + .select(pe.name) + .where(pe.name.isin(payments_with_reference) & pe.docstatus.eq(1)) + .run(as_dict=True) + ) + + return advance_payment_entries + + +def get_journals_with_so_po_reference() -> list: + advance_doctypes = get_advance_doctypes() + jea = qb.DocType("Journal Entry Account") + journals_with_reference = ( + qb.from_(jea) + .select(jea.parent) + .distinct() + .where(jea.reference_type.isin(advance_doctypes) & jea.docstatus.eq(1)) + .run() + ) + je = qb.DocType("Journal Entry") + advance_payment_entries = ( + qb.from_(je) + .select(ConstantColumn("Journal Entry").as_("doctype")) + .select(je.name) + .where(je.name.isin(journals_with_reference) & je.docstatus.eq(1)) + .run(as_dict=True) + ) + + return advance_payment_entries + + +def make_advance_ledger_entries(vouchers: list): + for x in vouchers: + frappe.get_doc(x.doctype, x.name).make_advance_payment_ledger_entries() + + +def execute(): + """ + Description: + Create Advance Payment Ledger Entry for all Payments made against Sales / Purchase Orders + """ + frappe.db.truncate("Advance Payment Ledger Entry") + payment_entries = get_payments_with_so_po_reference() + make_advance_ledger_entries(payment_entries) + + journals = get_journals_with_so_po_reference() + make_advance_ledger_entries(journals) From c6bfdcf50319486ab7348420667b5d4060e666c8 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 18 Oct 2024 16:22:38 +0530 Subject: [PATCH 455/734] chore: update patchex.txt (cherry picked from commit 8ab7194b1d6112af4ddb8034fb34dd9d49cf623f) --- erpnext/patches.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index f48dc00fc8e..64c74f9d645 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -359,6 +359,7 @@ erpnext.patches.v15_0.allow_on_submit_dimensions_for_repostable_doctypes erpnext.patches.v14_0.update_flag_for_return_invoices #2024-03-22 erpnext.patches.v15_0.create_accounting_dimensions_in_payment_request erpnext.patches.v14_0.update_pos_return_ledger_entries #2024-08-16 +erpnext.patches.v15_0.create_advance_payment_ledger_records # below migration patch should always run last erpnext.patches.v14_0.migrate_gl_to_payment_ledger erpnext.stock.doctype.delivery_note.patches.drop_unused_return_against_index # 2023-12-20 From 063cef576cbebb8fb69b980a14d39b42caada964 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 18 Oct 2024 16:25:58 +0530 Subject: [PATCH 456/734] chore: update ignore_linked_doctypes for Journal Entry (cherry picked from commit 767ae6a372969ecbd03e7b66af3d9ede4202e9b0) --- erpnext/accounts/doctype/journal_entry/journal_entry.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index d11cc27ccb7..bd25b13e999 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -219,6 +219,7 @@ class JournalEntry(AccountsController): "Repost Accounting Ledger Items", "Unreconcile Payment", "Unreconcile Payment Entries", + "Advance Payment Ledger Entry", ) self.make_gl_entries(1) self.make_advance_payment_ledger_entries() From 164d7cc896138b1c7bbfbdf00efe39fdb71c9dbb Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 22 Oct 2024 16:07:14 +0530 Subject: [PATCH 457/734] refactor: handle 'no data' situation in patch (cherry picked from commit 8e3bf7dc09dfdbe51043179dd32c04ab1f61f23e) --- .../create_advance_payment_ledger_records.py | 38 ++++++++++--------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/erpnext/patches/v15_0/create_advance_payment_ledger_records.py b/erpnext/patches/v15_0/create_advance_payment_ledger_records.py index 64d8e3c0f3c..13b4d95c760 100644 --- a/erpnext/patches/v15_0/create_advance_payment_ledger_records.py +++ b/erpnext/patches/v15_0/create_advance_payment_ledger_records.py @@ -10,6 +10,7 @@ def get_advance_doctypes() -> list: def get_payments_with_so_po_reference() -> list: + advance_payment_entries = [] advance_doctypes = get_advance_doctypes() per = qb.DocType("Payment Entry Reference") payments_with_reference = ( @@ -19,19 +20,21 @@ def get_payments_with_so_po_reference() -> list: .where(per.reference_doctype.isin(advance_doctypes) & per.docstatus.eq(1)) .run() ) - pe = qb.DocType("Payment Entry") - advance_payment_entries = ( - qb.from_(pe) - .select(ConstantColumn("Payment Entry").as_("doctype")) - .select(pe.name) - .where(pe.name.isin(payments_with_reference) & pe.docstatus.eq(1)) - .run(as_dict=True) - ) + if payments_with_reference: + pe = qb.DocType("Payment Entry") + advance_payment_entries = ( + qb.from_(pe) + .select(ConstantColumn("Payment Entry").as_("doctype")) + .select(pe.name) + .where(pe.name.isin(payments_with_reference) & pe.docstatus.eq(1)) + .run(as_dict=True) + ) return advance_payment_entries def get_journals_with_so_po_reference() -> list: + advance_journal_entries = [] advance_doctypes = get_advance_doctypes() jea = qb.DocType("Journal Entry Account") journals_with_reference = ( @@ -41,16 +44,17 @@ def get_journals_with_so_po_reference() -> list: .where(jea.reference_type.isin(advance_doctypes) & jea.docstatus.eq(1)) .run() ) - je = qb.DocType("Journal Entry") - advance_payment_entries = ( - qb.from_(je) - .select(ConstantColumn("Journal Entry").as_("doctype")) - .select(je.name) - .where(je.name.isin(journals_with_reference) & je.docstatus.eq(1)) - .run(as_dict=True) - ) + if journals_with_reference: + je = qb.DocType("Journal Entry") + advance_journal_entries = ( + qb.from_(je) + .select(ConstantColumn("Journal Entry").as_("doctype")) + .select(je.name) + .where(je.name.isin(journals_with_reference) & je.docstatus.eq(1)) + .run(as_dict=True) + ) - return advance_payment_entries + return advance_journal_entries def make_advance_ledger_entries(vouchers: list): From 68a95c7dbc943933e65ee0d08d30fdf6a2cbd592 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 22 Oct 2024 17:04:35 +0530 Subject: [PATCH 458/734] refactor: move creation logic to controller (cherry picked from commit ad88bde448d96cf50ece9a65fb3110b8bd89257f) --- .../doctype/journal_entry/journal_entry.py | 26 ---------- .../doctype/payment_entry/payment_entry.py | 20 -------- erpnext/controllers/accounts_controller.py | 50 +++++++++++++++++++ 3 files changed, 50 insertions(+), 46 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index bd25b13e999..fb5c563d790 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -234,32 +234,6 @@ class JournalEntry(AccountsController): def get_title(self): return self.pay_to_recd_from or self.accounts[0].account - def make_advance_payment_ledger_entries(self): - if self.docstatus == 1 or self.docstatus == 2: - advance_payment_doctypes = frappe.get_hooks( - "advance_payment_receivable_doctypes" - ) + frappe.get_hooks("advance_payment_payable_doctypes") - - advance_doctype_references = [ - x for x in self.accounts if x.reference_type in advance_payment_doctypes - ] - - for x in advance_doctype_references: - # Looking for payments - dr_or_cr = "credit" if x.account_type == "Receivable" else "debit" - - amount = x.get(dr_or_cr) - if amount > 0: - doc = frappe.new_doc("Advance Payment Ledger Entry") - doc.company = self.company - doc.voucher_type = self.doctype - doc.voucher_no = self.name - doc.against_voucher_type = x.reference_type - doc.against_voucher_no = x.reference_name - doc.amount = amount if self.docstatus == 1 else -1 * amount - doc.event = "Submit" if self.docstatus == 1 else "Cancel" - doc.save() - def update_advance_paid(self): advance_paid = frappe._dict() for d in self.get("accounts"): diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index abd6ea37e6d..8832b87eec7 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -1852,26 +1852,6 @@ class PaymentEntry(AccountsController): allocated_negative_outstanding, ) - def make_advance_payment_ledger_entries(self): - if self.docstatus == 1 or self.docstatus == 2: - advance_payment_doctypes = frappe.get_hooks( - "advance_payment_receivable_doctypes" - ) + frappe.get_hooks("advance_payment_payable_doctypes") - - advance_doctype_references = [ - x for x in self.references if x.reference_doctype in advance_payment_doctypes - ] - for x in advance_doctype_references: - doc = frappe.new_doc("Advance Payment Ledger Entry") - doc.company = self.company - doc.voucher_type = self.doctype - doc.voucher_no = self.name - doc.against_voucher_type = x.reference_doctype - doc.against_voucher_no = x.reference_name - doc.amount = x.allocated_amount if self.docstatus == 1 else -1 * x.allocated_amount - doc.event = "Submit" if self.docstatus == 1 else "Cancel" - doc.save() - @frappe.whitelist() def set_matched_payment_requests(self, matched_payment_requests): """ diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 23498ad6883..a7a36b7e643 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -2570,6 +2570,56 @@ class AccountsController(TransactionBase): repost_ledger.insert() repost_ledger.submit() + def get_advance_payment_doctypes(self) -> list: + return frappe.get_hooks("advance_payment_receivable_doctypes") + frappe.get_hooks( + "advance_payment_payable_doctypes" + ) + + def make_advance_payment_ledger_for_journal(self): + advance_payment_doctypes = self.get_advance_payment_doctypes() + advance_doctype_references = [ + x for x in self.accounts if x.reference_type in advance_payment_doctypes + ] + + for x in advance_doctype_references: + # Looking for payments + dr_or_cr = "credit" if x.account_type == "Receivable" else "debit" + + amount = x.get(dr_or_cr) + if amount > 0: + doc = frappe.new_doc("Advance Payment Ledger Entry") + doc.company = self.company + doc.voucher_type = self.doctype + doc.voucher_no = self.name + doc.against_voucher_type = x.reference_type + doc.against_voucher_no = x.reference_name + doc.amount = amount if self.docstatus == 1 else -1 * amount + doc.event = "Submit" if self.docstatus == 1 else "Cancel" + doc.save() + + def make_advance_payment_ledger_for_payment(self): + advance_payment_doctypes = self.get_advance_payment_doctypes() + advance_doctype_references = [ + x for x in self.references if x.reference_doctype in advance_payment_doctypes + ] + for x in advance_doctype_references: + doc = frappe.new_doc("Advance Payment Ledger Entry") + doc.company = self.company + doc.voucher_type = self.doctype + doc.voucher_no = self.name + doc.against_voucher_type = x.reference_doctype + doc.against_voucher_no = x.reference_name + doc.amount = x.allocated_amount if self.docstatus == 1 else -1 * x.allocated_amount + doc.event = "Submit" if self.docstatus == 1 else "Cancel" + doc.save() + + def make_advance_payment_ledger_entries(self): + if self.docstatus != 0: + if self.doctype == "Journal Entry": + self.make_advance_payment_ledger_for_journal() + elif self.doctype == "Payment Entry": + self.make_advance_payment_ledger_for_payment() + @frappe.whitelist() def get_tax_rate(account_head): From 07a394a1c509b0539041f5fc88f1d000ff749951 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 30 Oct 2024 10:16:47 +0530 Subject: [PATCH 459/734] refactor: handle currency on advance payment ledger (cherry picked from commit ae6a81cd07e5f78a4b6107028a1322ec82978ae9) --- erpnext/controllers/accounts_controller.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index a7a36b7e643..6870c06bf5d 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1958,7 +1958,7 @@ class AccountsController(TransactionBase): >>>>>>> 2b2360bf7b (refactor: calculate advance from advance ledger) advance = ( frappe.qb.from_(adv) - .select(adv.currency, Abs(Sum(adv.amount)).as_("amount")) + .select(adv.currency.as_("account_currency"), Abs(Sum(adv.amount)).as_("amount")) .where( (adv.against_voucher_type == self.doctype) & (adv.against_voucher_no == self.name) @@ -2602,6 +2602,11 @@ class AccountsController(TransactionBase): advance_doctype_references = [ x for x in self.references if x.reference_doctype in advance_payment_doctypes ] + currency = ( + self.paid_from_account_currency + if self.payment_type == "Receive" + else self.paid_to_account_currency + ) for x in advance_doctype_references: doc = frappe.new_doc("Advance Payment Ledger Entry") doc.company = self.company @@ -2610,6 +2615,7 @@ class AccountsController(TransactionBase): doc.against_voucher_type = x.reference_doctype doc.against_voucher_no = x.reference_name doc.amount = x.allocated_amount if self.docstatus == 1 else -1 * x.allocated_amount + doc.currency = currency doc.event = "Submit" if self.docstatus == 1 else "Cancel" doc.save() From d830ce1d88f7377627f047458f8c07b11b6b3725 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 30 Oct 2024 12:21:41 +0530 Subject: [PATCH 460/734] test: USD Sales Order with advance payment (cherry picked from commit 6c731561f3afb7208a7d8489f25c6d2008d6fd71) --- .../doctype/sales_order/test_sales_order.py | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index 244a6b1ddad..c2c587899d4 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -1327,6 +1327,64 @@ class TestSalesOrder(AccountsTestMixin, FrappeTestCase): so.reload() self.assertEqual(so.advance_paid, 0) + def create_foreign_currency_usd_account(self): + account_name = "Debtors USD" + if not frappe.db.get_value( + "Account", filters={"account_name": account_name, "company": "_Test Company"} + ): + acc = frappe.new_doc("Account") + acc.account_name = account_name + acc.parent_account = "Accounts Receivable - _TC" + acc.company = "_Test Company" + acc.account_currency = "USD" + acc.account_type = "Receivable" + acc.insert() + else: + name = frappe.db.get_value( + "Account", + filters={"account_name": account_name, "company": "_Test Company"}, + fieldname="name", + pluck=True, + ) + acc = frappe.get_doc("Account", name) + self.debtors_usd = acc.name + + def test_advance_paid_and_currency_with_payment(self): + from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry + + self.create_customer("_Test USD Customer", "USD") + self.create_foreign_currency_usd_account() + + so = make_sales_order(customer=self.customer, currency="USD", qty=1, rate=100, do_not_submit=True) + so.conversion_rate = 80 + so.submit() + + pe_exchange_rate = 85 + pe = get_payment_entry("Sales Order", so.name, bank_account="_Test Bank - _TC") + pe.reference_no = "1" + pe.reference_date = nowdate() + pe.paid_from = self.debtors_usd + pe.paid_from_account_currency = "USD" + pe.source_exchange_rate = pe_exchange_rate + pe.paid_amount = so.grand_total + pe.received_amount = pe_exchange_rate * pe.paid_amount + pe.references[0].outstanding_amount = 100 + pe.references[0].total_amount = 100 + pe.references[0].allocated_amount = 100 + pe.save().submit() + + so.reload() + self.assertEqual(so.advance_paid, 100) + self.assertEqual(so.party_account_currency, "USD") + + # cancel advance payment + pe.reload() + pe.cancel() + + so.reload() + self.assertEqual(so.advance_paid, 0) + self.assertEqual(so.party_account_currency, "USD") + def test_cancel_sales_order_after_cancel_payment_entry(self): from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry From c8be4f3f781119516128d113365da736a149ebc1 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 30 Oct 2024 12:58:29 +0530 Subject: [PATCH 461/734] refactor: use dr / cr account currency field for journals (cherry picked from commit 9c1a4e284c0cbc0f943920a5501a6503ce4c5fde) --- erpnext/controllers/accounts_controller.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 6870c06bf5d..03910516dea 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -2583,7 +2583,11 @@ class AccountsController(TransactionBase): for x in advance_doctype_references: # Looking for payments - dr_or_cr = "credit" if x.account_type == "Receivable" else "debit" + dr_or_cr = ( + "credit_in_account_currency" + if x.account_type == "Receivable" + else "debit_in_account_currency" + ) amount = x.get(dr_or_cr) if amount > 0: @@ -2595,6 +2599,7 @@ class AccountsController(TransactionBase): doc.against_voucher_no = x.reference_name doc.amount = amount if self.docstatus == 1 else -1 * amount doc.event = "Submit" if self.docstatus == 1 else "Cancel" + doc.currency = x.account_currency doc.save() def make_advance_payment_ledger_for_payment(self): From 7f9ae4e0447a2572a9909b88753e9311365ef464 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 30 Oct 2024 13:07:39 +0530 Subject: [PATCH 462/734] test: advance and currency from Journal (cherry picked from commit 18250825126be744b6bf4db3ce6bf23670d0a12a) --- .../doctype/sales_order/test_sales_order.py | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index c2c587899d4..640669efbb7 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -1385,6 +1385,57 @@ class TestSalesOrder(AccountsTestMixin, FrappeTestCase): self.assertEqual(so.advance_paid, 0) self.assertEqual(so.party_account_currency, "USD") + def test_advance_paid_and_currency_with_journal(self): + from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry + + self.create_customer("_Test USD Customer", "USD") + self.create_foreign_currency_usd_account() + + so = make_sales_order(customer=self.customer, currency="USD", qty=1, rate=100, do_not_submit=True) + so.conversion_rate = 80 + so.submit() + + je_exchange_rate = 85 + je = frappe.get_doc( + { + "doctype": "Journal Entry", + "company": "_Test Company", + "voucher_type": "Journal Entry", + "posting_date": so.transaction_date, + "multi_currency": True, + "accounts": [ + { + "account": self.debtors_usd, + "party_type": "Customer", + "party": so.customer, + "credit": 8500, + "credit_in_account_currency": 100, + "is_advance": "Yes", + "reference_type": so.doctype, + "reference_name": so.name, + "exchange_rate": je_exchange_rate, + }, + { + "account": "_Test Bank - _TC", + "debit": 8500, + "debit_in_account_currency": 8500, + }, + ], + } + ) + je.save().submit() + so.reload() + self.assertEqual(so.advance_paid, 100) + self.assertEqual(so.party_account_currency, "USD") + + # cancel advance payment + je.reload() + je.cancel() + + so.reload() + self.assertEqual(so.advance_paid, 0) + self.assertEqual(so.party_account_currency, "USD") + def test_cancel_sales_order_after_cancel_payment_entry(self): from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry From 16c1fc75b5683cb75ab482ce63c07ab07e1cfb06 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 31 Oct 2024 10:08:41 +0530 Subject: [PATCH 463/734] chore: move tests to advance payment ledger doctype (cherry picked from commit 14cef3d4c4056bc4840c5a840c43b26fd803b388) --- .../test_advance_payment_ledger_entry.py | 116 +++++++++++++++++- .../doctype/sales_order/test_sales_order.py | 109 ---------------- 2 files changed, 113 insertions(+), 112 deletions(-) diff --git a/erpnext/accounts/doctype/advance_payment_ledger_entry/test_advance_payment_ledger_entry.py b/erpnext/accounts/doctype/advance_payment_ledger_entry/test_advance_payment_ledger_entry.py index 750a658102d..c953291ac6f 100644 --- a/erpnext/accounts/doctype/advance_payment_ledger_entry/test_advance_payment_ledger_entry.py +++ b/erpnext/accounts/doctype/advance_payment_ledger_entry/test_advance_payment_ledger_entry.py @@ -1,8 +1,13 @@ # Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt -# import frappe +import frappe from frappe.tests import IntegrationTestCase, UnitTestCase +from frappe.utils import nowdate, today + +from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry +from erpnext.accounts.test.accounts_mixin import AccountsTestMixin +from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order # On IntegrationTestCase, the doctype test records and all # link-field test record depdendencies are recursively loaded @@ -11,10 +16,115 @@ EXTRA_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"] IGNORE_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"] -class TestAdvancePaymentLedgerEntry(IntegrationTestCase): +class TestAdvancePaymentLedgerEntry(AccountsTestMixin, IntegrationTestCase): """ Integration tests for AdvancePaymentLedgerEntry. Use this class for testing interactions between multiple components. """ - pass + def setUp(self): + self.create_company() + self.create_usd_receivable_account() + self.create_usd_payable_account() + self.create_item() + self.clear_old_entries() + + def tearDown(self): + frappe.db.rollback() + + def create_sales_order(self, qty=1, rate=100, currency="INR", do_not_submit=False): + """ + Helper method + """ + so = make_sales_order( + company=self.company, + customer=self.customer, + currency=currency, + item=self.item, + qty=qty, + rate=rate, + transaction_date=today(), + do_not_submit=do_not_submit, + ) + return so + + def test_so_advance_paid_and_currency_with_payment(self): + self.create_customer("_Test USD Customer", "USD") + + so = self.create_sales_order(currency="USD", do_not_submit=True) + so.conversion_rate = 80 + so.submit() + + pe_exchange_rate = 85 + pe = get_payment_entry(so.doctype, so.name, bank_account=self.cash) + pe.reference_no = "1" + pe.reference_date = nowdate() + pe.paid_from = self.debtors_usd + pe.paid_from_account_currency = "USD" + pe.source_exchange_rate = pe_exchange_rate + pe.paid_amount = so.grand_total + pe.received_amount = pe_exchange_rate * pe.paid_amount + pe.references[0].outstanding_amount = 100 + pe.references[0].total_amount = 100 + pe.references[0].allocated_amount = 100 + pe.save().submit() + + so.reload() + self.assertEqual(so.advance_paid, 100) + self.assertEqual(so.party_account_currency, "USD") + + # cancel advance payment + pe.reload() + pe.cancel() + + so.reload() + self.assertEqual(so.advance_paid, 0) + self.assertEqual(so.party_account_currency, "USD") + + def test_so_advance_paid_and_currency_with_journal(self): + self.create_customer("_Test USD Customer", "USD") + + so = self.create_sales_order(currency="USD", do_not_submit=True) + so.conversion_rate = 80 + so.submit() + + je_exchange_rate = 85 + je = frappe.get_doc( + { + "doctype": "Journal Entry", + "company": self.company, + "voucher_type": "Journal Entry", + "posting_date": so.transaction_date, + "multi_currency": True, + "accounts": [ + { + "account": self.debtors_usd, + "party_type": "Customer", + "party": so.customer, + "credit": 8500, + "credit_in_account_currency": 100, + "is_advance": "Yes", + "reference_type": so.doctype, + "reference_name": so.name, + "exchange_rate": je_exchange_rate, + }, + { + "account": self.cash, + "debit": 8500, + "debit_in_account_currency": 8500, + }, + ], + } + ) + je.save().submit() + so.reload() + self.assertEqual(so.advance_paid, 100) + self.assertEqual(so.party_account_currency, "USD") + + # cancel advance payment + je.reload() + je.cancel() + + so.reload() + self.assertEqual(so.advance_paid, 0) + self.assertEqual(so.party_account_currency, "USD") diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index 640669efbb7..244a6b1ddad 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -1327,115 +1327,6 @@ class TestSalesOrder(AccountsTestMixin, FrappeTestCase): so.reload() self.assertEqual(so.advance_paid, 0) - def create_foreign_currency_usd_account(self): - account_name = "Debtors USD" - if not frappe.db.get_value( - "Account", filters={"account_name": account_name, "company": "_Test Company"} - ): - acc = frappe.new_doc("Account") - acc.account_name = account_name - acc.parent_account = "Accounts Receivable - _TC" - acc.company = "_Test Company" - acc.account_currency = "USD" - acc.account_type = "Receivable" - acc.insert() - else: - name = frappe.db.get_value( - "Account", - filters={"account_name": account_name, "company": "_Test Company"}, - fieldname="name", - pluck=True, - ) - acc = frappe.get_doc("Account", name) - self.debtors_usd = acc.name - - def test_advance_paid_and_currency_with_payment(self): - from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry - - self.create_customer("_Test USD Customer", "USD") - self.create_foreign_currency_usd_account() - - so = make_sales_order(customer=self.customer, currency="USD", qty=1, rate=100, do_not_submit=True) - so.conversion_rate = 80 - so.submit() - - pe_exchange_rate = 85 - pe = get_payment_entry("Sales Order", so.name, bank_account="_Test Bank - _TC") - pe.reference_no = "1" - pe.reference_date = nowdate() - pe.paid_from = self.debtors_usd - pe.paid_from_account_currency = "USD" - pe.source_exchange_rate = pe_exchange_rate - pe.paid_amount = so.grand_total - pe.received_amount = pe_exchange_rate * pe.paid_amount - pe.references[0].outstanding_amount = 100 - pe.references[0].total_amount = 100 - pe.references[0].allocated_amount = 100 - pe.save().submit() - - so.reload() - self.assertEqual(so.advance_paid, 100) - self.assertEqual(so.party_account_currency, "USD") - - # cancel advance payment - pe.reload() - pe.cancel() - - so.reload() - self.assertEqual(so.advance_paid, 0) - self.assertEqual(so.party_account_currency, "USD") - - def test_advance_paid_and_currency_with_journal(self): - from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry - - self.create_customer("_Test USD Customer", "USD") - self.create_foreign_currency_usd_account() - - so = make_sales_order(customer=self.customer, currency="USD", qty=1, rate=100, do_not_submit=True) - so.conversion_rate = 80 - so.submit() - - je_exchange_rate = 85 - je = frappe.get_doc( - { - "doctype": "Journal Entry", - "company": "_Test Company", - "voucher_type": "Journal Entry", - "posting_date": so.transaction_date, - "multi_currency": True, - "accounts": [ - { - "account": self.debtors_usd, - "party_type": "Customer", - "party": so.customer, - "credit": 8500, - "credit_in_account_currency": 100, - "is_advance": "Yes", - "reference_type": so.doctype, - "reference_name": so.name, - "exchange_rate": je_exchange_rate, - }, - { - "account": "_Test Bank - _TC", - "debit": 8500, - "debit_in_account_currency": 8500, - }, - ], - } - ) - je.save().submit() - so.reload() - self.assertEqual(so.advance_paid, 100) - self.assertEqual(so.party_account_currency, "USD") - - # cancel advance payment - je.reload() - je.cancel() - - so.reload() - self.assertEqual(so.advance_paid, 0) - self.assertEqual(so.party_account_currency, "USD") - def test_cancel_sales_order_after_cancel_payment_entry(self): from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry From 91a276d4edbbd34ac40e63770fd9627791c76761 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 31 Oct 2024 10:24:20 +0530 Subject: [PATCH 464/734] test: PO 'Advance Paid' and curreny when using payment (cherry picked from commit ca85c75e3950d2dd1d7b5cba88ea61809b0e2f2a) --- .../test_advance_payment_ledger_entry.py | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/erpnext/accounts/doctype/advance_payment_ledger_entry/test_advance_payment_ledger_entry.py b/erpnext/accounts/doctype/advance_payment_ledger_entry/test_advance_payment_ledger_entry.py index c953291ac6f..4fe206a351b 100644 --- a/erpnext/accounts/doctype/advance_payment_ledger_entry/test_advance_payment_ledger_entry.py +++ b/erpnext/accounts/doctype/advance_payment_ledger_entry/test_advance_payment_ledger_entry.py @@ -7,6 +7,7 @@ from frappe.utils import nowdate, today from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry from erpnext.accounts.test.accounts_mixin import AccountsTestMixin +from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order # On IntegrationTestCase, the doctype test records and all @@ -48,6 +49,22 @@ class TestAdvancePaymentLedgerEntry(AccountsTestMixin, IntegrationTestCase): ) return so + def create_purchase_order(self, qty=1, rate=100, currency="INR", do_not_submit=False): + """ + Helper method + """ + po = create_purchase_order( + company=self.company, + customer=self.supplier, + currency=currency, + item=self.item, + qty=qty, + rate=rate, + transaction_date=today(), + do_not_submit=do_not_submit, + ) + return po + def test_so_advance_paid_and_currency_with_payment(self): self.create_customer("_Test USD Customer", "USD") @@ -128,3 +145,36 @@ class TestAdvancePaymentLedgerEntry(AccountsTestMixin, IntegrationTestCase): so.reload() self.assertEqual(so.advance_paid, 0) self.assertEqual(so.party_account_currency, "USD") + + def test_po_advance_paid_and_currency_with_payment(self): + self.create_supplier("_Test USD Supplier", "USD") + + po = self.create_purchase_order(currency="USD", do_not_submit=True) + po.conversion_rate = 80 + po.submit() + + pe_exchange_rate = 85 + pe = get_payment_entry(po.doctype, po.name, bank_account=self.cash) + pe.reference_no = "1" + pe.reference_date = nowdate() + pe.paid_to = self.creditors_usd + pe.paid_to_account_currency = "USD" + pe.target_exchange_rate = pe_exchange_rate + pe.received_amount = po.grand_total + pe.paid_amount = pe_exchange_rate * pe.received_amount + pe.references[0].outstanding_amount = 100 + pe.references[0].total_amount = 100 + pe.references[0].allocated_amount = 100 + pe.save().submit() + + po.reload() + self.assertEqual(po.advance_paid, 100) + self.assertEqual(po.party_account_currency, "USD") + + # cancel advance payment + pe.reload() + pe.cancel() + + po.reload() + self.assertEqual(po.advance_paid, 0) + self.assertEqual(po.party_account_currency, "USD") From d0a655d5ae15732781f3e1ca824a4374b3295ad9 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 31 Oct 2024 10:27:20 +0530 Subject: [PATCH 465/734] test: PO advance and currency from journal (cherry picked from commit cf7b8f1b41c4ff9e86b6cb2eef01e5bd1d49d95e) --- .../test_advance_payment_ledger_entry.py | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/erpnext/accounts/doctype/advance_payment_ledger_entry/test_advance_payment_ledger_entry.py b/erpnext/accounts/doctype/advance_payment_ledger_entry/test_advance_payment_ledger_entry.py index 4fe206a351b..f9ac59bfa80 100644 --- a/erpnext/accounts/doctype/advance_payment_ledger_entry/test_advance_payment_ledger_entry.py +++ b/erpnext/accounts/doctype/advance_payment_ledger_entry/test_advance_payment_ledger_entry.py @@ -178,3 +178,51 @@ class TestAdvancePaymentLedgerEntry(AccountsTestMixin, IntegrationTestCase): po.reload() self.assertEqual(po.advance_paid, 0) self.assertEqual(po.party_account_currency, "USD") + + def test_po_advance_paid_and_currency_with_journal(self): + self.create_supplier("_Test USD Supplier", "USD") + + po = self.create_purchase_order(currency="USD", do_not_submit=True) + po.conversion_rate = 80 + po.submit() + + je_exchange_rate = 85 + je = frappe.get_doc( + { + "doctype": "Journal Entry", + "company": self.company, + "voucher_type": "Journal Entry", + "posting_date": po.transaction_date, + "multi_currency": True, + "accounts": [ + { + "account": self.creditors_usd, + "party_type": "Supplier", + "party": po.supplier, + "debit": 8500, + "debit_in_account_currency": 100, + "is_advance": "Yes", + "reference_type": po.doctype, + "reference_name": po.name, + "exchange_rate": je_exchange_rate, + }, + { + "account": self.cash, + "credit": 8500, + "credit_in_account_currency": 8500, + }, + ], + } + ) + je.save().submit() + po.reload() + self.assertEqual(po.advance_paid, 100) + self.assertEqual(po.party_account_currency, "USD") + + # cancel advance payment + je.reload() + je.cancel() + + po.reload() + self.assertEqual(po.advance_paid, 0) + self.assertEqual(po.party_account_currency, "USD") From ba09ddcc3a0d02fc8cb318dd381e11e07afcccf5 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 1 Nov 2024 14:10:45 +0530 Subject: [PATCH 466/734] chore: resolve conflict --- erpnext/controllers/accounts_controller.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 03910516dea..d6016ef7d1d 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1948,14 +1948,8 @@ class AccountsController(TransactionBase): return stock_items -<<<<<<< HEAD - def set_total_advance_paid(self): - ple = frappe.qb.DocType("Payment Ledger Entry") - party = self.customer if self.doctype == "Sales Order" else self.supplier -======= def calculate_total_advance_from_ledger(self): adv = frappe.qb.DocType("Advance Payment Ledger Entry") ->>>>>>> 2b2360bf7b (refactor: calculate advance from advance ledger) advance = ( frappe.qb.from_(adv) .select(adv.currency.as_("account_currency"), Abs(Sum(adv.amount)).as_("amount")) @@ -1968,13 +1962,10 @@ class AccountsController(TransactionBase): ) return advance -<<<<<<< HEAD -======= def set_total_advance_paid(self): advance = self.calculate_total_advance_from_ledger() advance_paid, order_total = None, None ->>>>>>> 2b2360bf7b (refactor: calculate advance from advance ledger) if advance: advance = advance[0] From 426010e21a3a02e2f2711a44f9b3e30bb209e6e2 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 1 Nov 2024 14:14:03 +0530 Subject: [PATCH 467/734] refactor: fetch correct hook variable --- erpnext/controllers/accounts_controller.py | 9 +++------ .../v15_0/create_advance_payment_ledger_records.py | 4 +--- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index d6016ef7d1d..1ae6b5256a5 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -349,9 +349,8 @@ class AccountsController(TransactionBase): adv = qb.DocType("Advance Payment Ledger Entry") qb.from_(adv).delete().where(adv.voucher_type.eq(self.doctype) & adv.voucher_no.eq(self.name)).run() - advance_payment_doctypes = frappe.get_hooks("advance_payment_receivable_doctypes") + frappe.get_hooks( - "advance_payment_payable_doctypes" - ) + advance_payment_doctypes = frappe.get_hooks("advance_payment_doctypes") + if self.doctype in advance_payment_doctypes: qb.from_(adv).delete().where( adv.against_voucher_type.eq(self.doctype) & adv.against_voucher_no.eq(self.name) @@ -2562,9 +2561,7 @@ class AccountsController(TransactionBase): repost_ledger.submit() def get_advance_payment_doctypes(self) -> list: - return frappe.get_hooks("advance_payment_receivable_doctypes") + frappe.get_hooks( - "advance_payment_payable_doctypes" - ) + return frappe.get_hooks("advance_payment_doctypes") def make_advance_payment_ledger_for_journal(self): advance_payment_doctypes = self.get_advance_payment_doctypes() diff --git a/erpnext/patches/v15_0/create_advance_payment_ledger_records.py b/erpnext/patches/v15_0/create_advance_payment_ledger_records.py index 13b4d95c760..8d247885cab 100644 --- a/erpnext/patches/v15_0/create_advance_payment_ledger_records.py +++ b/erpnext/patches/v15_0/create_advance_payment_ledger_records.py @@ -4,9 +4,7 @@ from frappe.query_builder.custom import ConstantColumn def get_advance_doctypes() -> list: - return frappe.get_hooks("advance_payment_receivable_doctypes") + frappe.get_hooks( - "advance_payment_payable_doctypes" - ) + return frappe.get_hooks("advance_payment_doctypes") def get_payments_with_so_po_reference() -> list: From 9bfcad31fd45d1585830d059bd8126853a1eef12 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 1 Nov 2024 20:08:07 +0530 Subject: [PATCH 468/734] refactor: replace non-existant IntegrationTestCase --- .../test_advance_payment_ledger_entry.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/erpnext/accounts/doctype/advance_payment_ledger_entry/test_advance_payment_ledger_entry.py b/erpnext/accounts/doctype/advance_payment_ledger_entry/test_advance_payment_ledger_entry.py index f9ac59bfa80..2f578aed172 100644 --- a/erpnext/accounts/doctype/advance_payment_ledger_entry/test_advance_payment_ledger_entry.py +++ b/erpnext/accounts/doctype/advance_payment_ledger_entry/test_advance_payment_ledger_entry.py @@ -2,7 +2,7 @@ # See license.txt import frappe -from frappe.tests import IntegrationTestCase, UnitTestCase +from frappe.tests.utils import FrappeTestCase from frappe.utils import nowdate, today from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry @@ -10,14 +10,8 @@ from erpnext.accounts.test.accounts_mixin import AccountsTestMixin from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order -# On IntegrationTestCase, the doctype test records and all -# link-field test record depdendencies are recursively loaded -# Use these module variables to add/remove to/from that list -EXTRA_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"] -IGNORE_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"] - -class TestAdvancePaymentLedgerEntry(AccountsTestMixin, IntegrationTestCase): +class TestAdvancePaymentLedgerEntry(AccountsTestMixin, FrappeTestCase): """ Integration tests for AdvancePaymentLedgerEntry. Use this class for testing interactions between multiple components. From a689830bff72e85c940e4fc3a897edaeaa2b2c38 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sun, 3 Nov 2024 04:20:01 +0530 Subject: [PATCH 469/734] fix: validation trigger (backport #43926) (#43943) fix: validation trigger (#43926) (cherry picked from commit ba9fb4effc32f7e5454b8a0bab664195b6cb2108) Co-authored-by: rohitwaghchaure --- erpnext/manufacturing/doctype/bom/bom.py | 7 +++++++ erpnext/manufacturing/doctype/work_order/work_order.py | 10 ++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index 009320c7a18..843528de706 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -968,6 +968,13 @@ class BOM(WebsiteGenerator): if not d.batch_size or d.batch_size <= 0: d.batch_size = 1 + if not d.workstation and not d.workstation_type: + frappe.throw( + _( + "Row {0}: Workstation or Workstation Type is mandatory for an operation {1}" + ).format(d.idx, d.operation) + ) + def get_tree_representation(self) -> BOMTree: """Get a complete tree representation preserving order of child items.""" return BOMTree(self.name) diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index d13cd27a095..1ebcde75366 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -173,10 +173,16 @@ class WorkOrder(Document): self.get_items_and_operations_from_bom() def validate_workstation_type(self): + if not self.docstatus.is_submitted(): + return + for row in self.operations: if not row.workstation and not row.workstation_type: - msg = f"Row {row.idx}: Workstation or Workstation Type is mandatory for an operation {row.operation}" - frappe.throw(_(msg)) + frappe.throw( + _("Row {0}: Workstation or Workstation Type is mandatory for an operation {1}").format( + row.idx, row.operation + ) + ) def validate_sales_order(self): if self.sales_order: From ce42d847b3c98f29f82ecc165dfa7a371a916399 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sun, 3 Nov 2024 04:20:20 +0530 Subject: [PATCH 470/734] fix: valuation rate for sales / purchase return for serial / batch nos (backport #43925) (#43942) fix: valuation rate for sales / purchase return for serial / batch nos (#43925) (cherry picked from commit 01bb1612da5e28a6b80640db4f0e77107ad00017) Co-authored-by: rohitwaghchaure --- .../controllers/sales_and_purchase_return.py | 37 ++- erpnext/controllers/stock_controller.py | 5 + .../delivery_note/test_delivery_note.py | 262 ++++++++++++++++++ .../purchase_receipt/test_purchase_receipt.py | 228 +++++++++++++++ .../serial_and_batch_bundle.py | 94 ++++++- erpnext/stock/serial_batch_bundle.py | 30 +- 6 files changed, 643 insertions(+), 13 deletions(-) diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py index 6da23834b61..b1ba8e22c2e 100644 --- a/erpnext/controllers/sales_and_purchase_return.py +++ b/erpnext/controllers/sales_and_purchase_return.py @@ -597,6 +597,10 @@ def make_return_doc(doctype: str, source_name: str, target_doc=None, return_agai if default_warehouse_for_sales_return: target_doc.warehouse = default_warehouse_for_sales_return + if not source_doc.use_serial_batch_fields and source_doc.serial_and_batch_bundle: + target_doc.serial_no = None + target_doc.batch_no = None + if ( (source_doc.serial_no or source_doc.batch_no) and not source_doc.serial_and_batch_bundle @@ -899,6 +903,7 @@ def get_serial_batches_based_on_bundle(field, _bundle_ids): "`tabSerial and Batch Entry`.`serial_no`", "`tabSerial and Batch Entry`.`batch_no`", "`tabSerial and Batch Entry`.`qty`", + "`tabSerial and Batch Entry`.`incoming_rate`", "`tabSerial and Batch Bundle`.`voucher_detail_no`", "`tabSerial and Batch Bundle`.`voucher_type`", "`tabSerial and Batch Bundle`.`voucher_no`", @@ -920,15 +925,23 @@ def get_serial_batches_based_on_bundle(field, _bundle_ids): if key not in available_dict: available_dict[key] = frappe._dict( - {"qty": 0.0, "serial_nos": defaultdict(float), "batches": defaultdict(float)} + { + "qty": 0.0, + "serial_nos": defaultdict(float), + "batches": defaultdict(float), + "serial_nos_valuation": defaultdict(float), + "batches_valuation": defaultdict(float), + } ) available_dict[key]["qty"] += row.qty if row.serial_no: available_dict[key]["serial_nos"][row.serial_no] += row.qty + available_dict[key]["serial_nos_valuation"][row.serial_no] = row.incoming_rate elif row.batch_no: available_dict[key]["batches"][row.batch_no] += row.qty + available_dict[key]["batches_valuation"][row.batch_no] = row.incoming_rate return available_dict @@ -964,12 +977,13 @@ def get_serial_and_batch_bundle(field, doctype, reference_ids, is_rejected=False ) ) else: - fields = [ - "serial_and_batch_bundle", - ] + fields = ["serial_and_batch_bundle"] if is_rejected: - fields.extend(["rejected_serial_and_batch_bundle", "return_qty_from_rejected_warehouse"]) + fields.append("rejected_serial_and_batch_bundle") + + if doctype == "Purchase Receipt Item": + fields.append("return_qty_from_rejected_warehouse") del filters["rejected_serial_and_batch_bundle"] data = frappe.get_all( @@ -1003,7 +1017,14 @@ def filter_serial_batches(parent_doc, data, row, warehouse_field=None, qty_field warehouse = row.get(warehouse_field) qty = abs(row.get(qty_field)) - filterd_serial_batch = frappe._dict({"serial_nos": [], "batches": defaultdict(float)}) + filterd_serial_batch = frappe._dict( + { + "serial_nos": [], + "batches": defaultdict(float), + "serial_nos_valuation": data.get("serial_nos_valuation"), + "batches_valuation": data.get("batches_valuation"), + } + ) if data.serial_nos: available_serial_nos = [] @@ -1013,7 +1034,7 @@ def filter_serial_batches(parent_doc, data, row, warehouse_field=None, qty_field if available_serial_nos: if parent_doc.doctype in ["Purchase Invoice", "Purchase Reecipt"]: - available_serial_nos = get_available_serial_nos(available_serial_nos) + available_serial_nos = get_available_serial_nos(available_serial_nos, warehouse) if len(available_serial_nos) > qty: filterd_serial_batch["serial_nos"] = sorted(available_serial_nos[0 : cint(qty)]) @@ -1098,6 +1119,8 @@ def make_serial_batch_bundle_for_return(data, child_doc, parent_doc, warehouse_f "warehouse": warehouse, "serial_nos": data.get("serial_nos"), "batches": data.get("batches"), + "serial_nos_valuation": data.get("serial_nos_valuation"), + "batches_valuation": data.get("batches_valuation"), "posting_date": parent_doc.posting_date, "posting_time": parent_doc.posting_time, "voucher_type": parent_doc.doctype, diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 0714bdd3a63..046a0c7da30 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -334,6 +334,11 @@ class StockController(AccountsController): } ) + if self.doctype in ["Sales Invoice", "Delivery Note"]: + row.db_set( + "incoming_rate", frappe.db.get_value("Serial and Batch Bundle", bundle, "avg_rate") + ) + def get_reference_ids(self, table_name, qty_field=None, bundle_field=None) -> tuple[str, list[str]]: field = { "Sales Invoice": "sales_invoice_item", diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py index 0cfb427c670..9acdce8bebc 100644 --- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py @@ -3,6 +3,7 @@ import json +from collections import defaultdict import frappe from frappe.tests.utils import FrappeTestCase @@ -2080,6 +2081,264 @@ class TestDeliveryNote(FrappeTestCase): self.assertEqual(stock_value_difference, 100.0 * 5) + def test_delivery_note_return_valuation_without_use_serial_batch_field(self): + from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_return + + batch_item = make_item( + "_Test Delivery Note Return Valuation Batch Item", + properties={ + "has_batch_no": 1, + "create_new_batch": 1, + "is_stock_item": 1, + "batch_number_series": "BRTN-DNN-BI-.#####", + }, + ).name + + serial_item = make_item( + "_Test Delivery Note Return Valuation Serial Item", + properties={"has_serial_no": 1, "is_stock_item": 1, "serial_no_series": "SRTN-DNN-TP-.#####"}, + ).name + + batches = {} + serial_nos = [] + for qty, rate in {3: 300, 2: 100}.items(): + se = make_stock_entry( + item_code=batch_item, target="_Test Warehouse - _TC", qty=qty, basic_rate=rate + ) + batches[get_batch_from_bundle(se.items[0].serial_and_batch_bundle)] = qty + + for qty, rate in {2: 100, 1: 50}.items(): + make_stock_entry(item_code=serial_item, target="_Test Warehouse - _TC", qty=qty, basic_rate=rate) + serial_nos.extend(get_serial_nos_from_bundle(se.items[0].serial_and_batch_bundle)) + + dn = create_delivery_note( + item_code=batch_item, + qty=5, + rate=1000, + use_serial_batch_fields=0, + batches=batches, + do_not_submit=True, + ) + + bundle_id = make_serial_batch_bundle( + frappe._dict( + { + "item_code": serial_item, + "warehouse": dn.items[0].warehouse, + "qty": 3, + "voucher_type": "Delivery Note", + "serial_nos": serial_nos, + "posting_date": dn.posting_date, + "posting_time": dn.posting_time, + "type_of_transaction": "Outward", + "do_not_submit": True, + } + ) + ).name + + dn.append( + "items", + { + "item_code": serial_item, + "qty": 3, + "rate": 700, + "base_rate": 700, + "item_name": serial_item, + "uom": "Nos", + "stock_uom": "Nos", + "conversion_factor": 1, + "warehouse": dn.items[0].warehouse, + "use_serial_batch_fields": 0, + "serial_and_batch_bundle": bundle_id, + }, + ) + + dn.save() + dn.submit() + dn.reload() + + batch_no_valuation = defaultdict(float) + serial_no_valuation = defaultdict(float) + + for row in dn.items: + if row.serial_and_batch_bundle: + bundle_data = frappe.get_all( + "Serial and Batch Entry", + filters={"parent": row.serial_and_batch_bundle}, + fields=["incoming_rate", "serial_no", "batch_no"], + ) + + for d in bundle_data: + if d.batch_no: + batch_no_valuation[d.batch_no] = d.incoming_rate + elif d.serial_no: + serial_no_valuation[d.serial_no] = d.incoming_rate + + return_entry = make_sales_return(dn.name) + + return_entry.save() + return_entry.submit() + return_entry.reload() + + for row in return_entry.items: + if row.item_code == batch_item: + bundle_data = frappe.get_all( + "Serial and Batch Entry", + filters={"parent": row.serial_and_batch_bundle}, + fields=["incoming_rate", "batch_no"], + ) + + for d in bundle_data: + self.assertEqual(d.incoming_rate, batch_no_valuation[d.batch_no]) + else: + bundle_data = frappe.get_all( + "Serial and Batch Entry", + filters={"parent": row.serial_and_batch_bundle}, + fields=["incoming_rate", "serial_no"], + ) + + for d in bundle_data: + self.assertEqual(d.incoming_rate, serial_no_valuation[d.serial_no]) + + def test_delivery_note_return_valuation_with_use_serial_batch_field(self): + from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_return + + batch_item = make_item( + "_Test Delivery Note Return Valuation WITH Batch Item", + properties={ + "has_batch_no": 1, + "create_new_batch": 1, + "is_stock_item": 1, + "batch_number_series": "BRTN-DNN-BIW-.#####", + }, + ).name + + serial_item = make_item( + "_Test Delivery Note Return Valuation WITH Serial Item", + properties={"has_serial_no": 1, "is_stock_item": 1, "serial_no_series": "SRTN-DNN-TPW-.#####"}, + ).name + + batches = [] + serial_nos = [] + for qty, rate in {3: 300, 2: 100}.items(): + se = make_stock_entry( + item_code=batch_item, target="_Test Warehouse - _TC", qty=qty, basic_rate=rate + ) + batches.append(get_batch_from_bundle(se.items[0].serial_and_batch_bundle)) + + for qty, rate in {2: 100, 1: 50}.items(): + se = make_stock_entry( + item_code=serial_item, target="_Test Warehouse - _TC", qty=qty, basic_rate=rate + ) + serial_nos.extend(get_serial_nos_from_bundle(se.items[0].serial_and_batch_bundle)) + + dn = create_delivery_note( + item_code=batch_item, + qty=3, + rate=1000, + use_serial_batch_fields=1, + batch_no=batches[0], + do_not_submit=True, + ) + + dn.append( + "items", + { + "item_code": batch_item, + "qty": 2, + "rate": 1000, + "base_rate": 1000, + "item_name": batch_item, + "uom": dn.items[0].uom, + "stock_uom": dn.items[0].uom, + "conversion_factor": 1, + "warehouse": dn.items[0].warehouse, + "use_serial_batch_fields": 1, + "batch_no": batches[1], + }, + ) + + dn.append( + "items", + { + "item_code": serial_item, + "qty": 2, + "rate": 700, + "base_rate": 700, + "item_name": serial_item, + "uom": "Nos", + "stock_uom": "Nos", + "conversion_factor": 1, + "warehouse": dn.items[0].warehouse, + "use_serial_batch_fields": 1, + "serial_no": "\n".join(serial_nos[0:2]), + }, + ) + + dn.append( + "items", + { + "item_code": serial_item, + "qty": 1, + "rate": 700, + "base_rate": 700, + "item_name": serial_item, + "uom": "Nos", + "stock_uom": "Nos", + "conversion_factor": 1, + "warehouse": dn.items[0].warehouse, + "use_serial_batch_fields": 1, + "serial_no": serial_nos[-1], + }, + ) + + dn.save() + dn.submit() + dn.reload() + + batch_no_valuation = defaultdict(float) + serial_no_valuation = defaultdict(float) + + for row in dn.items: + if row.serial_and_batch_bundle: + bundle_data = frappe.get_all( + "Serial and Batch Entry", + filters={"parent": row.serial_and_batch_bundle}, + fields=["incoming_rate", "serial_no", "batch_no"], + ) + + for d in bundle_data: + if d.batch_no: + batch_no_valuation[d.batch_no] = d.incoming_rate + elif d.serial_no: + serial_no_valuation[d.serial_no] = d.incoming_rate + + return_entry = make_sales_return(dn.name) + + return_entry.save() + return_entry.submit() + return_entry.reload() + + for row in return_entry.items: + if row.item_code == batch_item: + bundle_data = frappe.get_all( + "Serial and Batch Entry", + filters={"parent": row.serial_and_batch_bundle}, + fields=["incoming_rate", "batch_no"], + ) + + for d in bundle_data: + self.assertEqual(d.incoming_rate, batch_no_valuation[d.batch_no]) + else: + bundle_data = frappe.get_all( + "Serial and Batch Entry", + filters={"parent": row.serial_and_batch_bundle}, + fields=["incoming_rate", "serial_no"], + ) + + for d in bundle_data: + self.assertEqual(d.incoming_rate, serial_no_valuation[d.serial_no]) + def create_delivery_note(**args): dn = frappe.new_doc("Delivery Note") @@ -2107,6 +2366,9 @@ def create_delivery_note(**args): if args.get("batch_no"): batches = frappe._dict({args.batch_no: qty}) + if args.get("batches"): + batches = frappe._dict(args.batches) + bundle_id = make_serial_batch_bundle( frappe._dict( { diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index 6d0fe27033f..64c3d2c67b2 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -3672,6 +3672,234 @@ class TestPurchaseReceipt(FrappeTestCase): self.assertEqual(pr.items[0].conversion_factor, 1.0) + def test_purchase_receipt_return_valuation_without_use_serial_batch_field(self): + from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchase_return + + batch_item = make_item( + "_Test Purchase Receipt Return Valuation Batch Item", + properties={ + "has_batch_no": 1, + "create_new_batch": 1, + "is_stock_item": 1, + "batch_number_series": "BRTN-TPRBI-.#####", + }, + ).name + + serial_item = make_item( + "_Test Purchase Receipt Return Valuation Serial Item", + properties={"has_serial_no": 1, "is_stock_item": 1, "serial_no_series": "SRTN-TPRSI-.#####"}, + ).name + + rej_warehouse = create_warehouse("_Test Purchase Warehouse For Rejected Qty") + + pr = make_purchase_receipt( + item_code=batch_item, + received_qty=10, + qty=8, + rejected_qty=2, + rejected_warehouse=rej_warehouse, + rate=300, + do_not_submit=1, + use_serial_batch_fields=0, + ) + + pr.append( + "items", + { + "item_code": serial_item, + "qty": 2, + "rate": 100, + "base_rate": 100, + "item_name": serial_item, + "uom": "Nos", + "stock_uom": "Nos", + "conversion_factor": 1, + "rejected_qty": 1, + "warehouse": pr.items[0].warehouse, + "use_serial_batch_fields": 0, + "rejected_warehouse": rej_warehouse, + }, + ) + + pr.save() + pr.submit() + pr.reload() + + batch_no = get_batch_from_bundle(pr.items[0].serial_and_batch_bundle) + rejected_batch_no = get_batch_from_bundle(pr.items[0].rejected_serial_and_batch_bundle) + + self.assertEqual(batch_no, rejected_batch_no) + + return_entry = make_purchase_return(pr.name) + + return_entry.save() + return_entry.submit() + return_entry.reload() + + for row in return_entry.items: + if row.item_code == batch_item: + bundle_data = frappe.get_all( + "Serial and Batch Entry", + filters={"parent": row.serial_and_batch_bundle}, + pluck="incoming_rate", + ) + + for incoming_rate in bundle_data: + self.assertEqual(incoming_rate, 300.00) + else: + bundle_data = frappe.get_all( + "Serial and Batch Entry", + filters={"parent": row.serial_and_batch_bundle}, + pluck="incoming_rate", + ) + + for incoming_rate in bundle_data: + self.assertEqual(incoming_rate, 100.00) + + for row in return_entry.items: + if row.item_code == batch_item: + bundle_data = frappe.get_all( + "Serial and Batch Entry", + filters={"parent": row.rejected_serial_and_batch_bundle}, + pluck="incoming_rate", + ) + + for incoming_rate in bundle_data: + self.assertEqual(incoming_rate, 0) + else: + bundle_data = frappe.get_all( + "Serial and Batch Entry", + filters={"parent": row.rejected_serial_and_batch_bundle}, + pluck="incoming_rate", + ) + + for incoming_rate in bundle_data: + self.assertEqual(incoming_rate, 0) + + def test_purchase_receipt_return_valuation_with_use_serial_batch_field(self): + from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchase_return + + batch_item = make_item( + "_Test Purchase Receipt Return Valuation With Batch Item", + properties={"has_batch_no": 1, "create_new_batch": 1, "is_stock_item": 1}, + ).name + + serial_item = make_item( + "_Test Purchase Receipt Return Valuation With Serial Item", + properties={"has_serial_no": 1, "is_stock_item": 1}, + ).name + + rej_warehouse = create_warehouse("_Test Purchase Warehouse For Rejected Qty") + + batch_no = "BATCH-RTN-BNU-TPRBI-0001" + serial_nos = ["SNU-RTN-TPRSI-0001", "SNU-RTN-TPRSI-0002", "SNU-RTN-TPRSI-0003"] + + if not frappe.db.exists("Batch", batch_no): + frappe.get_doc( + { + "doctype": "Batch", + "batch_id": batch_no, + "item": batch_item, + } + ).insert() + + for serial_no in serial_nos: + if not frappe.db.exists("Serial No", serial_no): + frappe.get_doc( + { + "doctype": "Serial No", + "item_code": serial_item, + "serial_no": serial_no, + } + ).insert() + + pr = make_purchase_receipt( + item_code=batch_item, + received_qty=10, + qty=8, + rejected_qty=2, + rejected_warehouse=rej_warehouse, + batch_no=batch_no, + use_serial_batch_fields=1, + rate=300, + do_not_submit=1, + ) + + pr.append( + "items", + { + "item_code": serial_item, + "qty": 2, + "rate": 100, + "base_rate": 100, + "item_name": serial_item, + "uom": "Nos", + "stock_uom": "Nos", + "conversion_factor": 1, + "rejected_qty": 1, + "warehouse": pr.items[0].warehouse, + "use_serial_batch_fields": 1, + "rejected_warehouse": rej_warehouse, + "serial_no": "\n".join(serial_nos[:2]), + "rejected_serial_no": serial_nos[2], + }, + ) + + pr.save() + pr.submit() + pr.reload() + + batch_no = get_batch_from_bundle(pr.items[0].serial_and_batch_bundle) + rejected_batch_no = get_batch_from_bundle(pr.items[0].rejected_serial_and_batch_bundle) + + self.assertEqual(batch_no, rejected_batch_no) + + return_entry = make_purchase_return(pr.name) + + return_entry.save() + return_entry.submit() + return_entry.reload() + + for row in return_entry.items: + if row.item_code == batch_item: + bundle_data = frappe.get_all( + "Serial and Batch Entry", + filters={"parent": row.serial_and_batch_bundle}, + pluck="incoming_rate", + ) + + for incoming_rate in bundle_data: + self.assertEqual(incoming_rate, 300.00) + else: + bundle_data = frappe.get_all( + "Serial and Batch Entry", + filters={"parent": row.serial_and_batch_bundle}, + pluck="incoming_rate", + ) + + for incoming_rate in bundle_data: + self.assertEqual(incoming_rate, 100.00) + + for row in return_entry.items: + if row.item_code == batch_item: + bundle_data = frappe.get_all( + "Serial and Batch Entry", + filters={"parent": row.rejected_serial_and_batch_bundle}, + pluck="incoming_rate", + ) + + for incoming_rate in bundle_data: + self.assertEqual(incoming_rate, 0) + else: + bundle_data = frappe.get_all( + "Serial and Batch Entry", + filters={"parent": row.rejected_serial_and_batch_bundle}, + pluck="incoming_rate", + ) + + for incoming_rate in bundle_data: + self.assertEqual(incoming_rate, 0) + 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/serial_and_batch_bundle/serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py index 94ec8675db8..ed6d5e155d7 100644 --- a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py +++ b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py @@ -197,7 +197,7 @@ class SerialandBatchBundle(Document): def throw_error_message(self, message, exception=frappe.ValidationError): frappe.throw(_(message), exception, title=_("Error")) - def set_incoming_rate(self, row=None, save=False, allow_negative_stock=False): + def set_incoming_rate(self, parent=None, row=None, save=False, allow_negative_stock=False): if self.type_of_transaction not in ["Inward", "Outward"] or self.voucher_type in [ "Installation Note", "Job Card", @@ -206,13 +206,70 @@ class SerialandBatchBundle(Document): ]: return - if self.type_of_transaction == "Outward": + if return_aginst := self.get_return_aginst(parent=parent): + self.set_valuation_rate_for_return_entry(return_aginst, save) + elif self.type_of_transaction == "Outward": self.set_incoming_rate_for_outward_transaction( row, save, allow_negative_stock=allow_negative_stock ) else: self.set_incoming_rate_for_inward_transaction(row, save) + def set_valuation_rate_for_return_entry(self, return_aginst, save=False): + if valuation_details := self.get_valuation_rate_for_return_entry(return_aginst): + for row in self.entries: + if row.serial_no: + valuation_rate = valuation_details["serial_nos"].get(row.serial_no) + else: + valuation_rate = valuation_details["batches"].get(row.batch_no) + + row.incoming_rate = valuation_rate + row.stock_value_difference = flt(row.qty) * flt(row.incoming_rate) + + if save: + row.db_set( + { + "incoming_rate": row.incoming_rate, + "stock_value_difference": row.stock_value_difference, + } + ) + + def get_valuation_rate_for_return_entry(self, return_aginst): + valuation_details = frappe._dict( + { + "serial_nos": defaultdict(float), + "batches": defaultdict(float), + } + ) + + bundle_data = frappe.get_all( + "Serial and Batch Bundle", + fields=[ + "`tabSerial and Batch Entry`.`serial_no`", + "`tabSerial and Batch Entry`.`batch_no`", + "`tabSerial and Batch Entry`.`incoming_rate`", + ], + filters=[ + ["Serial and Batch Bundle", "voucher_no", "=", return_aginst], + ["Serial and Batch Entry", "docstatus", "=", 1], + ["Serial and Batch Bundle", "is_cancelled", "=", 0], + ["Serial and Batch Bundle", "item_code", "=", self.item_code], + ["Serial and Batch Bundle", "warehouse", "=", self.warehouse], + ], + order_by="`tabSerial and Batch Bundle`.`creation`, `tabSerial and Batch Entry`.`idx`", + ) + + if not bundle_data: + return {} + + for row in bundle_data: + if row.serial_no: + valuation_details["serial_nos"][row.serial_no] = row.incoming_rate + else: + valuation_details["batches"][row.batch_no] = row.incoming_rate + + return valuation_details + def calculate_total_qty(self, save=True): self.total_qty = 0.0 for d in self.entries: @@ -327,6 +384,33 @@ class SerialandBatchBundle(Document): return sle + def get_return_aginst(self, parent=None): + return_aginst = None + + if parent and parent.get("is_return") and parent.get("return_against"): + return parent.get("return_against") + + if ( + self.voucher_type + in [ + "Delivery Note", + "Sales Invoice", + "Purchase Invoice", + "Purchase Receipt", + "POS Invoice", + "Subcontracting Receipt", + ] + and self.voucher_type + and self.voucher_no + ): + voucher_details = frappe.db.get_value( + self.voucher_type, self.voucher_no, ["is_return", "return_against"], as_dict=True + ) + if voucher_details and voucher_details.get("is_return") and voucher_details.get("return_against"): + return voucher_details.get("return_against") + + return return_aginst + def set_incoming_rate_for_inward_transaction(self, row=None, save=False): valuation_field = "valuation_rate" if self.voucher_type in ["Sales Invoice", "Delivery Note", "Quotation"]: @@ -354,7 +438,9 @@ class SerialandBatchBundle(Document): rate = frappe.db.get_value(child_table, self.voucher_detail_no, valuation_field) for d in self.entries: - if (d.incoming_rate == rate) and d.qty and d.stock_value_difference: + if self.is_rejected: + rate = 0.0 + elif (d.incoming_rate == rate) and d.qty and d.stock_value_difference: continue d.incoming_rate = flt(rate, precision) @@ -403,7 +489,7 @@ class SerialandBatchBundle(Document): # If user has changed the rate in the child table if self.docstatus == 0: - self.set_incoming_rate(save=True, row=row) + self.set_incoming_rate(parent=parent, row=row, save=True) if self.docstatus == 0 and parent.get("is_return") and parent.is_new(): self.reset_qty(row, qty_field=qty_field) diff --git a/erpnext/stock/serial_batch_bundle.py b/erpnext/stock/serial_batch_bundle.py index 46724be5927..47521475d0a 100644 --- a/erpnext/stock/serial_batch_bundle.py +++ b/erpnext/stock/serial_batch_bundle.py @@ -1088,6 +1088,8 @@ class SerialBatchCreation: frappe.db.bulk_insert("Serial No", fields=fields, values=set(serial_nos_details)) def set_serial_batch_entries(self, doc): + incoming_rate = self.get("incoming_rate") + if self.get("serial_nos"): serial_no_wise_batch = frappe._dict({}) if self.has_batch_no: @@ -1095,30 +1097,54 @@ class SerialBatchCreation: qty = -1 if self.type_of_transaction == "Outward" else 1 for serial_no in self.serial_nos: + if self.get("serial_nos_valuation"): + incoming_rate = self.get("serial_nos_valuation").get(serial_no) + doc.append( "entries", { "serial_no": serial_no, "qty": qty, "batch_no": serial_no_wise_batch.get(serial_no) or self.get("batch_no"), - "incoming_rate": self.get("incoming_rate"), + "incoming_rate": incoming_rate, }, ) elif self.get("batches"): for batch_no, batch_qty in self.batches.items(): + if self.get("batches_valuation"): + incoming_rate = self.get("batches_valuation").get(batch_no) + doc.append( "entries", { "batch_no": batch_no, "qty": batch_qty * (-1 if self.type_of_transaction == "Outward" else 1), - "incoming_rate": self.get("incoming_rate"), + "incoming_rate": incoming_rate, }, ) def create_batch(self): from erpnext.stock.doctype.batch.batch import make_batch + if self.is_rejected: + bundle = frappe.db.get_value( + "Serial and Batch Bundle", + { + "voucher_no": self.voucher_no, + "voucher_type": self.voucher_type, + "voucher_detail_no": self.voucher_detail_no, + "is_rejected": 0, + "docstatus": 1, + "is_cancelled": 0, + }, + "name", + ) + + if bundle: + if batch_no := frappe.db.get_value("Serial and Batch Entry", {"parent": bundle}, "batch_no"): + return batch_no + return make_batch( frappe._dict( { From 9a2b0a965d40e54ff5e26121d212e55ff4a483d4 Mon Sep 17 00:00:00 2001 From: hyaray Date: Fri, 25 Oct 2024 20:22:04 +0800 Subject: [PATCH 471/734] refactor: use year current year start date as default (cherry picked from commit d54283ded578264b3ce9556ad2b7f525f25bbf9c) --- erpnext/accounts/doctype/fiscal_year/fiscal_year.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/fiscal_year/fiscal_year.js b/erpnext/accounts/doctype/fiscal_year/fiscal_year.js index a44b52f08f8..aeb9f982b4d 100644 --- a/erpnext/accounts/doctype/fiscal_year/fiscal_year.js +++ b/erpnext/accounts/doctype/fiscal_year/fiscal_year.js @@ -4,10 +4,7 @@ frappe.ui.form.on("Fiscal Year", { onload: function (frm) { if (frm.doc.__islocal) { - frm.set_value( - "year_start_date", - frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1) - ); + frm.set_value("year_start_date", frappe.datetime.year_start()); } }, year_start_date: function (frm) { From 10d8cc9d66054864afd232ac7377eed4c6f1272a Mon Sep 17 00:00:00 2001 From: ramyasusee Date: Wed, 30 Oct 2024 17:40:11 +0530 Subject: [PATCH 472/734] fix: map reference number while reversing journal (cherry picked from commit 77de783cd414eb2fa23c80668ae10efb87ccfd92) --- erpnext/accounts/doctype/journal_entry/journal_entry.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index fb5c563d790..aeaadae0b30 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -1671,6 +1671,8 @@ def make_reverse_journal_entry(source_name, target_doc=None): "debit": "credit", "credit_in_account_currency": "debit_in_account_currency", "credit": "debit", + "reference_type": "reference_type", + "reference_name": "reference_name", }, }, }, From 7ad664d89a3383f2005d938cefffb0a798b204a2 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 4 Nov 2024 13:23:44 +0530 Subject: [PATCH 473/734] perf: avoid reposting of entries created after stock reco (backport #43950) (#43961) perf: avoid reposting of entries created after stock reco (#43950) (cherry picked from commit 7cfe1c8d59360dd9887e83991077326b62a9d895) Co-authored-by: rohitwaghchaure --- .../test_stock_reconciliation.py | 54 +++++++++++++++++++ erpnext/stock/stock_ledger.py | 12 +++++ 2 files changed, 66 insertions(+) diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py index 9bb6ba9ec90..9754443d4ac 100644 --- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py @@ -1275,6 +1275,60 @@ class TestStockReconciliation(FrappeTestCase, StockTestMixin): qty = get_batch_qty(batch_id, warehouse, batch_item_code) self.assertEqual(qty, 110) + def test_skip_reposting_for_entries_after_stock_reco(self): + from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry + + item_code = create_item("Test Item For Skip Reposting After Stock Reco", is_stock_item=1).name + + warehouse = "_Test Warehouse - _TC" + + make_stock_entry( + posting_date="2024-11-01", + posting_time="11:00", + item_code=item_code, + target=warehouse, + qty=10, + basic_rate=100, + ) + + create_stock_reconciliation( + posting_date="2024-11-02", + posting_time="11:00", + item_code=item_code, + warehouse=warehouse, + qty=20, + rate=100, + ) + + se = make_stock_entry( + posting_date="2024-11-03", + posting_time="11:00", + item_code=item_code, + source=warehouse, + qty=15, + ) + + stock_value_difference = frappe.db.get_value( + "Stock Ledger Entry", {"voucher_no": se.name, "is_cancelled": 0}, "stock_value_difference" + ) + + self.assertEqual(stock_value_difference, 1500.00 * -1) + + make_stock_entry( + posting_date="2024-10-29", + posting_time="11:00", + item_code=item_code, + target=warehouse, + qty=10, + basic_rate=100, + ) + + stock_value_difference = frappe.db.get_value( + "Stock Ledger Entry", {"voucher_no": se.name, "is_cancelled": 0}, "stock_value_difference" + ) + + self.assertEqual(stock_value_difference, 1500.00 * -1) + 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/stock_ledger.py b/erpnext/stock/stock_ledger.py index 0f058d0a64b..fa53e6b47a9 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -623,9 +623,21 @@ class update_entries_after: if sle.dependant_sle_voucher_detail_no: entries_to_fix = self.get_dependent_entries_to_fix(entries_to_fix, sle) + if self.has_stock_reco_with_serial_batch(sle): + break + if self.exceptions: self.raise_exceptions() + def has_stock_reco_with_serial_batch(self, sle): + if ( + sle.vocher_type == "Stock Reconciliation" + and frappe.db.get_value(sle.voucher_type, sle.voucher_no, "set_posting_time") == 1 + ): + return not (sle.batch_no or sle.serial_no or sle.serial_and_batch_bundle) + + return False + def process_sle_against_current_timestamp(self): sl_entries = self.get_sle_against_current_voucher() for sle in sl_entries: From 9f7afda4db26e9695c8ab45433d1c0885f076417 Mon Sep 17 00:00:00 2001 From: CaseSolved Date: Wed, 25 Sep 2024 19:09:05 +0100 Subject: [PATCH 474/734] fix: SO link on PO and add in missing dashboard references on both (cherry picked from commit 2017fd80d1bf930fd5185ddb3ddb427eb73841f8) --- .../purchase_order_dashboard.py | 21 +++++++++++++++---- .../sales_order/sales_order_dashboard.py | 4 +++- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order_dashboard.py b/erpnext/buying/doctype/purchase_order/purchase_order_dashboard.py index 36fe079fc98..6ae50e71416 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order_dashboard.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order_dashboard.py @@ -14,18 +14,31 @@ def get_data(): "Material Request": ["items", "material_request"], "Supplier Quotation": ["items", "supplier_quotation"], "Project": ["items", "project"], + "Sales Order": ["items", "sales_order"], + "BOM": ["items", "bom"], + "Production Plan": ["items", "production_plan"], + "Blanket Order": ["items", "blanket_order"], }, "transactions": [ - {"label": _("Related"), "items": ["Purchase Receipt", "Purchase Invoice"]}, - {"label": _("Payment"), "items": ["Payment Entry", "Journal Entry", "Payment Request"]}, + { + "label": _("Related"), + "items": ["Purchase Receipt", "Purchase Invoice", "Sales Order"] + }, + { + "label": _("Payment"), + "items": ["Payment Entry", "Journal Entry", "Payment Request"] + }, { "label": _("Reference"), - "items": ["Material Request", "Supplier Quotation", "Project", "Auto Repeat"], + "items": ["Supplier Quotation", "Project", "Auto Repeat"], + }, + { + "label": _("Manufacturing"), + "items": ["Material Request", "BOM", "Production Plan", "Blanket Order"], }, { "label": _("Sub-contracting"), "items": ["Subcontracting Order", "Subcontracting Receipt", "Stock Entry"], }, - {"label": _("Internal"), "items": ["Sales Order"]}, ], } diff --git a/erpnext/selling/doctype/sales_order/sales_order_dashboard.py b/erpnext/selling/doctype/sales_order/sales_order_dashboard.py index c84009725b8..7c1c0deb33f 100644 --- a/erpnext/selling/doctype/sales_order/sales_order_dashboard.py +++ b/erpnext/selling/doctype/sales_order/sales_order_dashboard.py @@ -15,6 +15,8 @@ def get_data(): }, "internal_links": { "Quotation": ["items", "prevdoc_docname"], + "BOM": ["items", "bom_no"], + "Blanket Order": ["items", "blanket_order"], }, "transactions": [ { @@ -23,7 +25,7 @@ def get_data(): }, {"label": _("Purchasing"), "items": ["Material Request", "Purchase Order"]}, {"label": _("Projects"), "items": ["Project"]}, - {"label": _("Manufacturing"), "items": ["Work Order"]}, + {"label": _("Manufacturing"), "items": ["Work Order", "BOM", "Blanket Order"]}, {"label": _("Reference"), "items": ["Quotation", "Auto Repeat", "Stock Reservation Entry"]}, {"label": _("Payment"), "items": ["Payment Entry", "Payment Request", "Journal Entry"]}, ], From 84a40c282bc0f5833cb7a063e8f69a023bdf4f51 Mon Sep 17 00:00:00 2001 From: CaseSolved Date: Wed, 25 Sep 2024 19:27:21 +0100 Subject: [PATCH 475/734] chore: linting (cherry picked from commit be6970c850d3c81f1b32ae00ede2e49c588ab2e9) --- .../doctype/purchase_order/purchase_order_dashboard.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order_dashboard.py b/erpnext/buying/doctype/purchase_order/purchase_order_dashboard.py index 6ae50e71416..3fb8b30f139 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order_dashboard.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order_dashboard.py @@ -20,14 +20,8 @@ def get_data(): "Blanket Order": ["items", "blanket_order"], }, "transactions": [ - { - "label": _("Related"), - "items": ["Purchase Receipt", "Purchase Invoice", "Sales Order"] - }, - { - "label": _("Payment"), - "items": ["Payment Entry", "Journal Entry", "Payment Request"] - }, + {"label": _("Related"), "items": ["Purchase Receipt", "Purchase Invoice", "Sales Order"]}, + {"label": _("Payment"), "items": ["Payment Entry", "Journal Entry", "Payment Request"]}, { "label": _("Reference"), "items": ["Supplier Quotation", "Project", "Auto Repeat"], From 1b8bd0e1f3b496682ce3bb3c1716889a5b805ade Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 5 Nov 2024 10:25:32 +0530 Subject: [PATCH 476/734] refactor: avoid permission issue for non-admin (cherry picked from commit c832d9fb9a7378a7a7186bf9bd9661e92dcfdf9b) --- erpnext/controllers/accounts_controller.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 1ae6b5256a5..e47e9917149 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -2588,6 +2588,7 @@ class AccountsController(TransactionBase): doc.amount = amount if self.docstatus == 1 else -1 * amount doc.event = "Submit" if self.docstatus == 1 else "Cancel" doc.currency = x.account_currency + doc.flags.ignore_permissions = 1 doc.save() def make_advance_payment_ledger_for_payment(self): @@ -2610,6 +2611,7 @@ class AccountsController(TransactionBase): doc.amount = x.allocated_amount if self.docstatus == 1 else -1 * x.allocated_amount doc.currency = currency doc.event = "Submit" if self.docstatus == 1 else "Cancel" + doc.flags.ignore_permissions = 1 doc.save() def make_advance_payment_ledger_entries(self): From eaf6d0d7d88c930fdb7cf42ac938f93a80b6e7db Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 5 Nov 2024 10:31:45 +0530 Subject: [PATCH 477/734] refactor: update advance ledger role requirement (cherry picked from commit e41560d30ba0fe72b53cf41cf5fd34bc3a8a541b) --- .../advance_payment_ledger_entry.json | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/doctype/advance_payment_ledger_entry/advance_payment_ledger_entry.json b/erpnext/accounts/doctype/advance_payment_ledger_entry/advance_payment_ledger_entry.json index 1d0a5b42a31..290ed11c98e 100644 --- a/erpnext/accounts/doctype/advance_payment_ledger_entry/advance_payment_ledger_entry.json +++ b/erpnext/accounts/doctype/advance_payment_ledger_entry/advance_payment_ledger_entry.json @@ -70,25 +70,41 @@ "read_only": 1 } ], + "in_create": 1, "index_web_pages_for_search": 1, "links": [], - "modified": "2024-10-16 17:11:28.143979", + "modified": "2024-11-05 10:31:28.736671", "modified_by": "Administrator", "module": "Accounts", "name": "Advance Payment Ledger Entry", "owner": "Administrator", "permissions": [ { - "create": 1, - "delete": 1, "email": 1, "export": 1, "print": 1, "read": 1, "report": 1, - "role": "System Manager", - "share": 1, - "write": 1 + "role": "Accounts User", + "share": 1 + }, + { + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts Manager", + "share": 1 + }, + { + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Auditor", + "share": 1 } ], "sort_field": "creation", From b665e4e24ad9105d82af30b7190a50e720c8636f Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Tue, 5 Nov 2024 23:36:26 +0530 Subject: [PATCH 478/734] fix: add precision validation (cherry picked from commit 7daadcf42037db5a5f2d387bb59c7b3936e4f0a3) --- erpnext/assets/doctype/asset/asset.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 6bb3ff255cf..c788d5265a7 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -119,6 +119,7 @@ class Asset(AccountsController): # end: auto-generated types def validate(self): + self.validate_precision() self.validate_asset_values() self.validate_asset_and_reference() self.validate_item() @@ -306,6 +307,15 @@ class Asset(AccountsController): title=_("Missing Finance Book"), ) + def validate_precision(self): + float_precision = cint(frappe.db.get_default("float_precision")) or 2 + if self.gross_purchase_amount: + self.gross_purchase_amount = flt(self.gross_purchase_amount, float_precision) + if self.opening_accumulated_depreciation: + self.opening_accumulated_depreciation = flt( + self.opening_accumulated_depreciation, float_precision + ) + def validate_asset_values(self): if not self.asset_category: self.asset_category = frappe.get_cached_value("Item", self.item_code, "asset_category") @@ -471,6 +481,9 @@ class Asset(AccountsController): def validate_expected_value_after_useful_life(self): for row in self.get("finance_books"): + row.expected_value_after_useful_life = flt( + row.expected_value_after_useful_life, self.precision("gross_purchase_amount") + ) depr_schedule = get_depr_schedule(self.name, "Draft", row.finance_book) if not depr_schedule: From a38819cbd5cef2cc8615cdbd34f5d735378d2dd1 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 6 Nov 2024 10:05:44 +0530 Subject: [PATCH 479/734] perf: too many writes error during reposting (backport #43978) (#43983) perf: too many writes error during reposting (#43978) perf: too many writes error (cherry picked from commit 134c24b9c5218161c239be39aaeec55d06028ea9) Co-authored-by: rohitwaghchaure --- erpnext/stock/stock_ledger.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index fa53e6b47a9..ccf7c7643c8 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -1047,7 +1047,7 @@ class update_entries_after: rate = 0 # Material Transfer, Repack, Manufacturing if sle.voucher_type == "Stock Entry": - self.recalculate_amounts_in_stock_entry(sle.voucher_no) + self.recalculate_amounts_in_stock_entry(sle.voucher_no, sle.voucher_detail_no) rate = frappe.db.get_value("Stock Entry Detail", sle.voucher_detail_no, "valuation_rate") # Sales and Purchase Return elif sle.voucher_type in ( @@ -1176,14 +1176,15 @@ class update_entries_after: # Update outgoing item's rate, recalculate FG Item's rate and total incoming/outgoing amount if not sle.dependant_sle_voucher_detail_no: - self.recalculate_amounts_in_stock_entry(sle.voucher_no) + self.recalculate_amounts_in_stock_entry(sle.voucher_no, sle.voucher_detail_no) - def recalculate_amounts_in_stock_entry(self, voucher_no): + def recalculate_amounts_in_stock_entry(self, voucher_no, voucher_detail_no): stock_entry = frappe.get_doc("Stock Entry", voucher_no, for_update=True) stock_entry.calculate_rate_and_amount(reset_outgoing_rate=False, raise_error_if_no_rate=False) stock_entry.db_update() for d in stock_entry.items: - d.db_update() + if d.name == voucher_detail_no or (not d.s_warehouse and d.t_warehouse): + d.db_update() def update_rate_on_delivery_and_sales_return(self, sle, outgoing_rate): # Update item's incoming rate on transaction From ceccd8c2dcafb743169ce5b161aef3b822d007db Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 6 Nov 2024 10:30:06 +0530 Subject: [PATCH 480/734] chore: update serial_batch_bundle.py (backport #43981) (#43984) chore: update serial_batch_bundle.py (#43981) Avaliable -> Available (cherry picked from commit 30954ed645ed4174ff6ad08c3bb2d59c05df4a73) Co-authored-by: Ikko Eltociear Ashimine --- erpnext/stock/serial_batch_bundle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/serial_batch_bundle.py b/erpnext/stock/serial_batch_bundle.py index 47521475d0a..c1002095b62 100644 --- a/erpnext/stock/serial_batch_bundle.py +++ b/erpnext/stock/serial_batch_bundle.py @@ -984,7 +984,7 @@ class SerialBatchCreation: required_qty = flt(abs(self.actual_qty), precision) if required_qty - total_qty > 0: - msg = f"For the item {bold(doc.item_code)}, the Avaliable qty {bold(total_qty)} is less than the Required Qty {bold(required_qty)} in the warehouse {bold(doc.warehouse)}. Please add sufficient qty in the warehouse." + msg = f"For the item {bold(doc.item_code)}, the Available qty {bold(total_qty)} is less than the Required Qty {bold(required_qty)} in the warehouse {bold(doc.warehouse)}. Please add sufficient qty in the warehouse." frappe.throw(msg, title=_("Insufficient Stock")) def set_auto_serial_batch_entries_for_outward(self): From 9ccdb987d9deb844a1126fba1e33c55367484631 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 6 Nov 2024 05:21:49 +0000 Subject: [PATCH 481/734] chore(release): Bumped to Version 15.41.0 # [15.41.0](https://github.com/frappe/erpnext/compare/v15.40.0...v15.41.0) (2024-11-06) ### Bug Fixes * add precision validation ([b665e4e](https://github.com/frappe/erpnext/commit/b665e4e24ad9105d82af30b7190a50e720c8636f)) * deleting SO/PO will remove its advance payment ledger entry ([d84a3c4](https://github.com/frappe/erpnext/commit/d84a3c4f29df46156d94cef8ad195eaf59424e06)) * map reference number while reversing journal ([10d8cc9](https://github.com/frappe/erpnext/commit/10d8cc9d66054864afd232ac7377eed4c6f1272a)) * **return:** set default return warehouse ([e730b8c](https://github.com/frappe/erpnext/commit/e730b8c6e4e38d241f522108519767e4db4cc048)) * SO link on PO and add in missing dashboard references on both ([9f7afda](https://github.com/frappe/erpnext/commit/9f7afda4db26e9695c8ab45433d1c0885f076417)) * validation trigger (backport [#43926](https://github.com/frappe/erpnext/issues/43926)) ([#43943](https://github.com/frappe/erpnext/issues/43943)) ([a689830](https://github.com/frappe/erpnext/commit/a689830bff72e85c940e4fc3a897edaeaa2b2c38)) * valuation rate for sales / purchase return for serial / batch nos (backport [#43925](https://github.com/frappe/erpnext/issues/43925)) ([#43942](https://github.com/frappe/erpnext/issues/43942)) ([ce42d84](https://github.com/frappe/erpnext/commit/ce42d847b3c98f29f82ecc165dfa7a371a916399)) ### Features * advance payment ledger doctype ([b343334](https://github.com/frappe/erpnext/commit/b343334f694b79a92faabbba03984dca086ca8c0)) * remove Payroll Entry from Bank Account dashboard (backport [#43931](https://github.com/frappe/erpnext/issues/43931)) ([#43933](https://github.com/frappe/erpnext/issues/43933)) ([4a749ce](https://github.com/frappe/erpnext/commit/4a749cec72c060f98662191f3dcaf801496d0ab9)) ### Performance Improvements * avoid reposting of entries created after stock reco (backport [#43950](https://github.com/frappe/erpnext/issues/43950)) ([#43961](https://github.com/frappe/erpnext/issues/43961)) ([7ad664d](https://github.com/frappe/erpnext/commit/7ad664d89a3383f2005d938cefffb0a798b204a2)) * too many writes error during reposting (backport [#43978](https://github.com/frappe/erpnext/issues/43978)) ([#43983](https://github.com/frappe/erpnext/issues/43983)) ([a38819c](https://github.com/frappe/erpnext/commit/a38819cbd5cef2cc8615cdbd34f5d735378d2dd1)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 1bf4cf4a827..38ea1800995 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.40.0" +__version__ = "15.41.0" def get_default_company(user=None): From 20033eef9b53b96abefcaaeb5d967cc56d8aae1b Mon Sep 17 00:00:00 2001 From: "Nihantra C. Patel" <141945075+Nihantra-Patel@users.noreply.github.com> Date: Wed, 6 Nov 2024 12:02:29 +0530 Subject: [PATCH 482/734] fix: removed single quotes from deferred revenue (#43985) (cherry picked from commit 834d18840c0644140fe2d3af2883c44bd687550c) --- erpnext/accounts/deferred_revenue.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/deferred_revenue.py b/erpnext/accounts/deferred_revenue.py index cd34bf7f850..a88764cf1b2 100644 --- a/erpnext/accounts/deferred_revenue.py +++ b/erpnext/accounts/deferred_revenue.py @@ -58,7 +58,7 @@ def build_conditions(process_type, account, company): ) if account: - conditions += f"AND {deferred_account}='{frappe.db.escape(account)}'" + conditions += f"AND {deferred_account}={frappe.db.escape(account)}" elif company: conditions += f"AND p.company = {frappe.db.escape(company)}" From c3e61aebd27ad1afb3939b4ee6aa7b143a781432 Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Wed, 6 Nov 2024 13:21:19 +0530 Subject: [PATCH 483/734] fix: error when saving POS merge log (#43989) (cherry picked from commit c62596b323f81bcd2235e103765153e92802b43b) --- .../doctype/pos_invoice_merge_log/pos_invoice_merge_log.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py index aecdac0b3a1..c1d2f16e43c 100644 --- a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py +++ b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py @@ -438,7 +438,9 @@ def split_invoices(invoices): if not item.serial_no and not item.serial_and_batch_bundle: continue - return_against_is_added = any(d for d in _invoices if d.pos_invoice == pos_invoice.return_against) + return_against_is_added = any( + d for d in _invoices if d[0].pos_invoice == pos_invoice.return_against + ) if return_against_is_added: break From 633997b1b0c2c2bfa96ac7697093f18d3d5d56ae Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Wed, 6 Nov 2024 14:04:30 +0530 Subject: [PATCH 484/734] fix: ensure list has items (cherry picked from commit e13e688987755ed1456cf8d41666e50a906d439b) --- .../doctype/pos_invoice_merge_log/pos_invoice_merge_log.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py index c1d2f16e43c..5bb43b3fa72 100644 --- a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py +++ b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py @@ -439,7 +439,7 @@ def split_invoices(invoices): continue return_against_is_added = any( - d for d in _invoices if d[0].pos_invoice == pos_invoice.return_against + d for d in _invoices if d and d[0].pos_invoice == pos_invoice.return_against ) if return_against_is_added: break From 318830c57d0568bfea951aab7f43a7eafffca2a8 Mon Sep 17 00:00:00 2001 From: Abdeali Chharchhoda Date: Wed, 6 Nov 2024 19:16:01 +0530 Subject: [PATCH 485/734] fix: Update `dimension_filter_map` query --- .../accounting_dimension_filter.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.py b/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.py index 1954b4b0efe..7c843cf552e 100644 --- a/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.py +++ b/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.py @@ -74,12 +74,12 @@ def get_dimension_filter_map(): a.applicable_on_account, d.dimension_value, p.accounting_dimension, p.allow_or_restrict, a.is_mandatory FROM - `tabApplicable On Account` a, `tabAllowed Dimension` d, + `tabApplicable On Account` a, `tabAccounting Dimension Filter` p + LEFT JOIN `tabAllowed Dimension` d ON d.parent = p.name WHERE p.name = a.parent AND p.disabled = 0 - AND p.name = d.parent """, as_dict=1, ) @@ -97,7 +97,6 @@ def get_dimension_filter_map(): f.allow_or_restrict, f.is_mandatory, ) - frappe.flags.dimension_filter_map = dimension_filter_map return frappe.flags.dimension_filter_map From 47a8fc28dfc670c5257d80e4f2ff10304adc7334 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 7 Nov 2024 12:49:27 +0530 Subject: [PATCH 486/734] fix: task showing limit in customer portal (backport #44003) (#44005) fix: task showing limit in customer portal (#44003) (cherry picked from commit 44832c3b5cd38223072842cf0937aa5615f93114) Co-authored-by: Nihantra C. Patel <141945075+Nihantra-Patel@users.noreply.github.com> --- erpnext/templates/pages/projects.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/templates/pages/projects.py b/erpnext/templates/pages/projects.py index e3e26fc82a6..787c7c0069b 100644 --- a/erpnext/templates/pages/projects.py +++ b/erpnext/templates/pages/projects.py @@ -51,7 +51,7 @@ def get_tasks(project, start=0, search=None, item_status=None): "parent_task", ], limit_start=start, - limit_page_length=10, + limit_page_length=100, ) task_nest = [] for task in tasks: From eeff0a1252b81738ae7cfb5bdc751c3563611b28 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 25 Oct 2024 17:53:47 +0530 Subject: [PATCH 487/734] refactor: cancel old PR and invalidate tokens (cherry picked from commit cda7800777371a34a99d9b8d55c809e03f8abfcf) --- .../payment_request/payment_request.py | 65 +++++++++++++++++-- 1 file changed, 59 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index 2d4a64c317b..107ed8db8e1 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -1,7 +1,7 @@ import json import frappe -from frappe import _ +from frappe import _, qb from frappe.model.document import Document from frappe.query_builder.functions import Sum from frappe.utils import flt, nowdate @@ -564,11 +564,24 @@ def make_payment_request(**args): # fetches existing payment request `grand_total` amount existing_payment_request_amount = get_existing_payment_request_amount(ref_doc.doctype, ref_doc.name) - if existing_payment_request_amount: + def validate_and_calculate_grand_total(grand_total, existing_payment_request_amount): grand_total -= existing_payment_request_amount - if not grand_total: frappe.throw(_("Payment Request is already created")) + return grand_total + + if existing_payment_request_amount: + if args.order_type == "Shopping Cart": + # If Payment Request is in an advanced stage, then create for remaining amount. + if get_existing_payment_request_amount( + ref_doc.doctype, ref_doc.name, ["Initiated", "Partially Paid", "Payment Ordered", "Paid"] + ): + grand_total = validate_and_calculate_grand_total(grand_total, existing_payment_request_amount) + else: + # If PR's are processed, cancel all of them. + cancel_old_payment_requests(ref_doc.doctype, ref_doc.name) + else: + grand_total = validate_and_calculate_grand_total(grand_total, existing_payment_request_amount) if draft_payment_request: frappe.db.set_value( @@ -678,21 +691,47 @@ def get_amount(ref_doc, payment_account=None): frappe.throw(_("Payment Entry is already created")) -def get_existing_payment_request_amount(ref_dt, ref_dn): +def cancel_old_payment_requests(ref_dt, ref_dn): + PR = frappe.qb.DocType("Payment Request") + + if res := ( + frappe.qb.from_(PR) + .select(PR.name) + .where(PR.reference_doctype == ref_dt) + .where(PR.reference_name == ref_dn) + .where(PR.docstatus == 1) + .where(PR.status.isin(["Draft", "Requested"])) + .run(as_dict=True) + ): + for x in res: + doc = frappe.get_doc("Payment Request", x.name) + doc.flags.ignore_permissions = True + doc.cancel() + + if ireqs := get_irequests_of_payment_request(doc.name): + for ireq in ireqs: + frappe.db.set_value("Integration Request", ireq.name, "status", "Cancelled") + + +def get_existing_payment_request_amount(ref_dt, ref_dn, statuses: list | None = None) -> list: """ Return the total amount of Payment Requests against a reference document. """ PR = frappe.qb.DocType("Payment Request") - response = ( + query = ( frappe.qb.from_(PR) .select(Sum(PR.grand_total)) .where(PR.reference_doctype == ref_dt) .where(PR.reference_name == ref_dn) .where(PR.docstatus == 1) - .run() ) + if statuses: + query = query.where(PR.status.isin(statuses)) + + response = query.run() + return response[0][0] if response[0] else 0 @@ -915,3 +954,17 @@ def get_open_payment_requests_query(doctype, txt, searchfield, start, page_len, ) for pr in open_payment_requests ] + + +def get_irequests_of_payment_request(doc: str | None = None) -> list: + res = [] + if doc: + res = frappe.db.get_all( + "Integration Request", + { + "reference_doctype": "Payment Request", + "reference_docname": doc, + "status": "Queued", + }, + ) + return res From ff4751c9e8e58e4cf3561a123fdb2d5735514d37 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 6 Nov 2024 17:58:22 +0530 Subject: [PATCH 488/734] refactor: handle PR's in advance stage (cherry picked from commit 18c13a2cff8594d177777afb5519a9a20b968e09) --- .../payment_request/payment_request.py | 32 +++++++++++++++---- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index 107ed8db8e1..577a1ea2426 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -691,6 +691,21 @@ def get_amount(ref_doc, payment_account=None): frappe.throw(_("Payment Entry is already created")) +def get_irequest_status(payment_requests: None | list = None) -> list: + IR = frappe.qb.DocType("Integration Request") + res = [] + if payment_requests: + res = ( + frappe.qb.from_(IR) + .select(IR.name) + .where(IR.reference_doctype.eq("Payment Request")) + .where(IR.reference_docname.isin(payment_requests)) + .where(IR.status.isin(["Authorized", "Completed"])) + .run(as_dict=True) + ) + return res + + def cancel_old_payment_requests(ref_dt, ref_dn): PR = frappe.qb.DocType("Payment Request") @@ -703,14 +718,17 @@ def cancel_old_payment_requests(ref_dt, ref_dn): .where(PR.status.isin(["Draft", "Requested"])) .run(as_dict=True) ): - for x in res: - doc = frappe.get_doc("Payment Request", x.name) - doc.flags.ignore_permissions = True - doc.cancel() + if get_irequest_status([x.name for x in res]): + frappe.throw(_("Another Payment Request is already processed")) + else: + for x in res: + doc = frappe.get_doc("Payment Request", x.name) + doc.flags.ignore_permissions = True + doc.cancel() - if ireqs := get_irequests_of_payment_request(doc.name): - for ireq in ireqs: - frappe.db.set_value("Integration Request", ireq.name, "status", "Cancelled") + if ireqs := get_irequests_of_payment_request(doc.name): + for ireq in ireqs: + frappe.db.set_value("Integration Request", ireq.name, "status", "Cancelled") def get_existing_payment_request_amount(ref_dt, ref_dn, statuses: list | None = None) -> list: From 4819535a526d265f1a9da21dbf83fda7bb7bc6bc Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Thu, 7 Nov 2024 12:44:29 +0000 Subject: [PATCH 489/734] chore(release): Bumped to Version 15.41.1 ## [15.41.1](https://github.com/frappe/erpnext/compare/v15.41.0...v15.41.1) (2024-11-07) ### Bug Fixes * ensure list has items ([633997b](https://github.com/frappe/erpnext/commit/633997b1b0c2c2bfa96ac7697093f18d3d5d56ae)) * error when saving POS merge log ([#43989](https://github.com/frappe/erpnext/issues/43989)) ([c3e61ae](https://github.com/frappe/erpnext/commit/c3e61aebd27ad1afb3939b4ee6aa7b143a781432)) * removed single quotes from deferred revenue ([#43985](https://github.com/frappe/erpnext/issues/43985)) ([20033ee](https://github.com/frappe/erpnext/commit/20033eef9b53b96abefcaaeb5d967cc56d8aae1b)) * task showing limit in customer portal (backport [#44003](https://github.com/frappe/erpnext/issues/44003)) ([#44005](https://github.com/frappe/erpnext/issues/44005)) ([47a8fc2](https://github.com/frappe/erpnext/commit/47a8fc28dfc670c5257d80e4f2ff10304adc7334)) * Update `dimension_filter_map` query ([318830c](https://github.com/frappe/erpnext/commit/318830c57d0568bfea951aab7f43a7eafffca2a8)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 38ea1800995..94615249d02 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.41.0" +__version__ = "15.41.1" def get_default_company(user=None): From 58ca4a2b99a694ea891e1b3cfd949f799edacafb Mon Sep 17 00:00:00 2001 From: ljain112 Date: Wed, 18 Sep 2024 19:38:14 +0530 Subject: [PATCH 490/734] fix: improved the conditions for determining voucher subtypes (cherry picked from commit 00eee161904b5deb01ba80b6a3629bd7e2dd613f) --- erpnext/controllers/accounts_controller.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index e47e9917149..b14cf428c53 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1096,9 +1096,11 @@ class AccountsController(TransactionBase): return "Purchase Return" elif self.doctype == "Delivery Note" and self.is_return: return "Sales Return" - elif (self.doctype == "Sales Invoice" and self.is_return) or self.doctype == "Purchase Invoice": + elif self.doctype == "Sales Invoice" and self.is_return: return "Credit Note" - elif (self.doctype == "Purchase Invoice" and self.is_return) or self.doctype == "Sales Invoice": + elif self.doctype == "Sales Invoice" and self.is_debit_note: + return "Debit Note" + elif self.doctype == "Purchase Invoice" and self.is_return: return "Debit Note" return self.doctype From 107d53b3582ccdac3f237745efba4eca5a14a328 Mon Sep 17 00:00:00 2001 From: ljain112 Date: Mon, 30 Sep 2024 20:10:36 +0530 Subject: [PATCH 491/734] fix: patch (cherry picked from commit d76cc210860651377262371359b65e4f7ea9abb7) # Conflicts: # erpnext/patches.txt --- erpnext/patches.txt | 5 ++ .../update_sub_voucher_type_in_gl_entries.py | 57 +++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 erpnext/patches/v15_0/update_sub_voucher_type_in_gl_entries.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 64c74f9d645..01dc68d1d3a 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -379,5 +379,10 @@ erpnext.patches.v15_0.drop_index_posting_datetime_from_sle erpnext.patches.v15_0.add_disassembly_order_stock_entry_type #1 erpnext.patches.v15_0.set_standard_stock_entry_type erpnext.patches.v15_0.link_purchase_item_to_asset_doc +<<<<<<< HEAD erpnext.patches.v14_0.update_currency_exchange_settings_for_frankfurter erpnext.patches.v15_0.update_task_assignee_email_field_in_asset_maintenance_log +======= +erpnext.patches.v15_0.migrate_to_utm_analytics +erpnext.patches.v15_0.update_sub_voucher_type_in_gl_entries +>>>>>>> d76cc21086 (fix: patch) diff --git a/erpnext/patches/v15_0/update_sub_voucher_type_in_gl_entries.py b/erpnext/patches/v15_0/update_sub_voucher_type_in_gl_entries.py new file mode 100644 index 00000000000..7160a6ba87d --- /dev/null +++ b/erpnext/patches/v15_0/update_sub_voucher_type_in_gl_entries.py @@ -0,0 +1,57 @@ +import frappe + + +def execute(): + update_purchase_invoices() + update_sales_invoices() + update_sales_debit_notes() + + +def update_purchase_invoices(): + invoices = frappe.get_all( + "Purchase Invoice", + filters={"docstatus": 1, "is_return": 0}, + pluck="name", + ) + + if not invoices: + return + + update_gl_entry(doctype="Purchase Invoice", invoices=invoices, value="Purchase Invoice") + + +def update_sales_invoices(): + invoices = frappe.get_all( + "Sales Invoice", + filters={"docstatus": 1, "is_return": 0, "is_debit_note": 0}, + pluck="name", + ) + if not invoices: + return + + update_gl_entry(doctype="Sales Invoice", invoices=invoices, value="Sales Invoice") + + +def update_sales_debit_notes(): + invoices = frappe.get_all( + "Sales Invoice", + filters={"docstatus": 1, "is_debit_note": 1}, + pluck="name", + ) + + if not invoices: + return + + update_gl_entry(doctype="Sales Invoice", invoices=invoices, value="Debit Note") + + +def update_gl_entry(doctype, invoices, value): + gl_entry = frappe.qb.DocType("GL Entry") + ( + frappe.qb.update(gl_entry) + .set("voucher_subtype", value) + .where(gl_entry.voucher_subtype.isnotnull()) + .where(gl_entry.voucher_no.isin(invoices)) + .where(gl_entry.voucher_type == doctype) + .run() + ) From d7f91824c0e7b5a36ac5aa28f9d6d23580ed192e Mon Sep 17 00:00:00 2001 From: Smit Vora Date: Thu, 7 Nov 2024 13:35:54 +0530 Subject: [PATCH 492/734] test: test voucher subtype for sales invoice (cherry picked from commit ad6cc352f189449484e8560dc4a4c87ff8c0f894) --- .../sales_invoice/test_sales_invoice.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index d05da0dbf19..db6fd41e439 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -4005,6 +4005,25 @@ class TestSalesInvoice(FrappeTestCase): si.submit() self.assertEqual(si.remarks, f"Against Customer Order Test PO dated {format_date(nowdate())}") + def test_gl_voucher_subtype(self): + si = create_sales_invoice() + gl_entries = frappe.get_all( + "GL Entry", + filters={"voucher_type": "Sales Invoice", "voucher_no": si.name}, + pluck="voucher_subtype", + ) + + self.assertTrue(all([x == "Sales Invoice" for x in gl_entries])) + + si = create_sales_invoice(is_return=1, qty=-1) + gl_entries = frappe.get_all( + "GL Entry", + filters={"voucher_type": "Sales Invoice", "voucher_no": si.name}, + pluck="voucher_subtype", + ) + + self.assertTrue(all([x == "Credit Note" for x in gl_entries])) + def set_advance_flag(company, flag, default_account): frappe.db.set_value( From 6649d17b068fcacf7c8b24b1b1cdc5891c4735dd Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 8 Nov 2024 10:33:56 +0530 Subject: [PATCH 493/734] chore: resolve conflict --- erpnext/patches.txt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 01dc68d1d3a..1f1c2edc917 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -379,10 +379,6 @@ erpnext.patches.v15_0.drop_index_posting_datetime_from_sle erpnext.patches.v15_0.add_disassembly_order_stock_entry_type #1 erpnext.patches.v15_0.set_standard_stock_entry_type erpnext.patches.v15_0.link_purchase_item_to_asset_doc -<<<<<<< HEAD erpnext.patches.v14_0.update_currency_exchange_settings_for_frankfurter erpnext.patches.v15_0.update_task_assignee_email_field_in_asset_maintenance_log -======= -erpnext.patches.v15_0.migrate_to_utm_analytics erpnext.patches.v15_0.update_sub_voucher_type_in_gl_entries ->>>>>>> d76cc21086 (fix: patch) From 978a0078d8f2e93878b883643c94d327deb5fd06 Mon Sep 17 00:00:00 2001 From: "bhaveshkumar.j" Date: Mon, 21 Oct 2024 18:47:20 +0000 Subject: [PATCH 494/734] fix: NoneType while updating ordered_qty in SO for removed items (cherry picked from commit 442cdd7ce49ccbaff855bfac71f3222bc83b7d49) --- erpnext/buying/doctype/purchase_order/purchase_order.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index c03c896c29f..f4e1984332f 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -625,9 +625,11 @@ class PurchaseOrder(BuyingController): if not self.is_against_so(): return for item in removed_items: - prev_ordered_qty = frappe.get_cached_value( - "Sales Order Item", item.get("sales_order_item"), "ordered_qty" + prev_ordered_qty = ( + frappe.get_cached_value("Sales Order Item", item.get("sales_order_item"), "ordered_qty") + or 0.0 ) + frappe.db.set_value( "Sales Order Item", item.get("sales_order_item"), "ordered_qty", prev_ordered_qty - item.qty ) From 487b5776e62afc0f8b256e5028bbe5cac288ab4b Mon Sep 17 00:00:00 2001 From: Ravindu Nethmina <117300601+NethminaHiker360@users.noreply.github.com> Date: Mon, 28 Oct 2024 08:08:32 +0000 Subject: [PATCH 495/734] refactor: add "margin_type" and "margin_rate_or_amount" to no copy (cherry picked from commit 70f090c1ec3be1b904b19801b47dac16ed4a8775) --- erpnext/selling/doctype/sales_order/sales_order.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index a87c1352471..a15e8adb57c 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -1347,6 +1347,8 @@ def make_purchase_order_for_default_supplier(source_name, selected_items=None, t "discount_percentage", "discount_amount", "pricing_rules", + "margin_type", + "margin_rate_or_amount", ], "postprocess": update_item, "condition": lambda doc: doc.ordered_qty < doc.stock_qty From 6dcd015a39be3d5985482f8899da7dc670c52576 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 8 Nov 2024 12:23:50 +0530 Subject: [PATCH 496/734] fix: item not set in the batch quick entry form (backport #44028) (#44031) fix: item not set in the batch quick entry form (#44028) (cherry picked from commit 0399ccc51ea9d0f48f1221dda0761603d1cea22d) Co-authored-by: rohitwaghchaure --- erpnext/public/js/controllers/transaction.js | 51 +++++++++++--------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index adb72818fe8..1773586ac48 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -289,28 +289,6 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe } ]); } - - if(this.frm.fields_dict['items'].grid.get_field('serial_and_batch_bundle')) { - let sbb_field = this.frm.get_docfield('items', 'serial_and_batch_bundle'); - if (sbb_field) { - sbb_field.get_route_options_for_new_doc = (row) => { - return { - 'item_code': row.doc.item_code, - } - }; - } - } - - if(this.frm.fields_dict['items'].grid.get_field('batch_no')) { - let batch_no_field = this.frm.get_docfield('items', 'batch_no'); - if (batch_no_field) { - batch_no_field.get_route_options_for_new_doc = function(row) { - return { - 'item': row.doc.item_code - } - }; - } - } } is_return() { @@ -409,6 +387,35 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe this.setup_quality_inspection(); this.validate_has_items(); erpnext.utils.view_serial_batch_nos(this.frm); + this.set_route_options_for_new_doc(); + } + + set_route_options_for_new_doc() { + // While creating the batch from the link field, copy item from line item to batch form + + if(this.frm.fields_dict['items'].grid.get_field('batch_no')) { + let batch_no_field = this.frm.get_docfield('items', 'batch_no'); + if (batch_no_field) { + batch_no_field.get_route_options_for_new_doc = function(row) { + return { + 'item': row.doc.item_code + } + }; + } + } + + // While creating the SABB from the link field, copy item, doctype from line item to SABB form + if(this.frm.fields_dict['items'].grid.get_field('serial_and_batch_bundle')) { + let sbb_field = this.frm.get_docfield('items', 'serial_and_batch_bundle'); + if (sbb_field) { + sbb_field.get_route_options_for_new_doc = (row) => { + return { + "item_code": row.doc.item_code, + "voucher_type": this.frm.doc.doctype, + } + }; + } + } } scan_barcode() { From 5958d0c2570aa7ae3e0e86e1afdbef89758318f1 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 8 Nov 2024 12:24:21 +0530 Subject: [PATCH 497/734] fix: calculate percentage received and delivered considering over-receipt and over-delivery (backport #43870) (#44030) fix: calculate percentage received and delivered considering over-receipt and over-delivery (#43870) (cherry picked from commit adba1168c1bf28911b9664934d2e84bbae906dec) Co-authored-by: Nabin Hait --- erpnext/buying/doctype/purchase_order/purchase_order.py | 2 +- erpnext/selling/doctype/sales_order/sales_order.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index f4e1984332f..6ba9cb69f9f 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -581,7 +581,7 @@ class PurchaseOrder(BuyingController): def update_receiving_percentage(self): total_qty, received_qty = 0.0, 0.0 for item in self.items: - received_qty += item.received_qty + received_qty += min(item.received_qty, item.qty) total_qty += item.qty if total_qty: self.db_set("per_received", flt(received_qty / total_qty) * 100, update_modified=False) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index a15e8adb57c..62262b41021 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -584,7 +584,7 @@ class SalesOrder(SellingController): item_delivered_qty = item_delivered_qty[0][0] if item_delivered_qty else 0 item.db_set("delivered_qty", flt(item_delivered_qty), update_modified=False) - delivered_qty += item.delivered_qty + delivered_qty += min(item.delivered_qty, item.qty) tot_qty += item.qty if tot_qty != 0: From 56f25ae0657e941f865fb3a224dea3767c8c3ecb Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 8 Nov 2024 12:34:47 +0530 Subject: [PATCH 498/734] fix: sort by ascending to get the first period closing voucher (backport #44029) (#44035) fix: sort by ascending to get the first period closing voucher (#44029) (cherry picked from commit 42dcdcde1a7a982f376f3ddd6969f825b42203e0) Co-authored-by: Venkatesh <47534423+venkat102@users.noreply.github.com> --- .../doctype/period_closing_voucher/period_closing_voucher.py | 2 +- erpnext/patches.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py index 2a9b624a47a..1d4ee25241e 100644 --- a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py +++ b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py @@ -417,7 +417,7 @@ class PeriodClosingVoucher(AccountsController): "Period Closing Voucher", {"company": self.company, "docstatus": 1}, "name", - order_by="period_end_date", + order_by="period_end_date asc", ) if not first_pcv or first_pcv == self.name: diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 1f1c2edc917..ab416d9d6c3 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -315,7 +315,7 @@ erpnext.patches.v15_0.update_asset_value_for_manual_depr_entries erpnext.patches.v15_0.update_gpa_and_ndb_for_assdeprsch erpnext.patches.v14_0.create_accounting_dimensions_for_closing_balance erpnext.patches.v14_0.set_period_start_end_date_in_pcv -erpnext.patches.v14_0.update_closing_balances #29-10-2024 +erpnext.patches.v14_0.update_closing_balances #08-11-2024 execute:frappe.db.set_single_value("Accounts Settings", "merge_similar_account_heads", 0) erpnext.patches.v14_0.update_reference_type_in_journal_entry_accounts erpnext.patches.v14_0.update_subscription_details From f5610e29bed1b1bcc456e0354ad3deabfd28f2e7 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 8 Nov 2024 13:02:23 +0530 Subject: [PATCH 499/734] Release v15.41.2 (#44037) * fix: improved the conditions for determining voucher subtypes (cherry picked from commit 00eee161904b5deb01ba80b6a3629bd7e2dd613f) * fix: patch (cherry picked from commit d76cc210860651377262371359b65e4f7ea9abb7) # Conflicts: # erpnext/patches.txt * test: test voucher subtype for sales invoice (cherry picked from commit ad6cc352f189449484e8560dc4a4c87ff8c0f894) * chore: resolve conflict * fix: NoneType while updating ordered_qty in SO for removed items (cherry picked from commit 442cdd7ce49ccbaff855bfac71f3222bc83b7d49) * refactor: add "margin_type" and "margin_rate_or_amount" to no copy (cherry picked from commit 70f090c1ec3be1b904b19801b47dac16ed4a8775) * fix: item not set in the batch quick entry form (backport #44028) (#44031) fix: item not set in the batch quick entry form (#44028) (cherry picked from commit 0399ccc51ea9d0f48f1221dda0761603d1cea22d) Co-authored-by: rohitwaghchaure * fix: calculate percentage received and delivered considering over-receipt and over-delivery (backport #43870) (#44030) fix: calculate percentage received and delivered considering over-receipt and over-delivery (#43870) (cherry picked from commit adba1168c1bf28911b9664934d2e84bbae906dec) Co-authored-by: Nabin Hait * fix: sort by ascending to get the first period closing voucher (backport #44029) (#44035) fix: sort by ascending to get the first period closing voucher (#44029) (cherry picked from commit 42dcdcde1a7a982f376f3ddd6969f825b42203e0) Co-authored-by: Venkatesh <47534423+venkat102@users.noreply.github.com> --------- Co-authored-by: ljain112 Co-authored-by: Smit Vora Co-authored-by: ruthra kumar Co-authored-by: bhaveshkumar.j Co-authored-by: Ravindu Nethmina <117300601+NethminaHiker360@users.noreply.github.com> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> Co-authored-by: rohitwaghchaure Co-authored-by: Venkatesh <47534423+venkat102@users.noreply.github.com> --- .../period_closing_voucher.py | 2 +- .../sales_invoice/test_sales_invoice.py | 19 +++++++ .../doctype/purchase_order/purchase_order.py | 8 ++- erpnext/controllers/accounts_controller.py | 6 +- erpnext/patches.txt | 3 +- .../update_sub_voucher_type_in_gl_entries.py | 57 +++++++++++++++++++ erpnext/public/js/controllers/transaction.js | 51 ++++++++++------- .../doctype/sales_order/sales_order.py | 4 +- 8 files changed, 120 insertions(+), 30 deletions(-) create mode 100644 erpnext/patches/v15_0/update_sub_voucher_type_in_gl_entries.py diff --git a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py index 2a9b624a47a..1d4ee25241e 100644 --- a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py +++ b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py @@ -417,7 +417,7 @@ class PeriodClosingVoucher(AccountsController): "Period Closing Voucher", {"company": self.company, "docstatus": 1}, "name", - order_by="period_end_date", + order_by="period_end_date asc", ) if not first_pcv or first_pcv == self.name: diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index d05da0dbf19..db6fd41e439 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -4005,6 +4005,25 @@ class TestSalesInvoice(FrappeTestCase): si.submit() self.assertEqual(si.remarks, f"Against Customer Order Test PO dated {format_date(nowdate())}") + def test_gl_voucher_subtype(self): + si = create_sales_invoice() + gl_entries = frappe.get_all( + "GL Entry", + filters={"voucher_type": "Sales Invoice", "voucher_no": si.name}, + pluck="voucher_subtype", + ) + + self.assertTrue(all([x == "Sales Invoice" for x in gl_entries])) + + si = create_sales_invoice(is_return=1, qty=-1) + gl_entries = frappe.get_all( + "GL Entry", + filters={"voucher_type": "Sales Invoice", "voucher_no": si.name}, + pluck="voucher_subtype", + ) + + self.assertTrue(all([x == "Credit Note" for x in gl_entries])) + def set_advance_flag(company, flag, default_account): frappe.db.set_value( diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index c03c896c29f..6ba9cb69f9f 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -581,7 +581,7 @@ class PurchaseOrder(BuyingController): def update_receiving_percentage(self): total_qty, received_qty = 0.0, 0.0 for item in self.items: - received_qty += item.received_qty + received_qty += min(item.received_qty, item.qty) total_qty += item.qty if total_qty: self.db_set("per_received", flt(received_qty / total_qty) * 100, update_modified=False) @@ -625,9 +625,11 @@ class PurchaseOrder(BuyingController): if not self.is_against_so(): return for item in removed_items: - prev_ordered_qty = frappe.get_cached_value( - "Sales Order Item", item.get("sales_order_item"), "ordered_qty" + prev_ordered_qty = ( + frappe.get_cached_value("Sales Order Item", item.get("sales_order_item"), "ordered_qty") + or 0.0 ) + frappe.db.set_value( "Sales Order Item", item.get("sales_order_item"), "ordered_qty", prev_ordered_qty - item.qty ) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index e47e9917149..b14cf428c53 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1096,9 +1096,11 @@ class AccountsController(TransactionBase): return "Purchase Return" elif self.doctype == "Delivery Note" and self.is_return: return "Sales Return" - elif (self.doctype == "Sales Invoice" and self.is_return) or self.doctype == "Purchase Invoice": + elif self.doctype == "Sales Invoice" and self.is_return: return "Credit Note" - elif (self.doctype == "Purchase Invoice" and self.is_return) or self.doctype == "Sales Invoice": + elif self.doctype == "Sales Invoice" and self.is_debit_note: + return "Debit Note" + elif self.doctype == "Purchase Invoice" and self.is_return: return "Debit Note" return self.doctype diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 64c74f9d645..ab416d9d6c3 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -315,7 +315,7 @@ erpnext.patches.v15_0.update_asset_value_for_manual_depr_entries erpnext.patches.v15_0.update_gpa_and_ndb_for_assdeprsch erpnext.patches.v14_0.create_accounting_dimensions_for_closing_balance erpnext.patches.v14_0.set_period_start_end_date_in_pcv -erpnext.patches.v14_0.update_closing_balances #29-10-2024 +erpnext.patches.v14_0.update_closing_balances #08-11-2024 execute:frappe.db.set_single_value("Accounts Settings", "merge_similar_account_heads", 0) erpnext.patches.v14_0.update_reference_type_in_journal_entry_accounts erpnext.patches.v14_0.update_subscription_details @@ -381,3 +381,4 @@ erpnext.patches.v15_0.set_standard_stock_entry_type erpnext.patches.v15_0.link_purchase_item_to_asset_doc erpnext.patches.v14_0.update_currency_exchange_settings_for_frankfurter erpnext.patches.v15_0.update_task_assignee_email_field_in_asset_maintenance_log +erpnext.patches.v15_0.update_sub_voucher_type_in_gl_entries diff --git a/erpnext/patches/v15_0/update_sub_voucher_type_in_gl_entries.py b/erpnext/patches/v15_0/update_sub_voucher_type_in_gl_entries.py new file mode 100644 index 00000000000..7160a6ba87d --- /dev/null +++ b/erpnext/patches/v15_0/update_sub_voucher_type_in_gl_entries.py @@ -0,0 +1,57 @@ +import frappe + + +def execute(): + update_purchase_invoices() + update_sales_invoices() + update_sales_debit_notes() + + +def update_purchase_invoices(): + invoices = frappe.get_all( + "Purchase Invoice", + filters={"docstatus": 1, "is_return": 0}, + pluck="name", + ) + + if not invoices: + return + + update_gl_entry(doctype="Purchase Invoice", invoices=invoices, value="Purchase Invoice") + + +def update_sales_invoices(): + invoices = frappe.get_all( + "Sales Invoice", + filters={"docstatus": 1, "is_return": 0, "is_debit_note": 0}, + pluck="name", + ) + if not invoices: + return + + update_gl_entry(doctype="Sales Invoice", invoices=invoices, value="Sales Invoice") + + +def update_sales_debit_notes(): + invoices = frappe.get_all( + "Sales Invoice", + filters={"docstatus": 1, "is_debit_note": 1}, + pluck="name", + ) + + if not invoices: + return + + update_gl_entry(doctype="Sales Invoice", invoices=invoices, value="Debit Note") + + +def update_gl_entry(doctype, invoices, value): + gl_entry = frappe.qb.DocType("GL Entry") + ( + frappe.qb.update(gl_entry) + .set("voucher_subtype", value) + .where(gl_entry.voucher_subtype.isnotnull()) + .where(gl_entry.voucher_no.isin(invoices)) + .where(gl_entry.voucher_type == doctype) + .run() + ) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index adb72818fe8..1773586ac48 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -289,28 +289,6 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe } ]); } - - if(this.frm.fields_dict['items'].grid.get_field('serial_and_batch_bundle')) { - let sbb_field = this.frm.get_docfield('items', 'serial_and_batch_bundle'); - if (sbb_field) { - sbb_field.get_route_options_for_new_doc = (row) => { - return { - 'item_code': row.doc.item_code, - } - }; - } - } - - if(this.frm.fields_dict['items'].grid.get_field('batch_no')) { - let batch_no_field = this.frm.get_docfield('items', 'batch_no'); - if (batch_no_field) { - batch_no_field.get_route_options_for_new_doc = function(row) { - return { - 'item': row.doc.item_code - } - }; - } - } } is_return() { @@ -409,6 +387,35 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe this.setup_quality_inspection(); this.validate_has_items(); erpnext.utils.view_serial_batch_nos(this.frm); + this.set_route_options_for_new_doc(); + } + + set_route_options_for_new_doc() { + // While creating the batch from the link field, copy item from line item to batch form + + if(this.frm.fields_dict['items'].grid.get_field('batch_no')) { + let batch_no_field = this.frm.get_docfield('items', 'batch_no'); + if (batch_no_field) { + batch_no_field.get_route_options_for_new_doc = function(row) { + return { + 'item': row.doc.item_code + } + }; + } + } + + // While creating the SABB from the link field, copy item, doctype from line item to SABB form + if(this.frm.fields_dict['items'].grid.get_field('serial_and_batch_bundle')) { + let sbb_field = this.frm.get_docfield('items', 'serial_and_batch_bundle'); + if (sbb_field) { + sbb_field.get_route_options_for_new_doc = (row) => { + return { + "item_code": row.doc.item_code, + "voucher_type": this.frm.doc.doctype, + } + }; + } + } } scan_barcode() { diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index a87c1352471..62262b41021 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -584,7 +584,7 @@ class SalesOrder(SellingController): item_delivered_qty = item_delivered_qty[0][0] if item_delivered_qty else 0 item.db_set("delivered_qty", flt(item_delivered_qty), update_modified=False) - delivered_qty += item.delivered_qty + delivered_qty += min(item.delivered_qty, item.qty) tot_qty += item.qty if tot_qty != 0: @@ -1347,6 +1347,8 @@ def make_purchase_order_for_default_supplier(source_name, selected_items=None, t "discount_percentage", "discount_amount", "pricing_rules", + "margin_type", + "margin_rate_or_amount", ], "postprocess": update_item, "condition": lambda doc: doc.ordered_qty < doc.stock_qty From 4cde77d8d8f4a6f833e4f28d7cd6f08ac0005474 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 8 Nov 2024 13:05:48 +0530 Subject: [PATCH 500/734] fix: duplicate items and outdated item price in POS (backport #42978) (#44038) fix: duplicate items and outdated item price in POS (#42978) * fix: duplicate items and outdated item price in POS * fix: duplicate items and outdated item price in POS --formatter (cherry picked from commit 4ea2071265d524e1e09280ec8fe70d8861d1bda6) Co-authored-by: Nihantra C. Patel <141945075+Nihantra-Patel@users.noreply.github.com> --- erpnext/selling/page/point_of_sale/point_of_sale.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.py b/erpnext/selling/page/point_of_sale/point_of_sale.py index 2bb61a6439c..fad184d152c 100644 --- a/erpnext/selling/page/point_of_sale/point_of_sale.py +++ b/erpnext/selling/page/point_of_sale/point_of_sale.py @@ -159,6 +159,8 @@ def get_items(start, page_length, price_list, item_group, pos_profile, search_te if not items_data: return result + current_date = frappe.utils.today() + for item in items_data: uoms = frappe.get_doc("Item", item.item_code).get("uoms", []) @@ -167,12 +169,16 @@ def get_items(start, page_length, price_list, item_group, pos_profile, search_te item_price = frappe.get_all( "Item Price", - fields=["price_list_rate", "currency", "uom", "batch_no"], + fields=["price_list_rate", "currency", "uom", "batch_no", "valid_from", "valid_upto"], filters={ "price_list": price_list, "item_code": item.item_code, "selling": True, + "valid_from": ["<=", current_date], + "valid_upto": ["in", [None, "", current_date]], }, + order_by="valid_from desc", + limit=1, ) if not item_price: From 50fa77276e72e5c32afc66a14871f284e78dccdc Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 8 Nov 2024 16:13:52 +0530 Subject: [PATCH 501/734] refactor: depracate old method and handle inclusive tax --- erpnext/controllers/taxes_and_totals.py | 24 ++++++++++++----- erpnext/public/js/controllers/accounts.js | 9 ++++--- .../public/js/controllers/taxes_and_totals.js | 27 +++++++++++++------ 3 files changed, 41 insertions(+), 19 deletions(-) diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index fc0ba56d1f4..bf5beab1a82 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -8,6 +8,7 @@ import frappe from frappe import _, scrub from frappe.model.document import Document from frappe.utils import cint, flt, round_based_on_smallest_currency_fraction +from frappe.utils.deprecations import deprecated import erpnext from erpnext.accounts.doctype.journal_entry.journal_entry import get_exchange_rate @@ -74,7 +75,7 @@ class calculate_taxes_and_totals: self.calculate_net_total() self.calculate_tax_withholding_net_total() self.calculate_taxes() - self.manipulate_grand_total_for_inclusive_tax() + self.adjust_grand_total_for_inclusive_tax() self.calculate_totals() self._cleanup() self.calculate_total_net_weight() @@ -286,7 +287,7 @@ class calculate_taxes_and_totals: ): amount = flt(item.amount) - total_inclusive_tax_amount_per_qty - item.net_amount = flt(amount / (1 + cumulated_tax_fraction)) + item.net_amount = flt(amount / (1 + cumulated_tax_fraction), item.precision("net_amount")) item.net_rate = flt(item.net_amount / item.qty, item.precision("net_rate")) item.discount_percentage = flt( item.discount_percentage, item.precision("discount_percentage") @@ -531,7 +532,12 @@ class calculate_taxes_and_totals: tax.base_tax_amount = round(tax.base_tax_amount, 0) tax.base_tax_amount_after_discount_amount = round(tax.base_tax_amount_after_discount_amount, 0) + @deprecated def manipulate_grand_total_for_inclusive_tax(self): + # for backward compatablility - if in case used by an external application + return self.adjust_grand_total_for_inclusive_tax() + + def adjust_grand_total_for_inclusive_tax(self): # if fully inclusive taxes and diff if self.doc.get("taxes") and any(cint(t.included_in_print_rate) for t in self.doc.get("taxes")): last_tax = self.doc.get("taxes")[-1] @@ -553,17 +559,21 @@ class calculate_taxes_and_totals: diff = flt(diff, self.doc.precision("rounding_adjustment")) if diff and abs(diff) <= (5.0 / 10 ** last_tax.precision("tax_amount")): - self.doc.rounding_adjustment = diff + self.doc.grand_total_diff = diff + else: + self.doc.grand_total_diff = 0 def calculate_totals(self): if self.doc.get("taxes"): - self.doc.grand_total = flt(self.doc.get("taxes")[-1].total) + flt(self.doc.rounding_adjustment) + self.doc.grand_total = flt(self.doc.get("taxes")[-1].total) + flt( + self.doc.get("grand_total_diff") + ) else: self.doc.grand_total = flt(self.doc.net_total) if self.doc.get("taxes"): self.doc.total_taxes_and_charges = flt( - self.doc.grand_total - self.doc.net_total - flt(self.doc.rounding_adjustment), + self.doc.grand_total - self.doc.net_total - flt(self.doc.get("grand_total_diff")), self.doc.precision("total_taxes_and_charges"), ) else: @@ -626,8 +636,8 @@ class calculate_taxes_and_totals: self.doc.grand_total, self.doc.currency, self.doc.precision("rounded_total") ) - # if print_in_rate is set, we would have already calculated rounding adjustment - self.doc.rounding_adjustment += flt( + # rounding adjustment should always be the difference vetween grand and rounded total + self.doc.rounding_adjustment = flt( self.doc.rounded_total - self.doc.grand_total, self.doc.precision("rounding_adjustment") ) diff --git a/erpnext/public/js/controllers/accounts.js b/erpnext/public/js/controllers/accounts.js index c39fb524264..c7b08f1dc15 100644 --- a/erpnext/public/js/controllers/accounts.js +++ b/erpnext/public/js/controllers/accounts.js @@ -160,7 +160,7 @@ erpnext.accounts.taxes = { let tax = frappe.get_doc(cdt, cdn); try { me.validate_taxes_and_charges(cdt, cdn); - me.validate_inclusive_tax(tax); + me.validate_inclusive_tax(tax, frm); } catch(e) { tax.included_in_print_rate = 0; refresh_field("included_in_print_rate", tax.name, tax.parentfield); @@ -170,7 +170,8 @@ erpnext.accounts.taxes = { }); }, - validate_inclusive_tax: function(tax) { + validate_inclusive_tax: function(tax, frm) { + this.frm = this.frm || frm; let actual_type_error = function() { var msg = __("Actual type tax cannot be included in Item rate in row {0}", [tax.idx]) frappe.throw(msg); @@ -186,12 +187,12 @@ erpnext.accounts.taxes = { if(tax.charge_type == "Actual") { // inclusive tax cannot be of type Actual actual_type_error(); - } else if(tax.charge_type == "On Previous Row Amount" && + } else if(tax.charge_type == "On Previous Row Amount" && this.frm && !cint(this.frm.doc["taxes"][tax.row_id - 1].included_in_print_rate) ) { // referred row should also be an inclusive tax on_previous_row_error(tax.row_id); - } else if(tax.charge_type == "On Previous Row Total") { + } else if(tax.charge_type == "On Previous Row Total" && this.frm) { var taxes_not_included = $.map(this.frm.doc["taxes"].slice(0, tax.row_id), function(t) { return cint(t.included_in_print_rate) ? null : t; }); if(taxes_not_included.length > 0) { diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index dc7a5a8eef1..8b2d7943772 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -103,7 +103,7 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { this.determine_exclusive_rate(); this.calculate_net_total(); this.calculate_taxes(); - this.manipulate_grand_total_for_inclusive_tax(); + this.adjust_grand_total_for_inclusive_tax(); this.calculate_totals(); this._cleanup(); } @@ -185,7 +185,7 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { if (!this.discount_amount_applied) { erpnext.accounts.taxes.validate_taxes_and_charges(tax.doctype, tax.name); - erpnext.accounts.taxes.validate_inclusive_tax(tax); + erpnext.accounts.taxes.validate_inclusive_tax(tax, this.frm); } frappe.model.round_floats_in(tax); }); @@ -250,7 +250,7 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { if(!me.discount_amount_applied && item.qty && (total_inclusive_tax_amount_per_qty || cumulated_tax_fraction)) { var amount = flt(item.amount) - total_inclusive_tax_amount_per_qty; - item.net_amount = flt(amount / (1 + cumulated_tax_fraction)); + item.net_amount = flt(amount / (1 + cumulated_tax_fraction), precision("net_amount", item)); item.net_rate = item.qty ? flt(item.net_amount / item.qty, precision("net_rate", item)) : 0; me.set_in_company_currency(item, ["net_rate", "net_amount"]); @@ -305,6 +305,8 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { me.frm.doc.net_total += item.net_amount; me.frm.doc.base_net_total += item.base_net_amount; }); + + frappe.model.round_floats_in(this.frm.doc, ["total", "base_total", "net_total", "base_net_total"]); } calculate_shipping_charges() { @@ -523,7 +525,15 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { } } + /** + * @deprecated Use adjust_grand_total_for_inclusive_tax instead. + */ manipulate_grand_total_for_inclusive_tax() { + // for backward compatablility - if in case used by an external application + this.adjust_grand_total_for_inclusive_tax() + } + + adjust_grand_total_for_inclusive_tax() { var me = this; // if fully inclusive taxes and diff if (this.frm.doc["taxes"] && this.frm.doc["taxes"].length) { @@ -550,7 +560,9 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { diff = flt(diff, precision("rounding_adjustment")); if ( diff && Math.abs(diff) <= (5.0 / Math.pow(10, precision("tax_amount", last_tax))) ) { - me.frm.doc.rounding_adjustment = diff; + me.frm.doc.grand_total_diff = diff; + } else { + me.frm.doc.grand_total_diff = 0; } } } @@ -561,7 +573,7 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { var me = this; var tax_count = this.frm.doc["taxes"] ? this.frm.doc["taxes"].length : 0; this.frm.doc.grand_total = flt(tax_count - ? this.frm.doc["taxes"][tax_count - 1].total + flt(this.frm.doc.rounding_adjustment) + ? this.frm.doc["taxes"][tax_count - 1].total + flt(this.frm.doc.grand_total_diff) : this.frm.doc.net_total); if(["Quotation", "Sales Order", "Delivery Note", "Sales Invoice", "POS Invoice"].includes(this.frm.doc.doctype)) { @@ -621,7 +633,7 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { if(frappe.meta.get_docfield(this.frm.doc.doctype, "rounded_total", this.frm.doc.name)) { this.frm.doc.rounded_total = round_based_on_smallest_currency_fraction(this.frm.doc.grand_total, this.frm.doc.currency, precision("rounded_total")); - this.frm.doc.rounding_adjustment += flt(this.frm.doc.rounded_total - this.frm.doc.grand_total, + this.frm.doc.rounding_adjustment = flt(this.frm.doc.rounded_total - this.frm.doc.grand_total, precision("rounding_adjustment")); this.set_in_company_currency(this.frm.doc, ["rounding_adjustment", "rounded_total"]); @@ -689,8 +701,7 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { if (total_for_discount_amount) { $.each(this.frm._items || [], function(i, item) { distributed_amount = flt(me.frm.doc.discount_amount) * item.net_amount / total_for_discount_amount; - item.net_amount = flt(item.net_amount - distributed_amount, - precision("base_amount", item)); + item.net_amount = flt(item.net_amount - distributed_amount, precision("net_amount", item)); net_total += item.net_amount; // discount amount rounding loss adjustment if no taxes From 705a26a2fa5d1dc20c07dd4150771529c707e820 Mon Sep 17 00:00:00 2001 From: Smit Vora Date: Sat, 9 Nov 2024 15:14:21 +0530 Subject: [PATCH 502/734] fix: better gls for purchases with tax witholding (#42743) * fix: better gls for purchases with tax witholding * test: test case for purchase invoice gl entries with tax witholding * fix: use flag `_skip_merge` instead of skipping merge based on against account * test: fix test `test_single_threshold_tds` for newer implementation (cherry picked from commit e3cd6539c34891fae6aab3012b8c13145ccc84ec) --- .../purchase_invoice/purchase_invoice.py | 81 +++++++++++++------ .../purchase_invoice/test_purchase_invoice.py | 55 +++++++++++++ .../test_tax_withholding_category.py | 10 ++- erpnext/accounts/general_ledger.py | 4 + 4 files changed, 123 insertions(+), 27 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index b46fb9e9b30..b47e90eb77d 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -863,6 +863,7 @@ class PurchaseInvoice(BuyingController): self.make_tax_gl_entries(gl_entries) self.make_internal_transfer_gl_entries(gl_entries) + self.make_gl_entries_for_tax_withholding(gl_entries) gl_entries = make_regional_gl_entries(gl_entries, self) @@ -896,32 +897,37 @@ class PurchaseInvoice(BuyingController): ) if grand_total and not self.is_internal_transfer(): - against_voucher = self.name - if self.is_return and self.return_against and not self.update_outstanding_for_self: - against_voucher = self.return_against + self.add_supplier_gl_entry(gl_entries, base_grand_total, grand_total) - # Did not use base_grand_total to book rounding loss gle - gl_entries.append( - self.get_gl_dict( - { - "account": self.credit_to, - "party_type": "Supplier", - "party": self.supplier, - "due_date": self.due_date, - "against": self.against_expense_account, - "credit": base_grand_total, - "credit_in_account_currency": base_grand_total - if self.party_account_currency == self.company_currency - else grand_total, - "against_voucher": against_voucher, - "against_voucher_type": self.doctype, - "project": self.project, - "cost_center": self.cost_center, - }, - self.party_account_currency, - item=self, - ) - ) + def add_supplier_gl_entry( + self, gl_entries, base_grand_total, grand_total, against_account=None, remarks=None, skip_merge=False + ): + against_voucher = self.name + if self.is_return and self.return_against and not self.update_outstanding_for_self: + against_voucher = self.return_against + + # Did not use base_grand_total to book rounding loss gle + gl = { + "account": self.credit_to, + "party_type": "Supplier", + "party": self.supplier, + "due_date": self.due_date, + "against": against_account or self.against_expense_account, + "credit": base_grand_total, + "credit_in_account_currency": base_grand_total + if self.party_account_currency == self.company_currency + else grand_total, + "against_voucher": against_voucher, + "against_voucher_type": self.doctype, + "project": self.project, + "cost_center": self.cost_center, + "_skip_merge": skip_merge, + } + + if remarks: + gl["remarks"] = remarks + + gl_entries.append(self.get_gl_dict(gl, self.party_account_currency, item=self)) def make_item_gl_entries(self, gl_entries): # item gl entries @@ -1413,6 +1419,31 @@ class PurchaseInvoice(BuyingController): ) ) + def make_gl_entries_for_tax_withholding(self, gl_entries): + """ + Tax withholding amount is not part of supplier invoice. + Separate supplier GL Entry for correct reporting. + """ + if not self.apply_tds: + return + + for row in self.get("taxes"): + if not row.is_tax_withholding_account or not row.tax_amount: + continue + + base_tds_amount = row.base_tax_amount_after_discount_amount + tds_amount = row.tax_amount_after_discount_amount + + self.add_supplier_gl_entry(gl_entries, base_tds_amount, tds_amount) + self.add_supplier_gl_entry( + gl_entries, + -base_tds_amount, + -tds_amount, + against_account=row.account_head, + remarks=_("TDS Deducted"), + skip_merge=True, + ) + def make_payment_gl_entries(self, gl_entries): # Make Cash GL Entries if cint(self.is_paid) and self.cash_bank_account and self.paid_amount: diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index cf5bfedaebd..f5835deb0d0 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -1544,6 +1544,61 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin): payment_entry.load_from_db() self.assertEqual(payment_entry.taxes[0].allocated_amount, 0) + def test_purchase_gl_with_tax_withholding_tax(self): + company = "_Test Company" + + tds_account_args = { + "doctype": "Account", + "account_name": "TDS Payable", + "account_type": "Tax", + "parent_account": frappe.db.get_value( + "Account", {"account_name": "Duties and Taxes", "company": company} + ), + "company": company, + } + + tds_account = create_account(**tds_account_args) + tax_withholding_category = "Test TDS - 194 - Dividends - Individual" + + # Update tax withholding category with current fiscal year and rate details + create_tax_witholding_category(tax_withholding_category, company, tds_account) + + # create a new supplier to test + supplier = create_supplier( + supplier_name="_Test TDS Advance Supplier", + tax_withholding_category=tax_withholding_category, + ) + + pi = make_purchase_invoice( + supplier=supplier.name, + rate=3000, + qty=1, + item="_Test Non Stock Item", + do_not_submit=1, + ) + pi.apply_tds = 1 + pi.tax_withholding_category = tax_withholding_category + pi.save() + pi.submit() + + self.assertEqual(pi.taxes[0].tax_amount, 300) + self.assertEqual(pi.taxes[0].account_head, tds_account) + + gl_entries = frappe.get_all( + "GL Entry", + filters={"voucher_no": pi.name, "voucher_type": "Purchase Invoice", "account": "Creditors - _TC"}, + fields=["account", "against", "debit", "credit"], + ) + + for gle in gl_entries: + if gle.debit: + # GL Entry with TDS Amount + self.assertEqual(gle.against, tds_account) + self.assertEqual(gle.debit, 300) + else: + # GL Entry with Purchase Invoice Amount + self.assertEqual(gle.credit, 3000) + def test_provisional_accounting_entry(self): setup_provisional_accounting() diff --git a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py index c8893a13e86..2b7ae5fd689 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py @@ -74,11 +74,17 @@ class TestTaxWithholdingCategory(FrappeTestCase): self.assertEqual(pi.grand_total, 18000) # check gl entry for the purchase invoice - gl_entries = frappe.db.get_all("GL Entry", filters={"voucher_no": pi.name}, fields=["*"]) + gl_entries = frappe.db.get_all( + "GL Entry", + filters={"voucher_no": pi.name}, + fields=["account", "sum(debit) as debit", "sum(credit) as credit"], + group_by="account", + ) self.assertEqual(len(gl_entries), 3) for d in gl_entries: if d.account == pi.credit_to: - self.assertEqual(d.credit, 18000) + self.assertEqual(d.credit, 20000) + self.assertEqual(d.debit, 2000) elif d.account == pi.items[0].get("expense_account"): self.assertEqual(d.debit, 20000) elif d.account == pi.taxes[0].get("account_head"): diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py index 856e2b96af0..f9b503675aa 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -234,6 +234,10 @@ def merge_similar_entries(gl_map, precision=None): merge_properties = get_merge_properties(accounting_dimensions) for entry in gl_map: + if entry._skip_merge: + merged_gl_map.append(entry) + continue + entry.merge_key = get_merge_key(entry, merge_properties) # if there is already an entry in this account then just add it # to that entry From e6894b949c9058ac0141d57dd4b6a5527c656f62 Mon Sep 17 00:00:00 2001 From: HENRY Florian Date: Mon, 11 Nov 2024 05:46:17 +0100 Subject: [PATCH 503/734] fix: when company is created with other company template Chart of Account the Create Taxe Template failed (#42755) fix: when company if create with other company template Created Template Taxe failed (cherry picked from commit 8383883977783d33f509cf3d34b40f388a2b32a7) --- erpnext/setup/setup_wizard/operations/taxes_setup.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/setup/setup_wizard/operations/taxes_setup.py b/erpnext/setup/setup_wizard/operations/taxes_setup.py index 384673448b0..0faebb6ab4c 100644 --- a/erpnext/setup/setup_wizard/operations/taxes_setup.py +++ b/erpnext/setup/setup_wizard/operations/taxes_setup.py @@ -86,7 +86,10 @@ def simple_to_detailed(templates): def from_detailed_data(company_name, data): """Create Taxes and Charges Templates from detailed data.""" - coa_name = frappe.db.get_value("Company", company_name, "chart_of_accounts") + charts_company_name = company_name + if frappe.db.get_value("Company", company_name, "create_chart_of_accounts_based_on"): + charts_company_name = frappe.db.get_value("Company", company_name, "existing_company") + coa_name = frappe.db.get_value("Company", charts_company_name, "chart_of_accounts") coa_data = data.get("chart_of_accounts", {}) tax_templates = coa_data.get(coa_name) or coa_data.get("*", {}) tax_categories = data.get("tax_categories") From 865786e0b67fb35d2423b4cf281dd76cdca3bd51 Mon Sep 17 00:00:00 2001 From: HENRY Florian Date: Mon, 11 Nov 2024 05:47:51 +0100 Subject: [PATCH 504/734] feat: add template taxe for charts of account France - Plan Comptable General avec code (#42757) * feat: add template taxe for charts of account France - Plan Comptable General avec code * feat: add template taxe for charts of account France - Plan Comptable General avec code * feat: add template taxe for charts of account France - Plan Comptable General avec code * feat: add template taxe for charts of account France - Plan Comptable General avec code * feat: add template taxe for charts of account France - Plan Comptable General avec code (cherry picked from commit 1fe6efdeb9ef7a2d100a536a099b6de8b003e416) --- .../setup_wizard/data/country_wise_tax.json | 361 +++++++++++++++++- 1 file changed, 349 insertions(+), 12 deletions(-) diff --git a/erpnext/setup/setup_wizard/data/country_wise_tax.json b/erpnext/setup/setup_wizard/data/country_wise_tax.json index a746ebee7ed..efe1d705c51 100644 --- a/erpnext/setup/setup_wizard/data/country_wise_tax.json +++ b/erpnext/setup/setup_wizard/data/country_wise_tax.json @@ -449,18 +449,355 @@ }, "France": { - "France VAT 20%": { - "account_name": "VAT 20%", - "tax_rate": 20, - "default": 1 - }, - "France VAT 10%": { - "account_name": "VAT 10%", - "tax_rate": 10 - }, - "France VAT 5.5%": { - "account_name": "VAT 5.5%", - "tax_rate": 5.5 + "chart_of_accounts": { + "France - Plan Comptable General avec code": { + "sales_tax_templates": [ + { + "title": "TVA 20% CollectĆ©e", + "tax_category": "Vente Domestique", + "taxes": [ + { + "account_head": { + "account_name": "TVA 20% CollectĆ©e", + "account_number": "445720", + "root_type": "Liability", + "tax_rate": 20.0 + }, + "description": "TVA 20%", + "rate": 20 + } + ] + }, + { + "title": "TVA 10% CollectĆ©e", + "tax_category": "Vente Domestique", + "taxes": [ + { + "account_head": { + "account_name": "TVA 10% CollectĆ©e", + "account_number": "445710", + "root_type": "Liability", + "tax_rate": 10.0 + }, + "description": "TVA 10%", + "rate": 10 + } + ] + }, + { + "title": "TVA 5.5% CollectĆ©e", + "tax_category": "Vente Domestique", + "taxes": [ + { + "account_head": { + "account_name": "TVA 5.5% CollectĆ©e", + "account_number": "445755", + "root_type": "Liability", + "tax_rate": 5.5 + }, + "description": "TVA 5.5%", + "rate": 5.5 + } + ] + }, + { + "title": "TVA 2.1% CollectĆ©e", + "tax_category": "Vente Domestique", + "taxes": [ + { + "account_head": { + "account_name": "TVA 2.1% CollectĆ©e", + "account_number": "445721", + "root_type": "Liability", + "tax_rate": 2.10 + }, + "description": "TVA 2.1%", + "rate": 2.1 + } + ] + } + ], + "purchase_tax_templates": [ + { + "title": "TVA 20% DĆ©ductible", + "tax_category": "Achat Domestique", + "taxes": [ + { + "account_head": { + "account_name": "TVA 20% DĆ©ductible", + "account_number": "445620", + "root_type": "Asset", + "tax_rate": 20.0 + }, + "description": "TVA 20%", + "rate": 20 + } + ] + }, + { + "title": "TVA 10% DĆ©ductible", + "tax_category": "Achat Domestique", + "taxes": [ + { + "account_head": { + "account_name": "TVA 10% DĆ©ductible", + "account_number": "445610", + "root_type": "Asset", + "tax_rate": 10.0 + }, + "description": "TVA 10%", + "rate": 10 + } + ] + }, + { + "title": "TVA 5.5% DĆ©ductible", + "tax_category": "Achat Domestique", + "taxes": [ + { + "account_head": { + "account_name": "TVA 5.5% DĆ©ductible", + "account_number": "445655", + "root_type": "Asset", + "tax_rate": 5.5 + }, + "description": "TVA 5.5%", + "rate": 5.5 + } + ] + }, + { + "title": "TVA 2.1% DĆ©ductible", + "tax_category": "Achat Domestique", + "taxes": [ + { + "account_head": { + "account_name": "TVA 2.1% DĆ©ductible", + "account_number": "445621", + "root_type": "Asset", + "tax_rate": 2.1 + }, + "description": "TVA 2.1%", + "rate": 2.1 + } + ] + }, + { + "title": "TVA 20% DĆ©ductible - Incluse dans le prix", + "taxes": [ + { + "account_head": { + "account_name": "TVA 20% DĆ©ductible", + "account_number": "445620", + "root_type": "Asset", + "tax_rate": 20.0 + }, + "included_in_print_rate": 1, + "description": "TVA 20%", + "rate": 20 + } + ] + }, + { + "title": "TVA 10% DĆ©ductible - Incluse dans le prix", + "taxes": [ + { + "account_head": { + "account_name": "TVA 10% DĆ©ductible", + "account_number": "445610", + "root_type": "Asset", + "tax_rate": 10.0 + }, + "included_in_print_rate": 1, + "description": "TVA 10%", + "rate": 10 + } + ] + }, + { + "title": "TVA 5.5% DĆ©ductible - Incluse dans le prix", + "taxes": [ + { + "account_head": { + "account_name": "TVA 5.5% DĆ©ductible", + "account_number": "445655", + "root_type": "Asset", + "tax_rate": 5.5 + }, + "included_in_print_rate": 1, + "description": "TVA 5.5%", + "rate": 5.5 + } + ] + }, + { + "title": "TVA 2.1% DĆ©ductible - Incluse dans le prix", + "taxes": [ + { + "account_head": { + "account_name": "TVA 2.1% DĆ©ductible", + "account_number": "445621", + "root_type": "Asset", + "tax_rate": 2.1 + }, + "included_in_print_rate": 1, + "description": "TVA 2.1%", + "rate": 2.1 + } + ] + }, + { + "title": "TVA Intracommunautaire", + "tax_category": "Achat - EU", + "taxes": [ + { + "account_head": { + "account_name": "TVA dĆ©ductible sur acquisition intracommunautaires", + "account_number": "445662", + "root_type": "Asset", + "tax_rate": 20.0, + "add_deduct_tax": "Add" + }, + "description": "TVA dĆ©ductible sur acquisition intracommunautaires", + "rate": 20 + }, + { + "account_head": { + "account_name": "TVA due intracommunautaire", + "account_number": "445200", + "root_type": "Asset", + "tax_rate": 20.0, + "add_deduct_tax": "Deduct" + }, + "description": "TVA due intracommunautaire", + "rate": 20 + } + ] + } + ], + "item_tax_templates": [ + { + "title": "TVA 20% DĆ©ductible - Achat", + "taxes": [ + { + "tax_type": { + "account_name": "TVA 20% DĆ©ductible", + "account_number": "445620", + "root_type": "Asset", + "tax_rate": 20.0 + }, + "description": "TVA 20%", + "tax_rate": 20 + } + ] + }, + { + "title": "TVA 10% DĆ©ductible - Achat", + "taxes": [ + { + "tax_type": { + "account_name": "TVA 10% DĆ©ductible", + "account_number": "445610", + "root_type": "Asset", + "tax_rate": 10.0 + }, + "description": "TVA 10%", + "tax_rate": 10 + } + ] + }, + { + "title": "TVA 5.5% DĆ©ductible - Achat", + "taxes": [ + { + "tax_type": { + "account_name": "TVA 5.5% DĆ©ductible", + "account_number": "445655", + "root_type": "Asset", + "tax_rate": 5.5 + }, + "description": "TVA 5.5%", + "tax_rate": 5.5 + } + ] + }, + { + "title": "TVA 2.1% DĆ©ductible - Achat", + "taxes": [ + { + "tax_type": { + "account_name": "TVA 2.1% DĆ©ductible", + "account_number": "445621", + "root_type": "Asset", + "tax_rate": 2.1 + }, + "description": "TVA 2.1%", + "tax_rate": 2.1 + } + ] + }, + { + "title": "TVA 20% CollectĆ© - Vente", + "taxes": [ + { + "tax_type": { + "account_name": "TVA 20% CollectĆ©", + "account_number": "445720", + "root_type": "Liability", + "tax_rate": 20.0 + }, + "description": "TVA 20%", + "tax_rate": 20 + } + ] + }, + { + "title": "TVA 10% CollectĆ© - Vente", + "taxes": [ + { + "tax_type": { + "account_name": "TVA 10% CollectĆ©", + "account_number": "445710", + "root_type": "Liability", + "tax_rate": 10.0 + }, + "description": "TVA 10%", + "tax_rate": 10 + } + ] + }, + { + "title": "TVA 5.5% CollectĆ© - Vente", + "taxes": [ + { + "tax_type": { + "account_name": "TVA 5.5% CollectĆ©", + "account_number": "445755", + "root_type": "Liability", + "tax_rate": 5.5 + }, + "description": "TVA 5.5%", + "tax_rate": 5.5 + } + ] + }, + { + "title": "TVA 2.1% CollectĆ© - Vente", + "taxes": [ + { + "tax_type": { + "account_name": "TVA 2.1% CollectĆ©", + "account_number": "445721", + "root_type": "Liability", + "tax_rate": 2.1 + }, + "description": "TVA 2.1%", + "tax_rate": 2.1 + } + ] + } + ] + } } }, From 3e29ae85345fc8c3ec0851987804f3886852097f Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 11 Nov 2024 11:12:33 +0530 Subject: [PATCH 505/734] fix: slow reposting due to SABB update (cherry picked from commit 2447b3f424fb26d1a72676ac439c1b2c28cfeb11) --- erpnext/stock/doctype/stock_entry/stock_entry.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 5993580032f..f362f9d3da9 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -795,9 +795,6 @@ class StockEntry(StockController): self.set_total_incoming_outgoing_value() self.set_total_amount() - if not reset_outgoing_rate: - self.set_serial_and_batch_bundle() - def set_basic_rate(self, reset_outgoing_rate=True, raise_error_if_no_rate=True): """ Set rate for outgoing, scrapped and finished items From 7d098328d0bc6e74aca03e505f3c31d845c38d9b Mon Sep 17 00:00:00 2001 From: vishnu Date: Sun, 10 Nov 2024 06:55:16 +0000 Subject: [PATCH 506/734] fix: tyeerror while saving pick list (cherry picked from commit 22de0ecbdcb34ffde91b0788c66e6bb8beb18a17) --- erpnext/stock/doctype/pick_list/pick_list.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index 27a37ef1bab..ba1cc228bfa 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -109,7 +109,7 @@ class PickList(Document): "actual_qty", ) - if row.qty > bin_qty: + if row.qty > flt(bin_qty): frappe.throw( _( "At Row #{0}: The picked quantity {1} for the item {2} is greater than available stock {3} in the warehouse {4}." From 65ec7c56041612c17dce79def87ce31ee503f391 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 11 Nov 2024 12:27:26 +0530 Subject: [PATCH 507/734] fix: add default height to POS item card selector (backport #44071) (#44075) fix: add default height to POS item card selector (cherry picked from commit 5f5a514d6f7845be1a729427561e040b16ba4b5e) Co-authored-by: Kavin <78342682+kavin0411@users.noreply.github.com> --- erpnext/selling/page/point_of_sale/pos_item_selector.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/selling/page/point_of_sale/pos_item_selector.js b/erpnext/selling/page/point_of_sale/pos_item_selector.js index b5fa8849d60..862dfd54e5c 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_selector.js +++ b/erpnext/selling/page/point_of_sale/pos_item_selector.js @@ -99,7 +99,7 @@ erpnext.PointOfSale.ItemSelector = class { return `
${qty_to_display}
-
+
Date: Mon, 11 Nov 2024 12:51:48 +0530 Subject: [PATCH 508/734] chore: update `CODEOWNERS` (backport #44074) (#44081) chore: update `CODEOWNERS` (#44074) (cherry picked from commit 9a758ea826c091dcd298b19d50d8684e0147beae) Co-authored-by: s-aga-r --- CODEOWNERS | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index 9077c6783c7..4a19fc871b5 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -4,21 +4,21 @@ # the repo. Unless a later match takes precedence, erpnext/accounts/ @deepeshgarg007 @ruthra-kumar -erpnext/assets/ @anandbaburajan @deepeshgarg007 +erpnext/assets/ @khushi8112 @deepeshgarg007 erpnext/regional @deepeshgarg007 @ruthra-kumar erpnext/selling @deepeshgarg007 @ruthra-kumar erpnext/support/ @deepeshgarg007 pos* -erpnext/buying/ @rohitwaghchaure @s-aga-r -erpnext/maintenance/ @rohitwaghchaure @s-aga-r -erpnext/manufacturing/ @rohitwaghchaure @s-aga-r -erpnext/quality_management/ @rohitwaghchaure @s-aga-r -erpnext/stock/ @rohitwaghchaure @s-aga-r -erpnext/subcontracting @rohitwaghchaure @s-aga-r +erpnext/buying/ @rohitwaghchaure +erpnext/maintenance/ @rohitwaghchaure +erpnext/manufacturing/ @rohitwaghchaure +erpnext/quality_management/ @rohitwaghchaure +erpnext/stock/ @rohitwaghchaure +erpnext/subcontracting @rohitwaghchaure erpnext/controllers/ @deepeshgarg007 @rohitwaghchaure erpnext/patches/ @deepeshgarg007 .github/ @deepeshgarg007 -pyproject.toml @phot0n +pyproject.toml @akhilnarang From 34b5639d1c3a260f527ae02e2dc5ce05efd73e1c Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 11 Nov 2024 13:00:27 +0530 Subject: [PATCH 509/734] fix: task path (backport #44073) (#44078) fix: task path (#44073) (cherry picked from commit 8c99acb1b905d57515c42db3e24d480f96048aa1) Co-authored-by: Nihantra C. Patel <141945075+Nihantra-Patel@users.noreply.github.com> --- erpnext/projects/doctype/task/task.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/projects/doctype/task/task.py b/erpnext/projects/doctype/task/task.py index c03c99b25d5..5eae55d71c5 100755 --- a/erpnext/projects/doctype/task/task.py +++ b/erpnext/projects/doctype/task/task.py @@ -153,14 +153,14 @@ class Task(NestedSet): def validate_parent_template_task(self): if self.parent_task: if not frappe.db.get_value("Task", self.parent_task, "is_template"): - parent_task_format = f"""{self.parent_task}""" + parent_task_format = f"""{self.parent_task}""" frappe.throw(_("Parent Task {0} is not a Template Task").format(parent_task_format)) def validate_depends_on_tasks(self): if self.depends_on: for task in self.depends_on: if not frappe.db.get_value("Task", task.task, "is_template"): - dependent_task_format = f"""{task.task}""" + dependent_task_format = f"""{task.task}""" frappe.throw(_("Dependent Task {0} is not a Template Task").format(dependent_task_format)) def validate_completed_on(self): From 363f15124eaa857d112b04ae7429b11360015215 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 11 Nov 2024 13:12:30 +0530 Subject: [PATCH 510/734] fix: populate payment schedule from payment terms (backport #44082) (#44083) fix: populate payment schedule from payment terms (#44082) (cherry picked from commit c81eb6c824b04d8f174ece6900a5669b9a7c9461) Co-authored-by: Nihantra C. Patel <141945075+Nihantra-Patel@users.noreply.github.com> --- erpnext/public/js/controllers/transaction.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 1773586ac48..07ba90cd6d1 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -2446,7 +2446,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe payment_terms_template() { var me = this; const doc = this.frm.doc; - if(doc.payment_terms_template && doc.doctype !== 'Delivery Note' && doc.is_return == 0) { + if(doc.payment_terms_template && doc.doctype !== 'Delivery Note' && !doc.is_return) { var posting_date = doc.posting_date || doc.transaction_date; frappe.call({ method: "erpnext.controllers.accounts_controller.get_payment_terms", From be07421ab77b43681dffc7e038630b01ecc61984 Mon Sep 17 00:00:00 2001 From: Vishv-silveroak <108357657+Vishv-024@users.noreply.github.com> Date: Wed, 16 Oct 2024 18:03:40 +0530 Subject: [PATCH 511/734] fix: exception on register reports when filtered on cost center 1 (cherry picked from commit f01e1a8e20d6fa32e48ff22d850e737be5e32e2b) --- erpnext/accounts/report/utils.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/utils.py b/erpnext/accounts/report/utils.py index d6c1b95cf7c..2a72b10e4eb 100644 --- a/erpnext/accounts/report/utils.py +++ b/erpnext/accounts/report/utils.py @@ -255,7 +255,9 @@ def get_journal_entries(filters, args): ) .orderby(je.posting_date, je.name, order=Order.desc) ) - query = apply_common_conditions(filters, query, doctype="Journal Entry", payments=True) + query = apply_common_conditions( + filters, query, doctype="Journal Entry", child_doctype="Journal Entry Account", payments=True + ) journal_entries = query.run(as_dict=True) return journal_entries @@ -306,7 +308,9 @@ def apply_common_conditions(filters, query, doctype, child_doctype=None, payment query = query.where(parent_doc.posting_date <= filters.to_date) if payments: - if filters.get("cost_center"): + if doctype == "Journal Entry" and filters.get("cost_center"): + query = query.where(child_doc.cost_center == filters.cost_center) + elif filters.get("cost_center"): query = query.where(parent_doc.cost_center == filters.cost_center) else: if filters.get("cost_center"): From 2183b993300124df7652c2add8ffaa1504b66825 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 8 Nov 2024 12:08:05 +0530 Subject: [PATCH 512/734] test: basic report output (cherry picked from commit 657201b32485cc3471fc91fdb6b20c7d73abdf60) --- .../sales_register/test_sales_register.py | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 erpnext/accounts/report/sales_register/test_sales_register.py diff --git a/erpnext/accounts/report/sales_register/test_sales_register.py b/erpnext/accounts/report/sales_register/test_sales_register.py new file mode 100644 index 00000000000..6ed754fa837 --- /dev/null +++ b/erpnext/accounts/report/sales_register/test_sales_register.py @@ -0,0 +1,64 @@ +import frappe +from frappe.tests import IntegrationTestCase +from frappe.utils import getdate, today + +from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice +from erpnext.accounts.report.item_wise_sales_register.item_wise_sales_register import execute +from erpnext.accounts.test.accounts_mixin import AccountsTestMixin + + +class TestItemWiseSalesRegister(AccountsTestMixin, IntegrationTestCase): + def setUp(self): + self.create_company() + self.create_customer() + self.create_item() + + def tearDown(self): + frappe.db.rollback() + + def create_sales_invoice(self, rate=100, do_not_submit=False): + si = create_sales_invoice( + item=self.item, + company=self.company, + customer=self.customer, + debit_to=self.debit_to, + posting_date=today(), + parent_cost_center=self.cost_center, + cost_center=self.cost_center, + rate=rate, + price_list_rate=rate, + do_not_save=1, + ) + si = si.save() + if not do_not_submit: + si = si.submit() + return si + + def test_basic_report_output(self): + si = self.create_sales_invoice(rate=98) + + filters = frappe._dict({"from_date": today(), "to_date": today(), "company": self.company}) + report = execute(filters) + + self.assertEqual(len(report[1]), 1) + + expected_result = { + "item_code": si.items[0].item_code, + "invoice": si.name, + "posting_date": getdate(), + "customer": si.customer, + "debit_to": si.debit_to, + "company": self.company, + "income_account": si.items[0].income_account, + "stock_qty": 1.0, + "stock_uom": si.items[0].stock_uom, + "rate": 98.0, + "amount": 98.0, + "total_tax": 0, + "total_other_charges": 0, + "total": 98.0, + "currency": "INR", + } + + report_output = {k: v for k, v in report[1][0].items() if k in expected_result} + self.assertDictEqual(report_output, expected_result) From 2affa60ea9e606cc63c8328be836cc1302b7541a Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 11 Nov 2024 12:18:25 +0530 Subject: [PATCH 513/734] test: journals with cost center (cherry picked from commit c255f34eead1cba9210df609b22eb21e643f3d40) --- .../sales_register/test_sales_register.py | 121 +++++++++++++++++- 1 file changed, 120 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/report/sales_register/test_sales_register.py b/erpnext/accounts/report/sales_register/test_sales_register.py index 6ed754fa837..0bd67f39907 100644 --- a/erpnext/accounts/report/sales_register/test_sales_register.py +++ b/erpnext/accounts/report/sales_register/test_sales_register.py @@ -3,7 +3,7 @@ from frappe.tests import IntegrationTestCase from frappe.utils import getdate, today from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice -from erpnext.accounts.report.item_wise_sales_register.item_wise_sales_register import execute +from erpnext.accounts.report.sales_register.sales_register import execute from erpnext.accounts.test.accounts_mixin import AccountsTestMixin @@ -12,10 +12,27 @@ class TestItemWiseSalesRegister(AccountsTestMixin, IntegrationTestCase): self.create_company() self.create_customer() self.create_item() + self.create_child_cost_center() def tearDown(self): frappe.db.rollback() + def create_child_cost_center(self): + cc_name = "South Wing" + if frappe.db.exists("Cost Center", cc_name): + cc = frappe.get_doc("Cost Center", cc_name) + else: + cc = frappe.get_doc( + { + "doctype": "Cost Center", + "parent_cost_center": self.cost_center, + "company": self.company, + "is_group": False, + } + ) + cc = cc.save() + self.south_cc = cc.name + def create_sales_invoice(self, rate=100, do_not_submit=False): si = create_sales_invoice( item=self.item, @@ -62,3 +79,105 @@ class TestItemWiseSalesRegister(AccountsTestMixin, IntegrationTestCase): report_output = {k: v for k, v in report[1][0].items() if k in expected_result} self.assertDictEqual(report_output, expected_result) + + def test_journal_with_cost_center_filter(self): + je1 = frappe.get_doc( + { + "doctype": "Journal Entry", + "voucher_type": "Journal Entry", + "company": self.company, + "posting_date": getdate(), + "accounts": [ + { + "account": self.debit_to, + "party_type": "Customer", + "party": self.customer, + "credit_in_account_currency": 77, + "credit": 77, + "is_advance": "Yes", + "cost_center": self.south_cc, + }, + { + "account": self.cash, + "debit_in_account_currency": 77, + "debit": 77, + }, + ], + } + ) + je1.submit() + + je2 = frappe.get_doc( + { + "doctype": "Journal Entry", + "voucher_type": "Journal Entry", + "company": self.company, + "posting_date": getdate(), + "accounts": [ + { + "account": self.debit_to, + "party_type": "Customer", + "party": self.customer, + "credit_in_account_currency": 98, + "credit": 98, + "is_advance": "Yes", + "cost_center": self.cost_center, + }, + { + "account": self.cash, + "debit_in_account_currency": 98, + "debit": 98, + }, + ], + } + ) + je2.submit() + + filters = frappe._dict( + { + "from_date": today(), + "to_date": today(), + "company": self.company, + "include_payments": True, + "customer": self.customer, + "cost_center": self.cost_center, + } + ) + result = [x for x in execute(filters)[1] if x.voucher_no == je1.name] + expected_result = { + "voucher_type": je1.doctype, + "voucher_no": je1.name, + "posting_date": je1.posting_date, + "customer": self.customer, + "receivable_account": self.debit_to, + "net_total": 77, + "cost_center": self.south_cc, + "credit": 77, + } + result_output = {k: v for k, v in result.items() if k in expected_result} + self.assertDictEqual(result_output, expected_result) + + # Without cost center filter + filters = frappe._dict( + { + "from_date": today(), + "to_date": today(), + "company": self.company, + "include_payments": True, + "customer": self.customer, + "cost_center": self.south_cc, + } + ) + result = [x for x in execute(filters)[1] if x.voucher_no == je2.name] + expected_result = { + "voucher_type": je2.doctype, + "voucher_no": je2.name, + "posting_date": je2.posting_date, + "customer": self.customer, + "receivable_account": self.debit_to, + "net_total": 98, + "cost_center": self.south_cc, + "credit": 98, + } + result_output = {k: v for k, v in result.items() if k in expected_result} + self.assertDictEqual(result_output, expected_result) From 9724cefce8ee677865e2152ba6c4d867e0594692 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 11 Nov 2024 12:34:54 +0530 Subject: [PATCH 514/734] refactor(test): fix incorrect assertion (cherry picked from commit d6030e71121f5c9d7d25c4d11310f4e56cc46833) --- .../sales_register/test_sales_register.py | 26 +++++++------------ 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/erpnext/accounts/report/sales_register/test_sales_register.py b/erpnext/accounts/report/sales_register/test_sales_register.py index 0bd67f39907..dbb4cd89851 100644 --- a/erpnext/accounts/report/sales_register/test_sales_register.py +++ b/erpnext/accounts/report/sales_register/test_sales_register.py @@ -60,21 +60,14 @@ class TestItemWiseSalesRegister(AccountsTestMixin, IntegrationTestCase): self.assertEqual(len(report[1]), 1) expected_result = { - "item_code": si.items[0].item_code, - "invoice": si.name, - "posting_date": getdate(), - "customer": si.customer, - "debit_to": si.debit_to, - "company": self.company, - "income_account": si.items[0].income_account, - "stock_qty": 1.0, - "stock_uom": si.items[0].stock_uom, - "rate": 98.0, - "amount": 98.0, - "total_tax": 0, - "total_other_charges": 0, - "total": 98.0, - "currency": "INR", + "voucher_type": si.doctype, + "voucher_no": si.name, + "posting_date": si.posting_date, + "customer": self.customer, + "receivable_account": self.debit_to, + "net_total": 98, + "grand_total": 98, + "credit": 98, } report_output = {k: v for k, v in report[1][0].items() if k in expected_result} @@ -151,13 +144,12 @@ class TestItemWiseSalesRegister(AccountsTestMixin, IntegrationTestCase): "customer": self.customer, "receivable_account": self.debit_to, "net_total": 77, - "cost_center": self.south_cc, + "cost_center": self.cost_center, "credit": 77, } result_output = {k: v for k, v in result.items() if k in expected_result} self.assertDictEqual(result_output, expected_result) - # Without cost center filter filters = frappe._dict( { "from_date": today(), From d0e5568010969c01eae67ca9434f528c05185984 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 11 Nov 2024 13:06:09 +0530 Subject: [PATCH 515/734] refactor(test): pass all mandatory fields (cherry picked from commit c53e9637dd1ee39ebfeb713f39e0eaafb414e5c7) --- erpnext/accounts/report/sales_register/test_sales_register.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/accounts/report/sales_register/test_sales_register.py b/erpnext/accounts/report/sales_register/test_sales_register.py index dbb4cd89851..cb8f37a4f0f 100644 --- a/erpnext/accounts/report/sales_register/test_sales_register.py +++ b/erpnext/accounts/report/sales_register/test_sales_register.py @@ -28,6 +28,7 @@ class TestItemWiseSalesRegister(AccountsTestMixin, IntegrationTestCase): "parent_cost_center": self.cost_center, "company": self.company, "is_group": False, + "cost_center_name": cc_name, } ) cc = cc.save() From 2de9292ac0d93b1a904fdd46377631dead44c972 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 11 Nov 2024 14:16:49 +0530 Subject: [PATCH 516/734] refactor(test): assertion refactoring and exact decimals (cherry picked from commit 1d11131afeef9040ad98647c834daf2faafcb782) --- .../sales_register/test_sales_register.py | 39 ++++++++++--------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/erpnext/accounts/report/sales_register/test_sales_register.py b/erpnext/accounts/report/sales_register/test_sales_register.py index cb8f37a4f0f..50f12e52e33 100644 --- a/erpnext/accounts/report/sales_register/test_sales_register.py +++ b/erpnext/accounts/report/sales_register/test_sales_register.py @@ -22,12 +22,13 @@ class TestItemWiseSalesRegister(AccountsTestMixin, IntegrationTestCase): if frappe.db.exists("Cost Center", cc_name): cc = frappe.get_doc("Cost Center", cc_name) else: + parent = frappe.db.get_value("Cost Center", self.cost_center, "parent_cost_center") cc = frappe.get_doc( { "doctype": "Cost Center", - "parent_cost_center": self.cost_center, "company": self.company, "is_group": False, + "parent_cost_center": parent, "cost_center_name": cc_name, } ) @@ -63,12 +64,12 @@ class TestItemWiseSalesRegister(AccountsTestMixin, IntegrationTestCase): expected_result = { "voucher_type": si.doctype, "voucher_no": si.name, - "posting_date": si.posting_date, + "posting_date": getdate(), "customer": self.customer, "receivable_account": self.debit_to, - "net_total": 98, - "grand_total": 98, - "credit": 98, + "net_total": 98.0, + "grand_total": 98.0, + "debit": 98.0, } report_output = {k: v for k, v in report[1][0].items() if k in expected_result} @@ -89,7 +90,7 @@ class TestItemWiseSalesRegister(AccountsTestMixin, IntegrationTestCase): "credit_in_account_currency": 77, "credit": 77, "is_advance": "Yes", - "cost_center": self.south_cc, + "cost_center": self.cost_center, }, { "account": self.cash, @@ -115,7 +116,7 @@ class TestItemWiseSalesRegister(AccountsTestMixin, IntegrationTestCase): "credit_in_account_currency": 98, "credit": 98, "is_advance": "Yes", - "cost_center": self.cost_center, + "cost_center": self.south_cc, }, { "account": self.cash, @@ -137,19 +138,20 @@ class TestItemWiseSalesRegister(AccountsTestMixin, IntegrationTestCase): "cost_center": self.cost_center, } ) - result = [x for x in execute(filters)[1] if x.voucher_no == je1.name] + report_output = execute(filters)[1] + filtered_output = [x for x in report_output if x.get("voucher_no") == je1.name] + self.assertEqual(len(filtered_output), 1) expected_result = { "voucher_type": je1.doctype, "voucher_no": je1.name, "posting_date": je1.posting_date, "customer": self.customer, "receivable_account": self.debit_to, - "net_total": 77, - "cost_center": self.cost_center, - "credit": 77, + "net_total": 77.0, + "credit": 77.0, } - result_output = {k: v for k, v in result.items() if k in expected_result} - self.assertDictEqual(result_output, expected_result) + result_fields = {k: v for k, v in filtered_output[0].items() if k in expected_result} + self.assertDictEqual(result_fields, expected_result) filters = frappe._dict( { @@ -161,16 +163,17 @@ class TestItemWiseSalesRegister(AccountsTestMixin, IntegrationTestCase): "cost_center": self.south_cc, } ) - result = [x for x in execute(filters)[1] if x.voucher_no == je2.name] + report_output = execute(filters)[1] + filtered_output = [x for x in report_output if x.get("voucher_no") == je2.name] + self.assertEqual(len(filtered_output), 1) expected_result = { "voucher_type": je2.doctype, "voucher_no": je2.name, "posting_date": je2.posting_date, "customer": self.customer, "receivable_account": self.debit_to, - "net_total": 98, - "cost_center": self.south_cc, - "credit": 98, + "net_total": 98.0, + "credit": 98.0, } - result_output = {k: v for k, v in result.items() if k in expected_result} + result_output = {k: v for k, v in filtered_output[0].items() if k in expected_result} self.assertDictEqual(result_output, expected_result) From 2bce7353008573832c110050534ec9f5f6799a32 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 11 Nov 2024 15:06:55 +0530 Subject: [PATCH 517/734] chore: use FrappeTestCase --- erpnext/accounts/report/sales_register/test_sales_register.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/sales_register/test_sales_register.py b/erpnext/accounts/report/sales_register/test_sales_register.py index 50f12e52e33..693c33c0ca6 100644 --- a/erpnext/accounts/report/sales_register/test_sales_register.py +++ b/erpnext/accounts/report/sales_register/test_sales_register.py @@ -1,5 +1,5 @@ import frappe -from frappe.tests import IntegrationTestCase +from frappe.tests.utils import FrappeTestCase from frappe.utils import getdate, today from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice @@ -7,7 +7,7 @@ from erpnext.accounts.report.sales_register.sales_register import execute from erpnext.accounts.test.accounts_mixin import AccountsTestMixin -class TestItemWiseSalesRegister(AccountsTestMixin, IntegrationTestCase): +class TestItemWiseSalesRegister(AccountsTestMixin, FrappeTestCase): def setUp(self): self.create_company() self.create_customer() From 9ac54f694c27323baca073f6d790497d1c056507 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Mon, 11 Nov 2024 16:07:16 +0530 Subject: [PATCH 518/734] fix: item mapping from modal to batch form (#44090) (cherry picked from commit 9223ef2f3744fbd4f6962040b403b26a5925e67d) --- erpnext/public/js/utils/serial_no_batch_selector.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/erpnext/public/js/utils/serial_no_batch_selector.js b/erpnext/public/js/utils/serial_no_batch_selector.js index 8329e5c9d55..61b40d6fef0 100644 --- a/erpnext/public/js/utils/serial_no_batch_selector.js +++ b/erpnext/public/js/utils/serial_no_batch_selector.js @@ -437,6 +437,11 @@ erpnext.SerialBatchPackageSelector = class SerialNoBatchBundleUpdate { fieldname: "batch_no", label: __("Batch No"), in_list_view: 1, + get_route_options_for_new_doc: () => { + return { + item: this.item.item_code, + }; + }, change() { let doc = this.doc; if (!doc.qty && me.item.type_of_transaction === "Outward") { From f387a8fcebbd5958e50a010b05c0c53d9d98328d Mon Sep 17 00:00:00 2001 From: HarryPaulo Date: Mon, 11 Nov 2024 07:38:30 -0300 Subject: [PATCH 519/734] fix: add field conversion_factor when include_uom is settled (#43701) (cherry picked from commit cfe6af1f6815bc420d7b74e2122c3cb3d2fe6463) --- erpnext/stock/report/stock_projected_qty/stock_projected_qty.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py b/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py index 743656c6472..9b4520064d6 100644 --- a/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py +++ b/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py @@ -297,6 +297,7 @@ def get_item_map(item_code, include_uom): if include_uom: ucd = frappe.qb.DocType("UOM Conversion Detail") + query = query.select(ucd.conversion_factor) query = query.left_join(ucd).on((ucd.parent == item.name) & (ucd.uom == include_uom)) items = query.run(as_dict=True) From b314f3839bfd19f81eac6d712a343d837fd4cb0f Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 11 Nov 2024 16:12:01 +0530 Subject: [PATCH 520/734] fix: Negative stock validation against inventory dimension (backport #43834) (#43846) fix: Negative stock validation against inventory dimension (#43834) (cherry picked from commit c330a292d2c473e5f0213ce5d5b15ed9928e65aa) Co-authored-by: Nabin Hait --- .../test_inventory_dimension.py | 21 ++++-- .../stock_ledger_entry/stock_ledger_entry.py | 70 +++++++++---------- 2 files changed, 51 insertions(+), 40 deletions(-) diff --git a/erpnext/stock/doctype/inventory_dimension/test_inventory_dimension.py b/erpnext/stock/doctype/inventory_dimension/test_inventory_dimension.py index 8ce954d55e6..918399a7f66 100644 --- a/erpnext/stock/doctype/inventory_dimension/test_inventory_dimension.py +++ b/erpnext/stock/doctype/inventory_dimension/test_inventory_dimension.py @@ -16,6 +16,7 @@ from erpnext.stock.doctype.inventory_dimension.inventory_dimension import ( from erpnext.stock.doctype.item.test_item import create_item from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry +from erpnext.stock.doctype.stock_ledger_entry.stock_ledger_entry import InventoryDimensionNegativeStockError from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse @@ -426,39 +427,49 @@ class TestInventoryDimension(FrappeTestCase): warehouse = create_warehouse("Negative Stock Warehouse") + # Try issuing 10 qty, more than available stock against inventory dimension doc = make_stock_entry(item_code=item_code, source=warehouse, qty=10, do_not_submit=True) doc.items[0].inv_site = "Site 1" - self.assertRaises(frappe.ValidationError, doc.submit) + self.assertRaises(InventoryDimensionNegativeStockError, doc.submit) + + # cancel the stock entry doc.reload() if doc.docstatus == 1: doc.cancel() + # Receive 10 qty against inventory dimension doc = make_stock_entry(item_code=item_code, target=warehouse, qty=10, do_not_submit=True) - doc.items[0].to_inv_site = "Site 1" doc.submit() + # check inventory dimension value in stock ledger entry site_name = frappe.get_all( "Stock Ledger Entry", filters={"voucher_no": doc.name, "is_cancelled": 0}, fields=["inv_site"] )[0].inv_site self.assertEqual(site_name, "Site 1") + # Receive another 100 qty without inventory dimension + doc = make_stock_entry(item_code=item_code, target=warehouse, qty=100) + + # Try issuing 100 qty, more than available stock against inventory dimension + # Note: total available qty for the item is 110, but against inventory dimension, only 10 qty is available doc = make_stock_entry(item_code=item_code, source=warehouse, qty=100, do_not_submit=True) - doc.items[0].inv_site = "Site 1" - self.assertRaises(frappe.ValidationError, doc.submit) + self.assertRaises(InventoryDimensionNegativeStockError, doc.submit) + # disable validate_negative_stock for inventory dimension inv_dimension.reload() inv_dimension.db_set("validate_negative_stock", 0) frappe.local.inventory_dimensions = {} + # Try issuing 100 qty, more than available stock against inventory dimension doc = make_stock_entry(item_code=item_code, source=warehouse, qty=100, do_not_submit=True) - doc.items[0].inv_site = "Site 1" doc.submit() self.assertEqual(doc.docstatus, 1) + # check inventory dimension value in stock ledger entry site_name = frappe.get_all( "Stock Ledger Entry", filters={"voucher_no": doc.name, "is_cancelled": 0}, fields=["inv_site"] )[0].inv_site diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py index 319303dbbb0..5aeabeeec56 100644 --- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py +++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py @@ -8,6 +8,7 @@ import frappe from frappe import _, bold from frappe.core.doctype.role.role import get_users from frappe.model.document import Document +from frappe.query_builder.functions import Sum from frappe.utils import add_days, cint, flt, formatdate, get_datetime, getdate from erpnext.accounts.utils import get_fiscal_year @@ -25,6 +26,10 @@ class BackDatedStockTransaction(frappe.ValidationError): pass +class InventoryDimensionNegativeStockError(frappe.ValidationError): + pass + + exclude_from_linked_with = True @@ -104,61 +109,56 @@ class StockLedgerEntry(Document): self.posting_datetime = get_combine_datetime(self.posting_date, self.posting_time) def validate_inventory_dimension_negative_stock(self): - if self.is_cancelled: + if self.is_cancelled or self.actual_qty >= 0: return - extra_cond = "" - kwargs = {} - dimensions = self._get_inventory_dimensions() if not dimensions: return - for dimension, values in dimensions.items(): - kwargs[dimension] = values.get("value") - extra_cond += f" and {dimension} = %({dimension})s" - - kwargs.update( - { - "item_code": self.item_code, - "warehouse": self.warehouse, - "posting_date": self.posting_date, - "posting_time": self.posting_time, - "company": self.company, - "sle": self.name, - } - ) - - sle = get_previous_sle(kwargs, extra_cond=extra_cond) - qty_after_transaction = 0.0 flt_precision = cint(frappe.db.get_default("float_precision")) or 2 - if sle: - qty_after_transaction = sle.qty_after_transaction + for dimension, values in dimensions.items(): + dimension_value = values.get("value") + available_qty = self.get_available_qty_after_prev_transaction(dimension, dimension_value) - diff = qty_after_transaction + flt(self.actual_qty) - diff = flt(diff, flt_precision) - if diff < 0 and abs(diff) > 0.0001: - self.throw_validation_error(diff, dimensions) + diff = flt(available_qty + flt(self.actual_qty), flt_precision) # qty after current transaction + if diff < 0 and abs(diff) > 0.0001: + self.throw_validation_error(diff, dimension, dimension_value) - def throw_validation_error(self, diff, dimensions): - dimension_msg = _(", with the inventory {0}: {1}").format( - "dimensions" if len(dimensions) > 1 else "dimension", - ", ".join(f"{bold(d.doctype)} ({d.value})" for k, d in dimensions.items()), - ) + def get_available_qty_after_prev_transaction(self, dimension, dimension_value): + sle = frappe.qb.DocType("Stock Ledger Entry") + available_qty = ( + frappe.qb.from_(sle) + .select(Sum(sle.actual_qty)) + .where( + (sle.item_code == self.item_code) + & (sle.warehouse == self.warehouse) + & (sle.posting_datetime < self.posting_datetime) + & (sle.company == self.company) + & (sle.is_cancelled == 0) + & (sle[dimension] == dimension_value) + ) + ).run() + return available_qty[0][0] or 0 + + def throw_validation_error(self, diff, dimension, dimension_value): msg = _( - "{0} units of {1} are required in {2}{3}, on {4} {5} for {6} to complete the transaction." + "{0} units of {1} are required in {2} with the inventory dimension: {3} ({4}) on {5} {6} for {7} to complete the transaction." ).format( abs(diff), frappe.get_desk_link("Item", self.item_code), frappe.get_desk_link("Warehouse", self.warehouse), - dimension_msg, + frappe.bold(dimension), + frappe.bold(dimension_value), self.posting_date, self.posting_time, frappe.get_desk_link(self.voucher_type, self.voucher_no), ) - frappe.throw(msg, title=_("Inventory Dimension Negative Stock")) + frappe.throw( + msg, title=_("Inventory Dimension Negative Stock"), exc=InventoryDimensionNegativeStockError + ) def _get_inventory_dimensions(self): inv_dimensions = get_inventory_dimensions() From 762f3bac65f05d66a1f2a09d1805686128221d6f Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 11 Nov 2024 15:59:40 +0530 Subject: [PATCH 521/734] chore: filter report output on document name --- erpnext/accounts/report/sales_register/test_sales_register.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/sales_register/test_sales_register.py b/erpnext/accounts/report/sales_register/test_sales_register.py index 693c33c0ca6..95aa5add24c 100644 --- a/erpnext/accounts/report/sales_register/test_sales_register.py +++ b/erpnext/accounts/report/sales_register/test_sales_register.py @@ -59,7 +59,7 @@ class TestItemWiseSalesRegister(AccountsTestMixin, FrappeTestCase): filters = frappe._dict({"from_date": today(), "to_date": today(), "company": self.company}) report = execute(filters) - self.assertEqual(len(report[1]), 1) + res = [x for x in report[1] if x.get("voucher_no") == si.name] expected_result = { "voucher_type": si.doctype, @@ -72,7 +72,7 @@ class TestItemWiseSalesRegister(AccountsTestMixin, FrappeTestCase): "debit": 98.0, } - report_output = {k: v for k, v in report[1][0].items() if k in expected_result} + report_output = {k: v for k, v in res[0].items() if k in expected_result} self.assertDictEqual(report_output, expected_result) def test_journal_with_cost_center_filter(self): From ba6e068abc2a158de251525758032ba18f7ae0d9 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 8 Nov 2024 17:16:52 +0530 Subject: [PATCH 522/734] refactor(test): update tests for new rounding logic --- .../doctype/pos_invoice/test_pos_invoice.py | 2 +- .../test_pos_invoice_merge_log.py | 2 +- .../sales_invoice/test_sales_invoice.py | 65 +++++++++++-------- 3 files changed, 39 insertions(+), 30 deletions(-) diff --git a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py index f210a6434cf..1dbc630e62e 100644 --- a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py @@ -93,7 +93,7 @@ class TestPOSInvoice(unittest.TestCase): inv.save() - self.assertEqual(inv.net_total, 4298.25) + self.assertEqual(inv.net_total, 4298.24) self.assertEqual(inv.grand_total, 4900.00) def test_tax_calculation_with_multiple_items(self): diff --git a/erpnext/accounts/doctype/pos_invoice_merge_log/test_pos_invoice_merge_log.py b/erpnext/accounts/doctype/pos_invoice_merge_log/test_pos_invoice_merge_log.py index 20f6ddb5ba9..904d8e83b9c 100644 --- a/erpnext/accounts/doctype/pos_invoice_merge_log/test_pos_invoice_merge_log.py +++ b/erpnext/accounts/doctype/pos_invoice_merge_log/test_pos_invoice_merge_log.py @@ -343,7 +343,7 @@ class TestPOSInvoiceMergeLog(unittest.TestCase): inv.load_from_db() consolidated_invoice = frappe.get_doc("Sales Invoice", inv.consolidated_invoice) self.assertEqual(consolidated_invoice.status, "Return") - self.assertEqual(consolidated_invoice.rounding_adjustment, -0.001) + self.assertEqual(consolidated_invoice.rounding_adjustment, -0.002) finally: frappe.set_user("Administrator") diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index db6fd41e439..56f90ae8cd4 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -314,7 +314,8 @@ class TestSalesInvoice(FrappeTestCase): si.insert() # with inclusive tax - self.assertEqual(si.items[0].net_amount, 3947.368421052631) + self.assertEqual(si.items[0].net_amount, 3947.37) + self.assertEqual(si.net_total, si.base_net_total) self.assertEqual(si.net_total, 3947.37) self.assertEqual(si.grand_total, 5000) @@ -658,7 +659,7 @@ class TestSalesInvoice(FrappeTestCase): 62.5, 625.0, 50, - 499.97600115194473, + 499.98, ], "_Test Item Home Desktop 200": [ 190.66, @@ -669,7 +670,7 @@ class TestSalesInvoice(FrappeTestCase): 190.66, 953.3, 150, - 749.9968530500239, + 750, ], } @@ -682,20 +683,21 @@ class TestSalesInvoice(FrappeTestCase): self.assertEqual(d.get(k), expected_values[d.item_code][i]) # check net total - self.assertEqual(si.net_total, 1249.97) + self.assertEqual(si.base_net_total, si.net_total) + self.assertEqual(si.net_total, 1249.98) self.assertEqual(si.total, 1578.3) # check tax calculation expected_values = { "keys": ["tax_amount", "total"], - "_Test Account Excise Duty - _TC": [140, 1389.97], - "_Test Account Education Cess - _TC": [2.8, 1392.77], - "_Test Account S&H Education Cess - _TC": [1.4, 1394.17], - "_Test Account CST - _TC": [27.88, 1422.05], - "_Test Account VAT - _TC": [156.25, 1578.30], - "_Test Account Customs Duty - _TC": [125, 1703.30], - "_Test Account Shipping Charges - _TC": [100, 1803.30], - "_Test Account Discount - _TC": [-180.33, 1622.97], + "_Test Account Excise Duty - _TC": [140, 1389.98], + "_Test Account Education Cess - _TC": [2.8, 1392.78], + "_Test Account S&H Education Cess - _TC": [1.4, 1394.18], + "_Test Account CST - _TC": [27.88, 1422.06], + "_Test Account VAT - _TC": [156.25, 1578.31], + "_Test Account Customs Duty - _TC": [125, 1703.31], + "_Test Account Shipping Charges - _TC": [100, 1803.31], + "_Test Account Discount - _TC": [-180.33, 1622.98], } for d in si.get("taxes"): @@ -731,7 +733,7 @@ class TestSalesInvoice(FrappeTestCase): "base_rate": 2500, "base_amount": 25000, "net_rate": 40, - "net_amount": 399.9808009215558, + "net_amount": 399.98, "base_net_rate": 2000, "base_net_amount": 19999, }, @@ -745,7 +747,7 @@ class TestSalesInvoice(FrappeTestCase): "base_rate": 7500, "base_amount": 37500, "net_rate": 118.01, - "net_amount": 590.0531205155963, + "net_amount": 590.05, "base_net_rate": 5900.5, "base_net_amount": 29502.5, }, @@ -783,8 +785,13 @@ class TestSalesInvoice(FrappeTestCase): self.assertEqual(si.base_grand_total, 60795) self.assertEqual(si.grand_total, 1215.90) - self.assertEqual(si.rounding_adjustment, 0.01) - self.assertEqual(si.base_rounding_adjustment, 0.50) + # no rounding adjustment as the Smallest Currency Fraction Value of USD is 0.01 + if frappe.db.get_value("Currency", "USD", "smallest_currency_fraction_value") < 0.01: + self.assertEqual(si.rounding_adjustment, 0.10) + self.assertEqual(si.base_rounding_adjustment, 5.0) + else: + self.assertEqual(si.rounding_adjustment, 0.0) + self.assertEqual(si.base_rounding_adjustment, 0.0) def test_outstanding(self): w = self.make() @@ -2172,7 +2179,7 @@ class TestSalesInvoice(FrappeTestCase): def test_rounding_adjustment_2(self): si = create_sales_invoice(rate=400, do_not_save=True) - for rate in [400, 600, 100]: + for rate in [400.25, 600.30, 100.65]: si.append( "items", { @@ -2198,18 +2205,19 @@ class TestSalesInvoice(FrappeTestCase): ) si.save() si.submit() - self.assertEqual(si.net_total, 1271.19) - self.assertEqual(si.grand_total, 1500) - self.assertEqual(si.total_taxes_and_charges, 228.82) - self.assertEqual(si.rounding_adjustment, -0.01) + self.assertEqual(si.net_total, si.base_net_total) + self.assertEqual(si.net_total, 1272.20) + self.assertEqual(si.grand_total, 1501.20) + self.assertEqual(si.total_taxes_and_charges, 229) + self.assertEqual(si.rounding_adjustment, -0.20) round_off_account = frappe.get_cached_value("Company", "_Test Company", "round_off_account") expected_values = { - "_Test Account Service Tax - _TC": [0.0, 114.41], - "_Test Account VAT - _TC": [0.0, 114.41], - si.debit_to: [1500, 0.0], - round_off_account: [0.01, 0.01], - "Sales - _TC": [0.0, 1271.18], + "_Test Account Service Tax - _TC": [0.0, 114.50], + "_Test Account VAT - _TC": [0.0, 114.50], + si.debit_to: [1501, 0.0], + round_off_account: [0.20, 0.0], + "Sales - _TC": [0.0, 1272.20], } gl_entries = frappe.db.sql( @@ -2267,7 +2275,8 @@ class TestSalesInvoice(FrappeTestCase): si.save() si.submit() - self.assertEqual(si.net_total, 4007.16) + self.assertEqual(si.net_total, si.base_net_total) + self.assertEqual(si.net_total, 4007.15) self.assertEqual(si.grand_total, 4488.02) self.assertEqual(si.total_taxes_and_charges, 480.86) self.assertEqual(si.rounding_adjustment, -0.02) @@ -2280,7 +2289,7 @@ class TestSalesInvoice(FrappeTestCase): ["_Test Account Service Tax - _TC", 0.0, 240.43], ["_Test Account VAT - _TC", 0.0, 240.43], ["Sales - _TC", 0.0, 4007.15], - [round_off_account, 0.02, 0.01], + [round_off_account, 0.01, 0.0], ] ) From 3423d3c13d3da909cdb1e165d04107b9bccee5d6 Mon Sep 17 00:00:00 2001 From: Kavin <78342682+kavin0411@users.noreply.github.com> Date: Mon, 11 Nov 2024 11:56:56 +0530 Subject: [PATCH 523/734] fix: bind this object explicitly on callback event function (cherry picked from commit 5e790a0fcea2e1e2f714aa8e312e9b9870b646e3) --- erpnext/accounts/doctype/pos_invoice/pos_invoice.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.js b/erpnext/accounts/doctype/pos_invoice/pos_invoice.js index deb8bd7529d..6a537a2559a 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.js +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.js @@ -65,7 +65,7 @@ erpnext.selling.POSInvoiceController = class POSInvoiceController extends erpnex super.refresh(); if (doc.docstatus == 1 && !doc.is_return) { - this.frm.add_custom_button(__("Return"), this.make_sales_return, __("Create")); + this.frm.add_custom_button(__("Return"), this.make_sales_return.bind(this), __("Create")); this.frm.page.set_inner_btn_group_as_primary(__("Create")); } From 61559be8a46f410370c199140fbf261a559ce17e Mon Sep 17 00:00:00 2001 From: Kavin <78342682+kavin0411@users.noreply.github.com> Date: Mon, 11 Nov 2024 11:21:48 +0530 Subject: [PATCH 524/734] fix: update payment amount for partial pos return (cherry picked from commit 53ef6336b62fab0dedc19b74dc1e5c293bf933c3) --- erpnext/public/js/controllers/taxes_and_totals.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index 8b2d7943772..ee78e493db6 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -844,13 +844,13 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { ); } - if(!this.frm.doc.is_return){ - this.frm.doc.payments.find(payment => { - if (payment.default) { - payment.amount = total_amount_to_pay; - } - }); - } + this.frm.doc.payments.find(payment => { + if (payment.default) { + payment.amount = total_amount_to_pay; + } else { + payment.amount = 0 + } + }); this.frm.refresh_fields(); } From 2754793ff9d5164b97607f4a977bda8a67313c61 Mon Sep 17 00:00:00 2001 From: Bhavan23 Date: Sat, 9 Nov 2024 19:54:50 +0530 Subject: [PATCH 525/734] feat: Add item group filtering for search results (cherry picked from commit 5e7cf3899bb6d841f4e96f12e8aa08db120b8ab0) --- .../selling/page/point_of_sale/point_of_sale.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.py b/erpnext/selling/page/point_of_sale/point_of_sale.py index fad184d152c..13430b0523d 100644 --- a/erpnext/selling/page/point_of_sale/point_of_sale.py +++ b/erpnext/selling/page/point_of_sale/point_of_sale.py @@ -92,6 +92,20 @@ def search_by_term(search_term, warehouse, price_list): return {"items": [item]} +def filter_result_items(result, pos_profile): + if result and result.get("items"): + pos_item_groups = frappe.db.get_all("POS Item Group", {"parent": pos_profile}, pluck="item_group") + for i, item in enumerate(result.get("items")): + item_group = frappe.db.get_value( + "Item Group", frappe.db.get_value("Item", item.get("item_code"), "item_group"), "name" + ) + if item_group in pos_item_groups: + continue + else: + if result.get("items"): + result.get("items").pop(i) + + @frappe.whitelist() def get_items(start, page_length, price_list, item_group, pos_profile, search_term=""): warehouse, hide_unavailable_items = frappe.db.get_value( @@ -102,6 +116,7 @@ def get_items(start, page_length, price_list, item_group, pos_profile, search_te if search_term: result = search_by_term(search_term, warehouse, price_list) or [] + filter_result_items(result, pos_profile) if result: return result From aca157704058337ef5d09bccc0e4eafd23086c42 Mon Sep 17 00:00:00 2001 From: Bhavan23 Date: Sat, 9 Nov 2024 19:55:50 +0530 Subject: [PATCH 526/734] refactor: Relocate doc variable for better scope management (cherry picked from commit 488b60fc278bd0093056df8ac0b699bd43b139db) --- erpnext/selling/page/point_of_sale/pos_item_selector.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/selling/page/point_of_sale/pos_item_selector.js b/erpnext/selling/page/point_of_sale/pos_item_selector.js index 862dfd54e5c..207a444218b 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_selector.js +++ b/erpnext/selling/page/point_of_sale/pos_item_selector.js @@ -138,7 +138,6 @@ erpnext.PointOfSale.ItemSelector = class { make_search_bar() { const me = this; - const doc = me.events.get_frm().doc; this.$component.find(".search-field").html(""); this.$component.find(".item-group-field").html(""); @@ -163,6 +162,7 @@ erpnext.PointOfSale.ItemSelector = class { me.filter_items(); }, get_query: function () { + const doc = me.events.get_frm().doc; return { query: "erpnext.selling.page.point_of_sale.point_of_sale.item_group_query", filters: { From 55f7f63e6e12151d6ca383b6b2a5104282e32cfe Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 12 Nov 2024 11:39:30 +0530 Subject: [PATCH 527/734] refactor: simpler filtering (cherry picked from commit f072b1266e298b4717443a4b1ae6b17498bb7303) --- erpnext/selling/page/point_of_sale/point_of_sale.py | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.py b/erpnext/selling/page/point_of_sale/point_of_sale.py index 13430b0523d..28eadec9bc5 100644 --- a/erpnext/selling/page/point_of_sale/point_of_sale.py +++ b/erpnext/selling/page/point_of_sale/point_of_sale.py @@ -35,6 +35,7 @@ def search_by_term(search_term, warehouse, price_list): "description": item_doc.description, "is_stock_item": item_doc.is_stock_item, "item_code": item_doc.name, + "item_group": item_doc.item_group, "item_image": item_doc.image, "item_name": item_doc.item_name, "serial_no": serial_no, @@ -95,15 +96,7 @@ def search_by_term(search_term, warehouse, price_list): def filter_result_items(result, pos_profile): if result and result.get("items"): pos_item_groups = frappe.db.get_all("POS Item Group", {"parent": pos_profile}, pluck="item_group") - for i, item in enumerate(result.get("items")): - item_group = frappe.db.get_value( - "Item Group", frappe.db.get_value("Item", item.get("item_code"), "item_group"), "name" - ) - if item_group in pos_item_groups: - continue - else: - if result.get("items"): - result.get("items").pop(i) + result["items"] = [item for item in result.get("items") if item.get("item_group") in pos_item_groups] @frappe.whitelist() From e3d74684d53218d383f2338415f4493fa2a34263 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Tue, 12 Nov 2024 15:43:20 +0530 Subject: [PATCH 528/734] fix: purchase receipt creation from SCR (#44095) (cherry picked from commit 774845f886881592e80089161b658c70b352bd32) --- .../subcontracting_receipt/subcontracting_receipt.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py index a99f7ea1615..37cd43ac1f6 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py @@ -766,7 +766,11 @@ def make_purchase_receipt(source_name, target_doc=None, save=False, submit=False "postprocess": update_item, "condition": lambda doc: doc.name in po_sr_item_dict, }, - "Purchase Taxes and Charges": {"doctype": "Purchase Taxes and Charges", "reset_value": True}, + "Purchase Taxes and Charges": { + "doctype": "Purchase Taxes and Charges", + "reset_value": True, + "condition": lambda doc: not doc.is_tax_withholding_account, + }, }, postprocess=post_process, ) From 4ba07a40eb45f6d75c1d6e0775104b998f6cf537 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Tue, 12 Nov 2024 15:48:28 +0530 Subject: [PATCH 529/734] fix: not able to reconcile expired batches (#44012) (cherry picked from commit 8805e74784ee423c7e279138c1f0486ce6e0766e) --- erpnext/controllers/queries.py | 10 ++++++++-- erpnext/public/js/utils/serial_no_batch_selector.js | 11 +++++++++++ .../serial_and_batch_bundle.py | 3 +++ erpnext/stock/doctype/stock_entry/stock_entry.js | 4 ++++ .../stock_reconciliation/stock_reconciliation.py | 12 +++++++++++- erpnext/stock/serial_batch_bundle.py | 4 ++-- erpnext/stock/stock_ledger.py | 1 + 7 files changed, 40 insertions(+), 5 deletions(-) diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index 919bbb477f4..463cb859970 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -415,7 +415,6 @@ def get_batches_from_stock_ledger_entries(searchfields, txt, filters, start=0, p stock_ledger_entry.batch_no, Sum(stock_ledger_entry.actual_qty).as_("qty"), ) - .where((batch_table.expiry_date >= expiry_date) | (batch_table.expiry_date.isnull())) .where(stock_ledger_entry.is_cancelled == 0) .where( (stock_ledger_entry.item_code == filters.get("item_code")) @@ -428,6 +427,9 @@ def get_batches_from_stock_ledger_entries(searchfields, txt, filters, start=0, p .limit(page_len) ) + if not filters.get("include_expired_batches"): + query = query.where((batch_table.expiry_date >= expiry_date) | (batch_table.expiry_date.isnull())) + query = query.select( Concat("MFG-", batch_table.manufacturing_date).as_("manufacturing_date"), Concat("EXP-", batch_table.expiry_date).as_("expiry_date"), @@ -466,7 +468,6 @@ def get_batches_from_serial_and_batch_bundle(searchfields, txt, filters, start=0 bundle.batch_no, Sum(bundle.qty).as_("qty"), ) - .where((batch_table.expiry_date >= expiry_date) | (batch_table.expiry_date.isnull())) .where(stock_ledger_entry.is_cancelled == 0) .where( (stock_ledger_entry.item_code == filters.get("item_code")) @@ -479,6 +480,11 @@ def get_batches_from_serial_and_batch_bundle(searchfields, txt, filters, start=0 .limit(page_len) ) + if not filters.get("include_expired_batches"): + bundle_query = bundle_query.where( + (batch_table.expiry_date >= expiry_date) | (batch_table.expiry_date.isnull()) + ) + bundle_query = bundle_query.select( Concat("MFG-", batch_table.manufacturing_date), Concat("EXP-", batch_table.expiry_date), diff --git a/erpnext/public/js/utils/serial_no_batch_selector.js b/erpnext/public/js/utils/serial_no_batch_selector.js index 61b40d6fef0..de1faf36ef5 100644 --- a/erpnext/public/js/utils/serial_no_batch_selector.js +++ b/erpnext/public/js/utils/serial_no_batch_selector.js @@ -462,6 +462,8 @@ erpnext.SerialBatchPackageSelector = class SerialNoBatchBundleUpdate { is_inward = true; } + let include_expired_batches = me.include_expired_batches(); + return { query: "erpnext.controllers.queries.get_batch_no", filters: { @@ -469,6 +471,7 @@ erpnext.SerialBatchPackageSelector = class SerialNoBatchBundleUpdate { warehouse: this.item.s_warehouse || this.item.t_warehouse || this.item.warehouse, is_inward: is_inward, + include_expired_batches: include_expired_batches, }, }; }, @@ -497,6 +500,14 @@ erpnext.SerialBatchPackageSelector = class SerialNoBatchBundleUpdate { return fields; } + include_expired_batches() { + return ( + this.frm.doc.doctype === "Stock Reconciliation" || + (this.frm.doc.doctype === "Stock Entry" && + ["Material Receipt", "Material Transfer", "Material Issue"].includes(this.frm.doc.purpose)) + ); + } + get_auto_data() { let { qty, based_on } = this.dialog.get_values(); diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py index ed6d5e155d7..08aa978aa99 100644 --- a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py +++ b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py @@ -689,6 +689,9 @@ class SerialandBatchBundle(Document): serial_batches = {} for row in self.entries: if not row.qty and row.batch_no and not row.serial_no: + if self.voucher_type == "Stock Reconciliation" and self.type_of_transaction == "Inward": + continue + frappe.throw( _("At row {0}: Qty is mandatory for the batch {1}").format( bold(row.idx), bold(row.batch_no) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js index ea47ffd79ea..0e39c2a9756 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry.js @@ -117,6 +117,10 @@ frappe.ui.form.on("Stock Entry", { filters["is_inward"] = 1; } + if (["Material Receipt", "Material Transfer", "Material Issue"].includes(doc.purpose)) { + filters["include_expired_batches"] = 1; + } + return { query: "erpnext.controllers.queries.get_batch_no", filters: filters, diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index 8ee3d9b3901..f671c11712a 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -324,6 +324,7 @@ class StockReconciliation(StockController): row.item_code, posting_date=self.posting_date, posting_time=self.posting_time, + for_stock_levels=True, ) total_current_qty += current_qty @@ -1322,7 +1323,16 @@ def get_stock_balance_for( qty, rate = data if item_dict.get("has_batch_no"): - qty = get_batch_qty(batch_no, warehouse, posting_date=posting_date, posting_time=posting_time) or 0 + qty = ( + get_batch_qty( + batch_no, + warehouse, + posting_date=posting_date, + posting_time=posting_time, + for_stock_levels=True, + ) + or 0 + ) return { "qty": qty, diff --git a/erpnext/stock/serial_batch_bundle.py b/erpnext/stock/serial_batch_bundle.py index c1002095b62..cb156aea119 100644 --- a/erpnext/stock/serial_batch_bundle.py +++ b/erpnext/stock/serial_batch_bundle.py @@ -124,7 +124,7 @@ class SerialBatchBundle: "Outward": self.sle.actual_qty < 0, }.get(sn_doc.type_of_transaction) - if not condition: + if not condition and self.sle.actual_qty: correct_type = "Inward" if sn_doc.type_of_transaction == "Inward": correct_type = "Outward" @@ -133,7 +133,7 @@ class SerialBatchBundle: frappe.throw(_(msg), title=_("Incorrect Type of Transaction")) precision = sn_doc.precision("total_qty") - if flt(sn_doc.total_qty, precision) != flt(self.sle.actual_qty, precision): + if self.sle.actual_qty and flt(sn_doc.total_qty, precision) != flt(self.sle.actual_qty, precision): msg = f"Total qty {flt(sn_doc.total_qty, precision)} of Serial and Batch Bundle {link} is not equal to Actual Qty {flt(self.sle.actual_qty, precision)} in the {self.sle.voucher_type} {self.sle.voucher_no}" frappe.throw(_(msg)) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index ccf7c7643c8..26e7af150b9 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -1183,6 +1183,7 @@ class update_entries_after: stock_entry.calculate_rate_and_amount(reset_outgoing_rate=False, raise_error_if_no_rate=False) stock_entry.db_update() for d in stock_entry.items: + # Update only the row that matches the voucher_detail_no or the row containing the FG/Scrap Item. if d.name == voucher_detail_no or (not d.s_warehouse and d.t_warehouse): d.db_update() From 290bddea7777d73a3a31c18a255be8d8a39f3793 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 12 Nov 2024 18:12:13 +0530 Subject: [PATCH 530/734] fix: not able to cancel DN (backport #44108) (#44109) fix: not able to cancel DN (#44108) (cherry picked from commit e8882718c9d99db01f3dbc16c477c4e295b9becb) Co-authored-by: rohitwaghchaure --- erpnext/stock/serial_batch_bundle.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/serial_batch_bundle.py b/erpnext/stock/serial_batch_bundle.py index cb156aea119..3fed0195d69 100644 --- a/erpnext/stock/serial_batch_bundle.py +++ b/erpnext/stock/serial_batch_bundle.py @@ -288,7 +288,7 @@ class SerialBatchBundle: "Serial and Batch Bundle", self.sle.serial_and_batch_bundle, "docstatus" ) - if docstatus != 1: + if docstatus == 0: self.submit_serial_and_batch_bundle() if self.item_details.has_serial_no == 1: @@ -311,7 +311,9 @@ class SerialBatchBundle: if self.is_pos_transaction(): return - frappe.get_cached_doc("Serial and Batch Bundle", self.sle.serial_and_batch_bundle).cancel() + doc = frappe.get_cached_doc("Serial and Batch Bundle", self.sle.serial_and_batch_bundle) + if doc.docstatus == 1: + doc.cancel() def is_pos_transaction(self): if ( From 6e83fec5ca5286bd36b150e1b5660d0e71c3d270 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 12 Nov 2024 23:22:18 +0530 Subject: [PATCH 531/734] fix: consider service item cost in the RM cost of the BOM (backport #43962) (#44111) fix: consider service item cost in the RM cost of the BOM (#43962) (cherry picked from commit c0ffaa444cc870f1751468cdad8928515bbf26d6) Co-authored-by: rohitwaghchaure --- erpnext/manufacturing/doctype/bom/bom.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index 843528de706..c3105b8e9ec 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -742,11 +742,8 @@ class BOM(WebsiteGenerator): base_total_rm_cost = 0 for d in self.get("items"): - if not d.is_stock_item and self.rm_cost_as_per == "Valuation Rate": - continue - old_rate = d.rate - if not self.bom_creator: + if not self.bom_creator and d.is_stock_item: d.rate = self.get_rm_rate( { "company": self.company, From a833dd67f35d12ea60ff5103eea89c5156975cea Mon Sep 17 00:00:00 2001 From: NaviN <118178330+Navin-S-R@users.noreply.github.com> Date: Wed, 13 Nov 2024 12:23:16 +0530 Subject: [PATCH 532/734] fix: update per_billed value in Purchase Receipt while creating Debit Note (#43977) * fix: update per_billed value in Purchase Receipt while creating Debit Note * test: add unit test for validating per_billed value for partial Debit Note (cherry picked from commit 494fd7ceeab3edd56f7f4975101a611e9e8ad391) --- .../purchase_receipt/purchase_receipt.py | 11 ++++- .../purchase_receipt/test_purchase_receipt.py | 48 +++++++++++++++++++ 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 228bc35693b..efe1628d8e0 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -1059,6 +1059,8 @@ def get_billed_amount_against_po(po_items): def update_billing_percentage(pr_doc, update_modified=True, adjust_incoming_rate=False): # Update Billing % based on pending accepted qty + buying_settings = frappe.get_single("Buying Settings") + total_amount, total_billed_amount = 0, 0 item_wise_returned_qty = get_item_wise_returned_qty(pr_doc) @@ -1066,10 +1068,15 @@ def update_billing_percentage(pr_doc, update_modified=True, adjust_incoming_rate returned_qty = flt(item_wise_returned_qty.get(item.name)) returned_amount = flt(returned_qty) * flt(item.rate) pending_amount = flt(item.amount) - returned_amount - total_billable_amount = pending_amount if item.billed_amt <= pending_amount else item.billed_amt + if buying_settings.bill_for_rejected_quantity_in_purchase_invoice: + pending_amount = flt(item.amount) + + total_billable_amount = abs(flt(item.amount)) + if pending_amount > 0: + total_billable_amount = pending_amount if item.billed_amt <= pending_amount else item.billed_amt total_amount += total_billable_amount - total_billed_amount += flt(item.billed_amt) + total_billed_amount += abs(flt(item.billed_amt)) if pr_doc.get("is_return") and not total_amount and total_billed_amount: total_amount = total_billed_amount diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index 64c3d2c67b2..4b8d5101f43 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -3900,6 +3900,54 @@ class TestPurchaseReceipt(FrappeTestCase): for incoming_rate in bundle_data: self.assertEqual(incoming_rate, 0) + def test_purchase_return_partial_debit_note(self): + pr = make_purchase_receipt( + company="_Test Company with perpetual inventory", + warehouse="Stores - TCP1", + supplier_warehouse="Work In Progress - TCP1", + ) + + return_pr = make_purchase_receipt( + company="_Test Company with perpetual inventory", + warehouse="Stores - TCP1", + supplier_warehouse="Work In Progress - TCP1", + is_return=1, + return_against=pr.name, + qty=-2, + do_not_submit=1, + ) + return_pr.items[0].purchase_receipt_item = pr.items[0].name + return_pr.submit() + + # because new_doc isn't considering is_return portion of status_updater + returned = frappe.get_doc("Purchase Receipt", return_pr.name) + returned.update_prevdoc_status() + pr.load_from_db() + + # Check if Original PR updated + self.assertEqual(pr.items[0].returned_qty, 2) + self.assertEqual(pr.per_returned, 40) + + # Create first partial debit_note + pi_1 = make_purchase_invoice(return_pr.name) + pi_1.items[0].qty = -1 + pi_1.submit() + + # Check if the first partial debit billing percentage got updated + return_pr.reload() + self.assertEqual(return_pr.per_billed, 50) + self.assertEqual(return_pr.status, "Partly Billed") + + # Create second partial debit_note to complete the debit note + pi_2 = make_purchase_invoice(return_pr.name) + pi_2.items[0].qty = -1 + pi_2.submit() + + # Check if the second partial debit note billing percentage got updated + return_pr.reload() + self.assertEqual(return_pr.per_billed, 100) + self.assertEqual(return_pr.status, "Completed") + def prepare_data_for_internal_transfer(): from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier From bce7acf9ccae317eafe2be77dc06440e3c44edd9 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 13 Nov 2024 12:31:31 +0530 Subject: [PATCH 533/734] fix: incorrect produced qty in Production Plan Summary (backport #44112) (#44113) fix: incorrect produced qty in Production Plan Summary (#44112) (cherry picked from commit 0828c74fe39fa4b77a48a7139fa9c07121cfa2d8) Co-authored-by: rohitwaghchaure --- .../production_plan/production_plan.js | 20 ++++---- .../production_plan_summary.py | 51 +++++++++++++------ 2 files changed, 45 insertions(+), 26 deletions(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.js b/erpnext/manufacturing/doctype/production_plan/production_plan.js index aba213ebca4..e7ac7f7cc83 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.js +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.js @@ -87,17 +87,17 @@ frappe.ui.form.on("Production Plan", { if (frm.doc.docstatus === 1) { frm.trigger("show_progress"); - if (frm.doc.status !== "Completed") { - frm.add_custom_button( - __("Production Plan Summary"), - () => { - frappe.set_route("query-report", "Production Plan Summary", { - 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") + ); + if (frm.doc.status !== "Completed") { if (frm.doc.status === "Closed") { frm.add_custom_button( __("Re-open"), 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 5bc9236c1d5..c62cab77d61 100644 --- a/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.py +++ b/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.py @@ -27,32 +27,51 @@ def get_data(filters): def get_production_plan_item_details(filters, data, order_details): - itemwise_indent = {} - production_plan_doc = frappe.get_cached_doc("Production Plan", filters.get("production_plan")) for row in production_plan_doc.po_items: - work_order = frappe.get_value( + work_orders = frappe.get_all( "Work Order", - {"production_plan_item": row.name, "bom_no": row.bom_no, "production_item": row.item_code}, - "name", + filters={ + "production_plan_item": row.name, + "bom_no": row.bom_no, + "production_item": row.item_code, + }, + pluck="name", ) - if row.item_code not in itemwise_indent: - itemwise_indent.setdefault(row.item_code, {}) + order_qty = row.planned_qty + total_produced_qty = 0.0 + pending_qty = 0.0 + for work_order in work_orders: + produced_qty = flt(order_details.get((work_order, row.item_code), {}).get("produced_qty", 0)) + pending_qty = flt(order_qty) - produced_qty + + total_produced_qty += produced_qty + + data.append( + { + "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": order_qty, + "document_type": "Work Order", + "document_name": work_order or "", + "bom_level": 0, + "produced_qty": produced_qty, + "pending_qty": pending_qty, + } + ) + + order_qty = pending_qty data.append( { - "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"), + "indent": 0, "qty": row.planned_qty, - "document_type": "Work Order", - "document_name": work_order or "", - "bom_level": 0, - "produced_qty": order_details.get((work_order, row.item_code), {}).get("produced_qty", 0), - "pending_qty": flt(row.planned_qty) - - flt(order_details.get((work_order, row.item_code), {}).get("produced_qty", 0)), + "produced_qty": total_produced_qty, + "pending_qty": pending_qty, } ) From 8af005cef0c84a572fdb1e35928de5c228db610b Mon Sep 17 00:00:00 2001 From: Nihantra Patel Date: Wed, 13 Nov 2024 14:55:45 +0530 Subject: [PATCH 534/734] fix: Drop Shipping address based on customer shopping address (cherry picked from commit c7499f35285182837570e3aaa1d6b274783d2204) --- erpnext/public/js/controllers/transaction.js | 11 ++++++++--- erpnext/selling/doctype/sales_order/sales_order.py | 14 +++++++++++--- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 07ba90cd6d1..0efec214c0d 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -956,9 +956,14 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe if (frappe.meta.get_docfield(this.frm.doctype, "shipping_address") && ['Purchase Order', 'Purchase Receipt', 'Purchase Invoice'].includes(this.frm.doctype)) { - erpnext.utils.get_shipping_address(this.frm, function() { - set_party_account(set_pricing); - }); + let is_drop_ship = me.frm.doc.items.some(item => item.delivered_by_supplier); + + if (!is_drop_ship) { + console.log('get_shipping_address'); + erpnext.utils.get_shipping_address(this.frm, function() { + set_party_account(set_pricing); + }); + } } else { set_party_account(set_pricing); diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 62262b41021..88528d7178f 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -1402,9 +1402,17 @@ def make_purchase_order(source_name, selected_items=None, target_doc=None): target.payment_schedule = [] if is_drop_ship_order(target): - target.customer = source.customer - target.customer_name = source.customer_name - target.shipping_address = source.shipping_address_name + if source.shipping_address_name: + target.shipping_address = source.shipping_address_name + target.shipping_address_display = source.shipping_address + else: + target.shipping_address = source.customer_address + target.shipping_address_display = source.address_display + + target.customer_contact_person = source.contact_person + target.customer_contact_display = source.contact_display + target.customer_contact_mobile = source.contact_mobile + target.customer_contact_email = source.contact_email else: target.customer = target.customer_name = target.shipping_address = None From c1983a48469dc3c961c4653e3e20191ee73dee2d Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 13 Nov 2024 16:24:12 +0530 Subject: [PATCH 535/734] fix: Cannot read properties of undefined (reading 'work_order_closed') (backport #44117) (#44122) fix: Cannot read properties of undefined (reading 'work_order_closed') (#44117) (cherry picked from commit 13834014b50e46e8d432215a1ddf7c8895c8dda5) Co-authored-by: rohitwaghchaure --- erpnext/manufacturing/doctype/job_card/job_card.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/job_card/job_card.js b/erpnext/manufacturing/doctype/job_card/job_card.js index 4cc60a3b4a6..ad514efa55f 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.js +++ b/erpnext/manufacturing/doctype/job_card/job_card.js @@ -37,7 +37,7 @@ frappe.ui.form.on("Job Card", { frappe.flags.resume_job = 0; let has_items = frm.doc.items && frm.doc.items.length; - if (!frm.is_new() && frm.doc.__onload.work_order_closed) { + if (!frm.is_new() && frm.doc.__onload?.work_order_closed) { frm.disable_save(); return; } From b9ec43c354accd98bad6f08393664747ca49781a Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 13 Nov 2024 16:56:08 +0530 Subject: [PATCH 536/734] refactor: 'Partly Billed' status for Purchase Receipt (cherry picked from commit c58bbd25f2813ac4392877602ca89247127e3623) --- erpnext/stock/doctype/purchase_receipt/purchase_receipt.json | 4 ++-- erpnext/stock/doctype/purchase_receipt/purchase_receipt.py | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json index 61a180caba4..643f9e7a82f 100755 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json @@ -889,7 +889,7 @@ "no_copy": 1, "oldfieldname": "status", "oldfieldtype": "Select", - "options": "\nDraft\nTo Bill\nCompleted\nReturn Issued\nCancelled\nClosed", + "options": "\nDraft\nPartly Billed\nTo Bill\nCompleted\nReturn Issued\nCancelled\nClosed", "print_hide": 1, "print_width": "150px", "read_only": 1, @@ -1273,7 +1273,7 @@ "idx": 261, "is_submittable": 1, "links": [], - "modified": "2024-07-04 14:50:10.538472", + "modified": "2024-11-13 16:55:14.129055", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt", diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 228bc35693b..523acf51c5b 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -112,7 +112,9 @@ class PurchaseReceipt(BuyingController): shipping_address: DF.Link | None shipping_address_display: DF.SmallText | None shipping_rule: DF.Link | None - status: DF.Literal["", "Draft", "To Bill", "Completed", "Return Issued", "Cancelled", "Closed"] + status: DF.Literal[ + "", "Draft", "Partly Billed", "To Bill", "Completed", "Return Issued", "Cancelled", "Closed" + ] subcontracting_receipt: DF.Link | None supplied_items: DF.Table[PurchaseReceiptItemSupplied] supplier: DF.Link From 7c78e0025d37e081fe809459803b307492a477d2 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 13 Nov 2024 15:02:57 +0000 Subject: [PATCH 537/734] chore(release): Bumped to Version 15.42.0 # [15.42.0](https://github.com/frappe/erpnext/compare/v15.41.2...v15.42.0) (2024-11-13) ### Bug Fixes * add default height to POS item card selector (backport [#44071](https://github.com/frappe/erpnext/issues/44071)) ([#44075](https://github.com/frappe/erpnext/issues/44075)) ([65ec7c5](https://github.com/frappe/erpnext/commit/65ec7c56041612c17dce79def87ce31ee503f391)) * add field conversion_factor when include_uom is settled ([#43701](https://github.com/frappe/erpnext/issues/43701)) ([f387a8f](https://github.com/frappe/erpnext/commit/f387a8fcebbd5958e50a010b05c0c53d9d98328d)) * better gls for purchases with tax witholding ([#42743](https://github.com/frappe/erpnext/issues/42743)) ([705a26a](https://github.com/frappe/erpnext/commit/705a26a2fa5d1dc20c07dd4150771529c707e820)) * bind this object explicitly on callback event function ([3423d3c](https://github.com/frappe/erpnext/commit/3423d3c13d3da909cdb1e165d04107b9bccee5d6)) * calculate percentage received and delivered considering over-receipt and over-delivery (backport [#43870](https://github.com/frappe/erpnext/issues/43870)) ([#44030](https://github.com/frappe/erpnext/issues/44030)) ([5958d0c](https://github.com/frappe/erpnext/commit/5958d0c2570aa7ae3e0e86e1afdbef89758318f1)) * Cannot read properties of undefined (reading 'work_order_closed') (backport [#44117](https://github.com/frappe/erpnext/issues/44117)) ([#44122](https://github.com/frappe/erpnext/issues/44122)) ([c1983a4](https://github.com/frappe/erpnext/commit/c1983a48469dc3c961c4653e3e20191ee73dee2d)) * consider service item cost in the RM cost of the BOM (backport [#43962](https://github.com/frappe/erpnext/issues/43962)) ([#44111](https://github.com/frappe/erpnext/issues/44111)) ([6e83fec](https://github.com/frappe/erpnext/commit/6e83fec5ca5286bd36b150e1b5660d0e71c3d270)) * Drop Shipping address based on customer shopping address ([8af005c](https://github.com/frappe/erpnext/commit/8af005cef0c84a572fdb1e35928de5c228db610b)) * duplicate items and outdated item price in POS (backport [#42978](https://github.com/frappe/erpnext/issues/42978)) ([#44038](https://github.com/frappe/erpnext/issues/44038)) ([4cde77d](https://github.com/frappe/erpnext/commit/4cde77d8d8f4a6f833e4f28d7cd6f08ac0005474)) * exception on register reports when filtered on cost center ([be07421](https://github.com/frappe/erpnext/commit/be07421ab77b43681dffc7e038630b01ecc61984)) * improved the conditions for determining voucher subtypes ([58ca4a2](https://github.com/frappe/erpnext/commit/58ca4a2b99a694ea891e1b3cfd949f799edacafb)) * incorrect produced qty in Production Plan Summary (backport [#44112](https://github.com/frappe/erpnext/issues/44112)) ([#44113](https://github.com/frappe/erpnext/issues/44113)) ([bce7acf](https://github.com/frappe/erpnext/commit/bce7acf9ccae317eafe2be77dc06440e3c44edd9)) * item mapping from modal to batch form ([#44090](https://github.com/frappe/erpnext/issues/44090)) ([9ac54f6](https://github.com/frappe/erpnext/commit/9ac54f694c27323baca073f6d790497d1c056507)) * item not set in the batch quick entry form (backport [#44028](https://github.com/frappe/erpnext/issues/44028)) ([#44031](https://github.com/frappe/erpnext/issues/44031)) ([6dcd015](https://github.com/frappe/erpnext/commit/6dcd015a39be3d5985482f8899da7dc670c52576)) * Negative stock validation against inventory dimension (backport [#43834](https://github.com/frappe/erpnext/issues/43834)) ([#43846](https://github.com/frappe/erpnext/issues/43846)) ([b314f38](https://github.com/frappe/erpnext/commit/b314f3839bfd19f81eac6d712a343d837fd4cb0f)) * NoneType while updating ordered_qty in SO for removed items ([978a007](https://github.com/frappe/erpnext/commit/978a0078d8f2e93878b883643c94d327deb5fd06)) * not able to cancel DN (backport [#44108](https://github.com/frappe/erpnext/issues/44108)) ([#44109](https://github.com/frappe/erpnext/issues/44109)) ([290bdde](https://github.com/frappe/erpnext/commit/290bddea7777d73a3a31c18a255be8d8a39f3793)) * not able to reconcile expired batches ([#44012](https://github.com/frappe/erpnext/issues/44012)) ([4ba07a4](https://github.com/frappe/erpnext/commit/4ba07a40eb45f6d75c1d6e0775104b998f6cf537)) * patch ([107d53b](https://github.com/frappe/erpnext/commit/107d53b3582ccdac3f237745efba4eca5a14a328)) * populate payment schedule from payment terms (backport [#44082](https://github.com/frappe/erpnext/issues/44082)) ([#44083](https://github.com/frappe/erpnext/issues/44083)) ([363f151](https://github.com/frappe/erpnext/commit/363f15124eaa857d112b04ae7429b11360015215)) * purchase receipt creation from SCR ([#44095](https://github.com/frappe/erpnext/issues/44095)) ([e3d7468](https://github.com/frappe/erpnext/commit/e3d74684d53218d383f2338415f4493fa2a34263)) * slow reposting due to SABB update ([3e29ae8](https://github.com/frappe/erpnext/commit/3e29ae85345fc8c3ec0851987804f3886852097f)) * sort by ascending to get the first period closing voucher (backport [#44029](https://github.com/frappe/erpnext/issues/44029)) ([#44035](https://github.com/frappe/erpnext/issues/44035)) ([56f25ae](https://github.com/frappe/erpnext/commit/56f25ae0657e941f865fb3a224dea3767c8c3ecb)) * task path (backport [#44073](https://github.com/frappe/erpnext/issues/44073)) ([#44078](https://github.com/frappe/erpnext/issues/44078)) ([34b5639](https://github.com/frappe/erpnext/commit/34b5639d1c3a260f527ae02e2dc5ce05efd73e1c)) * tyeerror while saving pick list ([7d09832](https://github.com/frappe/erpnext/commit/7d098328d0bc6e74aca03e505f3c31d845c38d9b)) * update payment amount for partial pos return ([61559be](https://github.com/frappe/erpnext/commit/61559be8a46f410370c199140fbf261a559ce17e)) * update per_billed value in Purchase Receipt while creating Debit Note ([#43977](https://github.com/frappe/erpnext/issues/43977)) ([a833dd6](https://github.com/frappe/erpnext/commit/a833dd67f35d12ea60ff5103eea89c5156975cea)) * when company is created with other company template Chart of Account the Create Taxe Template failed ([#42755](https://github.com/frappe/erpnext/issues/42755)) ([e6894b9](https://github.com/frappe/erpnext/commit/e6894b949c9058ac0141d57dd4b6a5527c656f62)) ### Features * Add item group filtering for search results ([2754793](https://github.com/frappe/erpnext/commit/2754793ff9d5164b97607f4a977bda8a67313c61)) * add template taxe for charts of account France - Plan Comptable General avec code ([#42757](https://github.com/frappe/erpnext/issues/42757)) ([865786e](https://github.com/frappe/erpnext/commit/865786e0b67fb35d2423b4cf281dd76cdca3bd51)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 94615249d02..83fb70c9e8f 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.41.1" +__version__ = "15.42.0" def get_default_company(user=None): From cc07402b5e5f2acdb5d4626a38108799cdd09f68 Mon Sep 17 00:00:00 2001 From: Varun Verma Date: Mon, 14 Oct 2024 11:09:26 +0000 Subject: [PATCH 538/734] fix: bulk update invoice remarks during site upgrade fixes issue #43634 --- .../patches/v15_0/update_invoice_remarks.py | 164 ++++++++++++++---- 1 file changed, 131 insertions(+), 33 deletions(-) diff --git a/erpnext/patches/v15_0/update_invoice_remarks.py b/erpnext/patches/v15_0/update_invoice_remarks.py index 7060fe57e31..9146713815f 100644 --- a/erpnext/patches/v15_0/update_invoice_remarks.py +++ b/erpnext/patches/v15_0/update_invoice_remarks.py @@ -8,44 +8,142 @@ def execute(): def update_sales_invoice_remarks(): - si_list = frappe.db.get_all( - "Sales Invoice", - filters={ - "docstatus": 1, - "remarks": "No Remarks", - "po_no": ["!=", ""], - }, - fields=["name", "po_no"], - ) + """ + Update remarks in Sales Invoice. + Some sites may have very large volume of sales invoices. + In such cases, updating documents one by one won't be successful, especially during site migration step. + Refer to the bug report: https://github.com/frappe/erpnext/issues/43634 + In this case, a bulk update must be done. - for doc in si_list: - remarks = _("Against Customer Order {0}").format(doc.po_no) - update_remarks("Sales Invoice", doc.name, remarks) + Step 1: Update remarks in GL Entries + Step 2: Update remarks in Payment Ledger Entries + Step 3: Update remarks in Sales Invoice - Should be last step + """ + + ### Step 1: Update remarks in GL Entries + update_sales_invoice_gle_remarks() + + ### Step 2: Update remarks in Payment Ledger Entries + update_sales_invoice_ple_remarks() + + ### Step 3: Update remarks in Sales Invoice + update_query = """ + UPDATE `tabSales Invoice` + SET remarks = concat('Against Customer Order ', po_no) + WHERE po_no <> '' AND docstatus = %(docstatus)s and remarks = %(remarks)s + """ + + # Data for update query + values = {"remarks": "No Remarks", "docstatus": 1} + + # Execute query + frappe.db.sql(update_query, values=values, as_dict=0) def update_purchase_invoice_remarks(): - pi_list = frappe.db.get_all( - "Purchase Invoice", - filters={ - "docstatus": 1, - "remarks": "No Remarks", - "bill_no": ["!=", ""], - }, - fields=["name", "bill_no"], - ) + """ + Update remarks in Purchase Invoice. + Some sites may have very large volume of purchase invoices. + In such cases, updating documents one by one wont be successful, especially during site migration step. + Refer to the bug report: https://github.com/frappe/erpnext/issues/43634 + In this case, a bulk update must be done. - for doc in pi_list: - remarks = _("Against Supplier Invoice {0}").format(doc.bill_no) - update_remarks("Purchase Invoice", doc.name, remarks) + Step 1: Update remarks in GL Entries + Step 2: Update remarks in Payment Ledger Entries + Step 3: Update remarks in Purchase Invoice - Should be last step + """ + + ### Step 1: Update remarks in GL Entries + update_purchase_invoice_gle_remarks() + + ### Step 2: Update remarks in Payment Ledger Entries + update_purchase_invoice_ple_remarks() + + ### Step 3: Update remarks in Purchase Invoice + update_query = """ + UPDATE `tabPurchase Invoice` + SET remarks = concat('Against Supplier Invoice ', bill_no) + WHERE bill_no <> '' AND docstatus = %(docstatus)s and remarks = %(remarks)s + """ + + # Data for update query + values = {"remarks": "No Remarks", "docstatus": 1} + + # Execute query + frappe.db.sql(update_query, values=values, as_dict=0) -def update_remarks(doctype, docname, remarks): - filters = { - "voucher_type": doctype, - "remarks": "No Remarks", - "voucher_no": docname, - } +def update_sales_invoice_gle_remarks(): + ## Update query to update GL Entry - Updates all entries which are for Sales Invoice with No Remarks + update_query = """ + UPDATE + `tabGL Entry` as gle + INNER JOIN `tabSales Invoice` as si + ON gle.voucher_type = 'Sales Invoice' AND gle.voucher_no = si.name AND gle.remarks = %(remarks)s + SET + gle.remarks = concat('Against Customer Order ', si.po_no) + WHERE si.po_no <> '' AND si.docstatus = %(docstatus)s and si.remarks = %(remarks)s + """ - frappe.db.set_value(doctype, docname, "remarks", remarks) - frappe.db.set_value("GL Entry", filters, "remarks", remarks) - frappe.db.set_value("Payment Ledger Entry", filters, "remarks", remarks) + # Data for update query + values = {"remarks": "No Remarks", "docstatus": 1} + + # Execute query + frappe.db.sql(update_query, values=values, as_dict=0) + + +def update_sales_invoice_ple_remarks(): + ## Update query to update Payment Ledger Entry - Updates all entries which are for Sales Invoice with No Remarks + update_query = """ + UPDATE + `tabPayment Ledger Entry` as ple + INNER JOIN `tabSales Invoice` as si + ON ple.voucher_type = 'Sales Invoice' AND ple.voucher_no = si.name AND ple.remarks = %(remarks)s + SET + ple.remarks = concat('Against Customer Order ', si.po_no) + WHERE si.po_no <> '' AND si.docstatus = %(docstatus)s and si.remarks = %(remarks)s + """ + + ### Data for update query + values = {"remarks": "No Remarks", "docstatus": 1} + + ### Execute query + frappe.db.sql(update_query, values=values, as_dict=0) + + +def update_purchase_invoice_gle_remarks(): + ### Query to update GL Entry - Updates all entries which are for Purchase Invoice with No Remarks + update_query = """ + UPDATE + `tabGL Entry` as gle + INNER JOIN `tabPurchase Invoice` as pi + ON gle.voucher_type = 'Purchase Invoice' AND gle.voucher_no = pi.name AND gle.remarks = %(remarks)s + SET + gle.remarks = concat('Against Supplier Invoice ', pi.bill_no) + WHERE pi.bill_no <> '' AND pi.docstatus = %(docstatus)s and pi.remarks = %(remarks)s + """ + + ### Data for update query + values = {"remarks": "No Remarks", "docstatus": 1} + + ### Execute query + frappe.db.sql(update_query, values=values, as_dict=0) + + +def update_purchase_invoice_ple_remarks(): + ### Query to update Payment Ledger Entry - Updates all entries which are for Purchase Invoice with No Remarks + update_query = """ + UPDATE + `tabPayment Ledger Entry` as ple + INNER JOIN `tabPurchase Invoice` as pi + ON ple.voucher_type = 'Purchase Invoice' AND ple.voucher_no = pi.name AND ple.remarks = %(remarks)s + SET + ple.remarks = concat('Against Supplier Invoice ', pi.bill_no) + WHERE pi.bill_no <> '' AND pi.docstatus = %(docstatus)s and pi.remarks = %(remarks)s + """ + + ### Data for update query + values = {"remarks": "No Remarks", "docstatus": 1} + + ### Execute query + frappe.db.sql(update_query, values=values, as_dict=0) From 8e6249d361bb36a4fd1ef0cfa7333265bb8933d5 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 2 Aug 2024 11:35:15 +0530 Subject: [PATCH 539/734] feat: round off for opening entries (cherry picked from commit a5b228549c0c03517a53db8609f34fb6ab308445) --- erpnext/setup/doctype/company/company.json | 9 ++++++++- erpnext/setup/doctype/company/company.py | 1 + 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json index 284bd2b7f22..4b07037ad3e 100644 --- a/erpnext/setup/doctype/company/company.json +++ b/erpnext/setup/doctype/company/company.json @@ -49,6 +49,7 @@ "default_cash_account", "default_receivable_account", "round_off_account", + "round_off_for_opening", "round_off_cost_center", "write_off_account", "exchange_gain_loss_account", @@ -801,6 +802,12 @@ "fieldtype": "Link", "label": "Default Operating Cost Account", "options": "Account" + }, + { + "fieldname": "round_off_for_opening", + "fieldtype": "Link", + "label": "Round Off for Opening", + "options": "Account" } ], "icon": "fa fa-building", @@ -808,7 +815,7 @@ "image_field": "company_logo", "is_tree": 1, "links": [], - "modified": "2024-07-24 18:17:56.413971", + "modified": "2024-08-02 11:34:46.785377", "modified_by": "Administrator", "module": "Setup", "name": "Company", diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py index 8028b8e6af4..d781288c8bd 100644 --- a/erpnext/setup/doctype/company/company.py +++ b/erpnext/setup/doctype/company/company.py @@ -91,6 +91,7 @@ class Company(NestedSet): rgt: DF.Int round_off_account: DF.Link | None round_off_cost_center: DF.Link | None + round_off_for_opening: DF.Link | None sales_monthly_history: DF.SmallText | None series_for_depreciation_entry: DF.Data | None stock_adjustment_account: DF.Link | None From 9a3e9c4c9a627cac552a95ff880469ef3888c56f Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 2 Aug 2024 11:49:50 +0530 Subject: [PATCH 540/734] refactor: use separate round off for opening entries (cherry picked from commit 88e68168e36624d9ec31993c8f7a9b29add8c1ce) # Conflicts: # erpnext/accounts/general_ledger.py --- erpnext/accounts/doctype/account/account.json | 4 +- erpnext/accounts/doctype/account/account.py | 1 + .../purchase_invoice/purchase_invoice.py | 6 ++- .../doctype/sales_invoice/sales_invoice.py | 6 ++- erpnext/accounts/general_ledger.py | 52 ++++++++++++++++--- erpnext/controllers/accounts_controller.py | 6 ++- erpnext/setup/doctype/company/company.js | 1 + 7 files changed, 63 insertions(+), 13 deletions(-) diff --git a/erpnext/accounts/doctype/account/account.json b/erpnext/accounts/doctype/account/account.json index e87b59ea9cb..7b56444e635 100644 --- a/erpnext/accounts/doctype/account/account.json +++ b/erpnext/accounts/doctype/account/account.json @@ -121,7 +121,7 @@ "label": "Account Type", "oldfieldname": "account_type", "oldfieldtype": "Select", - "options": "\nAccumulated Depreciation\nAsset Received But Not Billed\nBank\nCash\nChargeable\nCapital Work in Progress\nCost of Goods Sold\nCurrent Asset\nCurrent Liability\nDepreciation\nDirect Expense\nDirect Income\nEquity\nExpense Account\nExpenses Included In Asset Valuation\nExpenses Included In Valuation\nFixed Asset\nIncome Account\nIndirect Expense\nIndirect Income\nLiability\nPayable\nReceivable\nRound Off\nStock\nStock Adjustment\nStock Received But Not Billed\nService Received But Not Billed\nTax\nTemporary", + "options": "\nAccumulated Depreciation\nAsset Received But Not Billed\nBank\nCash\nChargeable\nCapital Work in Progress\nCost of Goods Sold\nCurrent Asset\nCurrent Liability\nDepreciation\nDirect Expense\nDirect Income\nEquity\nExpense Account\nExpenses Included In Asset Valuation\nExpenses Included In Valuation\nFixed Asset\nIncome Account\nIndirect Expense\nIndirect Income\nLiability\nPayable\nReceivable\nRound Off\nRound Off for Opening\nStock\nStock Adjustment\nStock Received But Not Billed\nService Received But Not Billed\nTax\nTemporary", "search_index": 1 }, { @@ -191,7 +191,7 @@ "idx": 1, "is_tree": 1, "links": [], - "modified": "2024-06-27 16:23:04.444354", + "modified": "2024-08-19 15:19:11.095045", "modified_by": "Administrator", "module": "Accounts", "name": "Account", diff --git a/erpnext/accounts/doctype/account/account.py b/erpnext/accounts/doctype/account/account.py index 2c876e09725..b510651e68f 100644 --- a/erpnext/accounts/doctype/account/account.py +++ b/erpnext/accounts/doctype/account/account.py @@ -60,6 +60,7 @@ class Account(NestedSet): "Payable", "Receivable", "Round Off", + "Round Off for Opening", "Stock", "Stock Adjustment", "Stock Received But Not Billed", diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index b47e90eb77d..dc4051eecf4 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -1537,7 +1537,11 @@ class PurchaseInvoice(BuyingController): # eg: rounding_adjustment = 0.01 and exchange rate = 0.05 and precision of base_rounding_adjustment is 2 # then base_rounding_adjustment becomes zero and error is thrown in GL Entry if not self.is_internal_transfer() and self.rounding_adjustment and self.base_rounding_adjustment: - round_off_account, round_off_cost_center = get_round_off_account_and_cost_center( + ( + round_off_account, + round_off_cost_center, + round_off_for_opening, + ) = get_round_off_account_and_cost_center( self.company, "Purchase Invoice", self.name, self.use_company_roundoff_cost_center ) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index eb43de47a54..d24717a614d 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1633,7 +1633,11 @@ class SalesInvoice(SellingController): and self.base_rounding_adjustment and not self.is_internal_transfer() ): - round_off_account, round_off_cost_center = get_round_off_account_and_cost_center( + ( + round_off_account, + round_off_cost_center, + round_off_for_opening, + ) = get_round_off_account_and_cost_center( self.company, "Sales Invoice", self.name, self.use_company_roundoff_cost_center ) diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py index f9b503675aa..b3d78284616 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -7,7 +7,12 @@ import copy import frappe from frappe import _ from frappe.model.meta import get_field_precision +<<<<<<< HEAD from frappe.utils import cint, flt, formatdate, getdate, now +======= +from frappe.utils import cint, flt, formatdate, get_link_to_form, getdate, now +from frappe.utils.dashboard import cache_source +>>>>>>> 88e68168e3 (refactor: use separate round off for opening entries) import erpnext from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( @@ -496,16 +501,36 @@ def raise_debit_credit_not_equal_error(debit_credit_diff, voucher_type, voucher_ ) +def has_opening_entries(gl_map: list) -> bool: + for x in gl_map: + if x.is_opening == "Yes": + return True + return False + + def make_round_off_gle(gl_map, debit_credit_diff, precision): - round_off_account, round_off_cost_center = get_round_off_account_and_cost_center( + round_off_account, round_off_cost_center, round_off_for_opening = get_round_off_account_and_cost_center( gl_map[0].company, gl_map[0].voucher_type, gl_map[0].voucher_no ) round_off_gle = frappe._dict() round_off_account_exists = False + has_opening_entry = has_opening_entries(gl_map) + + if has_opening_entry: + if not round_off_for_opening: + frappe.throw( + _("Please set '{0}' in Company: {1}").format( + frappe.bold("Round Off for Opening"), get_link_to_form("Company", gl_map[0].company) + ) + ) + + account = round_off_for_opening + else: + account = round_off_account if gl_map[0].voucher_type != "Period Closing Voucher": for d in gl_map: - if d.account == round_off_account: + if d.account == account: round_off_gle = d if d.debit: debit_credit_diff -= flt(d.debit) - flt(d.credit) @@ -523,7 +548,7 @@ def make_round_off_gle(gl_map, debit_credit_diff, precision): round_off_gle.update( { - "account": round_off_account, + "account": account, "debit_in_account_currency": abs(debit_credit_diff) if debit_credit_diff < 0 else 0, "credit_in_account_currency": debit_credit_diff if debit_credit_diff > 0 else 0, "debit": abs(debit_credit_diff) if debit_credit_diff < 0 else 0, @@ -537,6 +562,9 @@ def make_round_off_gle(gl_map, debit_credit_diff, precision): } ) + if has_opening_entry: + round_off_gle.update({"is_opening": "Yes"}) + update_accounting_dimensions(round_off_gle) if not round_off_account_exists: gl_map.append(round_off_gle) @@ -561,8 +589,8 @@ def update_accounting_dimensions(round_off_gle): def get_round_off_account_and_cost_center(company, voucher_type, voucher_no, use_company_default=False): - round_off_account, round_off_cost_center = frappe.get_cached_value( - "Company", company, ["round_off_account", "round_off_cost_center"] + round_off_account, round_off_cost_center, round_off_for_opening = frappe.get_cached_value( + "Company", company, ["round_off_account", "round_off_cost_center", "round_off_for_opening"] ) or [None, None] # Use expense account as fallback @@ -578,12 +606,20 @@ def get_round_off_account_and_cost_center(company, voucher_type, voucher_no, use round_off_cost_center = parent_cost_center if not round_off_account: - frappe.throw(_("Please mention Round Off Account in Company")) + frappe.throw( + _("Please mention '{0}' in Company: {1}").format( + frappe.bold("Round Off Account"), get_link_to_form("Company", company) + ) + ) if not round_off_cost_center: - frappe.throw(_("Please mention Round Off Cost Center in Company")) + frappe.throw( + _("Please mention '{0}' in Company: {1}").format( + frappe.bold("Round Off Cost Center"), get_link_to_form("Company", company) + ) + ) - return round_off_account, round_off_cost_center + return round_off_account, round_off_cost_center, round_off_for_opening def make_reverse_gl_entries( diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index b14cf428c53..b4b23dd5f4c 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1298,7 +1298,11 @@ class AccountsController(TransactionBase): d.exchange_gain_loss = difference def make_precision_loss_gl_entry(self, gl_entries): - round_off_account, round_off_cost_center = get_round_off_account_and_cost_center( + ( + round_off_account, + round_off_cost_center, + round_off_for_opening, + ) = get_round_off_account_and_cost_center( self.company, "Purchase Invoice", self.name, self.use_company_roundoff_cost_center ) diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js index f14057a272e..52ff21dc407 100644 --- a/erpnext/setup/doctype/company/company.js +++ b/erpnext/setup/doctype/company/company.js @@ -252,6 +252,7 @@ erpnext.company.setup_queries = function (frm) { ["default_expense_account", { root_type: "Expense" }], ["default_income_account", { root_type: "Income" }], ["round_off_account", { root_type: "Expense" }], + ["round_off_for_opening", { root_type: "Liability" }], ["write_off_account", { root_type: "Expense" }], ["default_deferred_expense_account", {}], ["default_deferred_revenue_account", {}], From b28ff25180ae95fc737708c50b4917f18f82cb1f Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 29 Aug 2024 17:37:32 +0530 Subject: [PATCH 541/734] chore: default should return 3 elements (cherry picked from commit fc46ebcd7c9a3628ddd7b7a1f284d2c2d9d6b7f9) --- erpnext/accounts/general_ledger.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py index b3d78284616..c2fbdcc95f1 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -591,7 +591,7 @@ def update_accounting_dimensions(round_off_gle): def get_round_off_account_and_cost_center(company, voucher_type, voucher_no, use_company_default=False): round_off_account, round_off_cost_center, round_off_for_opening = frappe.get_cached_value( "Company", company, ["round_off_account", "round_off_cost_center", "round_off_for_opening"] - ) or [None, None] + ) or [None, None, None] # Use expense account as fallback if not round_off_account: From 186b646dee3038cb78311ec99e6f5f292c50a21d Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 30 Aug 2024 16:08:43 +0530 Subject: [PATCH 542/734] refactor: handle opening round off from sales invoice (cherry picked from commit 96e3c2ad1061af44b044fa64b0af16004d0d732e) --- .../doctype/sales_invoice/sales_invoice.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index d24717a614d..8baa36475da 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1641,6 +1641,21 @@ class SalesInvoice(SellingController): self.company, "Sales Invoice", self.name, self.use_company_roundoff_cost_center ) + if self.is_opening == "Yes" and self.rounding_adjustment: + if not round_off_for_opening: + frappe.throw( + _( + "Opening Invoice has rounding adjustment of {0}.

'{1}' account is required to post these values. Please set it in Company: {2}.

Or, '{3}' can be enabled to not post any rounding adjustment." + ).format( + frappe.bold(self.rounding_adjustment), + frappe.bold("Round Off for Opening"), + get_link_to_form("Company", self.company), + frappe.bold("Disable Rounded Total"), + ) + ) + else: + round_off_account = round_off_for_opening + gl_entries.append( self.get_gl_dict( { From 820692f24647ca6f47370594160cb1e204c66aa7 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 30 Aug 2024 17:55:02 +0530 Subject: [PATCH 543/734] test: rounding adjustment validation and posting (cherry picked from commit 5021c7ca2c3bb5f758d00c65eab1dcfec692d2a4) # Conflicts: # erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py --- .../sales_invoice/test_sales_invoice.py | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 56f90ae8cd4..d9d7d5fa3ba 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -3924,6 +3924,7 @@ class TestSalesInvoice(FrappeTestCase): self.assertEqual(len(res), 1) self.assertEqual(res[0][0], pos_return.return_against) +<<<<<<< HEAD @change_settings("Accounts Settings", {"enable_common_party_accounting": True}) def test_common_party_with_foreign_currency_jv(self): from erpnext.accounts.doctype.account.test_account import create_account @@ -4032,6 +4033,57 @@ class TestSalesInvoice(FrappeTestCase): ) self.assertTrue(all([x == "Credit Note" for x in gl_entries])) +======= + def test_validation_on_opening_invoice_with_rounding(self): + si = create_sales_invoice(qty=1, rate=99.98, do_not_submit=True) + si.is_opening = "Yes" + si.items[0].income_account = "Temporary Opening - _TC" + si.save() + self.assertRaises(frappe.ValidationError, si.submit) + + def test_opening_invoice_with_rounding_adjustment(self): + si = create_sales_invoice(qty=1, rate=99.98, do_not_submit=True) + si.is_opening = "Yes" + si.items[0].income_account = "Temporary Opening - _TC" + si.save() + + liability_root = frappe.db.get_all( + "Account", + filters={"company": si.company, "root_type": "Liability", "disabled": 0}, + order_by="lft", + limit=1, + )[0] + + # setup round off account + company = frappe.get_doc("Company", si.company) + if acc := frappe.db.exists( + "Account", + { + "account_name": "Round Off for Opening", + "account_type": "Round Off for Opening", + "company": si.company, + }, + ): + company.round_off_for_opening = acc + else: + acc = frappe.new_doc("Account") + acc.company = si.company + acc.parent_account = liability_root.name + acc.account_name = "Round Off for Opening" + acc.account_type = "Round Off for Opening" + acc.save() + company.round_off_for_opening = acc.name + company.save() + + si.reload() + si.submit() + res = frappe.db.get_all( + "GL Entry", + filters={"voucher_no": si.name, "is_opening": "Yes"}, + fields=["account", "debit", "credit", "is_opening"], + ) + self.assertEqual(len(res), 3) +>>>>>>> 5021c7ca2c (test: rounding adjustment validation and posting) def set_advance_flag(company, flag, default_account): From da2f6a045aa637514754bbd342bac48ce5847521 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 2 Sep 2024 16:33:55 +0530 Subject: [PATCH 544/734] test: opening round off with inclusive tax (cherry picked from commit 79267358d0fc95a10c9bcd03307ba1284e561526) --- .../sales_invoice/test_sales_invoice.py | 106 +++++++++++++----- 1 file changed, 79 insertions(+), 27 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index d9d7d5fa3ba..15bc234fec1 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -4041,39 +4041,40 @@ class TestSalesInvoice(FrappeTestCase): si.save() self.assertRaises(frappe.ValidationError, si.submit) + def _create_opening_roundoff_account(self, company_name): + liability_root = frappe.db.get_all( + "Account", + filters={"company": company_name, "root_type": "Liability", "disabled": 0}, + order_by="lft", + limit=1, + )[0] + + # setup round off account + if acc := frappe.db.exists( + "Account", + { + "account_name": "Round Off for Opening", + "account_type": "Round Off for Opening", + "company": company_name, + }, + ): + frappe.db.set_value("Company", company_name, "round_off_for_opening", acc) + else: + acc = frappe.new_doc("Account") + acc.company = company_name + acc.parent_account = liability_root.name + acc.account_name = "Round Off for Opening" + acc.account_type = "Round Off for Opening" + acc.save() + frappe.db.set_value("Company", company_name, "round_off_for_opening", acc.name) + def test_opening_invoice_with_rounding_adjustment(self): si = create_sales_invoice(qty=1, rate=99.98, do_not_submit=True) si.is_opening = "Yes" si.items[0].income_account = "Temporary Opening - _TC" si.save() - liability_root = frappe.db.get_all( - "Account", - filters={"company": si.company, "root_type": "Liability", "disabled": 0}, - order_by="lft", - limit=1, - )[0] - - # setup round off account - company = frappe.get_doc("Company", si.company) - if acc := frappe.db.exists( - "Account", - { - "account_name": "Round Off for Opening", - "account_type": "Round Off for Opening", - "company": si.company, - }, - ): - company.round_off_for_opening = acc - else: - acc = frappe.new_doc("Account") - acc.company = si.company - acc.parent_account = liability_root.name - acc.account_name = "Round Off for Opening" - acc.account_type = "Round Off for Opening" - acc.save() - company.round_off_for_opening = acc.name - company.save() + self._create_opening_roundoff_account(si.company) si.reload() si.submit() @@ -4085,6 +4086,57 @@ class TestSalesInvoice(FrappeTestCase): self.assertEqual(len(res), 3) >>>>>>> 5021c7ca2c (test: rounding adjustment validation and posting) + def _create_opening_invoice_with_inclusive_tax(self): + si = create_sales_invoice(qty=1, rate=90, do_not_submit=True) + si.is_opening = "Yes" + si.items[0].income_account = "Temporary Opening - _TC" + item_template = si.items[0].as_dict() + item_template.name = None + item_template.rate = 55 + si.append("items", item_template) + si.append( + "taxes", + { + "charge_type": "On Net Total", + "account_head": "_Test Account Service Tax - _TC", + "cost_center": "_Test Cost Center - _TC", + "description": "Testing...", + "rate": 5, + "included_in_print_rate": True, + }, + ) + # there will be 0.01 precision loss between Dr and Cr + # caused by 'included_in_print_tax' option + si.save() + return si + + def test_rounding_validation_for_opening_with_inclusive_tax(self): + si = self._create_opening_invoice_with_inclusive_tax() + # 'Round Off for Opening' not set in Company master + # Ledger level validation must be thrown + self.assertRaises(frappe.ValidationError, si.submit) + + def test_ledger_entries_on_opening_invoice_with_rounding_loss_by_inclusive_tax(self): + si = self._create_opening_invoice_with_inclusive_tax() + # 'Round Off for Opening' is set in Company master + self._create_opening_roundoff_account(si.company) + + si.submit() + actual = frappe.db.get_all( + "GL Entry", + filters={"voucher_no": si.name, "is_opening": "Yes"}, + fields=["account", "debit", "credit", "is_opening"], + order_by="account,debit", + ) + expected = [ + {"account": "_Test Account Service Tax - _TC", "debit": 0.0, "credit": 6.9, "is_opening": "Yes"}, + {"account": "Debtors - _TC", "debit": 145.0, "credit": 0.0, "is_opening": "Yes"}, + {"account": "Round Off for Opening - _TC", "debit": 0.0, "credit": 0.01, "is_opening": "Yes"}, + {"account": "Temporary Opening - _TC", "debit": 0.0, "credit": 138.09, "is_opening": "Yes"}, + ] + self.assertEqual(len(actual), 4) + self.assertEqual(expected, actual) + def set_advance_flag(company, flag, default_account): frappe.db.set_value( From 7eb4b422808e5df5353e169279a2356a454cd69f Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 2 Sep 2024 17:52:28 +0530 Subject: [PATCH 545/734] refactor: filter on account_type (cherry picked from commit 193ea9ad8f29358820fff45698772ea5adeffcfa) --- 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 52ff21dc407..72d28a705ad 100644 --- a/erpnext/setup/doctype/company/company.js +++ b/erpnext/setup/doctype/company/company.js @@ -252,7 +252,7 @@ erpnext.company.setup_queries = function (frm) { ["default_expense_account", { root_type: "Expense" }], ["default_income_account", { root_type: "Income" }], ["round_off_account", { root_type: "Expense" }], - ["round_off_for_opening", { root_type: "Liability" }], + ["round_off_for_opening", { root_type: "Liability", account_type: "Round Off for Opening" }], ["write_off_account", { root_type: "Expense" }], ["default_deferred_expense_account", {}], ["default_deferred_revenue_account", {}], From 8bec67cbcfb514dda6ba66f6bf0df07841bb8026 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 2 Sep 2024 17:53:02 +0530 Subject: [PATCH 546/734] refactor: handle opening round off on purchase invoice (cherry picked from commit a5d6a25a9654ebe7f91285b9266e9b7cd9797545) --- .../doctype/purchase_invoice/purchase_invoice.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index dc4051eecf4..ebc4efc08a0 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -1545,6 +1545,21 @@ class PurchaseInvoice(BuyingController): self.company, "Purchase Invoice", self.name, self.use_company_roundoff_cost_center ) + if self.is_opening == "Yes" and self.rounding_adjustment: + if not round_off_for_opening: + frappe.throw( + _( + "Opening Invoice has rounding adjustment of {0}.

'{1}' account is required to post these values. Please set it in Company: {2}.

Or, '{3}' can be enabled to not post any rounding adjustment." + ).format( + frappe.bold(self.rounding_adjustment), + frappe.bold("Round Off for Opening"), + get_link_to_form("Company", self.company), + frappe.bold("Disable Rounded Total"), + ) + ) + else: + round_off_account = round_off_for_opening + gl_entries.append( self.get_gl_dict( { From 9bfd5cdb2b8999fd5e2140c79338542f135f77ce Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 3 Sep 2024 11:04:26 +0530 Subject: [PATCH 547/734] test: opening purchase invoice with rounding adjustment (cherry picked from commit b7edc6dea908b887041998801a3d7724fc8777f1) # Conflicts: # erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py --- .../purchase_invoice/test_purchase_invoice.py | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index f5835deb0d0..f234157d949 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -2347,6 +2347,7 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin): frappe.db.set_single_value("Buying Settings", "maintain_same_rate", 1) +<<<<<<< HEAD def test_last_purchase_rate(self): item = create_item("_Test Item For Last Purchase Rate from PI", is_stock_item=1) pi1 = make_purchase_invoice(item_code=item.item_code, qty=10, rate=100) @@ -2364,6 +2365,66 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin): pi1.cancel() item.reload() self.assertEqual(item.last_purchase_rate, 0) +======= + def test_opening_invoice_rounding_adjustment_validation(self): + pi = make_purchase_invoice(do_not_save=1) + pi.items[0].rate = 99.98 + pi.items[0].qty = 1 + pi.items[0].expense_account = "Temporary Opening - _TC" + pi.is_opening = "Yes" + pi.save() + self.assertRaises(frappe.ValidationError, pi.submit) + + def _create_opening_roundoff_account(self, company_name): + liability_root = frappe.db.get_all( + "Account", + filters={"company": company_name, "root_type": "Liability", "disabled": 0}, + order_by="lft", + limit=1, + )[0] + + # setup round off account + if acc := frappe.db.exists( + "Account", + { + "account_name": "Round Off for Opening", + "account_type": "Round Off for Opening", + "company": company_name, + }, + ): + frappe.db.set_value("Company", company_name, "round_off_for_opening", acc) + else: + acc = frappe.new_doc("Account") + acc.company = company_name + acc.parent_account = liability_root.name + acc.account_name = "Round Off for Opening" + acc.account_type = "Round Off for Opening" + acc.save() + frappe.db.set_value("Company", company_name, "round_off_for_opening", acc.name) + + def test_ledger_entries_of_opening_invoice_with_rounding_adjustment(self): + pi = make_purchase_invoice(do_not_save=1) + pi.items[0].rate = 99.98 + pi.items[0].qty = 1 + pi.items[0].expense_account = "Temporary Opening - _TC" + pi.is_opening = "Yes" + pi.save() + self._create_opening_roundoff_account(pi.company) + pi.submit() + actual = frappe.db.get_all( + "GL Entry", + filters={"voucher_no": pi.name, "is_opening": "Yes", "is_cancelled": False}, + fields=["account", "debit", "credit", "is_opening"], + order_by="account,debit", + ) + expected = [ + {"account": "Creditors - _TC", "debit": 0.0, "credit": 100.0, "is_opening": "Yes"}, + {"account": "Round Off for Opening - _TC", "debit": 0.02, "credit": 0.0, "is_opening": "Yes"}, + {"account": "Temporary Opening - _TC", "debit": 99.98, "credit": 0.0, "is_opening": "Yes"}, + ] + self.assertEqual(len(actual), 3) + self.assertEqual(expected, actual) +>>>>>>> b7edc6dea9 (test: opening purchase invoice with rounding adjustment) def set_advance_flag(company, flag, default_account): From ba79560c0c5f4b214c9b52061fac9762c3f6b363 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 3 Sep 2024 11:05:34 +0530 Subject: [PATCH 548/734] refactor(test): filter for active ledger entries (cherry picked from commit cf11ac87fb7a9bb15518204900d776e06af60aaf) --- erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 15bc234fec1..adbab43001b 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -4124,7 +4124,7 @@ class TestSalesInvoice(FrappeTestCase): si.submit() actual = frappe.db.get_all( "GL Entry", - filters={"voucher_no": si.name, "is_opening": "Yes"}, + filters={"voucher_no": si.name, "is_opening": "Yes", "is_cancelled": False}, fields=["account", "debit", "credit", "is_opening"], order_by="account,debit", ) From 9598b1fc0fdf4c8be6145d0a1d4a9130635b595e Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 14 Nov 2024 12:35:41 +0530 Subject: [PATCH 549/734] chore: resolve conflicts --- .../doctype/purchase_invoice/test_purchase_invoice.py | 4 +--- erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py | 4 +--- erpnext/accounts/general_ledger.py | 5 ----- 3 files changed, 2 insertions(+), 11 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index f234157d949..f0b51c32c05 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -2347,7 +2347,6 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin): frappe.db.set_single_value("Buying Settings", "maintain_same_rate", 1) -<<<<<<< HEAD def test_last_purchase_rate(self): item = create_item("_Test Item For Last Purchase Rate from PI", is_stock_item=1) pi1 = make_purchase_invoice(item_code=item.item_code, qty=10, rate=100) @@ -2365,7 +2364,7 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin): pi1.cancel() item.reload() self.assertEqual(item.last_purchase_rate, 0) -======= + def test_opening_invoice_rounding_adjustment_validation(self): pi = make_purchase_invoice(do_not_save=1) pi.items[0].rate = 99.98 @@ -2424,7 +2423,6 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin): ] self.assertEqual(len(actual), 3) self.assertEqual(expected, actual) ->>>>>>> b7edc6dea9 (test: opening purchase invoice with rounding adjustment) def set_advance_flag(company, flag, default_account): diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index adbab43001b..90bec018257 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -3924,7 +3924,6 @@ class TestSalesInvoice(FrappeTestCase): self.assertEqual(len(res), 1) self.assertEqual(res[0][0], pos_return.return_against) -<<<<<<< HEAD @change_settings("Accounts Settings", {"enable_common_party_accounting": True}) def test_common_party_with_foreign_currency_jv(self): from erpnext.accounts.doctype.account.test_account import create_account @@ -4033,7 +4032,7 @@ class TestSalesInvoice(FrappeTestCase): ) self.assertTrue(all([x == "Credit Note" for x in gl_entries])) -======= + def test_validation_on_opening_invoice_with_rounding(self): si = create_sales_invoice(qty=1, rate=99.98, do_not_submit=True) si.is_opening = "Yes" @@ -4084,7 +4083,6 @@ class TestSalesInvoice(FrappeTestCase): fields=["account", "debit", "credit", "is_opening"], ) self.assertEqual(len(res), 3) ->>>>>>> 5021c7ca2c (test: rounding adjustment validation and posting) def _create_opening_invoice_with_inclusive_tax(self): si = create_sales_invoice(qty=1, rate=90, do_not_submit=True) diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py index c2fbdcc95f1..7d7c6f49e12 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -7,12 +7,7 @@ import copy import frappe from frappe import _ from frappe.model.meta import get_field_precision -<<<<<<< HEAD -from frappe.utils import cint, flt, formatdate, getdate, now -======= from frappe.utils import cint, flt, formatdate, get_link_to_form, getdate, now -from frappe.utils.dashboard import cache_source ->>>>>>> 88e68168e3 (refactor: use separate round off for opening entries) import erpnext from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( From 5d6451fca7de3a9deba0df0a8568dc98f8c1936f Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 12 Sep 2024 15:25:32 +0530 Subject: [PATCH 550/734] fix: broken apply on other item pricing rule (cherry picked from commit e5119a749cb0132ac389c416b1b285d3763486b9) --- erpnext/accounts/doctype/pricing_rule/pricing_rule.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py index 1a1ff78a217..72ad0d096bc 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py @@ -446,7 +446,10 @@ def get_pricing_rule_for_item(args, doc=None, for_validate=False): if isinstance(pricing_rule, str): pricing_rule = frappe.get_cached_doc("Pricing Rule", pricing_rule) update_pricing_rule_uom(pricing_rule, args) - pricing_rule.apply_rule_on_other_items = get_pricing_rule_items(pricing_rule) or [] + fetch_other_item = True if pricing_rule.apply_rule_on_other else False + pricing_rule.apply_rule_on_other_items = ( + get_pricing_rule_items(pricing_rule, other_items=fetch_other_item) or [] + ) if pricing_rule.get("suggestion"): continue From c59a7785039cdedc62c3321706799a35892ee7b3 Mon Sep 17 00:00:00 2001 From: UmakanthKaspa Date: Thu, 14 Nov 2024 20:15:20 +0530 Subject: [PATCH 551/734] fix: correctly set 'cannot_add_rows' property on allocations table field (cherry picked from commit 13ca2700f83892dd2074f31907dc08c013fde837) --- erpnext/public/js/utils/unreconcile.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/erpnext/public/js/utils/unreconcile.js b/erpnext/public/js/utils/unreconcile.js index de20f468ccb..4854bf6b452 100644 --- a/erpnext/public/js/utils/unreconcile.js +++ b/erpnext/public/js/utils/unreconcile.js @@ -119,11 +119,18 @@ erpnext.accounts.unreconcile_payment = { return r.message; }; + const allocationsTableField = unreconcile_dialog_fields.find( + (field) => field.fieldname === "allocations" + ); + + if (allocationsTableField) { + allocationsTableField.cannot_add_rows = true; + } + let d = new frappe.ui.Dialog({ title: "UnReconcile Allocations", fields: unreconcile_dialog_fields, size: "large", - cannot_add_rows: true, primary_action_label: "UnReconcile", primary_action(values) { let selected_allocations = values.allocations.filter((x) => x.__checked); From 41c8cfac733104c69c1f30b921c667031c146024 Mon Sep 17 00:00:00 2001 From: UmakanthKaspa Date: Mon, 11 Nov 2024 09:18:09 +0000 Subject: [PATCH 552/734] fix: apply posting date sorting to invoices in Payment Reconciliation similar to payments (cherry picked from commit 0bd83d920d42f461ced04a1ca3a35307b9796d16) --- .../payment_reconciliation/payment_reconciliation.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index 35d268accac..b08afc02c01 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -370,7 +370,11 @@ class PaymentReconciliation(Document): if self.invoice_limit: non_reconciled_invoices = non_reconciled_invoices[: self.invoice_limit] - + + non_reconciled_invoices = sorted( + non_reconciled_invoices, key=lambda k: k["posting_date"] or getdate(nowdate()) + ) + self.add_invoice_entries(non_reconciled_invoices) def add_invoice_entries(self, non_reconciled_invoices): From 5bd633b40f86d46ebc3bce68e0dc65c88489d81e Mon Sep 17 00:00:00 2001 From: UmakanthKaspa Date: Mon, 11 Nov 2024 18:06:53 +0530 Subject: [PATCH 553/734] fix: remove trailing whitespace (cherry picked from commit d6703eb88b08e358d66bf43a24225db318e1ddc7) --- .../doctype/payment_reconciliation/payment_reconciliation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index b08afc02c01..bbbb3c978ff 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -370,11 +370,11 @@ class PaymentReconciliation(Document): if self.invoice_limit: non_reconciled_invoices = non_reconciled_invoices[: self.invoice_limit] - + non_reconciled_invoices = sorted( non_reconciled_invoices, key=lambda k: k["posting_date"] or getdate(nowdate()) ) - + self.add_invoice_entries(non_reconciled_invoices) def add_invoice_entries(self, non_reconciled_invoices): From f4603910e4d4c11ca7cc48d8a44af5a35c98395d Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 15 Nov 2024 11:05:01 +0530 Subject: [PATCH 554/734] fix: broken UI on currency exchange (cherry picked from commit e91b65e7bd5bc23788d6329798d3ab8e1b4ab187) --- .../currency_exchange/currency_exchange.js | 34 ++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/erpnext/setup/doctype/currency_exchange/currency_exchange.js b/erpnext/setup/doctype/currency_exchange/currency_exchange.js index 82f0e22ee61..d4501e5d0da 100644 --- a/erpnext/setup/doctype/currency_exchange/currency_exchange.js +++ b/erpnext/setup/doctype/currency_exchange/currency_exchange.js @@ -1,30 +1,32 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt -extend_cscript(cur_frm.cscript, { - onload: function () { - if (cur_frm.doc.__islocal) { - cur_frm.set_value("to_currency", frappe.defaults.get_global_default("currency")); +frappe.ui.form.on("Currency Exchange", { + onload: function (frm) { + if (frm.doc.__islocal) { + frm.set_value("to_currency", frappe.defaults.get_global_default("currency")); } }, - refresh: function () { - cur_frm.cscript.set_exchange_rate_label(); + refresh: function (frm) { + // Don't trigger on Quick Entry form + if (typeof frm.is_dialog === "undefined") { + frm.trigger("set_exchange_rate_label"); + } }, - from_currency: function () { - cur_frm.cscript.set_exchange_rate_label(); + from_currency: function (frm) { + frm.trigger("set_exchange_rate_label"); }, - to_currency: function () { - cur_frm.cscript.set_exchange_rate_label(); + to_currency: function (frm) { + frm.trigger("set_exchange_rate_label"); }, - - set_exchange_rate_label: function () { - if (cur_frm.doc.from_currency && cur_frm.doc.to_currency) { - var default_label = __(frappe.meta.docfield_map[cur_frm.doctype]["exchange_rate"].label); - cur_frm.fields_dict.exchange_rate.set_label( - default_label + repl(" (1 %(from_currency)s = [?] %(to_currency)s)", cur_frm.doc) + set_exchange_rate_label: function (frm) { + if (frm.doc.from_currency && frm.doc.to_currency) { + var default_label = __(frappe.meta.docfield_map[frm.doctype]["exchange_rate"].label); + frm.fields_dict.exchange_rate.set_label( + default_label + repl(" (1 %(from_currency)s = [?] %(to_currency)s)", frm.doc) ); } }, From 1fe534290dcea0c689c48530b8a765c9111b71f6 Mon Sep 17 00:00:00 2001 From: Vishakh Desai Date: Sat, 28 Sep 2024 12:48:14 +0530 Subject: [PATCH 555/734] fix: Get Entries not showing accounts with no gain or loss in Exchange Rate Revaluation issue (cherry picked from commit 6de6f55b390c1e2180a4698ce25ceba920578b2c) --- .../exchange_rate_revaluation.py | 82 +++++++++++-------- 1 file changed, 48 insertions(+), 34 deletions(-) diff --git a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py index 8607d1ed71f..cd344214736 100644 --- a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py +++ b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py @@ -74,6 +74,21 @@ class ExchangeRateRevaluation(Document): if not (self.company and self.posting_date): frappe.throw(_("Please select Company and Posting Date to getting entries")) + def on_submit(self): + self.remove_accounts_without_gain_loss() + + def remove_accounts_without_gain_loss(self): + self.accounts = [account for account in self.accounts if account.gain_loss] + + if not self.accounts: + frappe.throw(_("At least one account with exchange gain or loss is required")) + + frappe.msgprint( + _("Removing rows without exchange gain or loss"), + alert=True, + indicator="yellow", + ) + def on_cancel(self): self.ignore_linked_doctypes = "GL Entry" @@ -248,23 +263,23 @@ class ExchangeRateRevaluation(Document): new_exchange_rate = get_exchange_rate(d.account_currency, company_currency, posting_date) new_balance_in_base_currency = flt(d.balance_in_account_currency * new_exchange_rate) gain_loss = flt(new_balance_in_base_currency, precision) - flt(d.balance, precision) - if gain_loss: - accounts.append( - { - "account": d.account, - "party_type": d.party_type, - "party": d.party, - "account_currency": d.account_currency, - "balance_in_base_currency": d.balance, - "balance_in_account_currency": d.balance_in_account_currency, - "zero_balance": d.zero_balance, - "current_exchange_rate": current_exchange_rate, - "new_exchange_rate": new_exchange_rate, - "new_balance_in_base_currency": new_balance_in_base_currency, - "new_balance_in_account_currency": d.balance_in_account_currency, - "gain_loss": gain_loss, - } - ) + + accounts.append( + { + "account": d.account, + "party_type": d.party_type, + "party": d.party, + "account_currency": d.account_currency, + "balance_in_base_currency": d.balance, + "balance_in_account_currency": d.balance_in_account_currency, + "zero_balance": d.zero_balance, + "current_exchange_rate": current_exchange_rate, + "new_exchange_rate": new_exchange_rate, + "new_balance_in_base_currency": new_balance_in_base_currency, + "new_balance_in_account_currency": d.balance_in_account_currency, + "gain_loss": gain_loss, + } + ) # Handle Accounts with '0' balance in Account/Base Currency for d in [x for x in account_details if x.zero_balance]: @@ -288,23 +303,22 @@ class ExchangeRateRevaluation(Document): current_exchange_rate * d.balance_in_account_currency ) - if gain_loss: - accounts.append( - { - "account": d.account, - "party_type": d.party_type, - "party": d.party, - "account_currency": d.account_currency, - "balance_in_base_currency": d.balance, - "balance_in_account_currency": d.balance_in_account_currency, - "zero_balance": d.zero_balance, - "current_exchange_rate": current_exchange_rate, - "new_exchange_rate": new_exchange_rate, - "new_balance_in_base_currency": new_balance_in_base_currency, - "new_balance_in_account_currency": new_balance_in_account_currency, - "gain_loss": gain_loss, - } - ) + accounts.append( + { + "account": d.account, + "party_type": d.party_type, + "party": d.party, + "account_currency": d.account_currency, + "balance_in_base_currency": d.balance, + "balance_in_account_currency": d.balance_in_account_currency, + "zero_balance": d.zero_balance, + "current_exchange_rate": current_exchange_rate, + "new_exchange_rate": new_exchange_rate, + "new_balance_in_base_currency": new_balance_in_base_currency, + "new_balance_in_account_currency": new_balance_in_account_currency, + "gain_loss": gain_loss, + } + ) return accounts From 381101f55235e2aa49e2b8229b4250eb3ea2474a Mon Sep 17 00:00:00 2001 From: Vishakh Desai Date: Sat, 28 Sep 2024 12:53:43 +0530 Subject: [PATCH 556/734] fix: linters (cherry picked from commit 9cc22b4cacaf958dad6aadf7a8b9b663326f5a05) --- .../exchange_rate_revaluation/exchange_rate_revaluation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py index cd344214736..c08bd3878d5 100644 --- a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py +++ b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py @@ -74,7 +74,7 @@ class ExchangeRateRevaluation(Document): if not (self.company and self.posting_date): frappe.throw(_("Please select Company and Posting Date to getting entries")) - def on_submit(self): + def before_submit(self): self.remove_accounts_without_gain_loss() def remove_accounts_without_gain_loss(self): From b6fe1f5842671421de52e015c968d2740aaad79e Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 15 Nov 2024 15:04:48 +0530 Subject: [PATCH 557/734] fix: stock ledger variance report filter options (backport #44137) (#44150) fix: stock ledger variance report filter options (#44137) (cherry picked from commit e8bbf6492fc120d34eb8b36fd2c61ee1a9fdc5dc) Co-authored-by: rohitwaghchaure --- .../stock_ledger_variance.js | 18 +++++++++++++++++- .../stock_ledger_variance.py | 18 ++++++++++++------ 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/erpnext/stock/report/stock_ledger_variance/stock_ledger_variance.js b/erpnext/stock/report/stock_ledger_variance/stock_ledger_variance.js index 07e7b59b514..5dfb6627662 100644 --- a/erpnext/stock/report/stock_ledger_variance/stock_ledger_variance.js +++ b/erpnext/stock/report/stock_ledger_variance/stock_ledger_variance.js @@ -47,7 +47,23 @@ frappe.query_reports["Stock Ledger Variance"] = { fieldname: "difference_in", fieldtype: "Select", label: __("Difference In"), - options: ["", "Qty", "Value", "Valuation"], + options: [ + { + // Check "Stock Ledger Invariant Check" report with A - B column + label: __("Quantity (A - B)"), + value: "Qty", + }, + { + // Check "Stock Ledger Invariant Check" report with G - D column + label: __("Value (G - D)"), + value: "Value", + }, + { + // Check "Stock Ledger Invariant Check" report with I - K column + label: __("Valuation (I - K)"), + value: "Valuation", + }, + ], }, { fieldname: "include_disabled", diff --git a/erpnext/stock/report/stock_ledger_variance/stock_ledger_variance.py b/erpnext/stock/report/stock_ledger_variance/stock_ledger_variance.py index 0b7e551c86f..808afadd05a 100644 --- a/erpnext/stock/report/stock_ledger_variance/stock_ledger_variance.py +++ b/erpnext/stock/report/stock_ledger_variance/stock_ledger_variance.py @@ -1,6 +1,8 @@ # Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt +import json + import frappe from frappe import _ from frappe.utils import cint, flt @@ -270,12 +272,16 @@ def has_difference(row, precision, difference_in, valuation_method): value_diff = flt(row.diff_value_diff, precision) valuation_diff = flt(row.valuation_diff, precision) else: - qty_diff = flt(row.difference_in_qty, precision) or flt(row.fifo_qty_diff, precision) - value_diff = ( - flt(row.diff_value_diff, precision) - or flt(row.fifo_value_diff, precision) - or flt(row.fifo_difference_diff, precision) - ) + qty_diff = flt(row.difference_in_qty, precision) + value_diff = flt(row.diff_value_diff, precision) + + if row.stock_queue and json.loads(row.stock_queue): + value_diff = value_diff or ( + flt(row.fifo_value_diff, precision) or flt(row.fifo_difference_diff, precision) + ) + + qty_diff = qty_diff or flt(row.fifo_qty_diff, precision) + valuation_diff = flt(row.valuation_diff, precision) or flt(row.fifo_valuation_diff, precision) if difference_in == "Qty" and qty_diff: From 725d107288675867dd14571b84945f00d4def45e Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 15 Nov 2024 16:55:32 +0530 Subject: [PATCH 558/734] fix: validation for serial no (backport #44133) (#44151) * fix: validation for serial no (#44133) (cherry picked from commit 93c8b4c39af90f9b60da7476855281a22bca0ffc) # Conflicts: # erpnext/stock/doctype/stock_entry/test_stock_entry.py * chore: fix conflicts --------- Co-authored-by: rohitwaghchaure --- .../serial_and_batch_bundle.py | 4 -- .../doctype/stock_entry/test_stock_entry.py | 55 ------------------- 2 files changed, 59 deletions(-) diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py index 08aa978aa99..68c47b0d577 100644 --- a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py +++ b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py @@ -749,10 +749,6 @@ class SerialandBatchBundle(Document): ) def validate_incorrect_serial_nos(self, serial_nos): - if self.voucher_type == "Stock Entry" and self.voucher_no: - if frappe.get_cached_value("Stock Entry", self.voucher_no, "purpose") == "Repack": - return - incorrect_serial_nos = frappe.get_all( "Serial No", filters={"name": ("in", serial_nos), "item_code": ("!=", self.item_code)}, diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py index a9529cc2ede..469b865dd59 100644 --- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py @@ -970,61 +970,6 @@ class TestStockEntry(FrappeTestCase): self.assertRaises(frappe.ValidationError, ste.submit) - def test_same_serial_nos_in_repack_or_manufacture_entries(self): - s1 = make_serialized_item(target_warehouse="_Test Warehouse - _TC") - serial_nos = get_serial_nos_from_bundle(s1.get("items")[0].serial_and_batch_bundle) - - s2 = make_stock_entry( - item_code="_Test Serialized Item With Series", - source="_Test Warehouse - _TC", - qty=2, - basic_rate=100, - purpose="Repack", - serial_no=serial_nos, - do_not_save=True, - ) - - frappe.flags.use_serial_and_batch_fields = True - - cls_obj = SerialBatchCreation( - { - "type_of_transaction": "Inward", - "serial_and_batch_bundle": s2.items[0].serial_and_batch_bundle, - "item_code": "_Test Serialized Item", - "warehouse": "_Test Warehouse - _TC", - } - ) - - cls_obj.duplicate_package() - bundle_id = cls_obj.serial_and_batch_bundle - doc = frappe.get_doc("Serial and Batch Bundle", bundle_id) - doc.db_set( - { - "item_code": "_Test Serialized Item", - "warehouse": "_Test Warehouse - _TC", - } - ) - - doc.load_from_db() - - s2.append( - "items", - { - "item_code": "_Test Serialized Item", - "t_warehouse": "_Test Warehouse - _TC", - "qty": 2, - "basic_rate": 120, - "expense_account": "Stock Adjustment - _TC", - "conversion_factor": 1.0, - "cost_center": "_Test Cost Center - _TC", - "serial_and_batch_bundle": bundle_id, - }, - ) - - s2.submit() - s2.cancel() - frappe.flags.use_serial_and_batch_fields = False - def test_quality_check(self): item_code = "_Test Item For QC" if not frappe.db.exists("Item", item_code): From d61f696f85084bfec858a32c4d819f021e0a6bae Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 15 Nov 2024 17:28:41 +0530 Subject: [PATCH 559/734] feat: inventory dimension for rejected materials (backport #44156) (#44165) feat: inventory dimension for rejected materials (#44156) (cherry picked from commit 9bf16df41ed15f8ed57558262db5277c0d9f3ae4) Co-authored-by: rohitwaghchaure --- erpnext/controllers/stock_controller.py | 9 ++++++ .../inventory_dimension.py | 23 +++++++++++-- .../test_inventory_dimension.py | 32 +++++++++++++++++-- 3 files changed, 58 insertions(+), 6 deletions(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 046a0c7da30..4eb67b6f42e 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -839,6 +839,15 @@ class StockController(AccountsController): if not dimension: continue + if ( + self.doctype in ["Purchase Invoice", "Purchase Receipt"] + and row.get("rejected_warehouse") + and sl_dict.get("warehouse") == row.get("rejected_warehouse") + ): + fieldname = f"rejected_{dimension.source_fieldname}" + sl_dict[dimension.target_fieldname] = row.get(fieldname) + continue + if self.doctype in [ "Purchase Invoice", "Purchase Receipt", diff --git a/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py b/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py index 3bafa12983f..4f8a166932d 100644 --- a/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py +++ b/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py @@ -107,6 +107,7 @@ class InventoryDimension(Document): self.source_fieldname, f"to_{self.source_fieldname}", f"from_{self.source_fieldname}", + f"rejected_{self.source_fieldname}", ], ) } @@ -171,12 +172,12 @@ class InventoryDimension(Document): if label_start_with: label = f"{label_start_with} {self.dimension_name}" - return [ + dimension_fields = [ dict( fieldname="inventory_dimension", fieldtype="Section Break", insert_after=self.get_insert_after_fieldname(doctype), - label="Inventory Dimension", + label=_("Inventory Dimension"), collapsible=1, ), dict( @@ -184,13 +185,29 @@ class InventoryDimension(Document): fieldtype="Link", insert_after="inventory_dimension", options=self.reference_document, - label=label, + label=_(label), search_index=1, reqd=self.reqd, mandatory_depends_on=self.mandatory_depends_on, ), ] + if doctype in ["Purchase Invoice Item", "Purchase Receipt Item"]: + dimension_fields.append( + dict( + fieldname="rejected_" + self.source_fieldname, + fieldtype="Link", + insert_after=self.source_fieldname, + options=self.reference_document, + label=_("Rejected " + self.dimension_name), + search_index=1, + reqd=self.reqd, + mandatory_depends_on=self.mandatory_depends_on, + ) + ) + + return dimension_fields + def add_custom_fields(self): custom_fields = {} diff --git a/erpnext/stock/doctype/inventory_dimension/test_inventory_dimension.py b/erpnext/stock/doctype/inventory_dimension/test_inventory_dimension.py index 918399a7f66..f8128ce0033 100644 --- a/erpnext/stock/doctype/inventory_dimension/test_inventory_dimension.py +++ b/erpnext/stock/doctype/inventory_dimension/test_inventory_dimension.py @@ -269,21 +269,47 @@ class TestInventoryDimension(FrappeTestCase): item_code = "Test Inventory Dimension Item" create_item(item_code) warehouse = create_warehouse("Store Warehouse") + rj_warehouse = create_warehouse("RJ Warehouse") + + if not frappe.db.exists("Store", "Rejected Store"): + frappe.get_doc({"doctype": "Store", "store_name": "Rejected Store"}).insert( + ignore_permissions=True + ) # Purchase Receipt -> Inward in Store 1 pr_doc = make_purchase_receipt( - item_code=item_code, warehouse=warehouse, qty=10, rate=100, do_not_submit=True + item_code=item_code, + warehouse=warehouse, + qty=10, + rejected_qty=5, + rate=100, + rejected_warehouse=rj_warehouse, + do_not_submit=True, ) pr_doc.items[0].store = "Store 1" + pr_doc.items[0].rejected_store = "Rejected Store" pr_doc.save() pr_doc.submit() - entries = get_voucher_sl_entries(pr_doc.name, ["warehouse", "store", "incoming_rate"]) + entries = frappe.get_all( + "Stock Ledger Entry", + filters={"voucher_no": pr_doc.name, "warehouse": warehouse}, + fields=["store"], + order_by="creation", + ) - self.assertEqual(entries[0].warehouse, warehouse) self.assertEqual(entries[0].store, "Store 1") + entries = frappe.get_all( + "Stock Ledger Entry", + filters={"voucher_no": pr_doc.name, "warehouse": rj_warehouse}, + fields=["store"], + order_by="creation", + ) + + self.assertEqual(entries[0].store, "Rejected Store") + # Stock Entry -> Transfer from Store 1 to Store 2 se_doc = make_stock_entry( item_code=item_code, qty=10, from_warehouse=warehouse, to_warehouse=warehouse, do_not_save=True From 2a54cd50041f40bcb5e748232521cbab4eee8387 Mon Sep 17 00:00:00 2001 From: UmakanthKaspa Date: Fri, 15 Nov 2024 05:43:25 +0000 Subject: [PATCH 560/734] refactor: set 'cannot_add_rows' directly in the allocations table field (optimized approach) (cherry picked from commit 5dd8eafdfc7e077f40c330819bcbcbb132bd3f40) --- erpnext/public/js/utils/unreconcile.js | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/erpnext/public/js/utils/unreconcile.js b/erpnext/public/js/utils/unreconcile.js index 4854bf6b452..7dba4705e40 100644 --- a/erpnext/public/js/utils/unreconcile.js +++ b/erpnext/public/js/utils/unreconcile.js @@ -100,6 +100,7 @@ erpnext.accounts.unreconcile_payment = { fieldtype: "Table", read_only: 1, fields: child_table_fields, + cannot_add_rows: true, }, ]; @@ -119,14 +120,6 @@ erpnext.accounts.unreconcile_payment = { return r.message; }; - const allocationsTableField = unreconcile_dialog_fields.find( - (field) => field.fieldname === "allocations" - ); - - if (allocationsTableField) { - allocationsTableField.cannot_add_rows = true; - } - let d = new frappe.ui.Dialog({ title: "UnReconcile Allocations", fields: unreconcile_dialog_fields, From 5848de76ea3049cf3b25c88cc0a510f7712945c5 Mon Sep 17 00:00:00 2001 From: vishakhdesai Date: Thu, 14 Nov 2024 17:31:34 +0530 Subject: [PATCH 561/734] fix: set conversion factor before applying price list (cherry picked from commit 9749fe23cc82181f1fb58aa481b0c3c7d633699e) --- erpnext/public/js/controllers/transaction.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 0efec214c0d..ca1b1c8c590 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -1237,8 +1237,8 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe }, callback: function(r) { if(!r.exc) { - me.apply_price_list(item, true) frappe.model.set_value(cdt, cdn, 'conversion_factor', r.message.conversion_factor); + me.apply_price_list(item, true); } } }); From 08f6ceeb5001d5e9159c7a9c0134467121298da0 Mon Sep 17 00:00:00 2001 From: vishakhdesai Date: Wed, 13 Nov 2024 17:27:49 +0530 Subject: [PATCH 562/734] fix: set default party type in Payment Entry (cherry picked from commit 19222690d31e486ccd634d7530f2c85eda7cf4c0) --- .../doctype/payment_entry/payment_entry.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index 7ababfec81a..a377aa04db2 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -26,6 +26,10 @@ frappe.ui.form.on("Payment Entry", { } erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype); + + if (frm.is_new()) { + set_default_party_type(frm); + } }, setup: function (frm) { @@ -403,6 +407,8 @@ frappe.ui.form.on("Payment Entry", { }, payment_type: function (frm) { + set_default_party_type(frm); + if (frm.doc.payment_type == "Internal Transfer") { $.each( [ @@ -1776,3 +1782,16 @@ frappe.ui.form.on("Payment Entry Deduction", { frm.events.set_unallocated_amount(frm); }, }); + +function set_default_party_type(frm) { + if (frm.doc.party) return; + + let party_type; + if (frm.doc.payment_type == "Receive") { + party_type = "Customer"; + } else if (frm.doc.payment_type == "Pay") { + party_type = "Supplier"; + } + + if (party_type) frm.set_value("party_type", party_type); +} From c98a0ccd1dc5007941544ca49e0c2ad68d050323 Mon Sep 17 00:00:00 2001 From: Ninad1306 Date: Fri, 8 Nov 2024 12:51:05 +0530 Subject: [PATCH 563/734] fix: added disable_rounded_total field (cherry picked from commit f8524d526b5922e8223e21409f2997f2e7cd2f5b) # Conflicts: # erpnext/selling/doctype/quotation/quotation.json --- .../selling/doctype/quotation/quotation.json | 47 +++++++++++++++++++ .../selling/doctype/quotation/quotation.py | 1 + 2 files changed, 48 insertions(+) diff --git a/erpnext/selling/doctype/quotation/quotation.json b/erpnext/selling/doctype/quotation/quotation.json index 982e7326775..8d6b96c8e67 100644 --- a/erpnext/selling/doctype/quotation/quotation.json +++ b/erpnext/selling/doctype/quotation/quotation.json @@ -65,6 +65,7 @@ "grand_total", "rounding_adjustment", "rounded_total", + "disable_rounded_total", "in_words", "section_break_44", "apply_discount_on", @@ -661,6 +662,7 @@ "width": "200px" }, { + "depends_on": "eval:!doc.disable_rounded_total", "fieldname": "base_rounding_adjustment", "fieldtype": "Currency", "label": "Rounding Adjustment (Company Currency)", @@ -709,6 +711,7 @@ "width": "200px" }, { + "depends_on": "eval:!doc.disable_rounded_total", "fieldname": "rounding_adjustment", "fieldtype": "Currency", "label": "Rounding Adjustment", @@ -1067,13 +1070,57 @@ "fieldname": "named_place", "fieldtype": "Data", "label": "Named Place" +<<<<<<< HEAD +======= + }, + { + "fieldname": "utm_campaign", + "fieldtype": "Link", + "label": "Campaign", + "oldfieldname": "campaign", + "oldfieldtype": "Link", + "options": "UTM Campaign", + "print_hide": 1 + }, + { + "fieldname": "utm_source", + "fieldtype": "Link", + "label": "Source", + "oldfieldname": "source", + "oldfieldtype": "Select", + "options": "UTM Source", + "print_hide": 1 + }, + { + "fieldname": "utm_medium", + "fieldtype": "Link", + "label": "Medium", + "options": "UTM Medium", + "print_hide": 1 + }, + { + "fieldname": "utm_content", + "fieldtype": "Data", + "label": "Content", + "print_hide": 1 + }, + { + "default": "0", + "fieldname": "disable_rounded_total", + "fieldtype": "Check", + "label": "Disable Rounded Total" +>>>>>>> f8524d526b (fix: added disable_rounded_total field) } ], "icon": "fa fa-shopping-cart", "idx": 82, "is_submittable": 1, "links": [], +<<<<<<< HEAD "modified": "2024-03-20 16:04:21.567847", +======= + "modified": "2024-11-07 18:37:11.715189", +>>>>>>> f8524d526b (fix: added disable_rounded_total field) "modified_by": "Administrator", "module": "Selling", "name": "Quotation", diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index a5994756c46..8e560b8d0ab 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -61,6 +61,7 @@ class Quotation(SellingController): customer_address: DF.Link | None customer_group: DF.Link | None customer_name: DF.Data | None + disable_rounded_total: DF.Check discount_amount: DF.Currency enq_det: DF.Text | None grand_total: DF.Currency From b6524946bc9a708b7d1dcc8c1c29b3d0feec7b4f Mon Sep 17 00:00:00 2001 From: Ninad1306 Date: Tue, 12 Nov 2024 15:25:14 +0530 Subject: [PATCH 564/734] test: test to validate rounded total (cherry picked from commit 5a6261d3b4b3084dc1f45bef6584c55043084336) --- .../selling/doctype/quotation/test_quotation.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/erpnext/selling/doctype/quotation/test_quotation.py b/erpnext/selling/doctype/quotation/test_quotation.py index 9a31e335a05..05f43f26559 100644 --- a/erpnext/selling/doctype/quotation/test_quotation.py +++ b/erpnext/selling/doctype/quotation/test_quotation.py @@ -715,6 +715,20 @@ class TestQuotation(FrappeTestCase): item_doc.taxes = [] item_doc.save() + def test_grand_total_and_rounded_total_values(self): + quotation = make_quotation(qty=6, rate=12.3, do_not_submit=1) + + self.assertEqual(quotation.grand_total, 73.8) + self.assertEqual(quotation.rounding_adjustment, 0.2) + self.assertEqual(quotation.rounded_total, 74) + + quotation.disable_rounded_total = 1 + quotation.save() + + self.assertEqual(quotation.grand_total, 73.8) + self.assertEqual(quotation.rounding_adjustment, 0) + self.assertEqual(quotation.rounded_total, 0) + test_records = frappe.get_test_records("Quotation") From c0d3f8cbbe347f7326b4acead396e28daec01fc0 Mon Sep 17 00:00:00 2001 From: sudarsan2001 Date: Thu, 14 Nov 2024 01:11:12 +0530 Subject: [PATCH 565/734] fix: set debit in transaction currency in GL Entry (cherry picked from commit 29a6eb21a3cf33cd5d7a76e476646bf9c5e63c24) --- .../doctype/payment_entry/payment_entry.py | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 8832b87eec7..3efcb155781 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -1248,13 +1248,22 @@ class PaymentEntry(AccountsController): base_unallocated_amount = self.unallocated_amount * exchange_rate gle = party_gl_dict.copy() - gle.update( - { - dr_or_cr + "_in_account_currency": self.unallocated_amount, - dr_or_cr: base_unallocated_amount, - } - ) + gle.update( + self.get_gl_dict( + { + "account": self.party_account, + "party_type": self.party_type, + "party": self.party, + "against": against_account, + "account_currency": self.party_account_currency, + "cost_center": self.cost_center, + dr_or_cr + "_in_account_currency": self.unallocated_amount, + dr_or_cr: base_unallocated_amount, + }, + item=self, + ) + ) if self.book_advance_payments_in_separate_party_account: gle.update( { From c30a17cd7a2fd009127d802596b66e81394c712d Mon Sep 17 00:00:00 2001 From: sudarsan2001 Date: Thu, 14 Nov 2024 01:28:16 +0530 Subject: [PATCH 566/734] test: add unit test to validate gl values (cherry picked from commit e8b8a589be75a840d75a2e46cf55364c040158a1) --- .../payment_entry/test_payment_entry.py | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py index 771c91a462c..2dcd5d6076f 100644 --- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py @@ -956,6 +956,51 @@ class TestPaymentEntry(FrappeTestCase): self.assertEqual(flt(expected_party_balance), party_balance) self.assertEqual(flt(expected_party_account_balance, 2), flt(party_account_balance, 2)) + def test_gl_of_multi_currency_payment_transaction(self): + from erpnext.setup.doctype.currency_exchange.test_currency_exchange import save_new_records + + save_new_records(self.globalTestRecords["Currency Exchange"]) + paid_from = create_account( + parent_account="Current Liabilities - _TC", + account_name="Cash USD", + company="_Test Company", + account_type="Cash", + account_currency="USD", + ) + payment_entry = create_payment_entry( + party="_Test Supplier USD", + paid_from=paid_from, + paid_to="_Test Payable USD - _TC", + paid_amount=100, + save=True, + ) + payment_entry.source_exchange_rate = 84.4 + payment_entry.target_exchange_rate = 84.4 + payment_entry.save() + payment_entry = payment_entry.submit() + gle = qb.DocType("GL Entry") + gl_entries = ( + qb.from_(gle) + .select( + gle.account, + gle.debit, + gle.credit, + gle.debit_in_account_currency, + gle.credit_in_account_currency, + gle.debit_in_transaction_currency, + gle.credit_in_transaction_currency, + ) + .orderby(gle.account) + .where(gle.voucher_no == payment_entry.name) + .run() + ) + expected_gl_entries = ( + ("_Test Payable USD - _TC", 8440.0, 0, 100.0, 0.0, 8440.0, 0.0), + (paid_from, 0, 8440.0, 0, 100.0, 0, 8440.0), + ) + + self.assertEqual(gl_entries, expected_gl_entries) + def test_multi_currency_payment_entry_with_taxes(self): payment_entry = create_payment_entry( party="_Test Supplier USD", paid_to="_Test Payable USD - _TC", save=True From 7cc31df58774d77ef185c0f327ae0623a156b675 Mon Sep 17 00:00:00 2001 From: sudarsan2001 Date: Thu, 14 Nov 2024 10:59:51 +0530 Subject: [PATCH 567/734] chore: change account name (cherry picked from commit 4a1cd5a8d6bf920b682fa731f1fc087e09ea94e5) --- .../accounts/doctype/payment_entry/test_payment_entry.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py index 2dcd5d6076f..c61598b54a5 100644 --- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py @@ -962,7 +962,7 @@ class TestPaymentEntry(FrappeTestCase): save_new_records(self.globalTestRecords["Currency Exchange"]) paid_from = create_account( parent_account="Current Liabilities - _TC", - account_name="Cash USD", + account_name="_Test Cash USD", company="_Test Company", account_type="Cash", account_currency="USD", @@ -995,10 +995,9 @@ class TestPaymentEntry(FrappeTestCase): .run() ) expected_gl_entries = ( - ("_Test Payable USD - _TC", 8440.0, 0, 100.0, 0.0, 8440.0, 0.0), - (paid_from, 0, 8440.0, 0, 100.0, 0, 8440.0), + (paid_from, 0.0, 8440.0, 0.0, 100.0, 0.0, 8440.0), + ("_Test Payable USD - _TC", 8440.0, 0.0, 100.0, 0.0, 8440.0, 0.0), ) - self.assertEqual(gl_entries, expected_gl_entries) def test_multi_currency_payment_entry_with_taxes(self): From d7deed6c450708e74a58469d852f909d9893a16a Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 18 Nov 2024 16:25:44 +0530 Subject: [PATCH 568/734] refactor: assume any of the foreign currency as transaction currency On a foreign currency payment entry, assume any one of the foreign currency as the transaction currency (cherry picked from commit 6681882bd8003cac1d8d4eda76141ec5a6b6b246) --- erpnext/accounts/doctype/payment_entry/payment_entry.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 3efcb155781..b9fad5c9010 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -1146,6 +1146,12 @@ class PaymentEntry(AccountsController): if self.payment_type in ("Receive", "Pay") and not self.get("party_account_field"): self.setup_party_account_field() + company_currency = erpnext.get_company_currency(self.company) + if self.paid_from_account_currency != company_currency: + self.currency = self.paid_from_account_currency + elif self.paid_to_account_currency != company_currency: + self.currency = self.paid_to_account_currency + gl_entries = [] self.add_party_gl_entries(gl_entries) self.add_bank_gl_entries(gl_entries) From b130e2065b5998ef42b754442fda9c5bf4cf60d9 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 18 Nov 2024 21:08:21 +0100 Subject: [PATCH 569/734] feat: new DocTypes "Code List" and "Common Code" (backport #43425) (#44173) Co-authored-by: David Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com> --- erpnext/edi/__init__.py | 0 erpnext/edi/doctype/__init__.py | 0 erpnext/edi/doctype/code_list/__init__.py | 0 erpnext/edi/doctype/code_list/code_list.js | 51 ++++ erpnext/edi/doctype/code_list/code_list.json | 112 +++++++++ erpnext/edi/doctype/code_list/code_list.py | 125 ++++++++++ .../edi/doctype/code_list/code_list_import.js | 218 ++++++++++++++++++ .../edi/doctype/code_list/code_list_import.py | 140 +++++++++++ .../edi/doctype/code_list/code_list_list.js | 8 + .../edi/doctype/code_list/test_code_list.py | 9 + erpnext/edi/doctype/common_code/__init__.py | 0 .../edi/doctype/common_code/common_code.js | 8 + .../edi/doctype/common_code/common_code.json | 103 +++++++++ .../edi/doctype/common_code/common_code.py | 114 +++++++++ .../doctype/common_code/common_code_list.js | 8 + .../doctype/common_code/test_common_code.py | 9 + erpnext/hooks.py | 8 + erpnext/modules.txt | 1 + 18 files changed, 914 insertions(+) create mode 100644 erpnext/edi/__init__.py create mode 100644 erpnext/edi/doctype/__init__.py create mode 100644 erpnext/edi/doctype/code_list/__init__.py create mode 100644 erpnext/edi/doctype/code_list/code_list.js create mode 100644 erpnext/edi/doctype/code_list/code_list.json create mode 100644 erpnext/edi/doctype/code_list/code_list.py create mode 100644 erpnext/edi/doctype/code_list/code_list_import.js create mode 100644 erpnext/edi/doctype/code_list/code_list_import.py create mode 100644 erpnext/edi/doctype/code_list/code_list_list.js create mode 100644 erpnext/edi/doctype/code_list/test_code_list.py create mode 100644 erpnext/edi/doctype/common_code/__init__.py create mode 100644 erpnext/edi/doctype/common_code/common_code.js create mode 100644 erpnext/edi/doctype/common_code/common_code.json create mode 100644 erpnext/edi/doctype/common_code/common_code.py create mode 100644 erpnext/edi/doctype/common_code/common_code_list.js create mode 100644 erpnext/edi/doctype/common_code/test_common_code.py diff --git a/erpnext/edi/__init__.py b/erpnext/edi/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/edi/doctype/__init__.py b/erpnext/edi/doctype/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/edi/doctype/code_list/__init__.py b/erpnext/edi/doctype/code_list/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/edi/doctype/code_list/code_list.js b/erpnext/edi/doctype/code_list/code_list.js new file mode 100644 index 00000000000..f8b9a2003fd --- /dev/null +++ b/erpnext/edi/doctype/code_list/code_list.js @@ -0,0 +1,51 @@ +// Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on("Code List", { + refresh: (frm) => { + if (!frm.doc.__islocal) { + frm.add_custom_button(__("Import Genericode File"), function () { + erpnext.edi.import_genericode(frm); + }); + } + }, + setup: (frm) => { + frm.savetrash = () => { + frm.validate_form_action("Delete"); + frappe.confirm( + __( + "Are you sure you want to delete {0}?

This action will also delete all associated Common Code documents.

", + [frm.docname.bold()] + ), + function () { + return frappe.call({ + method: "frappe.client.delete", + args: { + doctype: frm.doctype, + name: frm.docname, + }, + freeze: true, + freeze_message: __("Deleting {0} and all associated Common Code documents...", [ + frm.docname, + ]), + callback: function (r) { + if (!r.exc) { + frappe.utils.play_sound("delete"); + frappe.model.clear_doc(frm.doctype, frm.docname); + window.history.back(); + } + }, + }); + } + ); + }; + + frm.set_query("default_common_code", function (doc) { + return { + filters: { + code_list: doc.name, + }, + }; + }); + }, +}); diff --git a/erpnext/edi/doctype/code_list/code_list.json b/erpnext/edi/doctype/code_list/code_list.json new file mode 100644 index 00000000000..ffcc2f2b605 --- /dev/null +++ b/erpnext/edi/doctype/code_list/code_list.json @@ -0,0 +1,112 @@ +{ + "actions": [], + "allow_copy": 1, + "allow_rename": 1, + "autoname": "prompt", + "creation": "2024-09-29 06:55:03.920375", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "title", + "canonical_uri", + "url", + "default_common_code", + "column_break_nkls", + "version", + "publisher", + "publisher_id", + "section_break_npxp", + "description" + ], + "fields": [ + { + "fieldname": "title", + "fieldtype": "Data", + "label": "Title" + }, + { + "fieldname": "publisher", + "fieldtype": "Data", + "in_standard_filter": 1, + "label": "Publisher" + }, + { + "columns": 1, + "fieldname": "version", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Version" + }, + { + "fieldname": "description", + "fieldtype": "Small Text", + "label": "Description" + }, + { + "fieldname": "canonical_uri", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Canonical URI" + }, + { + "fieldname": "column_break_nkls", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_npxp", + "fieldtype": "Section Break" + }, + { + "fieldname": "publisher_id", + "fieldtype": "Data", + "in_standard_filter": 1, + "label": "Publisher ID" + }, + { + "fieldname": "url", + "fieldtype": "Data", + "label": "URL", + "options": "URL" + }, + { + "description": "This value shall be used when no matching Common Code for a record is found.", + "fieldname": "default_common_code", + "fieldtype": "Link", + "label": "Default Common Code", + "options": "Common Code" + } + ], + "index_web_pages_for_search": 1, + "links": [ + { + "link_doctype": "Common Code", + "link_fieldname": "code_list" + } + ], + "modified": "2024-11-16 17:01:40.260293", + "modified_by": "Administrator", + "module": "EDI", + "name": "Code List", + "naming_rule": "Set by user", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "search_fields": "description", + "show_title_field_in_link": 1, + "sort_field": "creation", + "sort_order": "DESC", + "states": [], + "title_field": "title" +} \ No newline at end of file diff --git a/erpnext/edi/doctype/code_list/code_list.py b/erpnext/edi/doctype/code_list/code_list.py new file mode 100644 index 00000000000..8957c6565b9 --- /dev/null +++ b/erpnext/edi/doctype/code_list/code_list.py @@ -0,0 +1,125 @@ +# Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from typing import TYPE_CHECKING + +import frappe +from frappe.model.document import Document + +if TYPE_CHECKING: + from lxml.etree import Element + + +class CodeList(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from frappe.types import DF + + canonical_uri: DF.Data | None + default_common_code: DF.Link | None + description: DF.SmallText | None + publisher: DF.Data | None + publisher_id: DF.Data | None + title: DF.Data | None + url: DF.Data | None + version: DF.Data | None + # end: auto-generated types + + def on_trash(self): + if not frappe.flags.in_bulk_delete: + self.__delete_linked_docs() + + def __delete_linked_docs(self): + self.db_set("default_common_code", None) + + linked_docs = frappe.get_all( + "Common Code", + filters={"code_list": self.name}, + fields=["name"], + ) + + for doc in linked_docs: + frappe.delete_doc("Common Code", doc.name) + + def get_codes_for(self, doctype: str, name: str) -> tuple[str]: + """Get the applicable codes for a doctype and name""" + return get_codes_for(self.name, doctype, name) + + def get_docnames_for(self, doctype: str, code: str) -> tuple[str]: + """Get the mapped docnames for a doctype and code""" + return get_docnames_for(self.name, doctype, code) + + def get_default_code(self) -> str | None: + """Get the default common code for this code list""" + return ( + frappe.db.get_value("Common Code", self.default_common_code, "common_code") + if self.default_common_code + else None + ) + + def from_genericode(self, root: "Element"): + """Extract Code List details from genericode XML""" + self.title = root.find(".//Identification/ShortName").text + self.version = root.find(".//Identification/Version").text + self.canonical_uri = root.find(".//CanonicalUri").text + # optionals + self.description = getattr(root.find(".//Identification/LongName"), "text", None) + self.publisher = getattr(root.find(".//Identification/Agency/ShortName"), "text", None) + if not self.publisher: + self.publisher = getattr(root.find(".//Identification/Agency/LongName"), "text", None) + self.publisher_id = getattr(root.find(".//Identification/Agency/Identifier"), "text", None) + self.url = getattr(root.find(".//Identification/LocationUri"), "text", None) + + +def get_codes_for(code_list: str, doctype: str, name: str) -> tuple[str]: + """Return the common code for a given record""" + CommonCode = frappe.qb.DocType("Common Code") + DynamicLink = frappe.qb.DocType("Dynamic Link") + + codes = ( + frappe.qb.from_(CommonCode) + .join(DynamicLink) + .on((CommonCode.name == DynamicLink.parent) & (DynamicLink.parenttype == "Common Code")) + .select(CommonCode.common_code) + .where( + (DynamicLink.link_doctype == doctype) + & (DynamicLink.link_name == name) + & (CommonCode.code_list == code_list) + ) + .distinct() + .orderby(CommonCode.common_code) + ).run() + + return tuple(c[0] for c in codes) if codes else () + + +def get_docnames_for(code_list: str, doctype: str, code: str) -> tuple[str]: + """Return the record name for a given common code""" + CommonCode = frappe.qb.DocType("Common Code") + DynamicLink = frappe.qb.DocType("Dynamic Link") + + docnames = ( + frappe.qb.from_(CommonCode) + .join(DynamicLink) + .on((CommonCode.name == DynamicLink.parent) & (DynamicLink.parenttype == "Common Code")) + .select(DynamicLink.link_name) + .where( + (DynamicLink.link_doctype == doctype) + & (CommonCode.common_code == code) + & (CommonCode.code_list == code_list) + ) + .distinct() + .orderby(DynamicLink.idx) + ).run() + + return tuple(d[0] for d in docnames) if docnames else () + + +def get_default_code(code_list: str) -> str | None: + """Return the default common code for a given code list""" + code_id = frappe.db.get_value("Code List", code_list, "default_common_code") + return frappe.db.get_value("Common Code", code_id, "common_code") if code_id else None diff --git a/erpnext/edi/doctype/code_list/code_list_import.js b/erpnext/edi/doctype/code_list/code_list_import.js new file mode 100644 index 00000000000..4a33f3e2fe6 --- /dev/null +++ b/erpnext/edi/doctype/code_list/code_list_import.js @@ -0,0 +1,218 @@ +frappe.provide("erpnext.edi"); + +erpnext.edi.import_genericode = function (listview_or_form) { + let doctype = "Code List"; + let docname = undefined; + if (listview_or_form.doc !== undefined) { + docname = listview_or_form.doc.name; + } + new frappe.ui.FileUploader({ + method: "erpnext.edi.doctype.code_list.code_list_import.import_genericode", + doctype: doctype, + docname: docname, + allow_toggle_private: false, + allow_take_photo: false, + on_success: function (_file_doc, r) { + listview_or_form.refresh(); + show_column_selection_dialog(r.message); + }, + }); +}; + +function show_column_selection_dialog(context) { + let title_description = __("If there is no title column, use the code column for the title."); + let default_title = get_default(context.columns, ["name", "Name", "code-name", "scheme-name"]); + let fields = [ + { + fieldtype: "HTML", + fieldname: "code_list_info", + options: `
${__( + "You are importing data for the code list:" + )} ${frappe.utils.get_form_link( + "Code List", + context.code_list, + true, + context.code_list_title + )}
`, + }, + { + fieldtype: "Section Break", + }, + { + fieldname: "import_column", + label: __("Import"), + fieldtype: "Column Break", + }, + { + fieldname: "title_column", + label: __("as Title"), + fieldtype: "Select", + reqd: 1, + options: context.columns, + default: default_title, + description: default_title ? null : title_description, + }, + { + fieldname: "code_column", + label: __("as Code"), + fieldtype: "Select", + options: context.columns, + reqd: 1, + default: get_default(context.columns, ["code", "Code", "value"]), + }, + { + fieldname: "filters_column", + label: __("Filter"), + fieldtype: "Column Break", + }, + ]; + + if (context.columns.length > 2) { + fields.splice(5, 0, { + fieldname: "description_column", + label: __("as Description"), + fieldtype: "Select", + options: [null].concat(context.columns), + default: get_default(context.columns, [ + "description", + "Description", + "remark", + __("description"), + __("Description"), + ]), + }); + } + + // Add filterable columns + for (let column in context.filterable_columns) { + fields.push({ + fieldname: `filter_${column}`, + label: __("by {}", [column]), + fieldtype: "Select", + options: [null].concat(context.filterable_columns[column]), + }); + } + + fields.push( + { + fieldname: "preview_section", + label: __("Preview"), + fieldtype: "Section Break", + }, + { + fieldname: "preview_html", + fieldtype: "HTML", + } + ); + + let d = new frappe.ui.Dialog({ + title: __("Select Columns and Filters"), + fields: fields, + primary_action_label: __("Import"), + size: "large", // This will make the modal wider + primary_action(values) { + let filters = {}; + for (let field in values) { + if (field.startsWith("filter_") && values[field]) { + filters[field.replace("filter_", "")] = values[field]; + } + } + frappe + .xcall("erpnext.edi.doctype.code_list.code_list_import.process_genericode_import", { + code_list_name: context.code_list, + file_name: context.file, + code_column: values.code_column, + title_column: values.title_column, + description_column: values.description_column, + filters: filters, + }) + .then((count) => { + frappe.msgprint(__("Import completed. {0} common codes created.", [count])); + }); + d.hide(); + }, + }); + + d.fields_dict.code_column.df.onchange = () => update_preview(d, context); + d.fields_dict.title_column.df.onchange = (e) => { + let field = d.fields_dict.title_column; + if (!e.target.value) { + field.df.description = title_description; + field.refresh(); + } else { + field.df.description = null; + field.refresh(); + } + update_preview(d, context); + }; + + // Add onchange events for filterable columns + for (let column in context.filterable_columns) { + d.fields_dict[`filter_${column}`].df.onchange = () => update_preview(d, context); + } + + d.show(); + update_preview(d, context); +} + +/** + * Return the first key from the keys array that is found in the columns array. + */ +function get_default(columns, keys) { + return keys.find((key) => columns.includes(key)); +} + +function update_preview(dialog, context) { + let code_column = dialog.get_value("code_column"); + let title_column = dialog.get_value("title_column"); + let description_column = dialog.get_value("description_column"); + + let html = ''; + if (title_column) html += ``; + if (code_column) html += ``; + if (description_column) html += ``; + + // Add headers for filterable columns + for (let column in context.filterable_columns) { + if (dialog.get_value(`filter_${column}`)) { + html += ``; + } + } + + html += ""; + + for (let i = 0; i < 3; i++) { + html += ""; + if (title_column) { + let title = context.example_values[title_column][i] || ""; + html += ``; + } + if (code_column) { + let code = context.example_values[code_column][i] || ""; + html += ``; + } + if (description_column) { + let description = context.example_values[description_column][i] || ""; + html += ``; + } + + // Add values for filterable columns + for (let column in context.filterable_columns) { + if (dialog.get_value(`filter_${column}`)) { + let value = context.example_values[column][i] || ""; + html += ``; + } + } + + html += ""; + } + + html += "
${__("Title")}${__("Code")}${__("Description")}${__(column)}
${truncate(title)}${truncate(code)}${truncate(description)}${truncate(value)}
"; + + dialog.fields_dict.preview_html.$wrapper.html(html); +} + +function truncate(value, maxLength = 40) { + if (typeof value !== "string") return ""; + return value.length > maxLength ? value.substring(0, maxLength - 3) + "..." : value; +} diff --git a/erpnext/edi/doctype/code_list/code_list_import.py b/erpnext/edi/doctype/code_list/code_list_import.py new file mode 100644 index 00000000000..50df3be471e --- /dev/null +++ b/erpnext/edi/doctype/code_list/code_list_import.py @@ -0,0 +1,140 @@ +import json + +import frappe +import requests +from frappe import _ +from lxml import etree + +URL_PREFIXES = ("http://", "https://") + + +@frappe.whitelist() +def import_genericode(): + doctype = "Code List" + docname = frappe.form_dict.docname + content = frappe.local.uploaded_file + + # recover the content, if it's a link + if (file_url := frappe.local.uploaded_file_url) and file_url.startswith(URL_PREFIXES): + try: + # If it's a URL, fetch the content and make it a local file (for durable audit) + response = requests.get(frappe.local.uploaded_file_url) + response.raise_for_status() + frappe.local.uploaded_file = content = response.content + frappe.local.uploaded_filename = frappe.local.uploaded_file_url.split("/")[-1] + frappe.local.uploaded_file_url = None + except Exception as e: + frappe.throw(f"
{e!s}
", title=_("Fetching Error")) + + if file_url := frappe.local.uploaded_file_url: + file_path = frappe.utils.file_manager.get_file_path(file_url) + with open(file_path.encode(), mode="rb") as f: + content = f.read() + + # Parse the xml content + parser = etree.XMLParser(remove_blank_text=True) + try: + root = etree.fromstring(content, parser=parser) + except Exception as e: + frappe.throw(f"
{e!s}
", title=_("Parsing Error")) + + # Extract the name (CanonicalVersionUri) from the parsed XML + name = root.find(".//CanonicalVersionUri").text + docname = docname or name + + if frappe.db.exists(doctype, docname): + code_list = frappe.get_doc(doctype, docname) + if code_list.name != name: + frappe.throw(_("The uploaded file does not match the selected Code List.")) + else: + # Create a new Code List document with the extracted name + code_list = frappe.new_doc(doctype) + code_list.name = name + + code_list.from_genericode(root) + code_list.save() + + # Attach the file and provide a recoverable identifier + file_doc = frappe.get_doc( + { + "doctype": "File", + "attached_to_doctype": "Code List", + "attached_to_name": code_list.name, + "folder": "Home/Attachments", + "file_name": frappe.local.uploaded_filename, + "file_url": frappe.local.uploaded_file_url, + "is_private": 1, + "content": content, + } + ).save() + + # Get available columns and example values + columns, example_values, filterable_columns = get_genericode_columns_and_examples(root) + + return { + "code_list": code_list.name, + "code_list_title": code_list.title, + "file": file_doc.name, + "columns": columns, + "example_values": example_values, + "filterable_columns": filterable_columns, + } + + +@frappe.whitelist() +def process_genericode_import( + code_list_name: str, + file_name: str, + code_column: str, + title_column: str | None = None, + description_column: str | None = None, + filters: str | None = None, +): + from erpnext.edi.doctype.common_code.common_code import import_genericode + + column_map = {"code": code_column, "title": title_column, "description": description_column} + + return import_genericode(code_list_name, file_name, column_map, json.loads(filters) if filters else None) + + +def get_genericode_columns_and_examples(root): + columns = [] + example_values = {} + filterable_columns = {} + + # Get column names + for column in root.findall(".//Column"): + column_id = column.get("Id") + columns.append(column_id) + example_values[column_id] = [] + filterable_columns[column_id] = set() + + # Get all values and count unique occurrences + for row in root.findall(".//SimpleCodeList/Row"): + for value in row.findall("Value"): + column_id = value.get("ColumnRef") + if column_id not in columns: + # Handle undeclared column + columns.append(column_id) + example_values[column_id] = [] + filterable_columns[column_id] = set() + + simple_value = value.find("./SimpleValue") + if simple_value is None: + continue + + filterable_columns[column_id].add(simple_value.text) + + # Get example values (up to 3) and filter columns with cardinality <= 5 + for row in root.findall(".//SimpleCodeList/Row")[:3]: + for value in row.findall("Value"): + column_id = value.get("ColumnRef") + simple_value = value.find("./SimpleValue") + if simple_value is None: + continue + + example_values[column_id].append(simple_value.text) + + filterable_columns = {k: list(v) for k, v in filterable_columns.items() if len(v) <= 5} + + return columns, example_values, filterable_columns diff --git a/erpnext/edi/doctype/code_list/code_list_list.js b/erpnext/edi/doctype/code_list/code_list_list.js new file mode 100644 index 00000000000..08125de2903 --- /dev/null +++ b/erpnext/edi/doctype/code_list/code_list_list.js @@ -0,0 +1,8 @@ +frappe.listview_settings["Code List"] = { + onload: function (listview) { + listview.page.add_inner_button(__("Import Genericode File"), function () { + erpnext.edi.import_genericode(listview); + }); + }, + hide_name_column: true, +}; diff --git a/erpnext/edi/doctype/code_list/test_code_list.py b/erpnext/edi/doctype/code_list/test_code_list.py new file mode 100644 index 00000000000..d37b1ee8f5a --- /dev/null +++ b/erpnext/edi/doctype/code_list/test_code_list.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +# import frappe +from frappe.tests.utils import FrappeTestCase + + +class TestCodeList(FrappeTestCase): + pass diff --git a/erpnext/edi/doctype/common_code/__init__.py b/erpnext/edi/doctype/common_code/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/edi/doctype/common_code/common_code.js b/erpnext/edi/doctype/common_code/common_code.js new file mode 100644 index 00000000000..646d5c85b74 --- /dev/null +++ b/erpnext/edi/doctype/common_code/common_code.js @@ -0,0 +1,8 @@ +// Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +// frappe.ui.form.on("Common Code", { +// refresh(frm) { + +// }, +// }); diff --git a/erpnext/edi/doctype/common_code/common_code.json b/erpnext/edi/doctype/common_code/common_code.json new file mode 100644 index 00000000000..b2cb43fa575 --- /dev/null +++ b/erpnext/edi/doctype/common_code/common_code.json @@ -0,0 +1,103 @@ +{ + "actions": [], + "autoname": "hash", + "creation": "2024-09-29 07:01:18.133067", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "code_list", + "title", + "common_code", + "description", + "column_break_wxsw", + "additional_data", + "section_break_rhgh", + "applies_to" + ], + "fields": [ + { + "fieldname": "code_list", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Code List", + "options": "Code List", + "reqd": 1, + "search_index": 1 + }, + { + "fieldname": "title", + "fieldtype": "Data", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Title", + "length": 300, + "reqd": 1 + }, + { + "fieldname": "column_break_wxsw", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_rhgh", + "fieldtype": "Section Break" + }, + { + "fieldname": "applies_to", + "fieldtype": "Table", + "label": "Applies To", + "options": "Dynamic Link" + }, + { + "fieldname": "common_code", + "fieldtype": "Data", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Common Code", + "length": 300, + "reqd": 1, + "search_index": 1 + }, + { + "fieldname": "additional_data", + "fieldtype": "Code", + "label": "Additional Data", + "max_height": "190px", + "read_only": 1 + }, + { + "fieldname": "description", + "fieldtype": "Small Text", + "in_list_view": 1, + "label": "Description", + "max_height": "60px" + } + ], + "links": [], + "modified": "2024-11-06 07:46:17.175687", + "modified_by": "Administrator", + "module": "EDI", + "name": "Common Code", + "naming_rule": "Random", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "search_fields": "common_code,description", + "show_title_field_in_link": 1, + "sort_field": "creation", + "sort_order": "DESC", + "states": [], + "title_field": "title" +} \ No newline at end of file diff --git a/erpnext/edi/doctype/common_code/common_code.py b/erpnext/edi/doctype/common_code/common_code.py new file mode 100644 index 00000000000..d558b2d282f --- /dev/null +++ b/erpnext/edi/doctype/common_code/common_code.py @@ -0,0 +1,114 @@ +# Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +import hashlib + +import frappe +from frappe import _ +from frappe.model.document import Document +from frappe.utils.data import get_link_to_form +from lxml import etree + + +class CommonCode(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from frappe.core.doctype.dynamic_link.dynamic_link import DynamicLink + from frappe.types import DF + + additional_data: DF.Code | None + applies_to: DF.Table[DynamicLink] + code_list: DF.Link + common_code: DF.Data + description: DF.SmallText | None + title: DF.Data + # end: auto-generated types + + def validate(self): + self.validate_distinct_references() + + def validate_distinct_references(self): + """Ensure no two Common Codes of the same Code List are linked to the same document.""" + for link in self.applies_to: + existing_links = frappe.get_all( + "Common Code", + filters=[ + ["name", "!=", self.name], + ["code_list", "=", self.code_list], + ["Dynamic Link", "link_doctype", "=", link.link_doctype], + ["Dynamic Link", "link_name", "=", link.link_name], + ], + fields=["name", "common_code"], + ) + + if existing_links: + existing_link = existing_links[0] + frappe.throw( + _("{0} {1} is already linked to Common Code {2}.").format( + link.link_doctype, + link.link_name, + get_link_to_form("Common Code", existing_link["name"], existing_link["common_code"]), + ) + ) + + def from_genericode(self, column_map: dict, xml_element: "etree.Element"): + """Populate the Common Code document from a genericode XML element + + Args: + column_map (dict): A mapping of column names to XML column references. Keys: code, title, description + code (etree.Element): The XML element representing a code in the genericode file + """ + title_column = column_map.get("title") + code_column = column_map["code"] + description_column = column_map.get("description") + + self.common_code = xml_element.find(f"./Value[@ColumnRef='{code_column}']/SimpleValue").text + + if title_column: + simple_value_title = xml_element.find(f"./Value[@ColumnRef='{title_column}']/SimpleValue") + self.title = simple_value_title.text if simple_value_title is not None else self.common_code + + if description_column: + simple_value_descr = xml_element.find(f"./Value[@ColumnRef='{description_column}']/SimpleValue") + self.description = simple_value_descr.text if simple_value_descr is not None else None + + self.additional_data = etree.tostring(xml_element, encoding="unicode", pretty_print=True) + + +def simple_hash(input_string, length=6): + return hashlib.blake2b(input_string.encode(), digest_size=length // 2).hexdigest() + + +def import_genericode(code_list: str, file_name: str, column_map: dict, filters: dict | None = None): + """Import genericode file and create Common Code entries""" + file_path = frappe.utils.file_manager.get_file_path(file_name) + parser = etree.XMLParser(remove_blank_text=True) + tree = etree.parse(file_path, parser=parser) + root = tree.getroot() + + # Construct the XPath expression + xpath_expr = ".//SimpleCodeList/Row" + filter_conditions = [ + f"Value[@ColumnRef='{column_ref}']/SimpleValue='{value}'" for column_ref, value in filters.items() + ] + if filter_conditions: + xpath_expr += "[" + " and ".join(filter_conditions) + "]" + + elements = root.xpath(xpath_expr) + total_elements = len(elements) + for i, xml_element in enumerate(elements, start=1): + common_code: "CommonCode" = frappe.new_doc("Common Code") + common_code.code_list = code_list + common_code.from_genericode(column_map, xml_element) + common_code.save() + frappe.publish_progress(i / total_elements * 100, title=_("Importing Common Codes")) + + return total_elements + + +def on_doctype_update(): + frappe.db.add_index("Common Code", ["code_list", "common_code"]) diff --git a/erpnext/edi/doctype/common_code/common_code_list.js b/erpnext/edi/doctype/common_code/common_code_list.js new file mode 100644 index 00000000000..de1b665b161 --- /dev/null +++ b/erpnext/edi/doctype/common_code/common_code_list.js @@ -0,0 +1,8 @@ +frappe.listview_settings["Common Code"] = { + onload: function (listview) { + listview.page.add_inner_button(__("Import Genericode File"), function () { + erpnext.edi.import_genericode(listview); + }); + }, + hide_name_column: true, +}; diff --git a/erpnext/edi/doctype/common_code/test_common_code.py b/erpnext/edi/doctype/common_code/test_common_code.py new file mode 100644 index 00000000000..e9c67b2cc82 --- /dev/null +++ b/erpnext/edi/doctype/common_code/test_common_code.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +# import frappe +from frappe.tests.utils import FrappeTestCase + + +class TestCommonCode(FrappeTestCase): + pass diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 30121e5f2cb..882adec4d51 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -35,6 +35,14 @@ doctype_js = { "Newsletter": "public/js/newsletter.js", "Contact": "public/js/contact.js", } +doctype_list_js = { + "Code List": [ + "edi/doctype/code_list/code_list_import.js", + ], + "Common Code": [ + "edi/doctype/code_list/code_list_import.js", + ], +} override_doctype_class = {"Address": "erpnext.accounts.custom.address.ERPNextAddress"} diff --git a/erpnext/modules.txt b/erpnext/modules.txt index c53cdf467d2..b8b12e90fb0 100644 --- a/erpnext/modules.txt +++ b/erpnext/modules.txt @@ -18,3 +18,4 @@ Communication Telephony Bulk Transaction Subcontracting +EDI \ No newline at end of file From 8cc59e3be71f005e2c970eba8a654bbd11eb95d3 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 18 Nov 2024 16:39:06 +0530 Subject: [PATCH 570/734] refactor: update test case (cherry picked from commit 4aab6f55f5bb47719fad95e366516a42b7f859e3) --- .../doctype/payment_entry/test_payment_entry.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py index c61598b54a5..8758110534f 100644 --- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py @@ -957,9 +957,12 @@ class TestPaymentEntry(FrappeTestCase): self.assertEqual(flt(expected_party_account_balance, 2), flt(party_account_balance, 2)) def test_gl_of_multi_currency_payment_transaction(self): - from erpnext.setup.doctype.currency_exchange.test_currency_exchange import save_new_records + from erpnext.setup.doctype.currency_exchange.test_currency_exchange import ( + save_new_records, + test_records, + ) - save_new_records(self.globalTestRecords["Currency Exchange"]) + save_new_records(test_records) paid_from = create_account( parent_account="Current Liabilities - _TC", account_name="_Test Cash USD", @@ -995,8 +998,8 @@ class TestPaymentEntry(FrappeTestCase): .run() ) expected_gl_entries = ( - (paid_from, 0.0, 8440.0, 0.0, 100.0, 0.0, 8440.0), - ("_Test Payable USD - _TC", 8440.0, 0.0, 100.0, 0.0, 8440.0, 0.0), + (paid_from, 0.0, 8440.0, 0.0, 100.0, 0.0, 100.0), + ("_Test Payable USD - _TC", 8440.0, 0.0, 100.0, 0.0, 100.0, 0.0), ) self.assertEqual(gl_entries, expected_gl_entries) From 0ea6691189cff2141a914667adc023b3226ed23b Mon Sep 17 00:00:00 2001 From: Ismail Arif <38789073+ismxilxrif@users.noreply.github.com> Date: Tue, 19 Nov 2024 09:56:35 +0800 Subject: [PATCH 571/734] chore: update oldest_items.json, change owner back to administrator Signed-off-by: Ismail Arif <38789073+ismxilxrif@users.noreply.github.com> (cherry picked from commit 7ceb24fb4cbff39b3f0e1cf2dd2db8f965aefe7e) --- erpnext/stock/dashboard_chart/oldest_items/oldest_items.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/dashboard_chart/oldest_items/oldest_items.json b/erpnext/stock/dashboard_chart/oldest_items/oldest_items.json index 46ad308f230..a55fe7a6a6c 100644 --- a/erpnext/stock/dashboard_chart/oldest_items/oldest_items.json +++ b/erpnext/stock/dashboard_chart/oldest_items/oldest_items.json @@ -15,7 +15,7 @@ "module": "Stock", "name": "Oldest Items", "number_of_groups": 0, - "owner": "rohitw1991@gmail.com", + "owner": "Administrator", "report_name": "Stock Ageing", "roles": [], "timeseries": 0, From c2748e923e84713c9341aa1763129911bec65e9b Mon Sep 17 00:00:00 2001 From: ajiragroup <108009061+ajiragroup@users.noreply.github.com> Date: Thu, 14 Nov 2024 16:13:54 +0545 Subject: [PATCH 572/734] refactor: update label and description on short year checkbox Is short/long year. (cherry picked from commit 1d6b9b405f0fef99d59330c7e1bd601f9e72c0b9) --- erpnext/accounts/doctype/fiscal_year/fiscal_year.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/fiscal_year/fiscal_year.json b/erpnext/accounts/doctype/fiscal_year/fiscal_year.json index 66db37fe13b..de8f0337a3d 100644 --- a/erpnext/accounts/doctype/fiscal_year/fiscal_year.json +++ b/erpnext/accounts/doctype/fiscal_year/fiscal_year.json @@ -72,10 +72,10 @@ }, { "default": "0", - "description": "Less than 12 months.", + "description": "More/Less than 12 months.", "fieldname": "is_short_year", "fieldtype": "Check", - "label": "Is Short Year", + "label": "Is Short/Long Year", "set_only_once": 1 } ], From 608966158aa81a4e71d31afec019c8913f89926a Mon Sep 17 00:00:00 2001 From: Nikolas Beckel <15029707+nikolas-beckel@users.noreply.github.com> Date: Tue, 19 Nov 2024 12:40:55 +0100 Subject: [PATCH 573/734] fix: check if pricing rule matches with coupon code (#44104) * fix: check if pricing rule matches with coupon code * fix: correct linting error (cherry picked from commit 9d31bf7647882d5118617c86161f82429778c919) --- .../accounts/doctype/pricing_rule/pricing_rule.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py index 72ad0d096bc..73cb2483811 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py @@ -451,6 +451,16 @@ def get_pricing_rule_for_item(args, doc=None, for_validate=False): get_pricing_rule_items(pricing_rule, other_items=fetch_other_item) or [] ) + if pricing_rule.coupon_code_based == 1: + if not args.coupon_code: + return item_details + + coupon_code = frappe.db.get_value( + doctype="Coupon Code", filters={"pricing_rule": pricing_rule.name}, fieldname="name" + ) + if args.coupon_code != coupon_code: + continue + if pricing_rule.get("suggestion"): continue @@ -476,9 +486,6 @@ def get_pricing_rule_for_item(args, doc=None, for_validate=False): pricing_rule.apply_rule_on_other_items ) - if pricing_rule.coupon_code_based == 1 and args.coupon_code is None: - return item_details - if not pricing_rule.validate_applied_rule: if pricing_rule.price_or_product_discount == "Price": apply_price_discount_rule(pricing_rule, item_details, args) From 7abcfca1cb1ec5d71126f5e9c9fd93bb51f65f17 Mon Sep 17 00:00:00 2001 From: Corentin Forler Date: Sat, 16 Nov 2024 14:35:58 +0100 Subject: [PATCH 574/734] fix(setup): Fix typo in COA setup (cherry picked from commit a245cc6b07b08a00088f09f19159ba97370695a0) --- erpnext/setup/setup_wizard/operations/taxes_setup.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/setup/setup_wizard/operations/taxes_setup.py b/erpnext/setup/setup_wizard/operations/taxes_setup.py index 0faebb6ab4c..6561f386c55 100644 --- a/erpnext/setup/setup_wizard/operations/taxes_setup.py +++ b/erpnext/setup/setup_wizard/operations/taxes_setup.py @@ -87,7 +87,10 @@ def simple_to_detailed(templates): def from_detailed_data(company_name, data): """Create Taxes and Charges Templates from detailed data.""" charts_company_name = company_name - if frappe.db.get_value("Company", company_name, "create_chart_of_accounts_based_on"): + if ( + frappe.db.get_value("Company", company_name, "create_chart_of_accounts_based_on") + == "Existing Company" + ): charts_company_name = frappe.db.get_value("Company", company_name, "existing_company") coa_name = frappe.db.get_value("Company", charts_company_name, "chart_of_accounts") coa_data = data.get("chart_of_accounts", {}) From 6bff9d39e312dc5a5f157def06cbfaa8b5a54fa6 Mon Sep 17 00:00:00 2001 From: RitvikSardana Date: Tue, 16 Apr 2024 12:20:16 +0530 Subject: [PATCH 575/734] fix: remove validate_name_in_customer function (cherry picked from commit 2b32d3644f909ba34a429c607335a0b7b54d7953) --- erpnext/setup/doctype/customer_group/customer_group.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/erpnext/setup/doctype/customer_group/customer_group.py b/erpnext/setup/doctype/customer_group/customer_group.py index 06f2f43374e..5dd0fd02011 100644 --- a/erpnext/setup/doctype/customer_group/customer_group.py +++ b/erpnext/setup/doctype/customer_group/customer_group.py @@ -71,14 +71,9 @@ class CustomerGroup(NestedSet): ) def on_update(self): - self.validate_name_with_customer() super().on_update() self.validate_one_root() - def validate_name_with_customer(self): - if frappe.db.exists("Customer", self.name): - frappe.msgprint(_("A customer with the same name already exists"), raise_exception=1) - def get_parent_customer_groups(customer_group): lft, rgt = frappe.db.get_value("Customer Group", customer_group, ["lft", "rgt"]) From ad0c65500a70c5dd13e245dd41037fdfa5ac34ea Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 19 Nov 2024 22:59:16 +0530 Subject: [PATCH 576/734] fix: update project cost from timesheet (backport #44211) (#44212) fix: update project cost from timesheet (#44211) (cherry picked from commit b21fb8f8b63aa517f6a0da8ec70e151213abc10b) Co-authored-by: rohitwaghchaure --- erpnext/projects/doctype/timesheet/timesheet.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/erpnext/projects/doctype/timesheet/timesheet.py b/erpnext/projects/doctype/timesheet/timesheet.py index 70494e9e966..7ab661c8822 100644 --- a/erpnext/projects/doctype/timesheet/timesheet.py +++ b/erpnext/projects/doctype/timesheet/timesheet.py @@ -169,10 +169,14 @@ class Timesheet(Document): task.save() tasks.append(data.task) - elif data.project and data.project not in projects: - frappe.get_doc("Project", data.project).update_project() + if data.project and data.project not in projects: projects.append(data.project) + for project in projects: + project_doc = frappe.get_doc("Project", project) + project_doc.update_project() + project_doc.save() + def validate_dates(self): for data in self.time_logs: if data.from_time and data.to_time and time_diff_in_hours(data.to_time, data.from_time) < 0: From f3c3f170a7d385aa971063d53de2650d6b3b6557 Mon Sep 17 00:00:00 2001 From: ljain112 Date: Tue, 19 Nov 2024 14:32:43 +0530 Subject: [PATCH 577/734] fix: validate sales team to ensure all sales person are enabled (cherry picked from commit 548dbb33eb0ce911aef70efdf686338185575620) --- erpnext/controllers/selling_controller.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index 49710de06f6..89a2111d50f 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -167,6 +167,9 @@ class SellingController(StockController): total = 0.0 sales_team = self.get("sales_team") + + self.validate_sales_team(sales_team) + for sales_person in sales_team: self.round_floats_in(sales_person) @@ -186,6 +189,20 @@ class SellingController(StockController): if sales_team and total != 100.0: throw(_("Total allocated percentage for sales team should be 100")) + def validate_sales_team(self, sales_team): + sales_persons = [d.sales_person for d in sales_team] + + if not sales_persons: + return + + sales_person_status = frappe.db.get_all( + "Sales Person", filters={"name": ["in", sales_persons]}, fields=["name", "enabled"] + ) + + for row in sales_person_status: + if not row.enabled: + frappe.throw(_("Sales Person {0} is disabled.").format(row.name)) + def validate_max_discount(self): for d in self.get("items"): if d.item_code: From 83b9680318700fab2f60f7f81f1e96125ad78f56 Mon Sep 17 00:00:00 2001 From: ljain112 Date: Tue, 19 Nov 2024 12:38:34 +0530 Subject: [PATCH 578/734] fix: disable conversion to user tz for sales order calender (cherry picked from commit cdf098c1939b4811da003d663f475c0c1d4d4899) --- erpnext/selling/doctype/sales_order/sales_order.py | 5 ++++- erpnext/selling/doctype/sales_order/sales_order_calendar.js | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 88528d7178f..d8b3f3c6dcf 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -1230,7 +1230,10 @@ def get_events(start, end, filters=None): """, {"start": start, "end": end}, as_dict=True, - update={"allDay": 0}, + update={ + "allDay": 0, + "convertToUserTz": 0, + }, ) return data diff --git a/erpnext/selling/doctype/sales_order/sales_order_calendar.js b/erpnext/selling/doctype/sales_order/sales_order_calendar.js index f4c0e2ba72a..59a32bde7a3 100644 --- a/erpnext/selling/doctype/sales_order/sales_order_calendar.js +++ b/erpnext/selling/doctype/sales_order/sales_order_calendar.js @@ -8,6 +8,7 @@ frappe.views.calendar["Sales Order"] = { id: "name", title: "customer_name", allDay: "allDay", + convertToUserTz: "convertToUserTz", }, gantt: true, filters: [ From 514fe69b6520b5a041889473312754ad178f2a10 Mon Sep 17 00:00:00 2001 From: Abdeali Chharchhoda Date: Tue, 19 Nov 2024 18:00:35 +0530 Subject: [PATCH 579/734] refactor: Update `Payment Request` search query in PE's reference (cherry picked from commit 4ab3499a173753a7bf8e8863edbcc4394c1f2469) --- .../doctype/payment_request/payment_request.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index 577a1ea2426..ae974a8cf0e 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -945,17 +945,18 @@ def validate_payment(doc, method=None): @frappe.whitelist() def get_open_payment_requests_query(doctype, txt, searchfield, start, page_len, filters): # permission checks in `get_list()` - reference_doctype = filters.get("reference_doctype") - reference_name = filters.get("reference_doctype") + filters = frappe._dict(filters) - if not reference_doctype or not reference_name: + if not filters.reference_doctype or not filters.reference_name: return [] + if txt: + filters.name = ["like", f"%{txt}%"] + open_payment_requests = frappe.get_list( "Payment Request", filters={ - "reference_doctype": filters["reference_doctype"], - "reference_name": filters["reference_name"], + **filters, "status": ["!=", "Paid"], "outstanding_amount": ["!=", 0], # for compatibility with old data "docstatus": 1, From 4335659905e3c430c221941a4657b9e60af36e60 Mon Sep 17 00:00:00 2001 From: "Nihantra C. Patel" <141945075+Nihantra-Patel@users.noreply.github.com> Date: Wed, 20 Nov 2024 12:00:41 +0530 Subject: [PATCH 580/734] fix: non group pos warehouse (cherry picked from commit d526be03946923004582fcbc9044c18734ca36f0) --- erpnext/selling/page/point_of_sale/pos_item_details.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/selling/page/point_of_sale/pos_item_details.js b/erpnext/selling/page/point_of_sale/pos_item_details.js index 4673eaa9858..ad4b4cd15be 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_details.js +++ b/erpnext/selling/page/point_of_sale/pos_item_details.js @@ -272,7 +272,7 @@ erpnext.PointOfSale.ItemDetails = class { }; this.warehouse_control.df.get_query = () => { return { - filters: { company: this.events.get_frm().doc.company }, + filters: { company: this.events.get_frm().doc.company, is_group: 0 }, }; }; this.warehouse_control.refresh(); From 74838394183432a4aa2cf839e844aa1735727e35 Mon Sep 17 00:00:00 2001 From: ljain112 Date: Mon, 18 Nov 2024 20:08:11 +0530 Subject: [PATCH 581/734] fix: payment reco for jv with negative dr or cr amount (cherry picked from commit fee79b944575f6797ae3846e7394158a1daaac2d) --- .../payment_reconciliation.py | 16 +++++++++------- erpnext/accounts/utils.py | 10 ++++++++++ 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index bbbb3c978ff..68e9eef711a 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -211,12 +211,14 @@ class PaymentReconciliation(Document): if self.get("cost_center"): conditions.append(jea.cost_center == self.cost_center) - dr_or_cr = ( - "credit_in_account_currency" - if erpnext.get_party_account_type(self.party_type) == "Receivable" - else "debit_in_account_currency" - ) - conditions.append(jea[dr_or_cr].gt(0)) + account_type = erpnext.get_party_account_type(self.party_type) + + if account_type == "Receivable": + dr_or_cr = jea.credit_in_account_currency - jea.debit_in_account_currency + elif account_type == "Payable": + dr_or_cr = jea.debit_in_account_currency - jea.credit_in_account_currency + + conditions.append(dr_or_cr.gt(0)) if self.bank_cash_account: conditions.append(jea.against_account.like(f"%%{self.bank_cash_account}%%")) @@ -231,7 +233,7 @@ class PaymentReconciliation(Document): je.posting_date, je.remark.as_("remarks"), jea.name.as_("reference_row"), - jea[dr_or_cr].as_("amount"), + dr_or_cr.as_("amount"), jea.is_advance, jea.exchange_rate, jea.account_currency.as_("currency"), diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 37dbaef51a8..144039b794f 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -630,6 +630,16 @@ def update_reference_in_journal_entry(d, journal_entry, do_not_save=False): if jv_detail.get("reference_type") in ["Sales Order", "Purchase Order"]: update_advance_paid.append((jv_detail.reference_type, jv_detail.reference_name)) + rev_dr_or_cr = ( + "debit_in_account_currency" + if d["dr_or_cr"] == "credit_in_account_currency" + else "credit_in_account_currency" + ) + if jv_detail.get(rev_dr_or_cr): + d["dr_or_cr"] = rev_dr_or_cr + d["allocated_amount"] = d["allocated_amount"] * -1 + d["unadjusted_amount"] = d["unadjusted_amount"] * -1 + if flt(d["unadjusted_amount"]) - flt(d["allocated_amount"]) != 0: # adjust the unreconciled balance amount_in_account_currency = flt(d["unadjusted_amount"]) - flt(d["allocated_amount"]) From 234741f35f6575e8f61d3a22d00774b203254f8a Mon Sep 17 00:00:00 2001 From: ljain112 Date: Tue, 19 Nov 2024 12:17:35 +0530 Subject: [PATCH 582/734] fix: added test cases (cherry picked from commit 6f9ea6422d9b03dea918267f6272b7bd36f96375) --- .../test_payment_reconciliation.py | 130 ++++++++++++++++++ 1 file changed, 130 insertions(+) diff --git a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py index 1b19949bb7e..3f0fb29d671 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py @@ -632,6 +632,42 @@ class TestPaymentReconciliation(FrappeTestCase): self.assertEqual(len(pr.get("invoices")), 0) self.assertEqual(len(pr.get("payments")), 0) + def test_negative_debit_or_credit_journal_against_invoice(self): + transaction_date = nowdate() + amount = 100 + si = self.create_sales_invoice(qty=1, rate=amount, posting_date=transaction_date) + + # credit debtors account to record a payment + je = self.create_journal_entry(self.bank, self.debit_to, amount, transaction_date) + je.accounts[1].party_type = "Customer" + je.accounts[1].party = self.customer + je.accounts[1].credit_in_account_currency = 0 + je.accounts[1].debit_in_account_currency = -1 * amount + je.save() + je.submit() + + pr = self.create_payment_reconciliation() + + pr.get_unreconciled_entries() + invoices = [x.as_dict() for x in pr.get("invoices")] + payments = [x.as_dict() for x in pr.get("payments")] + pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments})) + + # Difference amount should not be calculated for base currency accounts + for row in pr.allocation: + self.assertEqual(flt(row.get("difference_amount")), 0.0) + + pr.reconcile() + + # assert outstanding + si.reload() + self.assertEqual(si.status, "Paid") + self.assertEqual(si.outstanding_amount, 0) + + # check PR tool output + self.assertEqual(len(pr.get("invoices")), 0) + self.assertEqual(len(pr.get("payments")), 0) + def test_journal_against_journal(self): transaction_date = nowdate() sales = "Sales - _PR" @@ -954,6 +990,100 @@ class TestPaymentReconciliation(FrappeTestCase): frappe.db.get_value("Journal Entry", jea_parent.parent, "voucher_type"), "Exchange Gain Or Loss" ) + def test_difference_amount_via_negative_debit_or_credit_journal_entry(self): + # Make Sale Invoice + si = self.create_sales_invoice( + qty=1, rate=100, posting_date=nowdate(), do_not_save=True, do_not_submit=True + ) + si.customer = self.customer4 + si.currency = "EUR" + si.conversion_rate = 85 + si.debit_to = self.debtors_eur + si.save().submit() + + # Make payment using Journal Entry + je1 = self.create_journal_entry("HDFC - _PR", self.debtors_eur, 100, nowdate()) + je1.multi_currency = 1 + je1.accounts[0].exchange_rate = 1 + je1.accounts[0].credit_in_account_currency = -8000 + je1.accounts[0].credit = -8000 + je1.accounts[0].debit_in_account_currency = 0 + je1.accounts[0].debit = 0 + je1.accounts[1].party_type = "Customer" + je1.accounts[1].party = self.customer4 + je1.accounts[1].exchange_rate = 80 + je1.accounts[1].credit_in_account_currency = 100 + je1.accounts[1].credit = 8000 + je1.accounts[1].debit_in_account_currency = 0 + je1.accounts[1].debit = 0 + je1.save() + je1.submit() + + je2 = self.create_journal_entry("HDFC - _PR", self.debtors_eur, 200, nowdate()) + je2.multi_currency = 1 + je2.accounts[0].exchange_rate = 1 + je2.accounts[0].credit_in_account_currency = -16000 + je2.accounts[0].credit = -16000 + je2.accounts[0].debit_in_account_currency = 0 + je2.accounts[0].debit = 0 + je2.accounts[1].party_type = "Customer" + je2.accounts[1].party = self.customer4 + je2.accounts[1].exchange_rate = 80 + je2.accounts[1].credit_in_account_currency = 200 + je1.accounts[1].credit = 16000 + je1.accounts[1].debit_in_account_currency = 0 + je1.accounts[1].debit = 0 + je2.save() + je2.submit() + + pr = self.create_payment_reconciliation() + pr.party = self.customer4 + pr.receivable_payable_account = self.debtors_eur + pr.get_unreconciled_entries() + + self.assertEqual(len(pr.invoices), 1) + self.assertEqual(len(pr.payments), 2) + + # Test exact payment allocation + invoices = [x.as_dict() for x in pr.invoices] + payments = [pr.payments[0].as_dict()] + pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments})) + + self.assertEqual(pr.allocation[0].allocated_amount, 100) + self.assertEqual(pr.allocation[0].difference_amount, -500) + + # Test partial payment allocation (with excess payment entry) + pr.set("allocation", []) + pr.get_unreconciled_entries() + invoices = [x.as_dict() for x in pr.invoices] + payments = [pr.payments[1].as_dict()] + pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments})) + pr.allocation[0].difference_account = "Exchange Gain/Loss - _PR" + + self.assertEqual(pr.allocation[0].allocated_amount, 100) + self.assertEqual(pr.allocation[0].difference_amount, -500) + + # Check if difference journal entry gets generated for difference amount after reconciliation + pr.reconcile() + total_credit_amount = frappe.db.get_all( + "Journal Entry Account", + {"account": self.debtors_eur, "docstatus": 1, "reference_name": si.name}, + "sum(credit) as amount", + group_by="reference_name", + )[0].amount + + # total credit includes the exchange gain/loss amount + self.assertEqual(flt(total_credit_amount, 2), 8500) + + jea_parent = frappe.db.get_all( + "Journal Entry Account", + filters={"account": self.debtors_eur, "docstatus": 1, "reference_name": si.name, "credit": 500}, + fields=["parent"], + )[0] + self.assertEqual( + frappe.db.get_value("Journal Entry", jea_parent.parent, "voucher_type"), "Exchange Gain Or Loss" + ) + def test_difference_amount_via_payment_entry(self): # Make Sale Invoice si = self.create_sales_invoice( From 80f0d5b5ec8d20d70ec091650dba8ece6f2851c6 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 20 Nov 2024 12:51:29 +0530 Subject: [PATCH 583/734] chore: resolve conflict --- .../selling/doctype/quotation/quotation.json | 38 ------------------- 1 file changed, 38 deletions(-) diff --git a/erpnext/selling/doctype/quotation/quotation.json b/erpnext/selling/doctype/quotation/quotation.json index 8d6b96c8e67..d6ee87b5dee 100644 --- a/erpnext/selling/doctype/quotation/quotation.json +++ b/erpnext/selling/doctype/quotation/quotation.json @@ -1070,57 +1070,19 @@ "fieldname": "named_place", "fieldtype": "Data", "label": "Named Place" -<<<<<<< HEAD -======= - }, - { - "fieldname": "utm_campaign", - "fieldtype": "Link", - "label": "Campaign", - "oldfieldname": "campaign", - "oldfieldtype": "Link", - "options": "UTM Campaign", - "print_hide": 1 - }, - { - "fieldname": "utm_source", - "fieldtype": "Link", - "label": "Source", - "oldfieldname": "source", - "oldfieldtype": "Select", - "options": "UTM Source", - "print_hide": 1 - }, - { - "fieldname": "utm_medium", - "fieldtype": "Link", - "label": "Medium", - "options": "UTM Medium", - "print_hide": 1 - }, - { - "fieldname": "utm_content", - "fieldtype": "Data", - "label": "Content", - "print_hide": 1 }, { "default": "0", "fieldname": "disable_rounded_total", "fieldtype": "Check", "label": "Disable Rounded Total" ->>>>>>> f8524d526b (fix: added disable_rounded_total field) } ], "icon": "fa fa-shopping-cart", "idx": 82, "is_submittable": 1, "links": [], -<<<<<<< HEAD - "modified": "2024-03-20 16:04:21.567847", -======= "modified": "2024-11-07 18:37:11.715189", ->>>>>>> f8524d526b (fix: added disable_rounded_total field) "modified_by": "Administrator", "module": "Selling", "name": "Quotation", From ccf99cf98524a37e6111818d1d3d32a30866d19a Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 20 Nov 2024 08:41:13 +0000 Subject: [PATCH 584/734] chore(release): Bumped to Version 15.43.0 # [15.43.0](https://github.com/frappe/erpnext/compare/v15.42.0...v15.43.0) (2024-11-20) ### Bug Fixes * added disable_rounded_total field ([c98a0cc](https://github.com/frappe/erpnext/commit/c98a0ccd1dc5007941544ca49e0c2ad68d050323)) * added test cases ([234741f](https://github.com/frappe/erpnext/commit/234741f35f6575e8f61d3a22d00774b203254f8a)) * apply posting date sorting to invoices in Payment Reconciliation similar to payments ([41c8cfa](https://github.com/frappe/erpnext/commit/41c8cfac733104c69c1f30b921c667031c146024)) * broken apply on other item pricing rule ([5d6451f](https://github.com/frappe/erpnext/commit/5d6451fca7de3a9deba0df0a8568dc98f8c1936f)) * broken UI on currency exchange ([f460391](https://github.com/frappe/erpnext/commit/f4603910e4d4c11ca7cc48d8a44af5a35c98395d)) * bulk update invoice remarks during site upgrade ([cc07402](https://github.com/frappe/erpnext/commit/cc07402b5e5f2acdb5d4626a38108799cdd09f68)), closes [#43634](https://github.com/frappe/erpnext/issues/43634) * check if pricing rule matches with coupon code ([#44104](https://github.com/frappe/erpnext/issues/44104)) ([6089661](https://github.com/frappe/erpnext/commit/608966158aa81a4e71d31afec019c8913f89926a)) * correctly set 'cannot_add_rows' property on allocations table field ([c59a778](https://github.com/frappe/erpnext/commit/c59a7785039cdedc62c3321706799a35892ee7b3)) * disable conversion to user tz for sales order calender ([83b9680](https://github.com/frappe/erpnext/commit/83b9680318700fab2f60f7f81f1e96125ad78f56)) * Get Entries not showing accounts with no gain or loss in Exchange Rate Revaluation issue ([1fe5342](https://github.com/frappe/erpnext/commit/1fe534290dcea0c689c48530b8a765c9111b71f6)) * linters ([381101f](https://github.com/frappe/erpnext/commit/381101f55235e2aa49e2b8229b4250eb3ea2474a)) * non group pos warehouse ([4335659](https://github.com/frappe/erpnext/commit/4335659905e3c430c221941a4657b9e60af36e60)) * payment reco for jv with negative dr or cr amount ([7483839](https://github.com/frappe/erpnext/commit/74838394183432a4aa2cf839e844aa1735727e35)) * remove trailing whitespace ([5bd633b](https://github.com/frappe/erpnext/commit/5bd633b40f86d46ebc3bce68e0dc65c88489d81e)) * remove validate_name_in_customer function ([6bff9d3](https://github.com/frappe/erpnext/commit/6bff9d39e312dc5a5f157def06cbfaa8b5a54fa6)) * set conversion factor before applying price list ([5848de7](https://github.com/frappe/erpnext/commit/5848de76ea3049cf3b25c88cc0a510f7712945c5)) * set debit in transaction currency in GL Entry ([c0d3f8c](https://github.com/frappe/erpnext/commit/c0d3f8cbbe347f7326b4acead396e28daec01fc0)) * set default party type in Payment Entry ([08f6cee](https://github.com/frappe/erpnext/commit/08f6ceeb5001d5e9159c7a9c0134467121298da0)) * **setup:** Fix typo in COA setup ([7abcfca](https://github.com/frappe/erpnext/commit/7abcfca1cb1ec5d71126f5e9c9fd93bb51f65f17)) * stock ledger variance report filter options (backport [#44137](https://github.com/frappe/erpnext/issues/44137)) ([#44150](https://github.com/frappe/erpnext/issues/44150)) ([b6fe1f5](https://github.com/frappe/erpnext/commit/b6fe1f5842671421de52e015c968d2740aaad79e)) * update project cost from timesheet (backport [#44211](https://github.com/frappe/erpnext/issues/44211)) ([#44212](https://github.com/frappe/erpnext/issues/44212)) ([ad0c655](https://github.com/frappe/erpnext/commit/ad0c65500a70c5dd13e245dd41037fdfa5ac34ea)) * validate sales team to ensure all sales person are enabled ([f3c3f17](https://github.com/frappe/erpnext/commit/f3c3f170a7d385aa971063d53de2650d6b3b6557)) * validation for serial no (backport [#44133](https://github.com/frappe/erpnext/issues/44133)) ([#44151](https://github.com/frappe/erpnext/issues/44151)) ([725d107](https://github.com/frappe/erpnext/commit/725d107288675867dd14571b84945f00d4def45e)) ### Features * inventory dimension for rejected materials (backport [#44156](https://github.com/frappe/erpnext/issues/44156)) ([#44165](https://github.com/frappe/erpnext/issues/44165)) ([d61f696](https://github.com/frappe/erpnext/commit/d61f696f85084bfec858a32c4d819f021e0a6bae)) * new DocTypes "Code List" and "Common Code" (backport [#43425](https://github.com/frappe/erpnext/issues/43425)) ([#44173](https://github.com/frappe/erpnext/issues/44173)) ([b130e20](https://github.com/frappe/erpnext/commit/b130e2065b5998ef42b754442fda9c5bf4cf60d9)) * round off for opening entries ([8e6249d](https://github.com/frappe/erpnext/commit/8e6249d361bb36a4fd1ef0cfa7333265bb8933d5)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 83fb70c9e8f..3ab0bf95207 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.42.0" +__version__ = "15.43.0" def get_default_company(user=None): From 8b15a965dd0ffea82c1d55a2893a79cf1fd243bb Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 21 Nov 2024 14:51:33 +0530 Subject: [PATCH 585/734] fix: no permission to read Doctype (backport #44256) (#44258) fix: no permission to read Doctype (#44256) (cherry picked from commit 57293aa18a2d34b66b35dc346ec84c259c6d6c11) Co-authored-by: rohitwaghchaure --- .../report/serial_and_batch_summary/serial_and_batch_summary.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/erpnext/stock/report/serial_and_batch_summary/serial_and_batch_summary.py b/erpnext/stock/report/serial_and_batch_summary/serial_and_batch_summary.py index 15d9a12bc65..486828af1cc 100644 --- a/erpnext/stock/report/serial_and_batch_summary/serial_and_batch_summary.py +++ b/erpnext/stock/report/serial_and_batch_summary/serial_and_batch_summary.py @@ -106,8 +106,6 @@ def get_columns(filters, data): { "label": _("Voucher Type"), "fieldname": "voucher_type", - "fieldtype": "Link", - "options": "DocType", "width": 120, }, { From d4f0512a10550914b2aad1795fe8692658aab67a Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 21 Nov 2024 14:52:37 +0530 Subject: [PATCH 586/734] fix: added Stock UOM field for RM in work order (backport #44185) (#44237) * fix: added Stock UOM field for RM in work order (#44185) fix: added UOM field for RM in work order (cherry picked from commit cc571aca8f56310b7f87244ebdbdb91fd6b57222) # Conflicts: # erpnext/manufacturing/doctype/work_order_item/work_order_item.json # erpnext/patches.txt * chore: fix conflicts * chore: fix conflicts --------- Co-authored-by: rohitwaghchaure --- .../doctype/work_order_item/work_order_item.json | 13 +++++++++++-- .../doctype/work_order_item/work_order_item.py | 2 ++ erpnext/patches.txt | 1 + .../v14_0/update_stock_uom_in_work_order_item.py | 15 +++++++++++++++ 4 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 erpnext/patches/v14_0/update_stock_uom_in_work_order_item.py diff --git a/erpnext/manufacturing/doctype/work_order_item/work_order_item.json b/erpnext/manufacturing/doctype/work_order_item/work_order_item.json index 0f4d693544e..580168180a7 100644 --- a/erpnext/manufacturing/doctype/work_order_item/work_order_item.json +++ b/erpnext/manufacturing/doctype/work_order_item/work_order_item.json @@ -15,6 +15,7 @@ "include_item_in_manufacturing", "qty_section", "required_qty", + "stock_uom", "rate", "amount", "column_break_11", @@ -138,11 +139,19 @@ "in_list_view": 1, "label": "Returned Qty ", "read_only": 1 + }, + { + "fetch_from": "item_code.stock_uom", + "fieldname": "stock_uom", + "fieldtype": "Link", + "label": "Stock UOM", + "options": "UOM", + "read_only": 1 } ], "istable": 1, "links": [], - "modified": "2024-02-11 15:45:32.318374", + "modified": "2024-11-19 15:48:16.823384", "modified_by": "Administrator", "module": "Manufacturing", "name": "Work Order Item", @@ -153,4 +162,4 @@ "sort_order": "DESC", "states": [], "track_changes": 1 -} \ No newline at end of file +} diff --git a/erpnext/manufacturing/doctype/work_order_item/work_order_item.py b/erpnext/manufacturing/doctype/work_order_item/work_order_item.py index 267ca5d21de..04f78eb1c3b 100644 --- a/erpnext/manufacturing/doctype/work_order_item/work_order_item.py +++ b/erpnext/manufacturing/doctype/work_order_item/work_order_item.py @@ -25,6 +25,7 @@ class WorkOrderItem(Document): item_code: DF.Link | None item_name: DF.Data | None operation: DF.Link | None + operation_row_id: DF.Int parent: DF.Data parentfield: DF.Data parenttype: DF.Data @@ -32,6 +33,7 @@ class WorkOrderItem(Document): required_qty: DF.Float returned_qty: DF.Float source_warehouse: DF.Link | None + stock_uom: DF.Link | None transferred_qty: DF.Float # end: auto-generated types diff --git a/erpnext/patches.txt b/erpnext/patches.txt index ab416d9d6c3..2716aa9883b 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -382,3 +382,4 @@ erpnext.patches.v15_0.link_purchase_item_to_asset_doc erpnext.patches.v14_0.update_currency_exchange_settings_for_frankfurter erpnext.patches.v15_0.update_task_assignee_email_field_in_asset_maintenance_log erpnext.patches.v15_0.update_sub_voucher_type_in_gl_entries +erpnext.patches.v14_0.update_stock_uom_in_work_order_item diff --git a/erpnext/patches/v14_0/update_stock_uom_in_work_order_item.py b/erpnext/patches/v14_0/update_stock_uom_in_work_order_item.py new file mode 100644 index 00000000000..d611065d8f1 --- /dev/null +++ b/erpnext/patches/v14_0/update_stock_uom_in_work_order_item.py @@ -0,0 +1,15 @@ +import frappe + + +def execute(): + frappe.db.sql( + """ + UPDATE + `tabWork Order Item`, `tabItem` + SET + `tabWork Order Item`.stock_uom = `tabItem`.stock_uom + WHERE + `tabWork Order Item`.item_code = `tabItem`.name + AND `tabWork Order Item`.docstatus = 1 + """ + ) From b047425a6f9d3d3827226acce488f87e413c2aad Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 21 Nov 2024 14:51:33 +0530 Subject: [PATCH 587/734] fix: no permission to read Doctype (backport #44256) (#44258) fix: no permission to read Doctype (#44256) (cherry picked from commit 57293aa18a2d34b66b35dc346ec84c259c6d6c11) Co-authored-by: rohitwaghchaure (cherry picked from commit 8b15a965dd0ffea82c1d55a2893a79cf1fd243bb) --- .../report/serial_and_batch_summary/serial_and_batch_summary.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/erpnext/stock/report/serial_and_batch_summary/serial_and_batch_summary.py b/erpnext/stock/report/serial_and_batch_summary/serial_and_batch_summary.py index 15d9a12bc65..486828af1cc 100644 --- a/erpnext/stock/report/serial_and_batch_summary/serial_and_batch_summary.py +++ b/erpnext/stock/report/serial_and_batch_summary/serial_and_batch_summary.py @@ -106,8 +106,6 @@ def get_columns(filters, data): { "label": _("Voucher Type"), "fieldname": "voucher_type", - "fieldtype": "Link", - "options": "DocType", "width": 120, }, { From e0060f8ffea85a694c782cfb14da9c0d6f5ab7bb Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Thu, 21 Nov 2024 09:44:42 +0000 Subject: [PATCH 588/734] chore(release): Bumped to Version 15.43.1 ## [15.43.1](https://github.com/frappe/erpnext/compare/v15.43.0...v15.43.1) (2024-11-21) ### Bug Fixes * no permission to read Doctype (backport [#44256](https://github.com/frappe/erpnext/issues/44256)) ([#44258](https://github.com/frappe/erpnext/issues/44258)) ([b047425](https://github.com/frappe/erpnext/commit/b047425a6f9d3d3827226acce488f87e413c2aad)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 3ab0bf95207..b2c3e75f87c 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.43.0" +__version__ = "15.43.1" def get_default_company(user=None): From 0ffeb9f6adbc581ecc9a0403c2680079213f9d93 Mon Sep 17 00:00:00 2001 From: venkat102 Date: Mon, 18 Nov 2024 15:42:01 +0530 Subject: [PATCH 589/734] fix: include current invoice amount when tax_on_excess_amount is checked (cherry picked from commit b74f2896cdfc0571d352930d5d84737d0211cff3) --- .../tax_withholding_category/tax_withholding_category.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py index 46a0916c79f..69c7eb1153c 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -568,7 +568,7 @@ def get_tds_amount(ldc, parties, inv, tax_details, vouchers): if (cumulative_threshold and supp_credit_amt >= cumulative_threshold) and cint( tax_details.tax_on_excess_amount ): - supp_credit_amt = net_total - cumulative_threshold + supp_credit_amt = net_total + tax_withholding_net_total - cumulative_threshold if ldc and is_valid_certificate(ldc, inv.get("posting_date") or inv.get("transaction_date"), 0): tds_amount = get_lower_deduction_amount( From 08b896fc2cee813170e477548e5cf6c194b8b8e8 Mon Sep 17 00:00:00 2001 From: venkat102 Date: Mon, 18 Nov 2024 15:48:39 +0530 Subject: [PATCH 590/734] test: add unit test for tax on excess amount (cherry picked from commit 4820273595be0f49f021685931f34972c39f1f70) --- .../test_tax_withholding_category.py | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py index 2b7ae5fd689..f9f34380d55 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py @@ -167,6 +167,45 @@ class TestTaxWithholdingCategory(FrappeTestCase): for d in reversed(invoices): d.cancel() + def test_cumulative_threshold_with_tax_on_excess_amount(self): + invoices = [] + frappe.db.set_value("Supplier", "Test TDS Supplier3", "tax_withholding_category", "New TDS Category") + + # Invoice with tax and without exceeding single and cumulative thresholds + for _ in range(2): + pi = create_purchase_invoice(supplier="Test TDS Supplier3", rate=10000, do_not_save=True) + pi.apply_tds = 1 + pi.append( + "taxes", + { + "category": "Total", + "charge_type": "Actual", + "account_head": "_Test Account VAT - _TC", + "cost_center": "Main - _TC", + "tax_amount": 500, + "description": "Test", + "add_deduct_tax": "Add", + }, + ) + pi.save() + pi.submit() + invoices.append(pi) + + # Third Invoice exceeds single threshold and not exceeding cumulative threshold + pi1 = create_purchase_invoice(supplier="Test TDS Supplier3", rate=20000) + pi1.apply_tds = 1 + pi1.save() + pi1.submit() + invoices.append(pi1) + + # Cumulative threshold is 10,000 + # Threshold calculation should be only on the third invoice + self.assertTrue(len(pi1.taxes) > 0) + self.assertEqual(pi1.taxes[0].tax_amount, 1000) + + for d in reversed(invoices): + d.cancel() + def test_cumulative_threshold_tcs(self): frappe.db.set_value( "Customer", "Test TCS Customer", "tax_withholding_category", "Cumulative Threshold TCS" From 52e1551c234b03d7fc4f7da65761ea4c1c2a2ed0 Mon Sep 17 00:00:00 2001 From: venkat102 Date: Mon, 18 Nov 2024 15:42:01 +0530 Subject: [PATCH 591/734] fix: include current invoice amount when tax_on_excess_amount is checked (cherry picked from commit b74f2896cdfc0571d352930d5d84737d0211cff3) --- .../tax_withholding_category/tax_withholding_category.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py index 46a0916c79f..69c7eb1153c 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -568,7 +568,7 @@ def get_tds_amount(ldc, parties, inv, tax_details, vouchers): if (cumulative_threshold and supp_credit_amt >= cumulative_threshold) and cint( tax_details.tax_on_excess_amount ): - supp_credit_amt = net_total - cumulative_threshold + supp_credit_amt = net_total + tax_withholding_net_total - cumulative_threshold if ldc and is_valid_certificate(ldc, inv.get("posting_date") or inv.get("transaction_date"), 0): tds_amount = get_lower_deduction_amount( From 2d284de4264b708c569ab2739f5ec5b06e87ebaa Mon Sep 17 00:00:00 2001 From: venkat102 Date: Mon, 18 Nov 2024 15:48:39 +0530 Subject: [PATCH 592/734] test: add unit test for tax on excess amount (cherry picked from commit 4820273595be0f49f021685931f34972c39f1f70) --- .../test_tax_withholding_category.py | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py index 2b7ae5fd689..f9f34380d55 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py @@ -167,6 +167,45 @@ class TestTaxWithholdingCategory(FrappeTestCase): for d in reversed(invoices): d.cancel() + def test_cumulative_threshold_with_tax_on_excess_amount(self): + invoices = [] + frappe.db.set_value("Supplier", "Test TDS Supplier3", "tax_withholding_category", "New TDS Category") + + # Invoice with tax and without exceeding single and cumulative thresholds + for _ in range(2): + pi = create_purchase_invoice(supplier="Test TDS Supplier3", rate=10000, do_not_save=True) + pi.apply_tds = 1 + pi.append( + "taxes", + { + "category": "Total", + "charge_type": "Actual", + "account_head": "_Test Account VAT - _TC", + "cost_center": "Main - _TC", + "tax_amount": 500, + "description": "Test", + "add_deduct_tax": "Add", + }, + ) + pi.save() + pi.submit() + invoices.append(pi) + + # Third Invoice exceeds single threshold and not exceeding cumulative threshold + pi1 = create_purchase_invoice(supplier="Test TDS Supplier3", rate=20000) + pi1.apply_tds = 1 + pi1.save() + pi1.submit() + invoices.append(pi1) + + # Cumulative threshold is 10,000 + # Threshold calculation should be only on the third invoice + self.assertTrue(len(pi1.taxes) > 0) + self.assertEqual(pi1.taxes[0].tax_amount, 1000) + + for d in reversed(invoices): + d.cancel() + def test_cumulative_threshold_tcs(self): frappe.db.set_value( "Customer", "Test TCS Customer", "tax_withholding_category", "Cumulative Threshold TCS" From 81f1f1f1bbc7c8947776981873dd8b8794aa4d1b Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Fri, 22 Nov 2024 06:00:44 +0000 Subject: [PATCH 593/734] chore(release): Bumped to Version 15.43.2 ## [15.43.2](https://github.com/frappe/erpnext/compare/v15.43.1...v15.43.2) (2024-11-22) ### Bug Fixes * include current invoice amount when tax_on_excess_amount is checked ([52e1551](https://github.com/frappe/erpnext/commit/52e1551c234b03d7fc4f7da65761ea4c1c2a2ed0)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index b2c3e75f87c..9c78c0a1e2c 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.43.1" +__version__ = "15.43.2" def get_default_company(user=None): From 8b02402f6295296e9465d813996c90e08ffa889e Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Mon, 18 Nov 2024 17:59:55 +0530 Subject: [PATCH 594/734] fix: patch (#44191) (cherry picked from commit 495528a758019ae2ba51d44649227c4d9cb889af) --- .../patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py b/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py index c0d715063a8..2441075de30 100644 --- a/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py +++ b/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py @@ -38,7 +38,7 @@ def execute(): data = frappe.db.sql( """ SELECT - name, item_code, warehouse, voucher_type, voucher_no, posting_date, posting_time, company + name, item_code, warehouse, voucher_type, voucher_no, posting_date, posting_time, company, creation FROM `tabStock Ledger Entry` WHERE @@ -67,6 +67,7 @@ def execute(): "voucher_type": d.voucher_type, "voucher_no": d.voucher_no, "sle_id": d.name, + "creation": d.creation, }, allow_negative_stock=True, ) From 7f8334f29a83a6e85fe32dcc9a2247c90046acee Mon Sep 17 00:00:00 2001 From: Smit Vora Date: Thu, 21 Nov 2024 18:30:37 +0530 Subject: [PATCH 595/734] fix: toggle debit credit amounts for transaction currency too; minor refactor (cherry picked from commit 8e759c32c431834f8e77c1665cf8c36ba881582b) --- erpnext/accounts/general_ledger.py | 78 ++++++++++++------------------ 1 file changed, 30 insertions(+), 48 deletions(-) diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py index 7d7c6f49e12..19da840f543 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -315,66 +315,48 @@ def check_if_in_list(gle, gl_map): def toggle_debit_credit_if_negative(gl_map): + debit_credit_field_map = { + "debit": "credit", + "debit_in_account_currency": "credit_in_account_currency", + "debit_in_transaction_currency": "credit_in_transaction_currency", + } + for entry in gl_map: # toggle debit, credit if negative entry - if flt(entry.debit) < 0 and flt(entry.credit) < 0 and flt(entry.debit) == flt(entry.credit): - entry.credit *= -1 - entry.debit *= -1 + for debit_field, credit_field in debit_credit_field_map.items(): + debit = flt(entry.get(debit_field)) + credit = flt(entry.get(credit_field)) - if ( - flt(entry.debit_in_account_currency) < 0 - and flt(entry.credit_in_account_currency) < 0 - and flt(entry.debit_in_account_currency) == flt(entry.credit_in_account_currency) - ): - entry.credit_in_account_currency *= -1 - entry.debit_in_account_currency *= -1 + if debit < 0 and credit < 0 and debit == credit: + debit *= -1 + credit *= -1 - if flt(entry.debit) < 0: - entry.credit = flt(entry.credit) - flt(entry.debit) - entry.debit = 0.0 + if debit < 0: + credit = credit - debit + debit = 0.0 - if flt(entry.debit_in_account_currency) < 0: - entry.credit_in_account_currency = flt(entry.credit_in_account_currency) - flt( - entry.debit_in_account_currency - ) - entry.debit_in_account_currency = 0.0 + if credit < 0: + debit = debit - credit + credit = 0.0 - if flt(entry.credit) < 0: - entry.debit = flt(entry.debit) - flt(entry.credit) - entry.credit = 0.0 + # update net values + # In some scenarios net value needs to be shown in the ledger + # This method updates net values as debit or credit + if entry.post_net_value and debit and credit: + if debit > credit: + debit = debit - credit + credit = 0.0 - if flt(entry.credit_in_account_currency) < 0: - entry.debit_in_account_currency = flt(entry.debit_in_account_currency) - flt( - entry.credit_in_account_currency - ) - entry.credit_in_account_currency = 0.0 + else: + credit = credit - debit + debit = 0.0 - update_net_values(entry) + entry[debit_field] = debit + entry[credit_field] = credit return gl_map -def update_net_values(entry): - # In some scenarios net value needs to be shown in the ledger - # This method updates net values as debit or credit - if entry.post_net_value and entry.debit and entry.credit: - if entry.debit > entry.credit: - entry.debit = entry.debit - entry.credit - entry.debit_in_account_currency = ( - entry.debit_in_account_currency - entry.credit_in_account_currency - ) - entry.credit = 0 - entry.credit_in_account_currency = 0 - else: - entry.credit = entry.credit - entry.debit - entry.credit_in_account_currency = ( - entry.credit_in_account_currency - entry.debit_in_account_currency - ) - - entry.debit = 0 - entry.debit_in_account_currency = 0 - - def save_entries(gl_map, adv_adj, update_outstanding, from_repost=False): if not from_repost: validate_cwip_accounts(gl_map) From 11deff98d9ad32e29c4eb35a414478d51260848f Mon Sep 17 00:00:00 2001 From: Smit Vora Date: Thu, 21 Nov 2024 19:44:02 +0530 Subject: [PATCH 596/734] test: test case for toggling debit and credit if negative (cherry picked from commit a10e175bc926219a6d534eb5a1e006e66bf7b8fd) --- .../journal_entry/test_journal_entry.py | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py index 3d4b182d52d..8f4c4e3ccda 100644 --- a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py @@ -515,6 +515,55 @@ class TestJournalEntry(unittest.TestCase): self.assertEqual(row.debit_in_account_currency, 100) self.assertEqual(row.credit_in_account_currency, 100) + def test_toggle_debit_credit_if_negative(self): + from erpnext.accounts.general_ledger import process_gl_map + + # Create JV with defaut cost center - _Test Cost Center + frappe.db.set_single_value("Accounts Settings", "merge_similar_account_heads", 0) + + jv = frappe.new_doc("Journal Entry") + jv.posting_date = nowdate() + jv.company = "_Test Company" + jv.user_remark = "test" + jv.extend( + "accounts", + [ + { + "account": "_Test Cash - _TC", + "debit": 100 * -1, + "debit_in_account_currency": 100 * -1, + "exchange_rate": 1, + }, + { + "account": "_Test Bank - _TC", + "credit": 100 * -1, + "credit_in_account_currency": 100 * -1, + "exchange_rate": 1, + }, + ], + ) + + jv.flags.ignore_validate = True + jv.save() + + self.assertEqual(len(jv.accounts), 2) + + gl_map = jv.build_gl_map() + + for row in gl_map: + if row.account == "_Test Cash - _TC": + self.assertEqual(row.debit, 100 * -1) + self.assertEqual(row.debit_in_account_currency, 100 * -1) + self.assertEqual(row.debit_in_transaction_currency, 100 * -1) + + gl_map = process_gl_map(gl_map, False) + + for row in gl_map: + if row.account == "_Test Cash - _TC": + self.assertEqual(row.credit, 100) + self.assertEqual(row.credit_in_account_currency, 100) + self.assertEqual(row.credit_in_transaction_currency, 100) + def test_transaction_exchange_rate_on_journals(self): jv = make_journal_entry("_Test Bank - _TC", "_Test Receivable USD - _TC", 100, save=False) jv.accounts[0].update({"debit_in_account_currency": 8500, "exchange_rate": 1}) From 5f752e29f9e07300c21a63c69024befb837da1fc Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Mon, 18 Nov 2024 17:59:55 +0530 Subject: [PATCH 597/734] fix: patch (#44191) (cherry picked from commit 495528a758019ae2ba51d44649227c4d9cb889af) (cherry picked from commit 8b02402f6295296e9465d813996c90e08ffa889e) --- .../patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py b/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py index c0d715063a8..2441075de30 100644 --- a/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py +++ b/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py @@ -38,7 +38,7 @@ def execute(): data = frappe.db.sql( """ SELECT - name, item_code, warehouse, voucher_type, voucher_no, posting_date, posting_time, company + name, item_code, warehouse, voucher_type, voucher_no, posting_date, posting_time, company, creation FROM `tabStock Ledger Entry` WHERE @@ -67,6 +67,7 @@ def execute(): "voucher_type": d.voucher_type, "voucher_no": d.voucher_no, "sle_id": d.name, + "creation": d.creation, }, allow_negative_stock=True, ) From 17e00b397f3761e3d937d06cf1e05f927da123d1 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Fri, 22 Nov 2024 09:19:30 +0000 Subject: [PATCH 598/734] chore(release): Bumped to Version 15.43.3 ## [15.43.3](https://github.com/frappe/erpnext/compare/v15.43.2...v15.43.3) (2024-11-22) ### Bug Fixes * patch ([#44191](https://github.com/frappe/erpnext/issues/44191)) ([5f752e2](https://github.com/frappe/erpnext/commit/5f752e29f9e07300c21a63c69024befb837da1fc)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 9c78c0a1e2c..babb8246bdb 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.43.2" +__version__ = "15.43.3" def get_default_company(user=None): From a4398626f66bae98b520d75f72e9d00222789174 Mon Sep 17 00:00:00 2001 From: venkat102 Date: Fri, 22 Nov 2024 11:53:07 +0530 Subject: [PATCH 599/734] fix: filter with item group only if it is mentioned in pos profile (cherry picked from commit 09641073e3ff013fe49f22127e3164b22639c41e) --- erpnext/selling/page/point_of_sale/point_of_sale.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.py b/erpnext/selling/page/point_of_sale/point_of_sale.py index 28eadec9bc5..206e51bbc5a 100644 --- a/erpnext/selling/page/point_of_sale/point_of_sale.py +++ b/erpnext/selling/page/point_of_sale/point_of_sale.py @@ -96,6 +96,8 @@ def search_by_term(search_term, warehouse, price_list): def filter_result_items(result, pos_profile): if result and result.get("items"): pos_item_groups = frappe.db.get_all("POS Item Group", {"parent": pos_profile}, pluck="item_group") + if not pos_item_groups: + return result["items"] = [item for item in result.get("items") if item.get("item_group") in pos_item_groups] From 4856a9633e2e6cd46cf001799f01c7d290514b6a Mon Sep 17 00:00:00 2001 From: venkat102 Date: Thu, 21 Nov 2024 18:27:15 +0530 Subject: [PATCH 600/734] fix: make free qty round on large transaction qty (cherry picked from commit f9b816538584810aeb0721296899f7bb19dc1dd0) --- erpnext/accounts/doctype/pricing_rule/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index 572529580e8..087e2bffa4b 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -655,7 +655,7 @@ def get_product_discount_rule(pricing_rule, item_details, args=None, doc=None): if transaction_qty: qty = flt(transaction_qty) * qty / pricing_rule.recurse_for if pricing_rule.round_free_qty: - qty = math.floor(qty) + qty = (flt(transaction_qty) // pricing_rule.recurse_for) * (pricing_rule.free_qty or 1) if not qty: return From db1bc8a3db238d0729790c71226aef7136d2b230 Mon Sep 17 00:00:00 2001 From: venkat102 Date: Thu, 21 Nov 2024 18:37:14 +0530 Subject: [PATCH 601/734] test: add unit test to validate free qty round on large transaction qty (cherry picked from commit 013a6fc6ec2ae28720845f973276db3739fc8fd5) --- .../doctype/pricing_rule/test_pricing_rule.py | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py index b4c47a26eb1..965e2b267a3 100644 --- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py @@ -1137,6 +1137,45 @@ class TestPricingRule(FrappeTestCase): so.save() self.assertEqual(len(so.items), 1) + def test_pricing_rule_for_product_free_item_round_free_qty(self): + frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule") + test_record = { + "doctype": "Pricing Rule", + "title": "_Test Pricing Rule", + "apply_on": "Item Code", + "currency": "USD", + "items": [ + { + "item_code": "_Test Item", + } + ], + "selling": 1, + "rate": 0, + "min_qty": 100, + "max_qty": 0, + "price_or_product_discount": "Product", + "same_item": 1, + "free_qty": 10, + "round_free_qty": 1, + "is_recursive": 1, + "recurse_for": 100, + "company": "_Test Company", + } + frappe.get_doc(test_record.copy()).insert() + + # With pricing rule + so = make_sales_order(item_code="_Test Item", qty=100) + so.load_from_db() + self.assertEqual(so.items[1].is_free_item, 1) + self.assertEqual(so.items[1].item_code, "_Test Item") + self.assertEqual(so.items[1].qty, 10) + + so = make_sales_order(item_code="_Test Item", qty=150) + so.load_from_db() + self.assertEqual(so.items[1].is_free_item, 1) + self.assertEqual(so.items[1].item_code, "_Test Item") + self.assertEqual(so.items[1].qty, 10) + def test_apply_multiple_pricing_rules_for_discount_percentage_and_amount(self): frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 1") frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 2") From af74a3c32fb5ec83439ed3417706aa3d1a0deefd Mon Sep 17 00:00:00 2001 From: vimalraj27 Date: Wed, 20 Nov 2024 13:27:21 +0530 Subject: [PATCH 602/734] fix: Get submitted documents in validate_for_closed_fiscal_year (cherry picked from commit c607e5f940d7032cc7c11d870d3bf99b30bcef4c) --- .../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 f37e542dd89..0bd9a2a0515 100644 --- a/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py +++ b/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py @@ -45,7 +45,7 @@ class RepostAccountingLedger(Document): latest_pcv = ( frappe.db.get_all( "Period Closing Voucher", - filters={"company": self.company}, + filters={"company": self.company, "docstatus": 1}, order_by="period_end_date desc", pluck="period_end_date", limit=1, From 97f2341b98ea0cdb84cbf7dbaebdd32b208824b6 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Sun, 24 Nov 2024 22:23:11 +0530 Subject: [PATCH 603/734] fix: not able to fetch batch item price --- erpnext/stock/get_item_details.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 50db899433f..d5d492a2c93 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -940,11 +940,12 @@ def get_batch_based_item_price(params, item_code) -> float: params = parse_json(params) item_price = get_item_price(params, item_code, force_batch_no=True) + if not item_price: item_price = get_item_price(params, item_code, ignore_party=True, force_batch_no=True) - if item_price and item_price[0].uom == params.get("uom"): - return item_price[0].price_list_rate + if item_price and item_price[0][2] == params.get("uom"): + return item_price[0][1] return 0.0 From 8928e062b18db3729b16f93cd2b2c5420c24ada3 Mon Sep 17 00:00:00 2001 From: Abdeali Chharchhoda Date: Fri, 22 Nov 2024 19:50:52 +0530 Subject: [PATCH 604/734] refactor: added translate function for some columns of report (cherry picked from commit e545c913b529b2d4e8ee4d3efc8f8cc87ee567a0) --- .../accounts_receivable/accounts_receivable.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index 984d77dbad2..a58d4ab72ca 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -1013,15 +1013,15 @@ class ReceivablePayableReport: def get_columns(self): self.columns = [] - self.add_column("Posting Date", fieldtype="Date") + self.add_column(_("Posting Date"), fieldtype="Date") self.add_column( - label="Party Type", + label=_("Party Type"), fieldname="party_type", fieldtype="Data", width=100, ) self.add_column( - label="Party", + label=_("Party"), fieldname="party", fieldtype="Dynamic Link", options="party_type", @@ -1037,10 +1037,10 @@ class ReceivablePayableReport: if self.party_naming_by == "Naming Series": if self.account_type == "Payable": - label = "Supplier Name" + label = _("Supplier Name") fieldname = "supplier_name" else: - label = "Customer Name" + label = _("Customer Name") fieldname = "customer_name" self.add_column( label=label, @@ -1066,7 +1066,7 @@ class ReceivablePayableReport: width=180, ) - self.add_column(label="Due Date", fieldtype="Date") + self.add_column(label=_("Due Date"), fieldtype="Date") if self.account_type == "Payable": self.add_column(label=_("Bill No"), fieldname="bill_no", fieldtype="Data") From 0469b0d1ecca4b5d13232d3ba5a7f4275a582da4 Mon Sep 17 00:00:00 2001 From: l0gesh29 Date: Fri, 15 Nov 2024 12:54:04 +0530 Subject: [PATCH 605/734] feat: Show Aggregate Value from Subsidiary Companies (cherry picked from commit c23af6af41471b1d1e7b200d293ea4da95bbbb8e) # Conflicts: # erpnext/selling/report/sales_analytics/sales_analytics.js --- .../report/sales_analytics/sales_analytics.js | 21 ++++ .../report/sales_analytics/sales_analytics.py | 112 +++++++++++++----- 2 files changed, 101 insertions(+), 32 deletions(-) diff --git a/erpnext/selling/report/sales_analytics/sales_analytics.js b/erpnext/selling/report/sales_analytics/sales_analytics.js index a01103afb96..95b742ea584 100644 --- a/erpnext/selling/report/sales_analytics/sales_analytics.js +++ b/erpnext/selling/report/sales_analytics/sales_analytics.js @@ -73,6 +73,27 @@ frappe.query_reports["Sales Analytics"] = { default: "Monthly", reqd: 1, }, +<<<<<<< HEAD +======= + { + fieldname: "curves", + label: __("Curves"), + fieldtype: "Select", + options: [ + { value: "select", label: __("Select") }, + { value: "all", label: __("All") }, + { value: "non-zeros", label: __("Non-Zeros") }, + { value: "total", label: __("Total Only") }, + ], + default: "select", + reqd: 1, + }, + { + fieldname: "show_aggregate_value_from_subsidiary_companies", + label: __("Show Aggregate Value from Subsidiary Companies"), + fieldtype: "Check", + }, +>>>>>>> c23af6af41 (feat: Show Aggregate Value from Subsidiary Companies) ], get_datatable_options(options) { return Object.assign(options, { diff --git a/erpnext/selling/report/sales_analytics/sales_analytics.py b/erpnext/selling/report/sales_analytics/sales_analytics.py index 27d2e6e555e..262687ef19d 100644 --- a/erpnext/selling/report/sales_analytics/sales_analytics.py +++ b/erpnext/selling/report/sales_analytics/sales_analytics.py @@ -4,6 +4,8 @@ import frappe from frappe import _, scrub +from frappe.query_builder import DocType +from frappe.query_builder.functions import IfNull from frappe.utils import add_days, add_to_date, flt, getdate from erpnext.accounts.utils import get_fiscal_year @@ -37,7 +39,26 @@ class Analytics: ] self.get_period_date_ranges() + def update_company_list_for_parent_company(self): + company_list = [self.filters.get("company")] + + selected_company = self.filters.get("company") + if ( + selected_company + and self.filters.get("show_aggregate_value_from_subsidiary_companies") + and frappe.db.get_value("Company", selected_company, "is_group") + ): + lft, rgt = frappe.db.get_value("Company", selected_company, ["lft", "rgt"]) + child_companies = frappe.db.get_list( + "Company", filters={"lft": [">", lft], "rgt": ["<", rgt]}, pluck="name" + ) + + company_list.extend(child_companies) + + self.filters["company"] = company_list + def run(self): + self.update_company_list_for_parent_company() self.get_columns() self.get_data() self.get_chart_data() @@ -123,14 +144,23 @@ class Analytics: else: value_field = "total_qty" - self.entries = frappe.db.sql( - """ select s.order_type as entity, s.{value_field} as value_field, s.{date_field} - from `tab{doctype}` s where s.docstatus = 1 and s.company = %s and s.{date_field} between %s and %s - and ifnull(s.order_type, '') != '' order by s.order_type - """.format(date_field=self.date_field, value_field=value_field, doctype=self.filters.doc_type), - (self.filters.company, self.filters.from_date, self.filters.to_date), - as_dict=1, - ) + doctype = DocType(self.filters.doc_type) + + self.entries = ( + frappe.qb.from_(doctype) + .select( + doctype.order_type.as_("entity"), + doctype[self.date_field], + doctype[value_field].as_("value_field"), + ) + .where( + (doctype.docstatus == 1) + & (doctype.company.isin(self.filters.company)) + & (doctype[self.date_field].between(self.filters.from_date, self.filters.to_date)) + & (IfNull(doctype.order_type, "") != "") + ) + .orderby(doctype.order_type) + ).run(as_dict=True) self.get_teams() @@ -152,7 +182,7 @@ class Analytics: fields=[entity, entity_name, value_field, self.date_field], filters={ "docstatus": 1, - "company": self.filters.company, + "company": ["in", self.filters.company], self.date_field: ("between", [self.filters.from_date, self.filters.to_date]), }, ) @@ -167,16 +197,26 @@ class Analytics: else: value_field = "stock_qty" - self.entries = frappe.db.sql( - """ - select i.item_code as entity, i.item_name as entity_name, i.stock_uom, i.{value_field} as value_field, s.{date_field} - from `tab{doctype} Item` i , `tab{doctype}` s - where s.name = i.parent and i.docstatus = 1 and s.company = %s - and s.{date_field} between %s and %s - """.format(date_field=self.date_field, value_field=value_field, doctype=self.filters.doc_type), - (self.filters.company, self.filters.from_date, self.filters.to_date), - as_dict=1, - ) + doctype = DocType(self.filters.doc_type) + doctype_item = DocType(f"{self.filters.doc_type} Item") + + self.entries = ( + frappe.qb.from_(doctype_item) + .join(doctype) + .on(doctype.name == doctype_item.parent) + .select( + doctype_item.item_code.as_("entity"), + doctype_item.item_name.as_("entity_name"), + doctype_item.stock_uom, + doctype_item[value_field].as_("value_field"), + doctype[self.date_field], + ) + .where( + (doctype_item.docstatus == 1) + & (doctype.company.isin(self.filters.company)) + & (doctype[self.date_field].between(self.filters.from_date, self.filters.to_date)) + ) + ).run(as_dict=True) self.entity_names = {} for d in self.entries: @@ -201,7 +241,7 @@ class Analytics: fields=[entity_field, value_field, self.date_field], filters={ "docstatus": 1, - "company": self.filters.company, + "company": ["in", self.filters.company], self.date_field: ("between", [self.filters.from_date, self.filters.to_date]), }, ) @@ -213,16 +253,24 @@ class Analytics: else: value_field = "qty" - self.entries = frappe.db.sql( - f""" - select i.item_group as entity, i.{value_field} as value_field, s.{self.date_field} - from `tab{self.filters.doc_type} Item` i , `tab{self.filters.doc_type}` s - where s.name = i.parent and i.docstatus = 1 and s.company = %s - and s.{self.date_field} between %s and %s - """, - (self.filters.company, self.filters.from_date, self.filters.to_date), - as_dict=1, - ) + doctype = DocType(self.filters.doc_type) + doctype_item = DocType(f"{self.filters.doc_type} Item") + + self.entries = ( + frappe.qb.from_(doctype_item) + .join(doctype) + .on(doctype.name == doctype_item.parent) + .select( + doctype_item.item_group.as_("entity"), + doctype_item[value_field].as_("value_field"), + doctype[self.date_field], + ) + .where( + (doctype_item.docstatus == 1) + & (doctype.company.isin(self.filters.company)) + & (doctype[self.date_field].between(self.filters.from_date, self.filters.to_date)) + ) + ).run(as_dict=True) self.get_groups() @@ -239,7 +287,7 @@ class Analytics: fields=[entity, value_field, self.date_field], filters={ "docstatus": 1, - "company": self.filters.company, + "company": ["in", self.filters.company], "project": ["!=", ""], self.date_field: ("between", [self.filters.from_date, self.filters.to_date]), }, @@ -312,7 +360,7 @@ class Analytics: str(((posting_date.month - 1) // 3) + 1), str(posting_date.year) ) else: - year = get_fiscal_year(posting_date, company=self.filters.company) + year = get_fiscal_year(posting_date, company=self.filters.company[0]) period = str(year[0]) return period From 072c5b775377b9e9826215f3a1dbee2fdf728f44 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 25 Nov 2024 12:28:02 +0530 Subject: [PATCH 606/734] chore: resolve conflict --- .../report/sales_analytics/sales_analytics.js | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/erpnext/selling/report/sales_analytics/sales_analytics.js b/erpnext/selling/report/sales_analytics/sales_analytics.js index 95b742ea584..7c5d5436877 100644 --- a/erpnext/selling/report/sales_analytics/sales_analytics.js +++ b/erpnext/selling/report/sales_analytics/sales_analytics.js @@ -73,27 +73,11 @@ frappe.query_reports["Sales Analytics"] = { default: "Monthly", reqd: 1, }, -<<<<<<< HEAD -======= - { - fieldname: "curves", - label: __("Curves"), - fieldtype: "Select", - options: [ - { value: "select", label: __("Select") }, - { value: "all", label: __("All") }, - { value: "non-zeros", label: __("Non-Zeros") }, - { value: "total", label: __("Total Only") }, - ], - default: "select", - reqd: 1, - }, { fieldname: "show_aggregate_value_from_subsidiary_companies", label: __("Show Aggregate Value from Subsidiary Companies"), fieldtype: "Check", }, ->>>>>>> c23af6af41 (feat: Show Aggregate Value from Subsidiary Companies) ], get_datatable_options(options) { return Object.assign(options, { From 8cd455b050c9687bc1c5cb91dd6360035cea4f30 Mon Sep 17 00:00:00 2001 From: Sugesh393 Date: Fri, 22 Nov 2024 11:36:57 +0530 Subject: [PATCH 607/734] fix: set price_list_currency only if it exists (cherry picked from commit f0b9cb4019ff6a13fc2166f5023ecae9c4c65af3) --- erpnext/public/js/controllers/transaction.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index ca1b1c8c590..77db3292eb7 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -1904,8 +1904,14 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe callback: function(r) { if (!r.exc) { frappe.run_serially([ - () => me.frm.set_value("price_list_currency", r.message.parent.price_list_currency), - () => me.frm.set_value("plc_conversion_rate", r.message.parent.plc_conversion_rate), + () => { + if (r.message.parent.price_list_currency) + me.frm.set_value("price_list_currency", r.message.parent.price_list_currency); + }, + () => { + if (r.message.parent.plc_conversion_rate) + me.frm.set_value("plc_conversion_rate", r.message.parent.plc_conversion_rate); + }, () => { if(args.items.length) { me._set_values_for_item_list(r.message.children); From 20d0e95d7c3de85e0a0364dd9e7954f3aa6dbeee Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 25 Nov 2024 20:29:13 +0530 Subject: [PATCH 608/734] feat: available qty at company in sales transactions (backport #44260) (#44325) * feat: available qty at company in sales transactions (cherry picked from commit d8b9aef14f0a615555067258889bdb09cd6d117e) # Conflicts: # erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json # erpnext/selling/doctype/quotation_item/quotation_item.json # erpnext/selling/doctype/sales_order_item/sales_order_item.json # erpnext/stock/doctype/delivery_note_item/delivery_note_item.json * chore: fix conflicts * chore: fix conflicts * chore: fix conflicts * chore: fix conflicts --------- Co-authored-by: Rohit Waghchaure --- .../sales_invoice_item.json | 28 ++++++++++++++-- .../sales_invoice_item/sales_invoice_item.py | 1 + erpnext/controllers/selling_controller.py | 10 ++++-- .../quotation_item/quotation_item.json | 31 +++++++++++++++--- .../doctype/quotation_item/quotation_item.py | 1 + .../sales_order_item/sales_order_item.json | 29 ++++++++++++++--- .../sales_order_item/sales_order_item.py | 1 + .../delivery_note_item.json | 32 +++++++++++++++---- .../delivery_note_item/delivery_note_item.py | 1 + 9 files changed, 115 insertions(+), 19 deletions(-) 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 8b93f56a4c2..dd370ef414f 100644 --- a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json +++ b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json @@ -89,11 +89,14 @@ "incoming_rate", "item_tax_rate", "actual_batch_qty", - "actual_qty", "section_break_eoec", "serial_no", "column_break_ytgd", "batch_no", + "available_quantity_section", + "actual_qty", + "column_break_ogff", + "company_total_stock", "edit_references", "sales_order", "so_detail", @@ -675,7 +678,8 @@ "allow_on_submit": 1, "fieldname": "actual_qty", "fieldtype": "Float", - "label": "Available Qty at Warehouse", + "label": "Qty (Warehouse)", + "no_copy": 1, "oldfieldname": "actual_qty", "oldfieldtype": "Currency", "print_hide": 1, @@ -923,12 +927,30 @@ { "fieldname": "column_break_ytgd", "fieldtype": "Column Break" + }, + { + "fieldname": "available_quantity_section", + "fieldtype": "Section Break", + "label": "Available Quantity" + }, + { + "fieldname": "column_break_ogff", + "fieldtype": "Column Break" + }, + { + "allow_on_submit": 1, + "fieldname": "company_total_stock", + "fieldtype": "Float", + "label": "Qty (Company)", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 } ], "idx": 1, "istable": 1, "links": [], - "modified": "2024-10-28 15:06:40.980995", + "modified": "2024-11-25 16:27:33.287341", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice Item", diff --git a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.py b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.py index 9be1b42aab3..b7b0873c76b 100644 --- a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.py +++ b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.py @@ -28,6 +28,7 @@ class SalesInvoiceItem(Document): base_rate_with_margin: DF.Currency batch_no: DF.Link | None brand: DF.Data | None + company_total_stock: DF.Float conversion_factor: DF.Float cost_center: DF.Link customer_item_code: DF.Data | None diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index 89a2111d50f..bb59166d3f8 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -21,9 +21,15 @@ class SellingController(StockController): def onload(self): super().onload() - if self.doctype in ("Sales Order", "Delivery Note", "Sales Invoice"): + if self.doctype in ("Sales Order", "Delivery Note", "Sales Invoice", "Quotation"): for item in self.get("items") + (self.get("packed_items") or []): - item.update(get_bin_details(item.item_code, item.warehouse, include_child_warehouses=True)) + company = self.company + + item.update( + get_bin_details( + item.item_code, item.warehouse, company=company, include_child_warehouses=True + ) + ) def validate(self): super().validate() diff --git a/erpnext/selling/doctype/quotation_item/quotation_item.json b/erpnext/selling/doctype/quotation_item/quotation_item.json index 0e25313f76a..1ea19aaaf56 100644 --- a/erpnext/selling/doctype/quotation_item/quotation_item.json +++ b/erpnext/selling/doctype/quotation_item/quotation_item.json @@ -24,6 +24,10 @@ "uom", "conversion_factor", "stock_qty", + "available_quantity_section", + "actual_qty", + "column_break_ylrv", + "company_total_stock", "section_break_16", "price_list_rate", "base_price_list_rate", @@ -70,7 +74,6 @@ "prevdoc_docname", "item_balance", "projected_qty", - "actual_qty", "col_break4", "stock_balance", "item_tax_rate", @@ -460,9 +463,10 @@ "report_hide": 1 }, { + "allow_on_submit": 1, "fieldname": "actual_qty", "fieldtype": "Float", - "label": "Actual Qty", + "label": "Qty (Warehouse)", "no_copy": 1, "print_hide": 1, "read_only": 1, @@ -662,12 +666,31 @@ "label": "Has Alternative Item", "print_hide": 1, "read_only": 1 + }, + { + "fieldname": "available_quantity_section", + "fieldtype": "Section Break", + "label": "Available Quantity" + }, + { + "fieldname": "column_break_ylrv", + "fieldtype": "Column Break" + }, + { + "allow_on_submit": 1, + "fieldname": "company_total_stock", + "fieldtype": "Float", + "label": "Qty (Company)", + "no_copy": 1, + "print_hide": 1, + "read_only": 1, + "report_hide": 1 } ], "idx": 1, "istable": 1, "links": [], - "modified": "2023-11-14 18:24:24.619832", + "modified": "2024-11-24 15:18:43.952844", "modified_by": "Administrator", "module": "Selling", "name": "Quotation Item", @@ -677,4 +700,4 @@ "sort_order": "DESC", "states": [], "track_changes": 1 -} \ No newline at end of file +} diff --git a/erpnext/selling/doctype/quotation_item/quotation_item.py b/erpnext/selling/doctype/quotation_item/quotation_item.py index f209762c3ba..7d68eaf07ba 100644 --- a/erpnext/selling/doctype/quotation_item/quotation_item.py +++ b/erpnext/selling/doctype/quotation_item/quotation_item.py @@ -27,6 +27,7 @@ class QuotationItem(Document): blanket_order: DF.Link | None blanket_order_rate: DF.Currency brand: DF.Link | None + company_total_stock: DF.Float conversion_factor: DF.Float customer_item_code: DF.Data | None description: DF.TextEditor | None 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 d451768eaab..fb9e895ccb7 100644 --- a/erpnext/selling/doctype/sales_order_item/sales_order_item.json +++ b/erpnext/selling/doctype/sales_order_item/sales_order_item.json @@ -78,11 +78,14 @@ "against_blanket_order", "blanket_order", "blanket_order_rate", + "available_quantity_section", + "actual_qty", + "column_break_jpky", + "company_total_stock", "manufacturing_section_section", "bom_no", "planning_section", "projected_qty", - "actual_qty", "ordered_qty", "planned_qty", "production_plan_qty", @@ -636,7 +639,7 @@ "allow_on_submit": 1, "fieldname": "actual_qty", "fieldtype": "Float", - "label": "Actual Qty", + "label": "Qty (Warehouse)", "no_copy": 1, "print_hide": 1, "print_width": "70px", @@ -905,12 +908,30 @@ "label": "Is Stock Item", "print_hide": 1, "report_hide": 1 + }, + { + "allow_on_submit": 1, + "fieldname": "company_total_stock", + "fieldtype": "Float", + "label": "Qty (Company)", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "column_break_jpky", + "fieldtype": "Column Break" + }, + { + "fieldname": "available_quantity_section", + "fieldtype": "Section Break", + "label": "Available Quantity" } ], "idx": 1, "istable": 1, "links": [], - "modified": "2024-03-21 18:15:56.625005", + "modified": "2024-11-21 14:21:29.743474", "modified_by": "Administrator", "module": "Selling", "name": "Sales Order Item", @@ -921,4 +942,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.py b/erpnext/selling/doctype/sales_order_item/sales_order_item.py index fa7b9b968f3..888ea755e2e 100644 --- a/erpnext/selling/doctype/sales_order_item/sales_order_item.py +++ b/erpnext/selling/doctype/sales_order_item/sales_order_item.py @@ -30,6 +30,7 @@ class SalesOrderItem(Document): blanket_order_rate: DF.Currency bom_no: DF.Link | None brand: DF.Link | None + company_total_stock: DF.Float conversion_factor: DF.Float customer_item_code: DF.Data | None delivered_by_supplier: DF.Check 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 b8164b25753..56e5209da59 100644 --- a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json +++ b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json @@ -87,16 +87,19 @@ "column_break_rxvc", "batch_no", "available_qty_section", - "actual_batch_qty", "actual_qty", - "installed_qty", - "item_tax_rate", + "actual_batch_qty", "column_break_atna", + "company_total_stock", + "section_break_kejd", + "installed_qty", "packed_qty", + "column_break_fguf", "received_qty", "accounting_details_section", "expense_account", "column_break_71", + "item_tax_rate", "internal_transfer_section", "material_request", "purchase_order", @@ -519,7 +522,7 @@ "allow_on_submit": 1, "fieldname": "actual_qty", "fieldtype": "Float", - "label": "Available Qty at From Warehouse", + "label": "Qty (Warehouse)", "no_copy": 1, "oldfieldname": "actual_qty", "oldfieldtype": "Currency", @@ -907,13 +910,30 @@ { "fieldname": "column_break_rxvc", "fieldtype": "Column Break" + }, + { + "allow_on_submit": 1, + "fieldname": "company_total_stock", + "fieldtype": "Float", + "label": "Qty (Company)", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "section_break_kejd", + "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_fguf", + "fieldtype": "Column Break" } ], "idx": 1, "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2024-03-21 18:15:07.603672", + "modified": "2024-11-21 17:37:37.441498", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note Item", @@ -923,4 +943,4 @@ "sort_field": "modified", "sort_order": "DESC", "states": [] -} \ No newline at end of file +} diff --git a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.py b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.py index b76f7429728..716cd7d4856 100644 --- a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.py +++ b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.py @@ -30,6 +30,7 @@ class DeliveryNoteItem(Document): batch_no: DF.Link | None billed_amt: DF.Currency brand: DF.Link | None + company_total_stock: DF.Float conversion_factor: DF.Float cost_center: DF.Link | None customer_item_code: DF.Data | None From 0d3802873b62dae8c346a9e256ebaa04050acff6 Mon Sep 17 00:00:00 2001 From: Abdeali Chharchhoda Date: Mon, 25 Nov 2024 14:13:26 +0530 Subject: [PATCH 609/734] fix: Increase quantity by `1 UOM` when adding an item from the selector in POS (cherry picked from commit bbab850135efa852b3f2ee49ea644b0448f6dddf) --- erpnext/selling/page/point_of_sale/pos_controller.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/selling/page/point_of_sale/pos_controller.js b/erpnext/selling/page/point_of_sale/pos_controller.js index c2dd591838d..3028e12d5a2 100644 --- a/erpnext/selling/page/point_of_sale/pos_controller.js +++ b/erpnext/selling/page/point_of_sale/pos_controller.js @@ -556,7 +556,9 @@ erpnext.PointOfSale.Controller = class { const item_row_exists = !$.isEmptyObject(item_row); const from_selector = field === "qty" && value === "+1"; - if (from_selector) value = flt(item_row.stock_qty) + flt(value); + if (from_selector) { + value = flt(item_row.qty) + 1; // increase qty by 1 UOM + } if (item_row_exists) { if (field === "qty") value = flt(value); From 49dad1a456bd22d154deafbfc5b27930721b2240 Mon Sep 17 00:00:00 2001 From: Abdeali Chharchhoda Date: Mon, 25 Nov 2024 14:48:41 +0530 Subject: [PATCH 610/734] fix: Show available stock qty in `stock_uom` instead of `uom` (cherry picked from commit 84dcbe6639f9d727a974aecd6ed7a932ca4bb7bc) --- erpnext/selling/page/point_of_sale/pos_controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/selling/page/point_of_sale/pos_controller.js b/erpnext/selling/page/point_of_sale/pos_controller.js index 3028e12d5a2..9684c9635d9 100644 --- a/erpnext/selling/page/point_of_sale/pos_controller.js +++ b/erpnext/selling/page/point_of_sale/pos_controller.js @@ -689,7 +689,7 @@ erpnext.PointOfSale.Controller = class { const is_stock_item = resp[1]; frappe.dom.unfreeze(); - const bold_uom = item_row.uom.bold(); + const bold_uom = item_row.stock_uom.bold(); const bold_item_code = item_row.item_code.bold(); const bold_warehouse = warehouse.bold(); const bold_available_qty = available_qty.toString().bold(); From b65e16a91b71ca7e674c04d08c2a01a8a0fb1079 Mon Sep 17 00:00:00 2001 From: Abdeali Chharchhoda Date: Mon, 25 Nov 2024 16:17:03 +0530 Subject: [PATCH 611/734] revert: use `+ flt(value)` instead of direct increment (cherry picked from commit 112b4c705bbad2bd07e6be80e4d4128d73c14d73) --- erpnext/selling/page/point_of_sale/pos_controller.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/erpnext/selling/page/point_of_sale/pos_controller.js b/erpnext/selling/page/point_of_sale/pos_controller.js index 9684c9635d9..de3259ae2b7 100644 --- a/erpnext/selling/page/point_of_sale/pos_controller.js +++ b/erpnext/selling/page/point_of_sale/pos_controller.js @@ -556,9 +556,7 @@ erpnext.PointOfSale.Controller = class { const item_row_exists = !$.isEmptyObject(item_row); const from_selector = field === "qty" && value === "+1"; - if (from_selector) { - value = flt(item_row.qty) + 1; // increase qty by 1 UOM - } + if (from_selector) value = flt(item_row.qty) + flt(value); if (item_row_exists) { if (field === "qty") value = flt(value); From db21def58b21938dd3b2fc51fd0d432a97202665 Mon Sep 17 00:00:00 2001 From: Sugesh393 Date: Thu, 21 Nov 2024 11:05:49 +0530 Subject: [PATCH 612/734] fix: add company dynamic filters in number cards (cherry picked from commit 4e7725de669d544909124301c172b8c7d18221a2) --- .../number_card/total_incoming_bills/total_incoming_bills.json | 3 ++- .../total_incoming_payment/total_incoming_payment.json | 1 + .../number_card/total_outgoing_bills/total_outgoing_bills.json | 1 + .../total_outgoing_payment/total_outgoing_payment.json | 1 + erpnext/assets/number_card/asset_value/asset_value.json | 1 + .../new_assets_(this_year)/new_assets_(this_year).json | 1 + erpnext/assets/number_card/total_assets/total_assets.json | 1 + .../buying/number_card/active_suppliers/active_suppliers.json | 1 + erpnext/crm/number_card/open_opportunity/open_opportunity.json | 2 +- .../monthly_completed_work_order.json | 1 + .../monthly_quality_inspection/monthly_quality_inspection.json | 1 + .../monthly_total_work_order/monthly_total_work_order.json | 1 + .../number_card/ongoing_job_card/ongoing_job_card.json | 1 + .../selling/number_card/active_customers/active_customers.json | 2 +- .../number_card/total_active_items/total_active_items.json | 1 + .../stock/number_card/total_stock_value/total_stock_value.json | 1 + .../stock/number_card/total_warehouses/total_warehouses.json | 1 + 17 files changed, 18 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/number_card/total_incoming_bills/total_incoming_bills.json b/erpnext/accounts/number_card/total_incoming_bills/total_incoming_bills.json index 283e187b542..88c7cae3f69 100644 --- a/erpnext/accounts/number_card/total_incoming_bills/total_incoming_bills.json +++ b/erpnext/accounts/number_card/total_incoming_bills/total_incoming_bills.json @@ -4,13 +4,14 @@ "docstatus": 0, "doctype": "Number Card", "document_type": "Purchase Invoice", + "dynamic_filters_json": "[[\"Purchase Invoice\",\"company\",\"=\",\" frappe.defaults.get_user_default(\\\"Company\\\")\"]]", "filters_json": "[[\"Purchase Invoice\",\"docstatus\",\"=\",\"1\",false],[\"Purchase Invoice\",\"posting_date\",\"Timespan\",\"this year\",false]]", "function": "Sum", "idx": 0, "is_public": 1, "is_standard": 1, "label": "Total Incoming Bills", - "modified": "2020-07-22 13:06:46.045344", + "modified": "2024-11-20 19:08:37.043777", "modified_by": "Administrator", "module": "Accounts", "name": "Total Incoming Bills", diff --git a/erpnext/accounts/number_card/total_incoming_payment/total_incoming_payment.json b/erpnext/accounts/number_card/total_incoming_payment/total_incoming_payment.json index bc23c15b6a9..a53b222ed7d 100644 --- a/erpnext/accounts/number_card/total_incoming_payment/total_incoming_payment.json +++ b/erpnext/accounts/number_card/total_incoming_payment/total_incoming_payment.json @@ -4,6 +4,7 @@ "docstatus": 0, "doctype": "Number Card", "document_type": "Payment Entry", + "dynamic_filters_json": "[[\"Payment Entry\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", "filters_json": "[[\"Payment Entry\",\"docstatus\",\"=\",\"1\",false],[\"Payment Entry\",\"posting_date\",\"Timespan\",\"this year\",false],[\"Payment Entry\",\"payment_type\",\"=\",\"Receive\",false]]", "function": "Sum", "idx": 0, diff --git a/erpnext/accounts/number_card/total_outgoing_bills/total_outgoing_bills.json b/erpnext/accounts/number_card/total_outgoing_bills/total_outgoing_bills.json index fe916182102..092defd94bd 100644 --- a/erpnext/accounts/number_card/total_outgoing_bills/total_outgoing_bills.json +++ b/erpnext/accounts/number_card/total_outgoing_bills/total_outgoing_bills.json @@ -4,6 +4,7 @@ "docstatus": 0, "doctype": "Number Card", "document_type": "Sales Invoice", + "dynamic_filters_json": "[[\"Sales Invoice\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", "filters_json": "[[\"Sales Invoice\",\"docstatus\",\"=\",\"1\",false],[\"Sales Invoice\",\"posting_date\",\"Timespan\",\"this year\",false]]", "function": "Sum", "idx": 0, diff --git a/erpnext/accounts/number_card/total_outgoing_payment/total_outgoing_payment.json b/erpnext/accounts/number_card/total_outgoing_payment/total_outgoing_payment.json index d27be883500..d60f30f7c9a 100644 --- a/erpnext/accounts/number_card/total_outgoing_payment/total_outgoing_payment.json +++ b/erpnext/accounts/number_card/total_outgoing_payment/total_outgoing_payment.json @@ -4,6 +4,7 @@ "docstatus": 0, "doctype": "Number Card", "document_type": "Payment Entry", + "dynamic_filters_json": "[[\"Payment Entry\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", "filters_json": "[[\"Payment Entry\",\"docstatus\",\"=\",\"1\",false],[\"Payment Entry\",\"posting_date\",\"Timespan\",\"this year\",false],[\"Payment Entry\",\"payment_type\",\"=\",\"Pay\",false]]", "function": "Sum", "idx": 0, diff --git a/erpnext/assets/number_card/asset_value/asset_value.json b/erpnext/assets/number_card/asset_value/asset_value.json index 68e5f54c789..c437f4800f3 100644 --- a/erpnext/assets/number_card/asset_value/asset_value.json +++ b/erpnext/assets/number_card/asset_value/asset_value.json @@ -4,6 +4,7 @@ "docstatus": 0, "doctype": "Number Card", "document_type": "Asset", + "dynamic_filters_json": "[[\"Asset\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", "filters_json": "[]", "function": "Sum", "idx": 0, diff --git a/erpnext/assets/number_card/new_assets_(this_year)/new_assets_(this_year).json b/erpnext/assets/number_card/new_assets_(this_year)/new_assets_(this_year).json index 6c8fb356575..cdf52d011d4 100644 --- a/erpnext/assets/number_card/new_assets_(this_year)/new_assets_(this_year).json +++ b/erpnext/assets/number_card/new_assets_(this_year)/new_assets_(this_year).json @@ -3,6 +3,7 @@ "docstatus": 0, "doctype": "Number Card", "document_type": "Asset", + "dynamic_filters_json": "[[\"Asset\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", "filters_json": "[[\"Asset\",\"creation\",\"Timespan\",\"this year\",false]]", "function": "Count", "idx": 0, diff --git a/erpnext/assets/number_card/total_assets/total_assets.json b/erpnext/assets/number_card/total_assets/total_assets.json index d127de8f2c6..d8626da608c 100644 --- a/erpnext/assets/number_card/total_assets/total_assets.json +++ b/erpnext/assets/number_card/total_assets/total_assets.json @@ -3,6 +3,7 @@ "docstatus": 0, "doctype": "Number Card", "document_type": "Asset", + "dynamic_filters_json": "[[\"Asset\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", "filters_json": "[]", "function": "Count", "idx": 0, diff --git a/erpnext/buying/number_card/active_suppliers/active_suppliers.json b/erpnext/buying/number_card/active_suppliers/active_suppliers.json index 91d5b13b06f..61d34c6261e 100644 --- a/erpnext/buying/number_card/active_suppliers/active_suppliers.json +++ b/erpnext/buying/number_card/active_suppliers/active_suppliers.json @@ -3,6 +3,7 @@ "docstatus": 0, "doctype": "Number Card", "document_type": "Supplier", + "dynamic_filters_json": "[[\"Supplier\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", "filters_json": "[[\"Supplier\",\"disabled\",\"=\",\"0\"]]", "function": "Count", "idx": 0, diff --git a/erpnext/crm/number_card/open_opportunity/open_opportunity.json b/erpnext/crm/number_card/open_opportunity/open_opportunity.json index 6e06ed64da3..33a757feb14 100644 --- a/erpnext/crm/number_card/open_opportunity/open_opportunity.json +++ b/erpnext/crm/number_card/open_opportunity/open_opportunity.json @@ -3,7 +3,7 @@ "docstatus": 0, "doctype": "Number Card", "document_type": "Opportunity", - "dynamic_filters_json": "[[\"Opportunity\",\"status\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", + "dynamic_filters_json": "[[\"Opportunity\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", "filters_json": "[[\"Opportunity\",\"company\",\"=\",null,false]]", "function": "Count", "idx": 0, diff --git a/erpnext/manufacturing/number_card/monthly_completed_work_order/monthly_completed_work_order.json b/erpnext/manufacturing/number_card/monthly_completed_work_order/monthly_completed_work_order.json index 36c0b9ae75b..7381de707d7 100644 --- a/erpnext/manufacturing/number_card/monthly_completed_work_order/monthly_completed_work_order.json +++ b/erpnext/manufacturing/number_card/monthly_completed_work_order/monthly_completed_work_order.json @@ -3,6 +3,7 @@ "docstatus": 0, "doctype": "Number Card", "document_type": "Work Order", + "dynamic_filters_json": "[[\"Work Order\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", "filters_json": "[[\"Work Order\",\"status\",\"=\",\"Completed\"],[\"Work Order\",\"docstatus\",\"=\",1],[\"Work Order\",\"creation\",\"between\",[\"2020-06-08\",\"2020-07-08\"]]]", "function": "Count", "idx": 0, diff --git a/erpnext/manufacturing/number_card/monthly_quality_inspection/monthly_quality_inspection.json b/erpnext/manufacturing/number_card/monthly_quality_inspection/monthly_quality_inspection.json index 91a45365c0d..e716493483d 100644 --- a/erpnext/manufacturing/number_card/monthly_quality_inspection/monthly_quality_inspection.json +++ b/erpnext/manufacturing/number_card/monthly_quality_inspection/monthly_quality_inspection.json @@ -3,6 +3,7 @@ "docstatus": 0, "doctype": "Number Card", "document_type": "Quality Inspection", + "dynamic_filters_json": "[[\"Quality Inspection\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", "filters_json": "[[\"Quality Inspection\",\"docstatus\",\"=\",1],[\"Quality Inspection\",\"creation\",\"between\",[\"2020-06-08\",\"2020-07-08\"]]]", "function": "Count", "idx": 0, diff --git a/erpnext/manufacturing/number_card/monthly_total_work_order/monthly_total_work_order.json b/erpnext/manufacturing/number_card/monthly_total_work_order/monthly_total_work_order.json index 80d3b1520ae..e4b8c940454 100644 --- a/erpnext/manufacturing/number_card/monthly_total_work_order/monthly_total_work_order.json +++ b/erpnext/manufacturing/number_card/monthly_total_work_order/monthly_total_work_order.json @@ -3,6 +3,7 @@ "docstatus": 0, "doctype": "Number Card", "document_type": "Work Order", + "dynamic_filters_json": "[[\"Work Order\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", "filters_json": "[[\"Work Order\",\"docstatus\",\"=\",1],[\"Work Order\",\"creation\",\"between\",[\"2020-06-08\",\"2020-07-08\"]]]", "function": "Count", "idx": 0, diff --git a/erpnext/manufacturing/number_card/ongoing_job_card/ongoing_job_card.json b/erpnext/manufacturing/number_card/ongoing_job_card/ongoing_job_card.json index ba23ff34531..0138af827a6 100644 --- a/erpnext/manufacturing/number_card/ongoing_job_card/ongoing_job_card.json +++ b/erpnext/manufacturing/number_card/ongoing_job_card/ongoing_job_card.json @@ -3,6 +3,7 @@ "docstatus": 0, "doctype": "Number Card", "document_type": "Job Card", + "dynamic_filters_json": "[[\"Job Card\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", "filters_json": "[[\"Job Card\",\"status\",\"!=\",\"Completed\"],[\"Job Card\",\"docstatus\",\"=\",1]]", "function": "Count", "idx": 0, diff --git a/erpnext/selling/number_card/active_customers/active_customers.json b/erpnext/selling/number_card/active_customers/active_customers.json index 33776348477..8b027a14a0e 100644 --- a/erpnext/selling/number_card/active_customers/active_customers.json +++ b/erpnext/selling/number_card/active_customers/active_customers.json @@ -3,7 +3,7 @@ "docstatus": 0, "doctype": "Number Card", "document_type": "Customer", - "dynamic_filters_json": "", + "dynamic_filters_json": "[[\"Customer\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", "filters_json": "[[\"Customer\",\"disabled\",\"=\",\"0\"]]", "function": "Count", "idx": 0, diff --git a/erpnext/stock/number_card/total_active_items/total_active_items.json b/erpnext/stock/number_card/total_active_items/total_active_items.json index f6863b96d7c..e2db153be0a 100644 --- a/erpnext/stock/number_card/total_active_items/total_active_items.json +++ b/erpnext/stock/number_card/total_active_items/total_active_items.json @@ -3,6 +3,7 @@ "docstatus": 0, "doctype": "Number Card", "document_type": "Item", + "dynamic_filters_json": "[[\"Item\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", "filters_json": "[[\"Item\",\"disabled\",\"=\",0]]", "function": "Count", "idx": 0, diff --git a/erpnext/stock/number_card/total_stock_value/total_stock_value.json b/erpnext/stock/number_card/total_stock_value/total_stock_value.json index 8e480a6b3e6..001549ba4f5 100644 --- a/erpnext/stock/number_card/total_stock_value/total_stock_value.json +++ b/erpnext/stock/number_card/total_stock_value/total_stock_value.json @@ -4,6 +4,7 @@ "docstatus": 0, "doctype": "Number Card", "document_type": "Bin", + "dynamic_filters_json": "[[\"Bin\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", "filters_json": "[]", "function": "Sum", "idx": 0, diff --git a/erpnext/stock/number_card/total_warehouses/total_warehouses.json b/erpnext/stock/number_card/total_warehouses/total_warehouses.json index ab0836a3afe..8eadfac48d1 100644 --- a/erpnext/stock/number_card/total_warehouses/total_warehouses.json +++ b/erpnext/stock/number_card/total_warehouses/total_warehouses.json @@ -3,6 +3,7 @@ "docstatus": 0, "doctype": "Number Card", "document_type": "Warehouse", + "dynamic_filters_json": "[[\"Warehouse\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", "filters_json": "[[\"Warehouse\",\"disabled\",\"=\",0]]", "function": "Count", "idx": 0, From 7423d7d3372aecb0b6003e67313322061bdea673 Mon Sep 17 00:00:00 2001 From: Sugesh393 Date: Thu, 21 Nov 2024 12:09:24 +0530 Subject: [PATCH 613/734] fix: remove irrelavent filters (cherry picked from commit 29762c48265c21c62dfdb04f03736d076ef4ff68) --- .../buying/number_card/active_suppliers/active_suppliers.json | 1 - .../monthly_quality_inspection/monthly_quality_inspection.json | 1 - .../selling/number_card/active_customers/active_customers.json | 1 - .../stock/number_card/total_active_items/total_active_items.json | 1 - .../stock/number_card/total_stock_value/total_stock_value.json | 1 - 5 files changed, 5 deletions(-) diff --git a/erpnext/buying/number_card/active_suppliers/active_suppliers.json b/erpnext/buying/number_card/active_suppliers/active_suppliers.json index 61d34c6261e..91d5b13b06f 100644 --- a/erpnext/buying/number_card/active_suppliers/active_suppliers.json +++ b/erpnext/buying/number_card/active_suppliers/active_suppliers.json @@ -3,7 +3,6 @@ "docstatus": 0, "doctype": "Number Card", "document_type": "Supplier", - "dynamic_filters_json": "[[\"Supplier\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", "filters_json": "[[\"Supplier\",\"disabled\",\"=\",\"0\"]]", "function": "Count", "idx": 0, diff --git a/erpnext/manufacturing/number_card/monthly_quality_inspection/monthly_quality_inspection.json b/erpnext/manufacturing/number_card/monthly_quality_inspection/monthly_quality_inspection.json index e716493483d..91a45365c0d 100644 --- a/erpnext/manufacturing/number_card/monthly_quality_inspection/monthly_quality_inspection.json +++ b/erpnext/manufacturing/number_card/monthly_quality_inspection/monthly_quality_inspection.json @@ -3,7 +3,6 @@ "docstatus": 0, "doctype": "Number Card", "document_type": "Quality Inspection", - "dynamic_filters_json": "[[\"Quality Inspection\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", "filters_json": "[[\"Quality Inspection\",\"docstatus\",\"=\",1],[\"Quality Inspection\",\"creation\",\"between\",[\"2020-06-08\",\"2020-07-08\"]]]", "function": "Count", "idx": 0, diff --git a/erpnext/selling/number_card/active_customers/active_customers.json b/erpnext/selling/number_card/active_customers/active_customers.json index 8b027a14a0e..7a31a21f6fd 100644 --- a/erpnext/selling/number_card/active_customers/active_customers.json +++ b/erpnext/selling/number_card/active_customers/active_customers.json @@ -3,7 +3,6 @@ "docstatus": 0, "doctype": "Number Card", "document_type": "Customer", - "dynamic_filters_json": "[[\"Customer\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", "filters_json": "[[\"Customer\",\"disabled\",\"=\",\"0\"]]", "function": "Count", "idx": 0, diff --git a/erpnext/stock/number_card/total_active_items/total_active_items.json b/erpnext/stock/number_card/total_active_items/total_active_items.json index e2db153be0a..f6863b96d7c 100644 --- a/erpnext/stock/number_card/total_active_items/total_active_items.json +++ b/erpnext/stock/number_card/total_active_items/total_active_items.json @@ -3,7 +3,6 @@ "docstatus": 0, "doctype": "Number Card", "document_type": "Item", - "dynamic_filters_json": "[[\"Item\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", "filters_json": "[[\"Item\",\"disabled\",\"=\",0]]", "function": "Count", "idx": 0, diff --git a/erpnext/stock/number_card/total_stock_value/total_stock_value.json b/erpnext/stock/number_card/total_stock_value/total_stock_value.json index 001549ba4f5..8e480a6b3e6 100644 --- a/erpnext/stock/number_card/total_stock_value/total_stock_value.json +++ b/erpnext/stock/number_card/total_stock_value/total_stock_value.json @@ -4,7 +4,6 @@ "docstatus": 0, "doctype": "Number Card", "document_type": "Bin", - "dynamic_filters_json": "[[\"Bin\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]", "filters_json": "[]", "function": "Sum", "idx": 0, From 465a26f714baaff2f8a3a1da6dc12fe91c699d1e Mon Sep 17 00:00:00 2001 From: Ernesto Ruiz Date: Sat, 23 Nov 2024 09:07:18 -0600 Subject: [PATCH 614/734] chore: Add translations to QI validations in Update stock_controller.py chore: Add translations to QI validations in Update stock_controller.py (cherry picked from commit 6754f15487edb51b60ebaecc6fc43d34e2154e07) --- erpnext/controllers/stock_controller.py | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 4eb67b6f42e..8f0a5d5edf5 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -1008,11 +1008,13 @@ class StockController(AccountsController): def validate_qi_presence(self, row): """Check if QI is present on row level. Warn on save and stop on submit if missing.""" if not row.quality_inspection: - msg = f"Row #{row.idx}: Quality Inspection is required for Item {frappe.bold(row.item_code)}" + msg = _("Row #{0}: Quality Inspection is required for Item {1}").format( + row.idx, frappe.bold(row.item_code) + ) if self.docstatus == 1: - frappe.throw(_(msg), title=_("Inspection Required"), exc=QualityInspectionRequiredError) + frappe.throw(msg, title=_("Inspection Required"), exc=QualityInspectionRequiredError) else: - frappe.msgprint(_(msg), title=_("Inspection Required"), indicator="blue") + frappe.msgprint(msg, title=_("Inspection Required"), indicator="blue") def validate_qi_submission(self, row): """Check if QI is submitted on row level, during submission""" @@ -1021,11 +1023,13 @@ class StockController(AccountsController): if not qa_docstatus == 1: link = frappe.utils.get_link_to_form("Quality Inspection", row.quality_inspection) - msg = f"Row #{row.idx}: Quality Inspection {link} is not submitted for the item: {row.item_code}" + msg = _("Row #{0}: Quality Inspection {1} is not submitted for the item: {2}").format( + row.idx, link, row.item_code + ) if action == "Stop": - frappe.throw(_(msg), title=_("Inspection Submission"), exc=QualityInspectionNotSubmittedError) + frappe.throw(msg, title=_("Inspection Submission"), exc=QualityInspectionNotSubmittedError) else: - frappe.msgprint(_(msg), alert=True, indicator="orange") + frappe.msgprint(msg, alert=True, indicator="orange") def validate_qi_rejection(self, row): """Check if QI is rejected on row level, during submission""" @@ -1034,11 +1038,13 @@ class StockController(AccountsController): if qa_status == "Rejected": link = frappe.utils.get_link_to_form("Quality Inspection", row.quality_inspection) - msg = f"Row #{row.idx}: Quality Inspection {link} was rejected for item {row.item_code}" + msg = _("Row #{0}: Quality Inspection {1} was rejected for item {2}").format( + row.idx, link, row.item_code + ) if action == "Stop": - frappe.throw(_(msg), title=_("Inspection Rejected"), exc=QualityInspectionRejectedError) + frappe.throw(msg, title=_("Inspection Rejected"), exc=QualityInspectionRejectedError) else: - frappe.msgprint(_(msg), alert=True, indicator="orange") + frappe.msgprint(msg, alert=True, indicator="orange") def update_blanket_order(self): blanket_orders = list(set([d.blanket_order for d in self.items if d.blanket_order])) From cde19066fe6b42aca5cae6971805973ac18a01dd Mon Sep 17 00:00:00 2001 From: vishakhdesai Date: Mon, 25 Nov 2024 12:50:35 +0530 Subject: [PATCH 615/734] fix: use field precision instead of hardcoded precision in so and po (cherry picked from commit 1a1e2c7e01f76ed9f9cc727fb802ade8faa99440) # Conflicts: # erpnext/buying/doctype/purchase_order/purchase_order.js --- .../doctype/purchase_order/purchase_order.js | 29 +++++++++++--- .../doctype/sales_order/sales_order.js | 39 +++++++++++++------ 2 files changed, 50 insertions(+), 18 deletions(-) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js index de335bd6292..cb0aea0eb15 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.js +++ b/erpnext/buying/doctype/purchase_order/purchase_order.js @@ -93,7 +93,10 @@ frappe.ui.form.on("Purchase Order", { get_materials_from_supplier: function (frm) { let po_details = []; - if (frm.doc.supplied_items && (flt(frm.doc.per_received, 2) == 100 || frm.doc.status === "Closed")) { + if ( + frm.doc.supplied_items && + (flt(frm.doc.per_received, precision("per_received")) == 100 || frm.doc.status === "Closed") + ) { frm.doc.supplied_items.forEach((d) => { if (d.total_supplied_qty && d.total_supplied_qty != d.consumed_qty) { po_details.push(d.name); @@ -329,8 +332,8 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends ( if (!["Closed", "Delivered"].includes(doc.status)) { if ( this.frm.doc.status !== "Closed" && - flt(this.frm.doc.per_received, 2) < 100 && - flt(this.frm.doc.per_billed, 2) < 100 + flt(this.frm.doc.per_received, precision("per_received")) < 100 && + flt(this.frm.doc.per_billed, precision("per_billed")) < 100 ) { if (!this.frm.doc.__onload || this.frm.doc.__onload.can_update_items) { this.frm.add_custom_button(__("Update Items"), () => { @@ -344,7 +347,10 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends ( } } if (this.frm.has_perm("submit")) { - if (flt(doc.per_billed, 2) < 100 || flt(doc.per_received, 2) < 100) { + if ( + flt(doc.per_billed, precision("per_billed")) < 100 || + flt(doc.per_received, precision("per_received")) < 100 + ) { if (doc.status != "On Hold") { this.frm.add_custom_button( __("Hold"), @@ -382,8 +388,13 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends ( } if (doc.status != "Closed") { if (doc.status != "On Hold") { +<<<<<<< HEAD if (flt(doc.per_received) < 100 && allow_receipt) { cur_frm.add_custom_button( +======= + if (flt(doc.per_received, precision("per_received")) < 100 && allow_receipt) { + this.frm.add_custom_button( +>>>>>>> 1a1e2c7e01 (fix: use field precision instead of hardcoded precision in so and po) __("Purchase Receipt"), this.make_purchase_receipt, __("Create") @@ -408,14 +419,20 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends ( } } } +<<<<<<< HEAD if (flt(doc.per_billed) < 100) cur_frm.add_custom_button( +======= + // Please do not add precision in the below flt function + if (flt(doc.per_billed, precision("per_billed")) < 100) + this.frm.add_custom_button( +>>>>>>> 1a1e2c7e01 (fix: use field precision instead of hardcoded precision in so and po) __("Purchase Invoice"), this.make_purchase_invoice, __("Create") ); - if (flt(doc.per_billed, 2) < 100 && doc.status != "Delivered") { + if (flt(doc.per_billed, precision("per_billed")) < 100 && doc.status != "Delivered") { this.frm.add_custom_button( __("Payment"), () => this.make_payment_entry(), @@ -423,7 +440,7 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends ( ); } - if (flt(doc.per_billed, 2) < 100) { + if (flt(doc.per_billed, precision("per_billed")) < 100) { this.frm.add_custom_button( __("Payment Request"), function () { diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index 5387286c709..2c8a6cccceb 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -57,8 +57,8 @@ frappe.ui.form.on("Sales Order", { if (frm.doc.docstatus === 1) { if ( frm.doc.status !== "Closed" && - flt(frm.doc.per_delivered, 2) < 100 && - flt(frm.doc.per_billed, 2) < 100 && + flt(frm.doc.per_delivered, precision("per_delivered")) < 100 && + flt(frm.doc.per_billed, precision("per_billed")) < 100 && frm.has_perm("write") ) { frm.add_custom_button(__("Update Items"), () => { @@ -75,7 +75,7 @@ frappe.ui.form.on("Sales Order", { if ( frm.doc.__onload && frm.doc.__onload.has_unreserved_stock && - flt(frm.doc.per_picked) === 0 + flt(frm.doc.per_picked, precision("per_picked")) === 0 ) { frm.add_custom_button( __("Reserve"), @@ -604,7 +604,10 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex __("Status") ); - if (flt(doc.per_delivered, 2) < 100 || flt(doc.per_billed, 2) < 100) { + if ( + flt(doc.per_delivered, precision("per_delivered")) < 100 || + flt(doc.per_billed, precision("per_billed")) < 100 + ) { // close this.frm.add_custom_button(__("Close"), () => this.close_sales_order(), __("Status")); } @@ -627,7 +630,10 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex ) && !this.frm.doc.skip_delivery_note; if (this.frm.has_perm("submit")) { - if (flt(doc.per_delivered, 2) < 100 || flt(doc.per_billed, 2) < 100) { + if ( + flt(doc.per_delivered, precision("per_delivered")) < 100 || + flt(doc.per_billed, precision("per_billed")) < 100 + ) { // hold this.frm.add_custom_button( __("Hold"), @@ -645,8 +651,8 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex if ( (!doc.__onload || !doc.__onload.has_reserved_stock) && - flt(doc.per_picked, 2) < 100 && - flt(doc.per_delivered, 2) < 100 && + flt(doc.per_picked, precision("per_picked")) < 100 && + flt(doc.per_delivered, precision("per_delivered")) < 100 && frappe.model.can_create("Pick List") ) { this.frm.add_custom_button( @@ -664,7 +670,7 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex // delivery note if ( - flt(doc.per_delivered, 2) < 100 && + flt(doc.per_delivered, precision("per_delivered")) < 100 && (order_is_a_sale || order_is_a_custom_sale) && allow_delivery ) { @@ -686,7 +692,10 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex } // sales invoice - if (flt(doc.per_billed, 2) < 100 && frappe.model.can_create("Sales Invoice")) { + if ( + flt(doc.per_billed, precision("per_billed")) < 100 && + frappe.model.can_create("Sales Invoice") + ) { this.frm.add_custom_button( __("Sales Invoice"), () => me.make_sales_invoice(), @@ -698,7 +707,7 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex if ( (!doc.order_type || ((order_is_a_sale || order_is_a_custom_sale) && - flt(doc.per_delivered, 2) < 100)) && + flt(doc.per_delivered, precision("per_delivered")) < 100)) && frappe.model.can_create("Material Request") ) { this.frm.add_custom_button( @@ -723,7 +732,10 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex } // maintenance - if (flt(doc.per_delivered, 2) < 100 && (order_is_maintenance || order_is_a_custom_sale)) { + if ( + flt(doc.per_delivered, precision("per_delivered")) < 100 && + (order_is_maintenance || order_is_a_custom_sale) + ) { if (frappe.model.can_create("Maintenance Visit")) { this.frm.add_custom_button( __("Maintenance Visit"), @@ -741,7 +753,10 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex } // project - if (flt(doc.per_delivered, 2) < 100 && frappe.model.can_create("Project")) { + if ( + flt(doc.per_delivered, precision("per_delivered")) < 100 && + frappe.model.can_create("Project") + ) { this.frm.add_custom_button(__("Project"), () => this.make_project(), __("Create")); } From bc93de682b6de2cec6474c1067dd461fbc193732 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 26 Nov 2024 13:48:00 +0530 Subject: [PATCH 616/734] chore: resolve conflict --- .../buying/doctype/purchase_order/purchase_order.js | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js index cb0aea0eb15..9718ab19b19 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.js +++ b/erpnext/buying/doctype/purchase_order/purchase_order.js @@ -388,13 +388,8 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends ( } if (doc.status != "Closed") { if (doc.status != "On Hold") { -<<<<<<< HEAD - if (flt(doc.per_received) < 100 && allow_receipt) { - cur_frm.add_custom_button( -======= if (flt(doc.per_received, precision("per_received")) < 100 && allow_receipt) { this.frm.add_custom_button( ->>>>>>> 1a1e2c7e01 (fix: use field precision instead of hardcoded precision in so and po) __("Purchase Receipt"), this.make_purchase_receipt, __("Create") @@ -419,14 +414,9 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends ( } } } -<<<<<<< HEAD - if (flt(doc.per_billed) < 100) - cur_frm.add_custom_button( -======= // Please do not add precision in the below flt function if (flt(doc.per_billed, precision("per_billed")) < 100) this.frm.add_custom_button( ->>>>>>> 1a1e2c7e01 (fix: use field precision instead of hardcoded precision in so and po) __("Purchase Invoice"), this.make_purchase_invoice, __("Create") From ea0f24aa573e66d289be4af567405e072eb73968 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 26 Nov 2024 19:43:51 +0530 Subject: [PATCH 617/734] fix: billed qty and received amount in PO analysis report (backport #44349) (#44354) fix: billed qty and received amount in PO analysis report (#44349) (cherry picked from commit 2ab7ec5437834a63e0a0ebe5e081236370d56e8b) Co-authored-by: rohitwaghchaure --- .../purchase_order_analysis.py | 38 +++++++++++++++++-- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py index 084c3b5fc2b..1a250acd4d2 100644 --- a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py +++ b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py @@ -18,6 +18,7 @@ def execute(filters=None): columns = get_columns(filters) data = get_data(filters) + update_received_amount(data) if not data: return [], [], None, [] @@ -40,7 +41,6 @@ def get_data(filters): po = frappe.qb.DocType("Purchase Order") po_item = frappe.qb.DocType("Purchase Order Item") pi_item = frappe.qb.DocType("Purchase Invoice Item") - pr_item = frappe.qb.DocType("Purchase Receipt Item") query = ( frappe.qb.from_(po) @@ -48,8 +48,6 @@ def get_data(filters): .on(po_item.parent == po.name) .left_join(pi_item) .on((pi_item.po_detail == po_item.name) & (pi_item.docstatus == 1)) - .left_join(pr_item) - .on((pr_item.purchase_order_item == po_item.name) & (pr_item.docstatus == 1)) .select( po.transaction_date.as_("date"), po_item.schedule_date.as_("required_date"), @@ -63,7 +61,6 @@ def get_data(filters): (po_item.qty - po_item.received_qty).as_("pending_qty"), Sum(IfNull(pi_item.qty, 0)).as_("billed_qty"), po_item.base_amount.as_("amount"), - (pr_item.base_amount).as_("received_qty_amount"), (po_item.billed_amt * IfNull(po.conversion_rate, 1)).as_("billed_amount"), (po_item.base_amount - (po_item.billed_amt * IfNull(po.conversion_rate, 1))).as_( "pending_amount" @@ -95,6 +92,39 @@ def get_data(filters): return data +def update_received_amount(data): + pr_data = get_received_amount_data(data) + + for row in data: + row.received_qty_amount = flt(pr_data.get(row.name)) + + +def get_received_amount_data(data): + pr = frappe.qb.DocType("Purchase Receipt") + pr_item = frappe.qb.DocType("Purchase Receipt Item") + + query = ( + frappe.qb.from_(pr) + .inner_join(pr_item) + .on(pr_item.parent == pr.name) + .select( + pr_item.purchase_order_item, + Sum(pr_item.base_amount).as_("received_qty_amount"), + ) + .where((pr_item.parent == pr.name) & (pr.docstatus == 1)) + .groupby(pr_item.purchase_order_item) + ) + + query = query.where(pr_item.purchase_order_item.isin([row.name for row in data])) + + data = query.run() + + if not data: + return frappe._dict() + + return frappe._dict(data) + + def prepare_data(data, filters): completed, pending = 0, 0 pending_field = "pending_amount" From 89bd4eba46894efba5cfe9bc3411448d41366807 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 26 Nov 2024 21:18:26 +0530 Subject: [PATCH 618/734] fix: added validation for quality inspection (backport #44351) (#44357) fix: added validation for quality inspection (#44351) (cherry picked from commit 0fd50b504814e8f035be8d6cef62121513fb8639) Co-authored-by: rohitwaghchaure --- .../quality_inspection/quality_inspection.py | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/quality_inspection/quality_inspection.py b/erpnext/stock/doctype/quality_inspection/quality_inspection.py index 43efcbc8ca6..6890256dc04 100644 --- a/erpnext/stock/doctype/quality_inspection/quality_inspection.py +++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.py @@ -6,7 +6,7 @@ import frappe from frappe import _ from frappe.model.document import Document from frappe.model.mapper import get_mapped_doc -from frappe.utils import cint, cstr, flt, get_number_format_info +from frappe.utils import cint, cstr, flt, get_link_to_form, get_number_format_info from erpnext.stock.doctype.quality_inspection_template.quality_inspection_template import ( get_template_details, @@ -73,6 +73,27 @@ class QualityInspection(Document): if self.readings: self.inspect_and_set_status() + self.validate_inspection_required() + + def validate_inspection_required(self): + if self.reference_type in ["Purchase Receipt", "Purchase Invoice"] and not frappe.get_cached_value( + "Item", self.item_code, "inspection_required_before_purchase" + ): + frappe.throw( + _( + "'Inspection Required before Purchase' has disabled for the item {0}, no need to create the QI" + ).format(get_link_to_form("Item", self.item_code)) + ) + + if self.reference_type in ["Delivery Note", "Sales Invoice"] and not frappe.get_cached_value( + "Item", self.item_code, "inspection_required_before_delivery" + ): + frappe.throw( + _( + "'Inspection Required before Delivery' has disabled for the item {0}, no need to create the QI" + ).format(get_link_to_form("Item", self.item_code)) + ) + def before_submit(self): self.validate_readings_status_mandatory() From 7e61aca512c18adca6f8eb5b2300287898e8f3e4 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 26 Nov 2024 20:16:24 +0100 Subject: [PATCH 619/734] fix: unify company address query in sales transactions (backport #44361) (#44365) fix: unify company address query in sales transactions (#44361) * fix: unify company address query in sales transactions * refactor: get the correct field label (cherry picked from commit 3f92a57d63edf5c5b478736e25d77b5eb43eac94) Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com> --- .../doctype/sales_invoice/sales_invoice.js | 14 -------------- erpnext/public/js/queries.js | 6 +++++- erpnext/public/js/utils/sales_common.js | 1 + erpnext/selling/doctype/quotation/quotation.js | 14 -------------- erpnext/selling/doctype/sales_order/sales_order.js | 14 -------------- 5 files changed, 6 insertions(+), 43 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 5e248c8b235..75c71ef6eb3 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -741,20 +741,6 @@ frappe.ui.form.on("Sales Invoice", { }; }; - frm.set_query("company_address", function (doc) { - if (!doc.company) { - frappe.throw(__("Please set Company")); - } - - return { - query: "frappe.contacts.doctype.address.address.address_query", - filters: { - link_doctype: "Company", - link_name: doc.company, - }, - }; - }); - frm.set_query("pos_profile", function (doc) { if (!doc.company) { frappe.throw(__("Please set Company")); diff --git a/erpnext/public/js/queries.js b/erpnext/public/js/queries.js index d7edac3cb9f..5350f91746c 100644 --- a/erpnext/public/js/queries.js +++ b/erpnext/public/js/queries.js @@ -77,9 +77,13 @@ $.extend(erpnext.queries, { }, company_address_query: function (doc) { + if (!doc.company) { + frappe.throw(__("Please set {0}", [__(frappe.meta.get_label(doc.doctype, "company", doc.name))])); + } + return { query: "frappe.contacts.doctype.address.address.address_query", - filters: { is_your_company_address: 1, link_doctype: "Company", link_name: doc.company || "" }, + filters: { link_doctype: "Company", link_name: doc.company }, }; }, diff --git a/erpnext/public/js/utils/sales_common.js b/erpnext/public/js/utils/sales_common.js index ac4ecf52e63..373bf3d2115 100644 --- a/erpnext/public/js/utils/sales_common.js +++ b/erpnext/public/js/utils/sales_common.js @@ -52,6 +52,7 @@ erpnext.sales_common = { me.frm.set_query("customer_address", erpnext.queries.address_query); me.frm.set_query("shipping_address_name", erpnext.queries.address_query); me.frm.set_query("dispatch_address_name", erpnext.queries.dispatch_address_query); + me.frm.set_query("company_address", erpnext.queries.company_address_query); erpnext.accounts.dimensions.setup_dimension_filters(me.frm, me.frm.doctype); diff --git a/erpnext/selling/doctype/quotation/quotation.js b/erpnext/selling/doctype/quotation/quotation.js index a4c70d7f50f..7311857e350 100644 --- a/erpnext/selling/doctype/quotation/quotation.js +++ b/erpnext/selling/doctype/quotation/quotation.js @@ -24,20 +24,6 @@ frappe.ui.form.on("Quotation", { frm.set_df_property("packed_items", "cannot_add_rows", true); frm.set_df_property("packed_items", "cannot_delete_rows", true); - frm.set_query("company_address", function (doc) { - if (!doc.company) { - frappe.throw(__("Please set Company")); - } - - return { - query: "frappe.contacts.doctype.address.address.address_query", - filters: { - link_doctype: "Company", - link_name: doc.company, - }, - }; - }); - frm.set_query("serial_and_batch_bundle", "packed_items", (doc, cdt, cdn) => { let row = locals[cdt][cdn]; return { diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index 2c8a6cccceb..f285d638322 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -26,20 +26,6 @@ frappe.ui.form.on("Sales Order", { return doc.stock_qty <= doc.delivered_qty ? "green" : "orange"; }); - frm.set_query("company_address", function (doc) { - if (!doc.company) { - frappe.throw(__("Please set Company")); - } - - return { - query: "frappe.contacts.doctype.address.address.address_query", - filters: { - link_doctype: "Company", - link_name: doc.company, - }, - }; - }); - frm.set_query("bom_no", "items", function (doc, cdt, cdn) { var row = locals[cdt][cdn]; return { From e0cb5f9ba8dc8a5b23af958b5c988b3499ed4853 Mon Sep 17 00:00:00 2001 From: Raffael Meyer <14891507+barredterra@users.noreply.github.com> Date: Tue, 26 Nov 2024 21:10:34 +0100 Subject: [PATCH 620/734] feat(Dunning): separate tab "Address & Contact" (#44363) (cherry picked from commit e094473c65b63942dae8a422107452fa7e72d1ed) # Conflicts: # erpnext/accounts/doctype/dunning/dunning.json --- erpnext/accounts/doctype/dunning/dunning.json | 51 ++++++++++++++----- 1 file changed, 37 insertions(+), 14 deletions(-) diff --git a/erpnext/accounts/doctype/dunning/dunning.json b/erpnext/accounts/doctype/dunning/dunning.json index b7e8aeaaafd..85ce6d555d3 100644 --- a/erpnext/accounts/doctype/dunning/dunning.json +++ b/erpnext/accounts/doctype/dunning/dunning.json @@ -19,16 +19,6 @@ "currency", "column_break_11", "conversion_rate", - "address_and_contact_section", - "customer_address", - "address_display", - "contact_person", - "contact_display", - "column_break_16", - "company_address", - "company_address_display", - "contact_mobile", - "contact_email", "section_break_6", "dunning_type", "column_break_8", @@ -56,7 +46,21 @@ "income_account", "column_break_48", "cost_center", - "amended_from" + "amended_from", + "address_and_contact_tab", + "address_and_contact_section", + "customer_address", + "address_display", + "column_break_vodj", + "contact_person", + "contact_display", + "contact_mobile", + "contact_email", + "section_break_xban", + "column_break_16", + "company_address", + "company_address_display", + "column_break_lqmf" ], "fields": [ { @@ -178,10 +182,8 @@ "label": "Rate of Interest (%) Yearly" }, { - "collapsible": 1, "fieldname": "address_and_contact_section", - "fieldtype": "Section Break", - "label": "Address and Contact" + "fieldtype": "Section Break" }, { "fieldname": "address_display", @@ -377,11 +379,32 @@ { "fieldname": "column_break_48", "fieldtype": "Column Break" + }, + { + "fieldname": "address_and_contact_tab", + "fieldtype": "Tab Break", + "label": "Address & Contact" + }, + { + "fieldname": "column_break_vodj", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_xban", + "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_lqmf", + "fieldtype": "Column Break" } ], "is_submittable": 1, "links": [], +<<<<<<< HEAD "modified": "2023-06-15 15:46:53.865712", +======= + "modified": "2024-11-26 13:46:07.760867", +>>>>>>> e094473c65 (feat(Dunning): separate tab "Address & Contact" (#44363)) "modified_by": "Administrator", "module": "Accounts", "name": "Dunning", From c20def5d59eb0213c492d85e356b88f94fc91672 Mon Sep 17 00:00:00 2001 From: Raffael Meyer <14891507+barredterra@users.noreply.github.com> Date: Tue, 26 Nov 2024 21:12:39 +0100 Subject: [PATCH 621/734] chore: resolve conflicts --- erpnext/accounts/doctype/dunning/dunning.json | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/dunning/dunning.json b/erpnext/accounts/doctype/dunning/dunning.json index 85ce6d555d3..496097417ba 100644 --- a/erpnext/accounts/doctype/dunning/dunning.json +++ b/erpnext/accounts/doctype/dunning/dunning.json @@ -400,11 +400,7 @@ ], "is_submittable": 1, "links": [], -<<<<<<< HEAD - "modified": "2023-06-15 15:46:53.865712", -======= "modified": "2024-11-26 13:46:07.760867", ->>>>>>> e094473c65 (feat(Dunning): separate tab "Address & Contact" (#44363)) "modified_by": "Administrator", "module": "Accounts", "name": "Dunning", @@ -458,4 +454,4 @@ "states": [], "title_field": "customer_name", "track_changes": 1 -} \ No newline at end of file +} From 67809c781a9088b381c51c0c997737e47a8c14df Mon Sep 17 00:00:00 2001 From: venkat102 Date: Tue, 26 Nov 2024 14:58:57 +0530 Subject: [PATCH 622/734] fix: show cc on the email (cherry picked from commit 2dd5699f6d6b3ae465490e60f44f1bcd772265f2) --- .../process_statement_of_accounts.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py index bc3ba26beb7..bf1c8c0b66e 100644 --- a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py +++ b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py @@ -474,6 +474,7 @@ def send_emails(document_name, from_scheduler=False, posting_date=None): reference_doctype="Process Statement Of Accounts", reference_name=document_name, attachments=attachments, + expose_recipients="header", ) if doc.enable_auto_email and from_scheduler: From ef882de509b43c5e7ef1cfd2e08f942df18df17b Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 27 Nov 2024 10:31:30 +0530 Subject: [PATCH 623/734] feat: provision to disable item attribute (backport #44358) (#44370) * feat: provision to disable item attribute (#44358) (cherry picked from commit 123e3ef263cdf36101064fa999180425204b9963) # Conflicts: # erpnext/stock/doctype/item_attribute/item_attribute.json # erpnext/stock/doctype/item_variant_attribute/item_variant_attribute.json * chore: fix conflicts * chore: fix conflicts --------- Co-authored-by: rohitwaghchaure --- erpnext/stock/doctype/item/item.js | 105 +++++++++--------- .../item_attribute/item_attribute.json | 17 ++- .../doctype/item_attribute/item_attribute.py | 14 +++ .../item_variant_attribute.json | 12 +- .../item_variant_attribute.py | 1 + 5 files changed, 95 insertions(+), 54 deletions(-) diff --git a/erpnext/stock/doctype/item/item.js b/erpnext/stock/doctype/item/item.js index 81eeb914af0..4195506ad3b 100644 --- a/erpnext/stock/doctype/item/item.js +++ b/erpnext/stock/doctype/item/item.js @@ -663,39 +663,41 @@ $.extend(erpnext.item, { } frm.doc.attributes.forEach(function (d) { - let p = new Promise((resolve) => { - if (!d.numeric_values) { - frappe - .call({ - method: "frappe.client.get_list", - args: { - doctype: "Item Attribute Value", - filters: [["parent", "=", d.attribute]], - fields: ["attribute_value"], - limit_page_length: 0, - parent: "Item Attribute", - order_by: "idx", - }, - }) - .then((r) => { - if (r.message) { - attr_val_fields[d.attribute] = r.message.map(function (d) { - return d.attribute_value; - }); - resolve(); - } - }); - } else { - let values = []; - for (var i = d.from_range; i <= d.to_range; i = flt(i + d.increment, 6)) { - values.push(i); + if (!d.disabled) { + let p = new Promise((resolve) => { + if (!d.numeric_values) { + frappe + .call({ + method: "frappe.client.get_list", + args: { + doctype: "Item Attribute Value", + filters: [["parent", "=", d.attribute]], + fields: ["attribute_value"], + limit_page_length: 0, + parent: "Item Attribute", + order_by: "idx", + }, + }) + .then((r) => { + if (r.message) { + attr_val_fields[d.attribute] = r.message.map(function (d) { + return d.attribute_value; + }); + resolve(); + } + }); + } else { + let values = []; + for (var i = d.from_range; i <= d.to_range; i = flt(i + d.increment, 6)) { + values.push(i); + } + attr_val_fields[d.attribute] = values; + resolve(); } - attr_val_fields[d.attribute] = values; - resolve(); - } - }); + }); - promises.push(p); + promises.push(p); + } }, this); Promise.all(promises).then(() => { @@ -710,26 +712,29 @@ $.extend(erpnext.item, { for (var i = 0; i < frm.doc.attributes.length; i++) { var fieldtype, desc; var row = frm.doc.attributes[i]; - if (row.numeric_values) { - fieldtype = "Float"; - desc = - "Min Value: " + - row.from_range + - " , Max Value: " + - row.to_range + - ", in Increments of: " + - row.increment; - } else { - fieldtype = "Data"; - desc = ""; + + if (!row.disabled) { + if (row.numeric_values) { + fieldtype = "Float"; + desc = + "Min Value: " + + row.from_range + + " , Max Value: " + + row.to_range + + ", in Increments of: " + + row.increment; + } else { + fieldtype = "Data"; + desc = ""; + } + fields = fields.concat({ + label: row.attribute, + fieldname: row.attribute, + fieldtype: fieldtype, + reqd: 0, + description: desc, + }); } - fields = fields.concat({ - label: row.attribute, - fieldname: row.attribute, - fieldtype: fieldtype, - reqd: 0, - description: desc, - }); } if (frm.doc.image) { diff --git a/erpnext/stock/doctype/item_attribute/item_attribute.json b/erpnext/stock/doctype/item_attribute/item_attribute.json index 5c4678916f3..d9b0898ca7f 100644 --- a/erpnext/stock/doctype/item_attribute/item_attribute.json +++ b/erpnext/stock/doctype/item_attribute/item_attribute.json @@ -10,6 +10,8 @@ "field_order": [ "attribute_name", "numeric_values", + "column_break_vbik", + "disabled", "section_break_4", "from_range", "increment", @@ -70,15 +72,26 @@ "fieldtype": "Table", "label": "Item Attribute Values", "options": "Item Attribute Value" + }, + { + "fieldname": "column_break_vbik", + "fieldtype": "Column Break" + }, + { + "default": "0", + "fieldname": "disabled", + "fieldtype": "Check", + "label": "Disabled" } ], "icon": "fa fa-edit", "index_web_pages_for_search": 1, "links": [], - "modified": "2020-10-02 12:03:02.359202", + "modified": "2024-11-26 20:05:29.421714", "modified_by": "Administrator", "module": "Stock", "name": "Item Attribute", + "naming_rule": "By fieldname", "owner": "Administrator", "permissions": [ { @@ -94,4 +107,4 @@ "sort_field": "modified", "sort_order": "DESC", "track_changes": 1 -} \ No newline at end of file +} diff --git a/erpnext/stock/doctype/item_attribute/item_attribute.py b/erpnext/stock/doctype/item_attribute/item_attribute.py index 04421d6292e..3b9bcf93288 100644 --- a/erpnext/stock/doctype/item_attribute/item_attribute.py +++ b/erpnext/stock/doctype/item_attribute/item_attribute.py @@ -30,6 +30,7 @@ class ItemAttribute(Document): from erpnext.stock.doctype.item_attribute_value.item_attribute_value import ItemAttributeValue attribute_name: DF.Data + disabled: DF.Check from_range: DF.Float increment: DF.Float item_attribute_values: DF.Table[ItemAttributeValue] @@ -47,6 +48,19 @@ class ItemAttribute(Document): def on_update(self): self.validate_exising_items() + self.set_enabled_disabled_in_items() + + def set_enabled_disabled_in_items(self): + db_value = self.get_doc_before_save() + if not db_value or db_value.disabled != self.disabled: + item_variant_table = frappe.qb.DocType("Item Variant Attribute") + query = ( + frappe.qb.update(item_variant_table) + .set(item_variant_table.disabled, self.disabled) + .where(item_variant_table.attribute == self.name) + ) + + query.run() def validate_exising_items(self): """Validate that if there are existing items with attributes, they are valid""" diff --git a/erpnext/stock/doctype/item_variant_attribute/item_variant_attribute.json b/erpnext/stock/doctype/item_variant_attribute/item_variant_attribute.json index 9699ecbb3db..cfc752c1f61 100644 --- a/erpnext/stock/doctype/item_variant_attribute/item_variant_attribute.json +++ b/erpnext/stock/doctype/item_variant_attribute/item_variant_attribute.json @@ -11,6 +11,7 @@ "column_break_2", "attribute_value", "numeric_values", + "disabled", "section_break_4", "from_range", "increment", @@ -74,11 +75,18 @@ "fieldname": "to_range", "fieldtype": "Float", "label": "To Range" + }, + { + "default": "0", + "fetch_from": "attribute.disabled", + "fieldname": "disabled", + "fieldtype": "Check", + "label": "Disabled" } ], "istable": 1, "links": [], - "modified": "2023-07-14 17:15:19.112119", + "modified": "2024-11-26 20:10:49.873339", "modified_by": "Administrator", "module": "Stock", "name": "Item Variant Attribute", @@ -87,4 +95,4 @@ "sort_field": "modified", "sort_order": "DESC", "states": [] -} \ No newline at end of file +} diff --git a/erpnext/stock/doctype/item_variant_attribute/item_variant_attribute.py b/erpnext/stock/doctype/item_variant_attribute/item_variant_attribute.py index 756b7421761..ea239d2ccc9 100644 --- a/erpnext/stock/doctype/item_variant_attribute/item_variant_attribute.py +++ b/erpnext/stock/doctype/item_variant_attribute/item_variant_attribute.py @@ -16,6 +16,7 @@ class ItemVariantAttribute(Document): attribute: DF.Link attribute_value: DF.Data | None + disabled: DF.Check from_range: DF.Float increment: DF.Float numeric_values: DF.Check From a523c14fd574f3e22d90aef3b73d98f7953625d4 Mon Sep 17 00:00:00 2001 From: ljain112 Date: Wed, 27 Nov 2024 13:44:33 +0530 Subject: [PATCH 624/734] fix: correct placeholder index in message (cherry picked from commit d61cb9a4bfa3913b208d2d30f40c9b34639344fb) --- erpnext/accounts/doctype/payment_entry/payment_entry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index b9fad5c9010..3a80b5beb7a 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -1746,7 +1746,7 @@ class PaymentEntry(AccountsController): if paid_amount > total_negative_outstanding: if total_negative_outstanding == 0: frappe.msgprint( - _("Cannot {0} from {2} without any negative outstanding invoice").format( + _("Cannot {0} from {1} without any negative outstanding invoice").format( self.payment_type, self.party_type, ) From 860350a5b329a49d078f70f22c38a0a1a4ca2032 Mon Sep 17 00:00:00 2001 From: vishakhdesai Date: Wed, 27 Nov 2024 12:43:34 +0530 Subject: [PATCH 625/734] fix: remove field precision in SO and PO for percentage fields (cherry picked from commit eff9cd10cd6dcf47ad05311c5730da267482b870) # Conflicts: # erpnext/selling/doctype/sales_order/sales_order_list.js --- .../doctype/purchase_order/purchase_order.js | 22 ++++----- .../doctype/sales_order/sales_order.js | 45 ++++++------------- .../doctype/sales_order/sales_order_list.js | 14 ++++-- 3 files changed, 31 insertions(+), 50 deletions(-) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js index 9718ab19b19..e71f2418794 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.js +++ b/erpnext/buying/doctype/purchase_order/purchase_order.js @@ -93,10 +93,7 @@ frappe.ui.form.on("Purchase Order", { get_materials_from_supplier: function (frm) { let po_details = []; - if ( - frm.doc.supplied_items && - (flt(frm.doc.per_received, precision("per_received")) == 100 || frm.doc.status === "Closed") - ) { + if (frm.doc.supplied_items && (flt(frm.doc.per_received) == 100 || frm.doc.status === "Closed")) { frm.doc.supplied_items.forEach((d) => { if (d.total_supplied_qty && d.total_supplied_qty != d.consumed_qty) { po_details.push(d.name); @@ -332,8 +329,8 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends ( if (!["Closed", "Delivered"].includes(doc.status)) { if ( this.frm.doc.status !== "Closed" && - flt(this.frm.doc.per_received, precision("per_received")) < 100 && - flt(this.frm.doc.per_billed, precision("per_billed")) < 100 + flt(this.frm.doc.per_received) < 100 && + flt(this.frm.doc.per_billed) < 100 ) { if (!this.frm.doc.__onload || this.frm.doc.__onload.can_update_items) { this.frm.add_custom_button(__("Update Items"), () => { @@ -347,10 +344,7 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends ( } } if (this.frm.has_perm("submit")) { - if ( - flt(doc.per_billed, precision("per_billed")) < 100 || - flt(doc.per_received, precision("per_received")) < 100 - ) { + if (flt(doc.per_billed) < 100 || flt(doc.per_received) < 100) { if (doc.status != "On Hold") { this.frm.add_custom_button( __("Hold"), @@ -388,7 +382,7 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends ( } if (doc.status != "Closed") { if (doc.status != "On Hold") { - if (flt(doc.per_received, precision("per_received")) < 100 && allow_receipt) { + if (flt(doc.per_received) < 100 && allow_receipt) { this.frm.add_custom_button( __("Purchase Receipt"), this.make_purchase_receipt, @@ -415,14 +409,14 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends ( } } // Please do not add precision in the below flt function - if (flt(doc.per_billed, precision("per_billed")) < 100) + if (flt(doc.per_billed) < 100) this.frm.add_custom_button( __("Purchase Invoice"), this.make_purchase_invoice, __("Create") ); - if (flt(doc.per_billed, precision("per_billed")) < 100 && doc.status != "Delivered") { + if (flt(doc.per_billed) < 100 && doc.status != "Delivered") { this.frm.add_custom_button( __("Payment"), () => this.make_payment_entry(), @@ -430,7 +424,7 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends ( ); } - if (flt(doc.per_billed, precision("per_billed")) < 100) { + if (flt(doc.per_billed) < 100) { this.frm.add_custom_button( __("Payment Request"), function () { diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index f285d638322..45f6b364761 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -43,8 +43,8 @@ frappe.ui.form.on("Sales Order", { if (frm.doc.docstatus === 1) { if ( frm.doc.status !== "Closed" && - flt(frm.doc.per_delivered, precision("per_delivered")) < 100 && - flt(frm.doc.per_billed, precision("per_billed")) < 100 && + flt(frm.doc.per_delivered) < 100 && + flt(frm.doc.per_billed) < 100 && frm.has_perm("write") ) { frm.add_custom_button(__("Update Items"), () => { @@ -61,7 +61,7 @@ frappe.ui.form.on("Sales Order", { if ( frm.doc.__onload && frm.doc.__onload.has_unreserved_stock && - flt(frm.doc.per_picked, precision("per_picked")) === 0 + flt(frm.doc.per_picked) === 0 ) { frm.add_custom_button( __("Reserve"), @@ -590,10 +590,7 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex __("Status") ); - if ( - flt(doc.per_delivered, precision("per_delivered")) < 100 || - flt(doc.per_billed, precision("per_billed")) < 100 - ) { + if (flt(doc.per_delivered) < 100 || flt(doc.per_billed) < 100) { // close this.frm.add_custom_button(__("Close"), () => this.close_sales_order(), __("Status")); } @@ -616,10 +613,7 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex ) && !this.frm.doc.skip_delivery_note; if (this.frm.has_perm("submit")) { - if ( - flt(doc.per_delivered, precision("per_delivered")) < 100 || - flt(doc.per_billed, precision("per_billed")) < 100 - ) { + if (flt(doc.per_delivered) < 100 || flt(doc.per_billed) < 100) { // hold this.frm.add_custom_button( __("Hold"), @@ -637,8 +631,8 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex if ( (!doc.__onload || !doc.__onload.has_reserved_stock) && - flt(doc.per_picked, precision("per_picked")) < 100 && - flt(doc.per_delivered, precision("per_delivered")) < 100 && + flt(doc.per_picked) < 100 && + flt(doc.per_delivered) < 100 && frappe.model.can_create("Pick List") ) { this.frm.add_custom_button( @@ -656,7 +650,7 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex // delivery note if ( - flt(doc.per_delivered, precision("per_delivered")) < 100 && + flt(doc.per_delivered) < 100 && (order_is_a_sale || order_is_a_custom_sale) && allow_delivery ) { @@ -678,10 +672,7 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex } // sales invoice - if ( - flt(doc.per_billed, precision("per_billed")) < 100 && - frappe.model.can_create("Sales Invoice") - ) { + if (flt(doc.per_billed) < 100 && frappe.model.can_create("Sales Invoice")) { this.frm.add_custom_button( __("Sales Invoice"), () => me.make_sales_invoice(), @@ -692,8 +683,7 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex // material request if ( (!doc.order_type || - ((order_is_a_sale || order_is_a_custom_sale) && - flt(doc.per_delivered, precision("per_delivered")) < 100)) && + ((order_is_a_sale || order_is_a_custom_sale) && flt(doc.per_delivered) < 100)) && frappe.model.can_create("Material Request") ) { this.frm.add_custom_button( @@ -718,10 +708,7 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex } // maintenance - if ( - flt(doc.per_delivered, precision("per_delivered")) < 100 && - (order_is_maintenance || order_is_a_custom_sale) - ) { + if (flt(doc.per_delivered) < 100 && (order_is_maintenance || order_is_a_custom_sale)) { if (frappe.model.can_create("Maintenance Visit")) { this.frm.add_custom_button( __("Maintenance Visit"), @@ -739,10 +726,7 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex } // project - if ( - flt(doc.per_delivered, precision("per_delivered")) < 100 && - frappe.model.can_create("Project") - ) { + if (flt(doc.per_delivered) < 100 && frappe.model.can_create("Project")) { this.frm.add_custom_button(__("Project"), () => this.make_project(), __("Create")); } @@ -770,10 +754,7 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex } } // payment request - if ( - flt(doc.per_billed, precision("per_billed", doc)) < - 100 + frappe.boot.sysdefaults.over_billing_allowance - ) { + if (flt(doc.per_billed) < 100 + frappe.boot.sysdefaults.over_billing_allowance) { this.frm.add_custom_button( __("Payment Request"), () => this.make_payment_request(), diff --git a/erpnext/selling/doctype/sales_order/sales_order_list.js b/erpnext/selling/doctype/sales_order/sales_order_list.js index 5ccd5d551cb..73ef164f5c1 100644 --- a/erpnext/selling/doctype/sales_order/sales_order_list.js +++ b/erpnext/selling/doctype/sales_order/sales_order_list.js @@ -20,14 +20,20 @@ frappe.listview_settings["Sales Order"] = { return [__("On Hold"), "orange", "status,=,On Hold"]; } else if (doc.status === "Completed") { return [__("Completed"), "green", "status,=,Completed"]; +<<<<<<< HEAD } else if (!doc.skip_delivery_note && flt(doc.per_delivered, 2) < 100) { +======= + } else if (doc.advance_payment_status === "Requested") { + return [__("To Pay"), "gray", "advance_payment_status,=,Requested"]; + } else if (!doc.skip_delivery_note && flt(doc.per_delivered) < 100) { +>>>>>>> eff9cd10cd (fix: remove field precision in SO and PO for percentage fields) if (frappe.datetime.get_diff(doc.delivery_date) < 0) { // not delivered & overdue return [__("Overdue"), "red", "per_delivered,<,100|delivery_date,<,Today|status,!=,Closed"]; } else if (flt(doc.grand_total) === 0) { // not delivered (zeroount order) return [__("To Deliver"), "orange", "per_delivered,<,100|grand_total,=,0|status,!=,Closed"]; - } else if (flt(doc.per_billed, 2) < 100) { + } else if (flt(doc.per_billed) < 100) { // not delivered & not billed return [ __("To Deliver and Bill"), @@ -39,13 +45,13 @@ frappe.listview_settings["Sales Order"] = { return [__("To Deliver"), "orange", "per_delivered,<,100|per_billed,=,100|status,!=,Closed"]; } } else if ( - flt(doc.per_delivered, 2) === 100 && + flt(doc.per_delivered) === 100 && flt(doc.grand_total) !== 0 && - flt(doc.per_billed, 2) < 100 + flt(doc.per_billed) < 100 ) { // to bill return [__("To Bill"), "orange", "per_delivered,=,100|per_billed,<,100|status,!=,Closed"]; - } else if (doc.skip_delivery_note && flt(doc.per_billed, 2) < 100) { + } else if (doc.skip_delivery_note && flt(doc.per_billed) < 100) { return [__("To Bill"), "orange", "per_billed,<,100|status,!=,Closed"]; } }, From ca56709295002f86c0b936a75ed250415784ea06 Mon Sep 17 00:00:00 2001 From: ljain112 Date: Mon, 25 Nov 2024 17:31:07 +0530 Subject: [PATCH 626/734] fix: update gross profit for returned invoices (cherry picked from commit 8a42601e99a298782f43f252c64b1d692875d202) --- .../report/gross_profit/gross_profit.py | 65 +++++++++---------- 1 file changed, 32 insertions(+), 33 deletions(-) diff --git a/erpnext/accounts/report/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py index a9039a9cada..12a4ca76aab 100644 --- a/erpnext/accounts/report/gross_profit/gross_profit.py +++ b/erpnext/accounts/report/gross_profit/gross_profit.py @@ -440,6 +440,7 @@ class GrossProfitGenerator: if grouped_by_invoice: buying_amount = 0 + base_amount = 0 for row in reversed(self.si_list): if self.filters.get("group_by") == "Monthly": @@ -480,12 +481,11 @@ class GrossProfitGenerator: else: row.buying_amount = flt(self.get_buying_amount(row, row.item_code), self.currency_precision) - if grouped_by_invoice: - if row.indent == 1.0: - buying_amount += row.buying_amount - elif row.indent == 0.0: - row.buying_amount = buying_amount - buying_amount = 0 + if grouped_by_invoice and row.indent == 0.0: + row.buying_amount = buying_amount + row.base_amount = base_amount + buying_amount = 0 + base_amount = 0 # get buying rate if flt(row.qty): @@ -495,11 +495,19 @@ class GrossProfitGenerator: if self.is_not_invoice_row(row): row.buying_rate, row.base_rate = 0.0, 0.0 + if self.is_not_invoice_row(row): + self.update_return_invoices(row) + + if grouped_by_invoice and row.indent == 1.0: + buying_amount += row.buying_amount + base_amount += row.base_amount + # calculate gross profit row.gross_profit = flt(row.base_amount - row.buying_amount, self.currency_precision) if row.base_amount: row.gross_profit_percent = flt( - (row.gross_profit / row.base_amount) * 100.0, self.currency_precision + (row.gross_profit / row.base_amount) * 100.0, + self.currency_precision, ) else: row.gross_profit_percent = 0.0 @@ -510,33 +518,24 @@ class GrossProfitGenerator: if self.grouped: self.get_average_rate_based_on_group_by() + def update_return_invoices(self, row): + if row.parent in self.returned_invoices and row.item_code in self.returned_invoices[row.parent]: + returned_item_rows = self.returned_invoices[row.parent][row.item_code] + for returned_item_row in returned_item_rows: + # returned_items 'qty' should be stateful + if returned_item_row.qty != 0: + if row.qty >= abs(returned_item_row.qty): + row.qty += returned_item_row.qty + returned_item_row.qty = 0 + else: + row.qty = 0 + returned_item_row.qty += row.qty + row.base_amount += flt(returned_item_row.base_amount, self.currency_precision) + row.buying_amount = flt(flt(row.qty) * flt(row.buying_rate), self.currency_precision) + def get_average_rate_based_on_group_by(self): for key in list(self.grouped): - if self.filters.get("group_by") == "Invoice": - for row in self.grouped[key]: - if row.indent == 1.0: - if ( - row.parent in self.returned_invoices - and row.item_code in self.returned_invoices[row.parent] - ): - returned_item_rows = self.returned_invoices[row.parent][row.item_code] - for returned_item_row in returned_item_rows: - # returned_items 'qty' should be stateful - if returned_item_row.qty != 0: - if row.qty >= abs(returned_item_row.qty): - row.qty += returned_item_row.qty - returned_item_row.qty = 0 - else: - row.qty = 0 - returned_item_row.qty += row.qty - row.base_amount += flt(returned_item_row.base_amount, self.currency_precision) - row.buying_amount = flt( - flt(row.qty) * flt(row.buying_rate), self.currency_precision - ) - if flt(row.qty) or row.base_amount: - row = self.set_average_rate(row) - self.grouped_data.append(row) - elif self.filters.get("group_by") == "Payment Term": + if self.filters.get("group_by") == "Payment Term": for i, row in enumerate(self.grouped[key]): invoice_portion = 0 @@ -556,7 +555,7 @@ class GrossProfitGenerator: new_row = self.set_average_rate(new_row) self.grouped_data.append(new_row) - else: + elif self.filters.get("group_by") != "Invoice": for i, row in enumerate(self.grouped[key]): if i == 0: new_row = row From f4518cac9a3ae9b4a018b24f3ea29ec525e2236a Mon Sep 17 00:00:00 2001 From: ljain112 Date: Mon, 25 Nov 2024 18:45:17 +0530 Subject: [PATCH 627/734] fix: gp for return invoice (cherry picked from commit 00403515a8ba005e5ac5c0e7a5ff88a5bda5b170) --- erpnext/accounts/report/gross_profit/gross_profit.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/report/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py index 12a4ca76aab..5ba1e41b624 100644 --- a/erpnext/accounts/report/gross_profit/gross_profit.py +++ b/erpnext/accounts/report/gross_profit/gross_profit.py @@ -526,11 +526,16 @@ class GrossProfitGenerator: if returned_item_row.qty != 0: if row.qty >= abs(returned_item_row.qty): row.qty += returned_item_row.qty + row.base_amount += flt(returned_item_row.base_amount, self.currency_precision) returned_item_row.qty = 0 + returned_item_row.base_amount = 0 + else: row.qty = 0 + row.base_amount = 0 returned_item_row.qty += row.qty - row.base_amount += flt(returned_item_row.base_amount, self.currency_precision) + returned_item_row.base_amount += row.base_amount + row.buying_amount = flt(flt(row.qty) * flt(row.buying_rate), self.currency_precision) def get_average_rate_based_on_group_by(self): From 66af7f4a14e58029f80aa87b62a056c04be60969 Mon Sep 17 00:00:00 2001 From: ljain112 Date: Mon, 25 Nov 2024 19:05:08 +0530 Subject: [PATCH 628/734] fix: test case (cherry picked from commit af5a3e5a48c7da329a078c3d0e177593061d123b) --- erpnext/accounts/report/gross_profit/test_gross_profit.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/report/gross_profit/test_gross_profit.py b/erpnext/accounts/report/gross_profit/test_gross_profit.py index 83de93891fe..721be79ed88 100644 --- a/erpnext/accounts/report/gross_profit/test_gross_profit.py +++ b/erpnext/accounts/report/gross_profit/test_gross_profit.py @@ -418,12 +418,12 @@ class TestGrossProfit(FrappeTestCase): "item_name": self.item, "warehouse": "Stores - _GP", "qty": 0.0, - "avg._selling_rate": 0.0, + "avg._selling_rate": 100, "valuation_rate": 0.0, - "selling_amount": -100.0, + "selling_amount": 0.0, "buying_amount": 0.0, - "gross_profit": -100.0, - "gross_profit_%": 100.0, + "gross_profit": 0.0, + "gross_profit_%": 0.0, } gp_entry = [x for x in data if x.parent_invoice == sinv.name] # Both items of Invoice should have '0' qty From d6ef43858cee5b3a05a306588c258eb1d41b7031 Mon Sep 17 00:00:00 2001 From: venkat102 Date: Tue, 26 Nov 2024 22:13:57 +0530 Subject: [PATCH 629/734] fix: check difference with company currency (cherry picked from commit e2bae4cf07918adf815384b3db81717df95d9413) --- .../doctype/period_closing_voucher/period_closing_voucher.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py index 1d4ee25241e..7e0145e91a4 100644 --- a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py +++ b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py @@ -171,9 +171,7 @@ class PeriodClosingVoucher(AccountsController): pl_account_balances = self.get_account_balances_based_on_dimensions(report_type="Profit and Loss") for dimensions, account_balances in pl_account_balances.items(): for acc, balances in account_balances.items(): - balance_in_company_currency = flt(balances.debit_in_account_currency) - flt( - balances.credit_in_account_currency - ) + balance_in_company_currency = flt(balances.debit) - flt(balances.credit) if balance_in_company_currency and acc != "balances": self.pl_accounts_reverse_gle.append( self.get_gle_for_pl_account(acc, balances, dimensions) From 45e41827c76526412ec29e5d5678d90360f65631 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 27 Nov 2024 17:20:36 +0530 Subject: [PATCH 630/734] chore: resolve conflict --- erpnext/selling/doctype/sales_order/sales_order_list.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/erpnext/selling/doctype/sales_order/sales_order_list.js b/erpnext/selling/doctype/sales_order/sales_order_list.js index 73ef164f5c1..46d115a1713 100644 --- a/erpnext/selling/doctype/sales_order/sales_order_list.js +++ b/erpnext/selling/doctype/sales_order/sales_order_list.js @@ -20,13 +20,7 @@ frappe.listview_settings["Sales Order"] = { return [__("On Hold"), "orange", "status,=,On Hold"]; } else if (doc.status === "Completed") { return [__("Completed"), "green", "status,=,Completed"]; -<<<<<<< HEAD - } else if (!doc.skip_delivery_note && flt(doc.per_delivered, 2) < 100) { -======= - } else if (doc.advance_payment_status === "Requested") { - return [__("To Pay"), "gray", "advance_payment_status,=,Requested"]; } else if (!doc.skip_delivery_note && flt(doc.per_delivered) < 100) { ->>>>>>> eff9cd10cd (fix: remove field precision in SO and PO for percentage fields) if (frappe.datetime.get_diff(doc.delivery_date) < 0) { // not delivered & overdue return [__("Overdue"), "red", "per_delivered,<,100|delivery_date,<,Today|status,!=,Closed"]; From d073b005a8579e48266d1265ccf542f625ac595b Mon Sep 17 00:00:00 2001 From: venkat102 Date: Wed, 27 Nov 2024 14:02:11 +0530 Subject: [PATCH 631/734] fix: filter item with search fields (cherry picked from commit ebfbee3da50961019691d650d7d152502d541094) --- .../doctype/product_bundle/product_bundle.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/erpnext/selling/doctype/product_bundle/product_bundle.py b/erpnext/selling/doctype/product_bundle/product_bundle.py index 464cff01d7e..609f45a608f 100644 --- a/erpnext/selling/doctype/product_bundle/product_bundle.py +++ b/erpnext/selling/doctype/product_bundle/product_bundle.py @@ -5,6 +5,7 @@ import frappe from frappe import _ from frappe.model.document import Document +from frappe.query_builder import Criterion from frappe.utils import get_link_to_form @@ -93,15 +94,24 @@ class ProductBundle(Document): def get_new_item_code(doctype, txt, searchfield, start, page_len, filters): product_bundles = frappe.db.get_list("Product Bundle", {"disabled": 0}, pluck="name") + if not searchfield or searchfield == "name": + searchfield = frappe.get_meta("Item").get("search_fields") + + searchfield = searchfield.split(",") + searchfield.append("name") + item = frappe.qb.DocType("Item") query = ( frappe.qb.from_(item) - .select(item.item_code, item.item_name) - .where((item.is_stock_item == 0) & (item.is_fixed_asset == 0) & (item[searchfield].like(f"%{txt}%"))) + .select(item.name, item.item_name) + .where((item.is_stock_item == 0) & (item.is_fixed_asset == 0)) .limit(page_len) .offset(start) ) + if searchfield: + query = query.where(Criterion.any([item[fieldname].like(f"%{txt}%") for fieldname in searchfield])) + if product_bundles: query = query.where(item.name.notin(product_bundles)) From a2612d5f36eaeee10231b27b5c4bfea2c79208da Mon Sep 17 00:00:00 2001 From: venkat102 Date: Tue, 26 Nov 2024 00:03:42 +0530 Subject: [PATCH 632/734] fix: set debit transaction currency in gl entry (cherry picked from commit 6e19c06e58dd2ffa9cb4bee014a7c0671ff8bf80) --- .../doctype/payment_entry/payment_entry.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 3a80b5beb7a..89f52b46679 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -1219,11 +1219,19 @@ class PaymentEntry(AccountsController): dr_or_cr = "debit" if dr_or_cr == "credit" else "credit" gle.update( - { - dr_or_cr: allocated_amount_in_company_currency, - dr_or_cr + "_in_account_currency": d.allocated_amount, - "cost_center": cost_center, - } + self.get_gl_dict( + { + "account": self.party_account, + "party_type": self.party_type, + "party": self.party, + "against": against_account, + "account_currency": self.party_account_currency, + "cost_center": cost_center, + dr_or_cr + "_in_account_currency": d.allocated_amount, + dr_or_cr: allocated_amount_in_company_currency, + }, + item=self, + ) ) if self.book_advance_payments_in_separate_party_account: From 0e1f5ff391b7a6f277149e5a14203f4cde7fac6c Mon Sep 17 00:00:00 2001 From: Abdeali Chharchhoda Date: Wed, 27 Nov 2024 16:53:50 +0530 Subject: [PATCH 633/734] fix: Add translation for showing mandatory fields in error msg (cherry picked from commit f42ec6a124d272dec0ac9671b7eef13f55d81de2) --- erpnext/selling/doctype/quotation/quotation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index 8e560b8d0ab..2bc6ef8399e 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -568,7 +568,7 @@ def handle_mandatory_error(e, customer, lead_name): from frappe.utils import get_link_to_form mandatory_fields = e.args[0].split(":")[1].split(",") - mandatory_fields = [customer.meta.get_label(field.strip()) for field in mandatory_fields] + mandatory_fields = [_(customer.meta.get_label(field.strip())) for field in mandatory_fields] frappe.local.message_log = [] message = _("Could not auto create Customer due to the following missing mandatory field(s):") + "
" From fd1cbf4b6f83dbcc8c955490120351197ffc5d7f Mon Sep 17 00:00:00 2001 From: vimalraj27 Date: Wed, 27 Nov 2024 18:03:39 +0530 Subject: [PATCH 634/734] chore: Fix typo "Purchase Reecipt" (cherry picked from commit 21049bae91846548d0640d9d63c2f2e20d35e4ea) --- erpnext/controllers/sales_and_purchase_return.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py index 32234e5c551..7f9a5a35a73 100644 --- a/erpnext/controllers/sales_and_purchase_return.py +++ b/erpnext/controllers/sales_and_purchase_return.py @@ -1036,7 +1036,7 @@ def filter_serial_batches(parent_doc, data, row, warehouse_field=None, qty_field available_serial_nos.append(serial_no) if available_serial_nos: - if parent_doc.doctype in ["Purchase Invoice", "Purchase Reecipt"]: + if parent_doc.doctype in ["Purchase Invoice", "Purchase Receipt"]: available_serial_nos = get_available_serial_nos(available_serial_nos, warehouse) if len(available_serial_nos) > qty: @@ -1052,7 +1052,7 @@ def filter_serial_batches(parent_doc, data, row, warehouse_field=None, qty_field if batch_qty <= 0: continue - if parent_doc.doctype in ["Purchase Invoice", "Purchase Reecipt"]: + if parent_doc.doctype in ["Purchase Invoice", "Purchase Receipt"]: batch_qty = get_available_batch_qty( parent_doc, batch_no, From 3f577779be3c8a2140ffb24947252c15b848a008 Mon Sep 17 00:00:00 2001 From: Ninad1306 Date: Mon, 25 Nov 2024 10:44:25 +0530 Subject: [PATCH 635/734] fix: initially closing amt should be equal to expected amt (cherry picked from commit af9524920bc1de7587b8f73363edeab2a486e5a8) --- .../doctype/pos_closing_entry/pos_closing_entry.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js index 5711b27da04..7504c79141b 100644 --- a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js +++ b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js @@ -147,7 +147,7 @@ frappe.ui.form.on("POS Closing Entry", { frm.doc.grand_total += flt(doc.grand_total); frm.doc.net_total += flt(doc.net_total); frm.doc.total_quantity += flt(doc.total_qty); - refresh_payments(doc, frm); + refresh_payments(doc, frm, false); refresh_taxes(doc, frm); refresh_fields(frm); set_html_data(frm); @@ -172,7 +172,7 @@ function set_form_data(data, frm) { frm.doc.grand_total += flt(d.grand_total); frm.doc.net_total += flt(d.net_total); frm.doc.total_quantity += flt(d.total_qty); - refresh_payments(d, frm); + refresh_payments(d, frm, true); refresh_taxes(d, frm); }); } @@ -186,7 +186,7 @@ function add_to_pos_transaction(d, frm) { }); } -function refresh_payments(d, frm) { +function refresh_payments(d, frm, is_new) { d.payments.forEach((p) => { const payment = frm.doc.payment_reconciliation.find( (pay) => pay.mode_of_payment === p.mode_of_payment @@ -196,9 +196,7 @@ function refresh_payments(d, frm) { } if (payment) { payment.expected_amount += flt(p.amount); - if (payment.closing_amount === 0) { - payment.closing_amount = payment.expected_amount; - } + if (is_new) payment.closing_amount = payment.expected_amount; payment.difference = payment.closing_amount - payment.expected_amount; } else { frm.add_child("payment_reconciliation", { From 8d8027d4233f9181a34ee00b33ff15da2cae2da4 Mon Sep 17 00:00:00 2001 From: Sugesh393 Date: Tue, 26 Nov 2024 13:58:52 +0530 Subject: [PATCH 636/734] fix: set outstanding amount while creating payment request for invoices (cherry picked from commit 38e7d0a41e2a55ef98f2c6147c34b64708929854) --- erpnext/accounts/doctype/payment_request/payment_request.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index ae974a8cf0e..aaae8204a9b 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -667,11 +667,9 @@ def get_amount(ref_doc, payment_account=None): elif dt in ["Sales Invoice", "Purchase Invoice"]: if not ref_doc.get("is_pos"): if ref_doc.party_account_currency == ref_doc.currency: - grand_total = flt(ref_doc.rounded_total or ref_doc.grand_total) + grand_total = flt(ref_doc.outstanding_amount) else: - grand_total = flt( - flt(ref_doc.base_rounded_total or ref_doc.base_grand_total) / ref_doc.conversion_rate - ) + grand_total = flt(flt(ref_doc.outstanding_amount) / ref_doc.conversion_rate) elif dt == "Sales Invoice": for pay in ref_doc.payments: if pay.type == "Phone" and pay.account == payment_account: From 81b9832917ebf5487f5471b007d47278252cde02 Mon Sep 17 00:00:00 2001 From: Sugesh393 Date: Tue, 26 Nov 2024 13:59:45 +0530 Subject: [PATCH 637/734] test: add unit test to validate outstanding amount in payment request (cherry picked from commit bbe3bc95d08950bebc9ede64c11c9660a564a8a6) # Conflicts: # erpnext/accounts/doctype/payment_request/test_payment_request.py --- .../payment_request/test_payment_request.py | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/erpnext/accounts/doctype/payment_request/test_payment_request.py b/erpnext/accounts/doctype/payment_request/test_payment_request.py index b0c3dbf4d5b..cd5a1a26575 100644 --- a/erpnext/accounts/doctype/payment_request/test_payment_request.py +++ b/erpnext/accounts/doctype/payment_request/test_payment_request.py @@ -7,6 +7,7 @@ import unittest import frappe from frappe.tests.utils import FrappeTestCase, change_settings +from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_terms_template from erpnext.accounts.doctype.payment_request.payment_request import make_payment_request from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice @@ -524,3 +525,25 @@ class TestPaymentRequest(FrappeTestCase): self.assertEqual(pr.grand_total, 1000) so.load_from_db() +<<<<<<< HEAD +======= + self.assertEqual(so.advance_payment_status, "Requested") + + def test_partial_paid_invoice_with_payment_request(self): + si = create_sales_invoice(currency="INR", qty=1, rate=5000) + si.save() + si.submit() + + pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank - _TC") + pe.reference_no = "PAYEE0002" + pe.reference_date = frappe.utils.nowdate() + pe.paid_amount = 2500 + pe.references[0].allocated_amount = 2500 + pe.save() + pe.submit() + + si.load_from_db() + pr = make_payment_request(dt="Sales Invoice", dn=si.name, mute_email=1) + + self.assertEqual(pr.grand_total, si.outstanding_amount) +>>>>>>> bbe3bc95d0 (test: add unit test to validate outstanding amount in payment request) From d0d97c26a05ab961f88ece2609698ff37f3a5273 Mon Sep 17 00:00:00 2001 From: Sugesh393 Date: Wed, 27 Nov 2024 17:41:16 +0530 Subject: [PATCH 638/734] fix: reduce paid amount from grand total (cherry picked from commit 82907672d938b2b447f0f7382e8fc4e4c343e708) --- .../payment_request/payment_request.py | 40 +++++++++++++++++-- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index aaae8204a9b..1d31fd4b67b 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -3,7 +3,7 @@ import json import frappe from frappe import _, qb from frappe.model.document import Document -from frappe.query_builder.functions import Sum +from frappe.query_builder.functions import Abs, Sum from frappe.utils import flt, nowdate from frappe.utils.background_jobs import enqueue @@ -564,6 +564,8 @@ def make_payment_request(**args): # fetches existing payment request `grand_total` amount existing_payment_request_amount = get_existing_payment_request_amount(ref_doc.doctype, ref_doc.name) + existing_paid_amount = get_existing_paid_amount(ref_doc.doctype, ref_doc.name) + def validate_and_calculate_grand_total(grand_total, existing_payment_request_amount): grand_total -= existing_payment_request_amount if not grand_total: @@ -583,6 +585,15 @@ def make_payment_request(**args): else: grand_total = validate_and_calculate_grand_total(grand_total, existing_payment_request_amount) + if existing_paid_amount: + if ref_doc.party_account_currency == ref_doc.currency: + if ref_doc.conversion_rate: + grand_total -= flt(existing_paid_amount / ref_doc.conversion_rate) + else: + grand_total -= flt(existing_paid_amount) + else: + grand_total -= flt(existing_paid_amount / ref_doc.conversion_rate) + if draft_payment_request: frappe.db.set_value( "Payment Request", draft_payment_request, "grand_total", grand_total, update_modified=False @@ -667,9 +678,11 @@ def get_amount(ref_doc, payment_account=None): elif dt in ["Sales Invoice", "Purchase Invoice"]: if not ref_doc.get("is_pos"): if ref_doc.party_account_currency == ref_doc.currency: - grand_total = flt(ref_doc.outstanding_amount) + grand_total = flt(ref_doc.rounded_total or ref_doc.grand_total) else: - grand_total = flt(flt(ref_doc.outstanding_amount) / ref_doc.conversion_rate) + grand_total = flt( + flt(ref_doc.base_rounded_total or ref_doc.base_grand_total) / ref_doc.conversion_rate + ) elif dt == "Sales Invoice": for pay in ref_doc.payments: if pay.type == "Phone" and pay.account == payment_account: @@ -751,6 +764,27 @@ def get_existing_payment_request_amount(ref_dt, ref_dn, statuses: list | None = return response[0][0] if response[0] else 0 +def get_existing_paid_amount(doctype, name): + PL = frappe.qb.DocType("Payment Ledger Entry") + PER = frappe.qb.DocType("Payment Entry Reference") + + query = ( + frappe.qb.from_(PL) + .left_join(PER) + .on( + (PER.reference_doctype == PL.against_voucher_type) & (PER.reference_name == PL.against_voucher_no) + ) + .select(Abs(Sum(PL.amount)).as_("total_paid_amount")) + .where(PL.against_voucher_type.eq(doctype)) + .where(PL.against_voucher_no.eq(name)) + .where(PL.amount < 0) + .where(PER.payment_request.isnull()) + ) + response = query.run() + + return response[0][0] if response[0] else 0 + + def get_gateway_details(args): # nosemgrep """ Return gateway and payment account of default payment gateway From 0ab0b4f716acee6837e7c62f875a99c2d11881ef Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 27 Nov 2024 20:29:36 +0530 Subject: [PATCH 639/734] chore: resolve conflict --- .../accounts/doctype/payment_request/test_payment_request.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/erpnext/accounts/doctype/payment_request/test_payment_request.py b/erpnext/accounts/doctype/payment_request/test_payment_request.py index cd5a1a26575..4442dbdd7ea 100644 --- a/erpnext/accounts/doctype/payment_request/test_payment_request.py +++ b/erpnext/accounts/doctype/payment_request/test_payment_request.py @@ -525,9 +525,6 @@ class TestPaymentRequest(FrappeTestCase): self.assertEqual(pr.grand_total, 1000) so.load_from_db() -<<<<<<< HEAD -======= - self.assertEqual(so.advance_payment_status, "Requested") def test_partial_paid_invoice_with_payment_request(self): si = create_sales_invoice(currency="INR", qty=1, rate=5000) @@ -546,4 +543,3 @@ class TestPaymentRequest(FrappeTestCase): pr = make_payment_request(dt="Sales Invoice", dn=si.name, mute_email=1) self.assertEqual(pr.grand_total, si.outstanding_amount) ->>>>>>> bbe3bc95d0 (test: add unit test to validate outstanding amount in payment request) From 294ded20300f2aa3b08bf9bd8e59a20397b68235 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 27 Nov 2024 16:01:32 +0000 Subject: [PATCH 640/734] chore(release): Bumped to Version 15.44.0 # [15.44.0](https://github.com/frappe/erpnext/compare/v15.43.3...v15.44.0) (2024-11-27) ### Bug Fixes * correct placeholder index in message ([a523c14](https://github.com/frappe/erpnext/commit/a523c14fd574f3e22d90aef3b73d98f7953625d4)) * add company dynamic filters in number cards ([db21def](https://github.com/frappe/erpnext/commit/db21def58b21938dd3b2fc51fd0d432a97202665)) * added Stock UOM field for RM in work order (backport [#44185](https://github.com/frappe/erpnext/issues/44185)) ([#44237](https://github.com/frappe/erpnext/issues/44237)) ([d4f0512](https://github.com/frappe/erpnext/commit/d4f0512a10550914b2aad1795fe8692658aab67a)) * added validation for quality inspection (backport [#44351](https://github.com/frappe/erpnext/issues/44351)) ([#44357](https://github.com/frappe/erpnext/issues/44357)) ([89bd4eb](https://github.com/frappe/erpnext/commit/89bd4eba46894efba5cfe9bc3411448d41366807)) * billed qty and received amount in PO analysis report (backport [#44349](https://github.com/frappe/erpnext/issues/44349)) ([#44354](https://github.com/frappe/erpnext/issues/44354)) ([ea0f24a](https://github.com/frappe/erpnext/commit/ea0f24aa573e66d289be4af567405e072eb73968)) * check difference with company currency ([d6ef438](https://github.com/frappe/erpnext/commit/d6ef43858cee5b3a05a306588c258eb1d41b7031)) * filter item with search fields ([d073b00](https://github.com/frappe/erpnext/commit/d073b005a8579e48266d1265ccf542f625ac595b)) * filter with item group only if it is mentioned in pos profile ([a439862](https://github.com/frappe/erpnext/commit/a4398626f66bae98b520d75f72e9d00222789174)) * Get submitted documents in validate_for_closed_fiscal_year ([af74a3c](https://github.com/frappe/erpnext/commit/af74a3c32fb5ec83439ed3417706aa3d1a0deefd)) * gp for return invoice ([f4518ca](https://github.com/frappe/erpnext/commit/f4518cac9a3ae9b4a018b24f3ea29ec525e2236a)) * include current invoice amount when tax_on_excess_amount is checked ([0ffeb9f](https://github.com/frappe/erpnext/commit/0ffeb9f6adbc581ecc9a0403c2680079213f9d93)) * Increase quantity by `1 UOM` when adding an item from the selector in POS ([0d38028](https://github.com/frappe/erpnext/commit/0d3802873b62dae8c346a9e256ebaa04050acff6)) * initially closing amt should be equal to expected amt ([3f57777](https://github.com/frappe/erpnext/commit/3f577779be3c8a2140ffb24947252c15b848a008)) * make free qty round on large transaction qty ([4856a96](https://github.com/frappe/erpnext/commit/4856a9633e2e6cd46cf001799f01c7d290514b6a)) * no permission to read Doctype (backport [#44256](https://github.com/frappe/erpnext/issues/44256)) ([#44258](https://github.com/frappe/erpnext/issues/44258)) ([8b15a96](https://github.com/frappe/erpnext/commit/8b15a965dd0ffea82c1d55a2893a79cf1fd243bb)) * not able to fetch batch item price ([97f2341](https://github.com/frappe/erpnext/commit/97f2341b98ea0cdb84cbf7dbaebdd32b208824b6)) * patch ([#44191](https://github.com/frappe/erpnext/issues/44191)) ([8b02402](https://github.com/frappe/erpnext/commit/8b02402f6295296e9465d813996c90e08ffa889e)) * reduce paid amount from grand total ([d0d97c2](https://github.com/frappe/erpnext/commit/d0d97c26a05ab961f88ece2609698ff37f3a5273)) * remove field precision in SO and PO for percentage fields ([860350a](https://github.com/frappe/erpnext/commit/860350a5b329a49d078f70f22c38a0a1a4ca2032)) * remove irrelavent filters ([7423d7d](https://github.com/frappe/erpnext/commit/7423d7d3372aecb0b6003e67313322061bdea673)) * set debit transaction currency in gl entry ([a2612d5](https://github.com/frappe/erpnext/commit/a2612d5f36eaeee10231b27b5c4bfea2c79208da)) * set outstanding amount while creating payment request for invoices ([8d8027d](https://github.com/frappe/erpnext/commit/8d8027d4233f9181a34ee00b33ff15da2cae2da4)) * set price_list_currency only if it exists ([8cd455b](https://github.com/frappe/erpnext/commit/8cd455b050c9687bc1c5cb91dd6360035cea4f30)) * Show available stock qty in `stock_uom` instead of `uom` ([49dad1a](https://github.com/frappe/erpnext/commit/49dad1a456bd22d154deafbfc5b27930721b2240)) * show cc on the email ([67809c7](https://github.com/frappe/erpnext/commit/67809c781a9088b381c51c0c997737e47a8c14df)) * test case ([66af7f4](https://github.com/frappe/erpnext/commit/66af7f4a14e58029f80aa87b62a056c04be60969)) * toggle debit credit amounts for transaction currency too; minor refactor ([7f8334f](https://github.com/frappe/erpnext/commit/7f8334f29a83a6e85fe32dcc9a2247c90046acee)) * unify company address query in sales transactions (backport [#44361](https://github.com/frappe/erpnext/issues/44361)) ([#44365](https://github.com/frappe/erpnext/issues/44365)) ([7e61aca](https://github.com/frappe/erpnext/commit/7e61aca512c18adca6f8eb5b2300287898e8f3e4)) * update gross profit for returned invoices ([ca56709](https://github.com/frappe/erpnext/commit/ca56709295002f86c0b936a75ed250415784ea06)) * use field precision instead of hardcoded precision in so and po ([cde1906](https://github.com/frappe/erpnext/commit/cde19066fe6b42aca5cae6971805973ac18a01dd)) ### Features * available qty at company in sales transactions (backport [#44260](https://github.com/frappe/erpnext/issues/44260)) ([#44325](https://github.com/frappe/erpnext/issues/44325)) ([20d0e95](https://github.com/frappe/erpnext/commit/20d0e95d7c3de85e0a0364dd9e7954f3aa6dbeee)) * provision to disable item attribute (backport [#44358](https://github.com/frappe/erpnext/issues/44358)) ([#44370](https://github.com/frappe/erpnext/issues/44370)) ([ef882de](https://github.com/frappe/erpnext/commit/ef882de509b43c5e7ef1cfd2e08f942df18df17b)) * Show Aggregate Value from Subsidiary Companies ([0469b0d](https://github.com/frappe/erpnext/commit/0469b0d1ecca4b5d13232d3ba5a7f4275a582da4)) ### Reverts * use `+ flt(value)` instead of direct increment ([b65e16a](https://github.com/frappe/erpnext/commit/b65e16a91b71ca7e674c04d08c2a01a8a0fb1079)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index babb8246bdb..82b86724482 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.43.3" +__version__ = "15.44.0" def get_default_company(user=None): From 70b5b08d588ea4e9132f2ab51846a16d4da04241 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 27 Nov 2024 18:55:43 +0100 Subject: [PATCH 641/734] feat: add Company Contact Person in selling transactions (backport #44362) (#44398) * feat: add Company Contact Person in selling transactions (#44362) (cherry picked from commit f6776c7d6b37ce2766244dfeaf5939bda1a183ba) * chore: resolve merge conflicts --------- Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com> --- .../accounts/doctype/pos_invoice/pos_invoice.json | 10 +++++++++- .../accounts/doctype/pos_invoice/pos_invoice.py | 9 +++------ .../doctype/sales_invoice/sales_invoice.json | 14 +++++++++++--- .../doctype/sales_invoice/sales_invoice.py | 1 + erpnext/public/js/queries.js | 11 +++++++++++ erpnext/public/js/utils/sales_common.js | 1 + erpnext/selling/doctype/quotation/quotation.json | 12 ++++++++++-- erpnext/selling/doctype/quotation/quotation.py | 1 + .../selling/doctype/sales_order/sales_order.json | 12 ++++++++++-- erpnext/selling/doctype/sales_order/sales_order.py | 1 + .../stock/doctype/delivery_note/delivery_note.json | 12 ++++++++++-- .../stock/doctype/delivery_note/delivery_note.py | 1 + 12 files changed, 69 insertions(+), 16 deletions(-) diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json index d7b173667ec..42861140494 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json @@ -48,6 +48,7 @@ "shipping_address", "company_address", "company_address_display", + "company_contact_person", "currency_and_price_list", "currency", "conversion_rate", @@ -1558,12 +1559,19 @@ "fieldname": "update_billed_amount_in_delivery_note", "fieldtype": "Check", "label": "Update Billed Amount in Delivery Note" + }, + { + "fieldname": "company_contact_person", + "fieldtype": "Link", + "label": "Company Contact Person", + "options": "Contact", + "print_hide": 1 } ], "icon": "fa fa-file-text", "is_submittable": 1, "links": [], - "modified": "2024-03-20 16:00:34.268756", + "modified": "2024-11-26 13:10:50.309570", "modified_by": "Administrator", "module": "Accounts", "name": "POS Invoice", diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py index 20316e3394b..ab5a4092c33 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py @@ -32,12 +32,8 @@ class POSInvoice(SalesInvoice): from erpnext.accounts.doctype.payment_schedule.payment_schedule import PaymentSchedule from erpnext.accounts.doctype.pos_invoice_item.pos_invoice_item import POSInvoiceItem from erpnext.accounts.doctype.pricing_rule_detail.pricing_rule_detail import PricingRuleDetail - from erpnext.accounts.doctype.sales_invoice_advance.sales_invoice_advance import ( - SalesInvoiceAdvance, - ) - from erpnext.accounts.doctype.sales_invoice_payment.sales_invoice_payment import ( - SalesInvoicePayment, - ) + from erpnext.accounts.doctype.sales_invoice_advance.sales_invoice_advance import SalesInvoiceAdvance + from erpnext.accounts.doctype.sales_invoice_payment.sales_invoice_payment import SalesInvoicePayment from erpnext.accounts.doctype.sales_invoice_timesheet.sales_invoice_timesheet import ( SalesInvoiceTimesheet, ) @@ -75,6 +71,7 @@ class POSInvoice(SalesInvoice): company: DF.Link company_address: DF.Link | None company_address_display: DF.SmallText | None + company_contact_person: DF.Link | None consolidated_invoice: DF.Link | None contact_display: DF.SmallText | None contact_email: DF.Data | None diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index c44afd555e0..cb861e68cdc 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -159,8 +159,9 @@ "dispatch_address", "company_address_section", "company_address", - "company_addr_col_break", "company_address_display", + "company_addr_col_break", + "company_contact_person", "terms_tab", "payment_schedule_section", "ignore_default_payment_terms_template", @@ -2166,6 +2167,13 @@ "label": "Update Outstanding for Self", "no_copy": 1, "print_hide": 1 + }, + { + "fieldname": "company_contact_person", + "fieldtype": "Link", + "label": "Company Contact Person", + "options": "Contact", + "print_hide": 1 } ], "icon": "fa fa-file-text", @@ -2178,7 +2186,7 @@ "link_fieldname": "consolidated_invoice" } ], - "modified": "2024-07-18 15:30:39.428519", + "modified": "2024-11-26 12:34:09.110690", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice", @@ -2233,4 +2241,4 @@ "title_field": "title", "track_changes": 1, "track_seen": 1 -} +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 8baa36475da..410e934ab71 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -96,6 +96,7 @@ class SalesInvoice(SellingController): company: DF.Link company_address: DF.Link | None company_address_display: DF.SmallText | None + company_contact_person: DF.Link | None company_tax_id: DF.Data | None contact_display: DF.SmallText | None contact_email: DF.Data | None diff --git a/erpnext/public/js/queries.js b/erpnext/public/js/queries.js index 5350f91746c..fd5b7603844 100644 --- a/erpnext/public/js/queries.js +++ b/erpnext/public/js/queries.js @@ -56,6 +56,17 @@ $.extend(erpnext.queries, { } }, + company_contact_query: function (doc) { + if (!doc.company) { + frappe.throw(__("Please set {0}", [__(frappe.meta.get_label(doc.doctype, "company", doc.name))])); + } + + return { + query: "frappe.contacts.doctype.contact.contact.contact_query", + filters: { link_doctype: "Company", link_name: doc.company }, + }; + }, + address_query: function (doc) { if (frappe.dynamic_link) { if (!doc[frappe.dynamic_link.fieldname]) { diff --git a/erpnext/public/js/utils/sales_common.js b/erpnext/public/js/utils/sales_common.js index 373bf3d2115..ca2bed20c7f 100644 --- a/erpnext/public/js/utils/sales_common.js +++ b/erpnext/public/js/utils/sales_common.js @@ -49,6 +49,7 @@ erpnext.sales_common = { ); me.frm.set_query("contact_person", erpnext.queries.contact_query); + me.frm.set_query("company_contact_person", erpnext.queries.company_contact_query); me.frm.set_query("customer_address", erpnext.queries.address_query); me.frm.set_query("shipping_address_name", erpnext.queries.address_query); me.frm.set_query("dispatch_address_name", erpnext.queries.dispatch_address_query); diff --git a/erpnext/selling/doctype/quotation/quotation.json b/erpnext/selling/doctype/quotation/quotation.json index d6ee87b5dee..4d257ff69e7 100644 --- a/erpnext/selling/doctype/quotation/quotation.json +++ b/erpnext/selling/doctype/quotation/quotation.json @@ -96,8 +96,9 @@ "shipping_address", "company_address_section", "company_address", - "column_break_87", "company_address_display", + "column_break_87", + "company_contact_person", "terms_tab", "payment_schedule_section", "payment_terms_template", @@ -1076,13 +1077,20 @@ "fieldname": "disable_rounded_total", "fieldtype": "Check", "label": "Disable Rounded Total" + }, + { + "fieldname": "company_contact_person", + "fieldtype": "Link", + "label": "Company Contact Person", + "options": "Contact", + "print_hide": 1 } ], "icon": "fa fa-shopping-cart", "idx": 82, "is_submittable": 1, "links": [], - "modified": "2024-11-07 18:37:11.715189", + "modified": "2024-11-26 12:43:29.293637", "modified_by": "Administrator", "module": "Selling", "name": "Quotation", diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index 8e560b8d0ab..1922d6584ee 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -50,6 +50,7 @@ class Quotation(SellingController): company: DF.Link company_address: DF.Link | None company_address_display: DF.SmallText | None + company_contact_person: DF.Link | None competitors: DF.TableMultiSelect[CompetitorDetail] contact_display: DF.SmallText | None contact_email: DF.Data | None diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json index 0695c3fd9c4..1525b9632de 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.json +++ b/erpnext/selling/doctype/sales_order/sales_order.json @@ -113,8 +113,9 @@ "dispatch_address", "col_break46", "company_address", - "column_break_92", "company_address_display", + "column_break_92", + "company_contact_person", "payment_schedule_section", "payment_terms_section", "payment_terms_template", @@ -1640,13 +1641,20 @@ "no_copy": 1, "print_hide": 1, "report_hide": 1 + }, + { + "fieldname": "company_contact_person", + "fieldtype": "Link", + "label": "Company Contact Person", + "options": "Contact", + "print_hide": 1 } ], "icon": "fa fa-file-text", "idx": 105, "is_submittable": 1, "links": [], - "modified": "2024-05-23 16:35:54.905804", + "modified": "2024-11-26 12:42:06.872527", "modified_by": "Administrator", "module": "Selling", "name": "Sales Order", diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index d8b3f3c6dcf..374c37f99bf 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -85,6 +85,7 @@ class SalesOrder(SellingController): company: DF.Link company_address: DF.Link | None company_address_display: DF.SmallText | None + company_contact_person: DF.Link | None contact_display: DF.SmallText | None contact_email: DF.Data | None contact_mobile: DF.SmallText | None diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.json b/erpnext/stock/doctype/delivery_note/delivery_note.json index 87c333370b2..4a0580f0e94 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.json +++ b/erpnext/stock/doctype/delivery_note/delivery_note.json @@ -108,8 +108,9 @@ "dispatch_address", "company_address_section", "company_address", - "column_break_101", "company_address_display", + "column_break_101", + "company_contact_person", "terms_tab", "tc_name", "terms", @@ -1391,13 +1392,20 @@ "fieldname": "named_place", "fieldtype": "Data", "label": "Named Place" + }, + { + "fieldname": "company_contact_person", + "fieldtype": "Link", + "label": "Company Contact Person", + "options": "Contact", + "print_hide": 1 } ], "icon": "fa fa-truck", "idx": 146, "is_submittable": 1, "links": [], - "modified": "2024-03-20 16:05:02.854990", + "modified": "2024-11-26 12:44:28.258215", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note", diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index 68eb07d3a77..0bc6b28fe68 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -53,6 +53,7 @@ class DeliveryNote(SellingController): company: DF.Link company_address: DF.Link | None company_address_display: DF.SmallText | None + company_contact_person: DF.Link | None contact_display: DF.SmallText | None contact_email: DF.Data | None contact_mobile: DF.SmallText | None From 0fbc60a20e0958d93c935446f0271d9ee626ecb5 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 27 Nov 2024 23:52:16 +0100 Subject: [PATCH 642/734] fix: show "Send SMS" only when enabled (backport #43941) (#43970) fix: show "Send SMS" only when enabled (#43941) (cherry picked from commit 65088cbb1b7cf54858805b5a5bd1b38ff3e0e29d) Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com> --- erpnext/public/js/controllers/transaction.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 77db3292eb7..b342f38ac40 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -486,7 +486,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe setup_sms() { var me = this; let blacklist = ['Purchase Invoice', 'BOM']; - if(this.frm.doc.docstatus===1 && !["Lost", "Stopped", "Closed"].includes(this.frm.doc.status) + if(frappe.boot.sms_gateway_enabled && this.frm.doc.docstatus===1 && !["Lost", "Stopped", "Closed"].includes(this.frm.doc.status) && !blacklist.includes(this.frm.doctype)) { this.frm.page.add_menu_item(__('Send SMS'), function() { me.send_sms(); }); } From c3bc724523aab30585554e24f7e3907ae48f6ea2 Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Thu, 28 Nov 2024 11:36:05 +0530 Subject: [PATCH 643/734] fix: IndexError in Asset Depreciation Ledger when query result is empty (cherry picked from commit 7c393e5aa01f0c1e05470e1073adf42e89540601) --- .../asset_depreciation_ledger/asset_depreciation_ledger.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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 e1545bdcd87..a1ed6e1caa1 100644 --- a/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.py +++ b/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.py @@ -71,6 +71,7 @@ def get_data(filters): assets = [d.against_voucher for d in gl_entries] assets_details = get_assets_details(assets) + print(gl_entries) for d in gl_entries: asset_data = assets_details.get(d.against_voucher) @@ -89,7 +90,9 @@ def get_data(filters): & (DepreciationSchedule.schedule_date == d.posting_date) ) ).run(as_dict=True) - asset_data.accumulated_depreciation_amount = query[0]["accumulated_depreciation_amount"] + asset_data.accumulated_depreciation_amount = ( + query[0]["accumulated_depreciation_amount"] if query else 0 + ) else: asset_data.accumulated_depreciation_amount += d.debit From 5bbef90f08abfcf35feb8ef9101735cc906078f8 Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Thu, 28 Nov 2024 11:41:00 +0530 Subject: [PATCH 644/734] chore: removed print statement (cherry picked from commit 1737de7c10d96eaf8b6f8430202977336d969e00) --- .../asset_depreciation_ledger/asset_depreciation_ledger.py | 1 - 1 file changed, 1 deletion(-) 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 a1ed6e1caa1..a21103c719d 100644 --- a/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.py +++ b/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.py @@ -71,7 +71,6 @@ def get_data(filters): assets = [d.against_voucher for d in gl_entries] assets_details = get_assets_details(assets) - print(gl_entries) for d in gl_entries: asset_data = assets_details.get(d.against_voucher) From 173d60fb7d9aaab62e7bcd7e1e6ae5ec5f261518 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 28 Nov 2024 14:41:26 +0530 Subject: [PATCH 645/734] fix: typeerror on transaction.js (cherry picked from commit 46ce8780f2c7284ae6149a9b4a82e58fa1b017d7) --- erpnext/public/js/controllers/transaction.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index b342f38ac40..18cddd7f7a1 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -1124,7 +1124,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe apply_discount_on_item(doc, cdt, cdn, field) { var item = frappe.get_doc(cdt, cdn); - if(!item?.price_list_rate) { + if(item && !item.price_list_rate) { item[field] = 0.0; } else { this.price_list_rate(doc, cdt, cdn); From 8ab9fc7f55b7759033f540cd275630a30ace8e5b Mon Sep 17 00:00:00 2001 From: Ninad Parikh <109862100+Ninad1306@users.noreply.github.com> Date: Thu, 28 Nov 2024 15:59:52 +0530 Subject: [PATCH 646/734] fix: Data Should be Computed in Backend to Maintain Consistent Behaviour (#44195) (cherry picked from commit 69bd90b038e08b2348a100445acefb0d7fe7fecc) --- .../report/balance_sheet/balance_sheet.py | 4 ++ .../accounts/report/financial_statements.py | 65 +++++++++++++++++++ .../profit_and_loss_statement.py | 8 +++ erpnext/public/js/financial_statements.js | 39 ++++------- 4 files changed, 91 insertions(+), 25 deletions(-) diff --git a/erpnext/accounts/report/balance_sheet/balance_sheet.py b/erpnext/accounts/report/balance_sheet/balance_sheet.py index ab2f45d4f8b..fc19c40f8f9 100644 --- a/erpnext/accounts/report/balance_sheet/balance_sheet.py +++ b/erpnext/accounts/report/balance_sheet/balance_sheet.py @@ -7,6 +7,7 @@ from frappe import _ from frappe.utils import cint, flt from erpnext.accounts.report.financial_statements import ( + compute_growth_view_data, get_columns, get_data, get_filtered_list_for_consolidated_report, @@ -101,6 +102,9 @@ def execute(filters=None): period_list, asset, liability, equity, provisional_profit_loss, currency, filters ) + if filters.get("selected_view") == "Growth": + compute_growth_view_data(data, period_list) + return columns, data, message, chart, report_summary, primitive_summary diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py index c233f3c7e2b..ca947255788 100644 --- a/erpnext/accounts/report/financial_statements.py +++ b/erpnext/accounts/report/financial_statements.py @@ -2,6 +2,7 @@ # License: GNU General Public License v3. See license.txt +import copy import functools import math import re @@ -668,3 +669,67 @@ def get_filtered_list_for_consolidated_report(filters, period_list): filtered_summary_list.append(period) return filtered_summary_list + + +def compute_growth_view_data(data, columns): + data_copy = copy.deepcopy(data) + + for row_idx in range(len(data_copy)): + for column_idx in range(1, len(columns)): + previous_period_key = columns[column_idx - 1].get("key") + current_period_key = columns[column_idx].get("key") + current_period_value = data_copy[row_idx].get(current_period_key) + previous_period_value = data_copy[row_idx].get(previous_period_key) + annual_growth = 0 + + if current_period_value is None: + data[row_idx][current_period_key] = None + continue + + if previous_period_value == 0 and current_period_value > 0: + annual_growth = 1 + + elif previous_period_value > 0: + annual_growth = (current_period_value - previous_period_value) / previous_period_value + + growth_percent = round(annual_growth * 100, 2) + + data[row_idx][current_period_key] = growth_percent + + +def compute_margin_view_data(data, columns, accumulated_values): + if not columns: + return + + if not accumulated_values: + columns.append({"key": "total"}) + + data_copy = copy.deepcopy(data) + + base_row = None + for row in data_copy: + if row.get("account_name") == _("Income"): + base_row = row + break + + if not base_row: + return + + for row_idx in range(len(data_copy)): + # Taking the total income from each column (for all the financial years) as the base (100%) + row = data_copy[row_idx] + if not row: + continue + + for column in columns: + curr_period = column.get("key") + base_value = base_row[curr_period] + curr_value = row[curr_period] + + if curr_value is None or base_value <= 0: + data[row_idx][curr_period] = None + continue + + margin_percent = round((curr_value / base_value) * 100, 2) + + data[row_idx][curr_period] = margin_percent diff --git a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py index 35453f2ec44..2b6280c74b5 100644 --- a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py +++ b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py @@ -7,6 +7,8 @@ from frappe import _ from frappe.utils import flt from erpnext.accounts.report.financial_statements import ( + compute_growth_view_data, + compute_margin_view_data, get_columns, get_data, get_filtered_list_for_consolidated_report, @@ -68,6 +70,12 @@ def execute(filters=None): period_list, filters.periodicity, income, expense, net_profit_loss, currency, filters ) + if filters.get("selected_view") == "Growth": + compute_growth_view_data(data, period_list) + + if filters.get("selected_view") == "Margin": + compute_margin_view_data(data, period_list, filters.accumulated_values) + return columns, data, None, chart, report_summary, primitive_summary diff --git a/erpnext/public/js/financial_statements.js b/erpnext/public/js/financial_statements.js index bd80c254889..a58eb9f013a 100644 --- a/erpnext/public/js/financial_statements.js +++ b/erpnext/public/js/financial_statements.js @@ -9,40 +9,29 @@ erpnext.financial_statements = { data && column.colIndex >= 3 ) { - //Assuming that the first three columns are s.no, account name and the very first year of the accounting values, to calculate the relative percentage values of the successive columns. - const lastAnnualValue = row[column.colIndex - 1].content; - const currentAnnualvalue = data[column.fieldname]; - if (currentAnnualvalue == undefined) return "NA"; //making this not applicable for undefined/null values - let annualGrowth = 0; - if (lastAnnualValue == 0 && currentAnnualvalue > 0) { - //If the previous year value is 0 and the current value is greater than 0 - annualGrowth = 1; - } else if (lastAnnualValue > 0) { - annualGrowth = (currentAnnualvalue - lastAnnualValue) / lastAnnualValue; - } + const growthPercent = data[column.fieldname]; - const growthPercent = Math.round(annualGrowth * 10000) / 100; //calculating the rounded off percentage + if (growthPercent == undefined) return "NA"; //making this not applicable for undefined/null values - value = $(`${(growthPercent >= 0 ? "+" : "") + growthPercent + "%"}`); - if (growthPercent < 0) { - value = $(value).addClass("text-danger"); + if (column.fieldname === "total") { + value = $(`${growthPercent}`); } else { - value = $(value).addClass("text-success"); + value = $(`${(growthPercent >= 0 ? "+" : "") + growthPercent + "%"}`); + + if (growthPercent < 0) { + value = $(value).addClass("text-danger"); + } else { + value = $(value).addClass("text-success"); + } } value = $(value).wrap("

").parent().html(); return value; } else if (frappe.query_report.get_filter_value("selected_view") == "Margin" && data) { - if (column.fieldname == "account" && data.account_name == __("Income")) { - //Taking the total income from each column (for all the financial years) as the base (100%) - this.baseData = row; - } if (column.colIndex >= 2) { - //Assuming that the first two columns are s.no and account name, to calculate the relative percentage values of the successive columns. - const currentAnnualvalue = data[column.fieldname]; - const baseValue = this.baseData[column.colIndex].content; - if (currentAnnualvalue == undefined || baseValue <= 0) return "NA"; - const marginPercent = Math.round((currentAnnualvalue / baseValue) * 10000) / 100; + const marginPercent = data[column.fieldname]; + + if (marginPercent == undefined) return "NA"; //making this not applicable for undefined/null values value = $(`${marginPercent + "%"}`); if (marginPercent < 0) value = $(value).addClass("text-danger"); From 633be8d06bcbba0a7b08ba3e9172f67a7562343b Mon Sep 17 00:00:00 2001 From: David Date: Mon, 29 Jul 2024 20:01:22 +0200 Subject: [PATCH 647/734] fix: link cash flow rows and fix summary linking (cherry picked from commit b94af285875e23ce0d983831a7831f00934c67c2) # Conflicts: # erpnext/public/js/financial_statements.js --- .../accounts/report/cash_flow/cash_flow.js | 5 +- .../accounts/report/cash_flow/cash_flow.py | 58 +++++++++++-------- .../accounts/report/financial_statements.py | 10 ++-- erpnext/public/js/financial_statements.js | 20 +++++-- 4 files changed, 57 insertions(+), 36 deletions(-) diff --git a/erpnext/accounts/report/cash_flow/cash_flow.js b/erpnext/accounts/report/cash_flow/cash_flow.js index c824f0d9f38..bc76ee0a114 100644 --- a/erpnext/accounts/report/cash_flow/cash_flow.js +++ b/erpnext/accounts/report/cash_flow/cash_flow.js @@ -1,7 +1,10 @@ // Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.query_reports["Cash Flow"] = $.extend({}, erpnext.financial_statements); +frappe.query_reports["Cash Flow"] = $.extend(erpnext.financial_statements, { + name_field: "section", + parent_field: "parent_section", +}); erpnext.utils.add_dimensions("Cash Flow", 10); diff --git a/erpnext/accounts/report/cash_flow/cash_flow.py b/erpnext/accounts/report/cash_flow/cash_flow.py index 9b441671415..562ac5efb81 100644 --- a/erpnext/accounts/report/cash_flow/cash_flow.py +++ b/erpnext/accounts/report/cash_flow/cash_flow.py @@ -30,7 +30,7 @@ def execute(filters=None): company=filters.company, ) - cash_flow_accounts = get_cash_flow_accounts() + cash_flow_sections = get_cash_flow_accounts() # compute net profit / loss income = get_data( @@ -60,14 +60,14 @@ def execute(filters=None): summary_data = {} company_currency = frappe.get_cached_value("Company", filters.company, "default_currency") - for cash_flow_account in cash_flow_accounts: + for cash_flow_section in cash_flow_sections: section_data = [] data.append( { - "account_name": cash_flow_account["section_header"], - "parent_account": None, + "section_name": "'" + cash_flow_section["section_header"] + "'", + "parent_section": None, "indent": 0.0, - "account": cash_flow_account["section_header"], + "section": cash_flow_section["section_header"], } ) @@ -75,31 +75,40 @@ def execute(filters=None): # add first net income in operations section if net_profit_loss: net_profit_loss.update( - {"indent": 1, "parent_account": cash_flow_accounts[0]["section_header"]} + {"indent": 1, "parent_section": cash_flow_sections[0]["section_header"]} ) data.append(net_profit_loss) section_data.append(net_profit_loss) - for account in cash_flow_account["account_types"]: - account_data = get_account_type_based_data( - filters.company, account["account_type"], period_list, filters.accumulated_values, filters + for row in cash_flow_section["account_types"]: + row_data = get_account_type_based_data( + filters.company, row["account_type"], period_list, filters.accumulated_values, filters ) - account_data.update( + accounts = frappe.get_all( + "Account", + filters={ + "account_type": row["account_type"], + "is_group": 0, + }, + pluck="name", + ) + row_data.update( { - "account_name": account["label"], - "account": account["label"], + "section_name": row["label"], + "section": row["label"], "indent": 1, - "parent_account": cash_flow_account["section_header"], + "accounts": accounts, + "parent_section": cash_flow_section["section_header"], "currency": company_currency, } ) - data.append(account_data) - section_data.append(account_data) + data.append(row_data) + section_data.append(row_data) add_total_row_account( data, section_data, - cash_flow_account["section_footer"], + cash_flow_section["section_footer"], period_list, company_currency, summary_data, @@ -109,7 +118,7 @@ def execute(filters=None): add_total_row_account( data, data, _("Net Change in Cash"), period_list, company_currency, summary_data, filters ) - columns = get_columns(filters.periodicity, period_list, filters.accumulated_values, filters.company) + columns = get_columns(filters.periodicity, period_list, filters.accumulated_values, filters.company, True) chart = get_chart_data(columns, data, company_currency) @@ -217,8 +226,8 @@ def get_start_date(period, accumulated_values, company): def add_total_row_account(out, data, label, period_list, currency, summary_data, filters, consolidated=False): total_row = { - "account_name": "'" + _("{0}").format(label) + "'", - "account": "'" + _("{0}").format(label) + "'", + "section_name": "'" + _("{0}").format(label) + "'", + "section": "'" + _("{0}").format(label) + "'", "currency": currency, } @@ -229,7 +238,7 @@ def add_total_row_account(out, data, label, period_list, currency, summary_data, period_list = get_filtered_list_for_consolidated_report(filters, period_list) for row in data: - if row.get("parent_account"): + if row.get("parent_section"): for period in period_list: key = period if consolidated else period["key"] total_row.setdefault(key, 0.0) @@ -254,13 +263,14 @@ def get_report_summary(summary_data, currency): def get_chart_data(columns, data, currency): labels = [d.get("label") for d in columns[2:]] + print(data) datasets = [ { - "name": account.get("account").replace("'", ""), - "values": [account.get(d.get("fieldname")) for d in columns[2:]], + "name": section.get("section").replace("'", ""), + "values": [section.get(d.get("fieldname")) for d in columns[2:]], } - for account in data - if account.get("parent_account") is None and account.get("currency") + for section in data + if section.get("parent_section") is None and section.get("currency") ] datasets = datasets[:-1] diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py index ca947255788..76b3dfc65c8 100644 --- a/erpnext/accounts/report/financial_statements.py +++ b/erpnext/accounts/report/financial_statements.py @@ -335,8 +335,8 @@ def filter_out_zero_value_rows(data, parent_children_map, show_zero_values=False def add_total_row(out, root_type, balance_must_be, period_list, company_currency): total_row = { - "account_name": _("Total {0} ({1})").format(_(root_type), _(balance_must_be)), - "account": _("Total {0} ({1})").format(_(root_type), _(balance_must_be)), + "account_name": "'" + _("Total {0} ({1})").format(_(root_type), _(balance_must_be)) + "'", + "account": "'" + _("Total {0} ({1})").format(_(root_type), _(balance_must_be)) + "'", "currency": company_currency, "opening_balance": 0.0, } @@ -617,11 +617,11 @@ def get_cost_centers_with_children(cost_centers): return list(set(all_cost_centers)) -def get_columns(periodicity, period_list, accumulated_values=1, company=None): +def get_columns(periodicity, period_list, accumulated_values=1, company=None, cash_flow=False): columns = [ { - "fieldname": "account", - "label": _("Account"), + "fieldname": "stub", + "label": _("Account") if not cash_flow else _("Section"), "fieldtype": "Link", "options": "Account", "width": 300, diff --git a/erpnext/public/js/financial_statements.js b/erpnext/public/js/financial_statements.js index a58eb9f013a..b14b5cdcf37 100644 --- a/erpnext/public/js/financial_statements.js +++ b/erpnext/public/js/financial_statements.js @@ -28,6 +28,13 @@ erpnext.financial_statements = { return value; } else if (frappe.query_report.get_filter_value("selected_view") == "Margin" && data) { +<<<<<<< HEAD +======= + if (column.fieldname == "stub" && data.account_name == __("Income")) { + //Taking the total income from each column (for all the financial years) as the base (100%) + this.baseData = row; + } +>>>>>>> b94af28587 (fix: link cash flow rows and fix summary linking) if (column.colIndex >= 2) { const marginPercent = data[column.fieldname]; @@ -41,8 +48,9 @@ erpnext.financial_statements = { } } - if (data && column.fieldname == "account") { - value = data.account_name || value; + if (data && column.fieldname == "stub") { + // first column + value = data.section_name || data.account_name || value; if (filter && filter?.text && filter?.type == "contains") { if (!value.toLowerCase().includes(filter.text)) { @@ -50,7 +58,7 @@ erpnext.financial_statements = { } } - if (data.account) { + if (data.account || data.accounts) { column.link_onclick = "erpnext.financial_statements.open_general_ledger(" + JSON.stringify(data) + ")"; } @@ -59,7 +67,7 @@ erpnext.financial_statements = { value = default_formatter(value, row, column, data); - if (data && !data.parent_account) { + if (data && !data.parent_account && !data.parent_section) { value = $(`${value}`); var $value = $(value).css("font-weight", "bold"); @@ -73,13 +81,13 @@ erpnext.financial_statements = { return value; }, open_general_ledger: function (data) { - if (!data.account) return; + if (!data.account && !data.accounts) return; let project = $.grep(frappe.query_report.filters, function (e) { return e.df.fieldname == "project"; }); frappe.route_options = { - account: data.account, + account: data.account || data.accounts, company: frappe.query_report.get_filter_value("company"), from_date: data.from_date || data.year_start_date, to_date: data.to_date || data.year_end_date, From 48d6fcaab8e4fbc92c2cb8474c8cb9437e70449a Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 29 Nov 2024 10:43:36 +0530 Subject: [PATCH 648/734] chore: resolve conflict --- erpnext/public/js/financial_statements.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/erpnext/public/js/financial_statements.js b/erpnext/public/js/financial_statements.js index b14b5cdcf37..cf3efae6d52 100644 --- a/erpnext/public/js/financial_statements.js +++ b/erpnext/public/js/financial_statements.js @@ -28,13 +28,10 @@ erpnext.financial_statements = { return value; } else if (frappe.query_report.get_filter_value("selected_view") == "Margin" && data) { -<<<<<<< HEAD -======= if (column.fieldname == "stub" && data.account_name == __("Income")) { //Taking the total income from each column (for all the financial years) as the base (100%) this.baseData = row; } ->>>>>>> b94af28587 (fix: link cash flow rows and fix summary linking) if (column.colIndex >= 2) { const marginPercent = data[column.fieldname]; From ae81bb3c1bca02f4da1fb32ee74f2c8e2f7129a2 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 29 Nov 2024 10:52:38 +0530 Subject: [PATCH 649/734] chore: revert 'stub' --- erpnext/accounts/report/financial_statements.py | 2 +- erpnext/public/js/financial_statements.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py index 76b3dfc65c8..73e49983fb2 100644 --- a/erpnext/accounts/report/financial_statements.py +++ b/erpnext/accounts/report/financial_statements.py @@ -620,7 +620,7 @@ def get_cost_centers_with_children(cost_centers): def get_columns(periodicity, period_list, accumulated_values=1, company=None, cash_flow=False): columns = [ { - "fieldname": "stub", + "fieldname": "account", "label": _("Account") if not cash_flow else _("Section"), "fieldtype": "Link", "options": "Account", diff --git a/erpnext/public/js/financial_statements.js b/erpnext/public/js/financial_statements.js index cf3efae6d52..7a3877b9c46 100644 --- a/erpnext/public/js/financial_statements.js +++ b/erpnext/public/js/financial_statements.js @@ -28,7 +28,7 @@ erpnext.financial_statements = { return value; } else if (frappe.query_report.get_filter_value("selected_view") == "Margin" && data) { - if (column.fieldname == "stub" && data.account_name == __("Income")) { + if (column.fieldname == "account" && data.account_name == __("Income")) { //Taking the total income from each column (for all the financial years) as the base (100%) this.baseData = row; } @@ -45,7 +45,7 @@ erpnext.financial_statements = { } } - if (data && column.fieldname == "stub") { + if (data && column.fieldname == "account") { // first column value = data.section_name || data.account_name || value; From 5f785ede16341ab9e8db12db21b08d70c140de1a Mon Sep 17 00:00:00 2001 From: Abdeali Chharchhoda Date: Sat, 23 Nov 2024 14:47:31 +0530 Subject: [PATCH 650/734] refactor: Used object to get payment request status indicator (cherry picked from commit e1c4d6e1e666ee539caf746ed303003ac89f8024) # Conflicts: # erpnext/accounts/doctype/payment_request/payment_request_list.js --- .../payment_request/payment_request_list.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/erpnext/accounts/doctype/payment_request/payment_request_list.js b/erpnext/accounts/doctype/payment_request/payment_request_list.js index 183ca7c4584..a1e1549e9d9 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request_list.js +++ b/erpnext/accounts/doctype/payment_request/payment_request_list.js @@ -1,6 +1,17 @@ +const INDICATORS = { + "Partially Paid": "orange", + Cancelled: "red", + Draft: "gray", + Failed: "red", + Initiated: "green", + Paid: "blue", + Requested: "green", +}; + frappe.listview_settings["Payment Request"] = { add_fields: ["status"], get_indicator: function (doc) { +<<<<<<< HEAD if (doc.status == "Draft") { return [__("Draft"), "gray", "status,=,Draft"]; } @@ -15,5 +26,8 @@ frappe.listview_settings["Payment Request"] = { } else if (doc.status == "Cancelled") { return [__("Cancelled"), "red", "status,=,Cancelled"]; } +======= + return [__(doc.status), INDICATORS[doc.status] || "gray", `status,=,${doc.status}`]; +>>>>>>> e1c4d6e1e6 (refactor: Used object to get payment request status indicator) }, }; From 0d67c62f43758d453bc59ea0595046f48afa78ab Mon Sep 17 00:00:00 2001 From: Abdeali Chharchhoda Date: Sat, 23 Nov 2024 15:10:23 +0530 Subject: [PATCH 651/734] fix: Dashboard for `Payment Request` (cherry picked from commit 91955e27c38338ff72ae4d19d8f7e26880c5eb3a) --- .../payment_request/payment_request_dashboard.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 erpnext/accounts/doctype/payment_request/payment_request_dashboard.py diff --git a/erpnext/accounts/doctype/payment_request/payment_request_dashboard.py b/erpnext/accounts/doctype/payment_request/payment_request_dashboard.py new file mode 100644 index 00000000000..02ad5684792 --- /dev/null +++ b/erpnext/accounts/doctype/payment_request/payment_request_dashboard.py @@ -0,0 +1,14 @@ +from frappe import _ + + +def get_data(): + return { + "fieldname": "payment_request", + "internal_links": { + "Payment Entry": ["references", "payment_request"], + "Payment Order": ["references", "payment_order"], + }, + "transactions": [ + {"label": _("Payment"), "items": ["Payment Entry", "Payment Order"]}, + ], + } From 9c4b5814a65b9bed086326aa11538f5a9db99938 Mon Sep 17 00:00:00 2001 From: Abdeali Chharchhoda Date: Tue, 26 Nov 2024 12:10:05 +0530 Subject: [PATCH 652/734] revert: remove default `Payment Request` indicator color (cherry picked from commit 37ceb09955c4e5a12714d6422c6092a271382e4f) # Conflicts: # erpnext/accounts/doctype/payment_request/payment_request_list.js --- .../doctype/payment_request/payment_request_list.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/erpnext/accounts/doctype/payment_request/payment_request_list.js b/erpnext/accounts/doctype/payment_request/payment_request_list.js index a1e1549e9d9..61dae1451f9 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request_list.js +++ b/erpnext/accounts/doctype/payment_request/payment_request_list.js @@ -11,6 +11,7 @@ const INDICATORS = { frappe.listview_settings["Payment Request"] = { add_fields: ["status"], get_indicator: function (doc) { +<<<<<<< HEAD <<<<<<< HEAD if (doc.status == "Draft") { return [__("Draft"), "gray", "status,=,Draft"]; @@ -29,5 +30,10 @@ frappe.listview_settings["Payment Request"] = { ======= return [__(doc.status), INDICATORS[doc.status] || "gray", `status,=,${doc.status}`]; >>>>>>> e1c4d6e1e6 (refactor: Used object to get payment request status indicator) +======= + if (!doc.status || !INDICATORS[doc.status]) return; + + return [__(doc.status), INDICATORS[doc.status], `status,=,${doc.status}`]; +>>>>>>> 37ceb09955 (revert: remove default `Payment Request` indicator color) }, }; From 4b046160f8f9d43c34c1f9e093d052d4f9ca2e27 Mon Sep 17 00:00:00 2001 From: Abdeali Chharchhoda Date: Wed, 27 Nov 2024 15:52:45 +0530 Subject: [PATCH 653/734] refactor: Move `PR` link filters to client side (cherry picked from commit 2db2c8bce1c1f453818e7e693ded0c0eec8053ec) --- erpnext/accounts/doctype/payment_entry/payment_entry.js | 4 ++++ .../accounts/doctype/payment_request/payment_request.py | 7 +------ 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index a377aa04db2..2d27cccfff8 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -185,6 +185,10 @@ frappe.ui.form.on("Payment Entry", { filters: { reference_doctype: row.reference_doctype, reference_name: row.reference_name, + company: doc.company, + status: ["!=", "Paid"], + outstanding_amount: [">", 0], // for compatibility with old data + docstatus: 1, }, }; }); diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index 1d31fd4b67b..27e3aa83092 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -987,12 +987,7 @@ def get_open_payment_requests_query(doctype, txt, searchfield, start, page_len, open_payment_requests = frappe.get_list( "Payment Request", - filters={ - **filters, - "status": ["!=", "Paid"], - "outstanding_amount": ["!=", 0], # for compatibility with old data - "docstatus": 1, - }, + filters=filters, fields=["name", "grand_total", "outstanding_amount"], order_by="transaction_date ASC,creation ASC", ) From 5999a8e24f908dc84bce88969bdb192c03c2cd08 Mon Sep 17 00:00:00 2001 From: Abdeali Chharchhoda Date: Wed, 27 Nov 2024 17:18:10 +0530 Subject: [PATCH 654/734] fix: Add filter for `outstanding_amount` to fetch open PRs (cherry picked from commit 214dfab2697ec13e70262cb4af92ca812a2dcb80) --- erpnext/accounts/doctype/payment_entry/payment_entry.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 89f52b46679..5ebe1dd8754 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -2877,6 +2877,7 @@ def get_open_payment_requests_for_references(references=None): .where(Tuple(PR.reference_doctype, PR.reference_name).isin(list(refs))) .where(PR.status != "Paid") .where(PR.docstatus == 1) + .where(PR.outstanding_amount > 0) # to avoid old PRs with 0 outstanding amount .orderby(Coalesce(PR.transaction_date, PR.creation), order=frappe.qb.asc) ).run(as_dict=True) From 1c50111371eea3eb1abffb73babb550013f748b5 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 29 Nov 2024 14:50:41 +0530 Subject: [PATCH 655/734] chore: resolve conflict --- .../payment_request/payment_request_list.js | 21 ------------------- 1 file changed, 21 deletions(-) diff --git a/erpnext/accounts/doctype/payment_request/payment_request_list.js b/erpnext/accounts/doctype/payment_request/payment_request_list.js index 61dae1451f9..1027385aaaf 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request_list.js +++ b/erpnext/accounts/doctype/payment_request/payment_request_list.js @@ -11,29 +11,8 @@ const INDICATORS = { frappe.listview_settings["Payment Request"] = { add_fields: ["status"], get_indicator: function (doc) { -<<<<<<< HEAD -<<<<<<< HEAD - if (doc.status == "Draft") { - return [__("Draft"), "gray", "status,=,Draft"]; - } - if (doc.status == "Requested") { - return [__("Requested"), "green", "status,=,Requested"]; - } else if (doc.status == "Initiated") { - return [__("Initiated"), "green", "status,=,Initiated"]; - } else if (doc.status == "Partially Paid") { - return [__("Partially Paid"), "orange", "status,=,Partially Paid"]; - } else if (doc.status == "Paid") { - return [__("Paid"), "blue", "status,=,Paid"]; - } else if (doc.status == "Cancelled") { - return [__("Cancelled"), "red", "status,=,Cancelled"]; - } -======= - return [__(doc.status), INDICATORS[doc.status] || "gray", `status,=,${doc.status}`]; ->>>>>>> e1c4d6e1e6 (refactor: Used object to get payment request status indicator) -======= if (!doc.status || !INDICATORS[doc.status]) return; return [__(doc.status), INDICATORS[doc.status], `status,=,${doc.status}`]; ->>>>>>> 37ceb09955 (revert: remove default `Payment Request` indicator color) }, }; From e607795baed7544bcaedac031ed595b3a34eec4d Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 29 Nov 2024 15:49:18 +0530 Subject: [PATCH 656/734] fix: do not validate stock during inward (backport #44417) (#44427) fix: do not validate stock during inward (#44417) (cherry picked from commit d37d7b9811b8bd51f577004cfb89aefcab6d41cd) Co-authored-by: rohitwaghchaure --- .../doctype/serial_and_batch_bundle/serial_and_batch_bundle.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py index 68c47b0d577..dc30039a8c0 100644 --- a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py +++ b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py @@ -972,6 +972,9 @@ class SerialandBatchBundle(Document): ): return + if self.voucher_type in ["Sales Invoice", "Delivery Note"] and self.type_of_transaction == "Inward": + return + if not self.has_batch_no: return From 0e39aa349eda503339299acae94009c6e54b54cb Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 29 Nov 2024 17:00:59 +0530 Subject: [PATCH 657/734] fix: SABB print for packed items (backport #44413) (#44428) fix: SABB print for packed items (#44413) (cherry picked from commit 5266f236b70290bfcf32e51d86fb0a9a673f9576) Co-authored-by: rohitwaghchaure --- erpnext/controllers/print_settings.py | 8 +++++++- erpnext/stock/serial_batch_bundle.py | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/erpnext/controllers/print_settings.py b/erpnext/controllers/print_settings.py index f99711631ff..f05ef67c308 100644 --- a/erpnext/controllers/print_settings.py +++ b/erpnext/controllers/print_settings.py @@ -11,7 +11,13 @@ def set_print_templates_for_item_table(doc, settings): "items": { "qty": "templates/print_formats/includes/item_table_qty.html", "serial_and_batch_bundle": "templates/print_formats/includes/serial_and_batch_bundle.html", - } + }, + "packed_items": { + "serial_and_batch_bundle": "templates/print_formats/includes/serial_and_batch_bundle.html", + }, + "supplied_items": { + "serial_and_batch_bundle": "templates/print_formats/includes/serial_and_batch_bundle.html", + }, } doc.flags.compact_item_fields = ["description", "qty", "rate", "amount"] diff --git a/erpnext/stock/serial_batch_bundle.py b/erpnext/stock/serial_batch_bundle.py index 3fed0195d69..dd459bb30bc 100644 --- a/erpnext/stock/serial_batch_bundle.py +++ b/erpnext/stock/serial_batch_bundle.py @@ -478,7 +478,7 @@ def get_serial_or_batch_nos(bundle): html = "" for d in data: if d.serial_no: - html += f"" + html += f"" else: html += f"" From 0d41c23383961a332802de46c2919981e8250a3c Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 29 Nov 2024 18:12:24 +0530 Subject: [PATCH 658/734] fix: precision calculation causing 0.1 discrepancy (backport #44431) (#44436) fix: precision calculation causing 0.1 discrepancy (#44431) (cherry picked from commit 7f7564b581b8bc2e373b1bf6fc668b54f375d00c) Co-authored-by: rohitwaghchaure --- .../serial_and_batch_bundle/serial_and_batch_bundle.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py index dc30039a8c0..9b07c1e4d19 100644 --- a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py +++ b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py @@ -432,8 +432,6 @@ class SerialandBatchBundle(Document): valuation_field = "rate" child_table = "Subcontracting Receipt Item" - precision = frappe.get_precision(child_table, valuation_field) or 2 - if not rate and self.voucher_detail_no and self.voucher_no: rate = frappe.db.get_value(child_table, self.voucher_detail_no, valuation_field) @@ -443,9 +441,9 @@ class SerialandBatchBundle(Document): elif (d.incoming_rate == rate) and d.qty and d.stock_value_difference: continue - d.incoming_rate = flt(rate, precision) + d.incoming_rate = rate if d.qty: - d.stock_value_difference = flt(d.qty) * flt(d.incoming_rate) + d.stock_value_difference = d.qty * d.incoming_rate if save: d.db_set( From c81b5e3d9cb8acb6d15b294bb0545f713ba5748d Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 29 Nov 2024 18:13:05 +0530 Subject: [PATCH 659/734] fix: source warehouse not set in required items of WO (backport #44426) (#44434) fix: source warehouse not set in required items of WO (#44426) fix: source warehouse not set in required items of WO on data import (cherry picked from commit 4050ea07eb7c45b3a9babe9472faf2fa89f47a9c) Co-authored-by: rohitwaghchaure --- erpnext/manufacturing/doctype/work_order/work_order.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index 1ebcde75366..1d0df26800d 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -160,10 +160,18 @@ class WorkOrder(Document): self.validate_workstation_type() self.reset_use_multi_level_bom() + if self.source_warehouse: + self.set_warehouses() + validate_uom_is_integer(self, "stock_uom", ["qty", "produced_qty"]) self.set_required_items(reset_only_qty=len(self.get("required_items"))) + def set_warehouses(self): + for row in self.required_items: + if not row.source_warehouse: + row.source_warehouse = self.source_warehouse + def reset_use_multi_level_bom(self): if self.is_new(): return From 1f9797905973ef8adc74867adc3a1f052cf5e83b Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Fri, 29 Nov 2024 22:51:32 +0530 Subject: [PATCH 660/734] perf: cache product bundle items at document level (#44440) (cherry picked from commit 6de7320ef40f218cf6aa3b01d0dc1c0c431e68ed) --- erpnext/controllers/selling_controller.py | 32 ++++++++++++++++++----- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index bb59166d3f8..459cf36bf44 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -381,12 +381,32 @@ class SellingController(StockController): return il def has_product_bundle(self, item_code): - product_bundle = frappe.qb.DocType("Product Bundle") - return ( - frappe.qb.from_(product_bundle) - .select(product_bundle.name) - .where((product_bundle.new_item_code == item_code) & (product_bundle.disabled == 0)) - ).run() + product_bundle_items = getattr(self, "_product_bundle_items", None) + if product_bundle_items is None: + self._product_bundle_items = product_bundle_items = {} + + if item_code not in product_bundle_items: + self._fetch_product_bundle_items(item_code) + + return product_bundle_items[item_code] + + def _fetch_product_bundle_items(self, item_code): + product_bundle_items = self._product_bundle_items + items_to_fetch = {row.item_code for row in self.items if row.item_code not in product_bundle_items} + # fetch for requisite item_code even if it is not in items + items_to_fetch.add(item_code) + + items_with_product_bundle = { + row.new_item_code + for row in frappe.get_all( + "Product Bundle", + filters={"new_item_code": ("in", items_to_fetch), "disabled": 0}, + fields="new_item_code", + ) + } + + for item_code in items_to_fetch: + product_bundle_items[item_code] = item_code in items_with_product_bundle def get_already_delivered_qty(self, current_docname, so, so_detail): delivered_via_dn = frappe.db.sql( From 17c2734042ed0ed2f226ff2adb1ef70dfd63cfd9 Mon Sep 17 00:00:00 2001 From: Abdeali Chharchhoda Date: Fri, 29 Nov 2024 18:13:03 +0530 Subject: [PATCH 661/734] fix: added fieldname to avoid fieldname to translate (cherry picked from commit b80022133c0043d2ccfdcfef8770c2970adc9f0c) --- .../report/accounts_receivable/accounts_receivable.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index a58d4ab72ca..86e3dbc5b78 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -1013,7 +1013,7 @@ class ReceivablePayableReport: def get_columns(self): self.columns = [] - self.add_column(_("Posting Date"), fieldtype="Date") + self.add_column(_("Posting Date"), fieldname="posting_date", fieldtype="Date") self.add_column( label=_("Party Type"), fieldname="party_type", @@ -1066,7 +1066,7 @@ class ReceivablePayableReport: width=180, ) - self.add_column(label=_("Due Date"), fieldtype="Date") + self.add_column(label=_("Due Date"), fieldname="due_date", fieldtype="Date") if self.account_type == "Payable": self.add_column(label=_("Bill No"), fieldname="bill_no", fieldtype="Data") From 48059a7c74422fa2c462aabb041fb642b01a9ae2 Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Sat, 30 Nov 2024 00:11:03 +0530 Subject: [PATCH 662/734] perf: reduce queries during transaction save (cherry picked from commit b6b8a06fda0ffaffe3e163af77772520d8c3be1c) --- erpnext/accounts/party.py | 14 +++++++------- erpnext/controllers/selling_controller.py | 8 +------- erpnext/utilities/transaction_base.py | 10 +++++----- 3 files changed, 13 insertions(+), 19 deletions(-) diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index 65054aec53f..5be80872db8 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -29,6 +29,12 @@ from erpnext.accounts.utils import get_fiscal_year from erpnext.exceptions import InvalidAccountCurrency, PartyDisabled, PartyFrozen from erpnext.utilities.regional import temporary_flag +try: + from frappe.contacts.doctype.address.address import render_address as _render_address +except ImportError: + # Older frappe versions where this function is not available + from frappe.contacts.doctype.address.address import get_address_display as _render_address + PURCHASE_TRANSACTION_TYPES = { "Supplier Quotation", "Purchase Order", @@ -982,10 +988,4 @@ def add_party_account(party_type, party, company, account): 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) + return frappe.call(_render_address, address, check_permissions=check_permissions) diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index 459cf36bf44..b704cb30791 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -74,19 +74,13 @@ class SellingController(StockController): if customer: from erpnext.accounts.party import _get_party_details - fetch_payment_terms_template = False - if self.get("__islocal") or self.company != frappe.db.get_value( - self.doctype, self.name, "company" - ): - fetch_payment_terms_template = True - party_details = _get_party_details( customer, ignore_permissions=self.flags.ignore_permissions, doctype=self.doctype, company=self.company, posting_date=self.get("posting_date"), - fetch_payment_terms_template=fetch_payment_terms_template, + fetch_payment_terms_template=self.has_value_changed("company"), party_address=self.customer_address, shipping_address=self.shipping_address_name, company_address=self.get("company_address"), diff --git a/erpnext/utilities/transaction_base.py b/erpnext/utilities/transaction_base.py index 6fab5380c38..2e4bdac6aab 100644 --- a/erpnext/utilities/transaction_base.py +++ b/erpnext/utilities/transaction_base.py @@ -257,11 +257,11 @@ def validate_uom_is_integer(doc, uom_field, qty_fields, child_dt=None): if isinstance(qty_fields, str): qty_fields = [qty_fields] - distinct_uoms = list(set(d.get(uom_field) for d in doc.get_all_children())) - integer_uoms = list( - filter( - lambda uom: frappe.db.get_value("UOM", uom, "must_be_whole_number", cache=True) or None, - distinct_uoms, + distinct_uoms = tuple(set(uom for uom in (d.get(uom_field) for d in doc.get_all_children()) if uom)) + integer_uoms = set( + d[0] + for d in frappe.db.get_values( + "UOM", (("name", "in", distinct_uoms), ("must_be_whole_number", "=", 1)), cache=True ) ) From 579d8e293eb898b0bae0da31ca53b8aa46955884 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sat, 30 Nov 2024 13:42:54 +0530 Subject: [PATCH 663/734] fix: number format in the message (backport #44435) (#44438) fix: number format in the message (#44435) (cherry picked from commit 810c72a30c6f06b19ae1f606b22c0df8fa0dc7b2) Co-authored-by: rohitwaghchaure --- .../serial_and_batch_bundle/serial_and_batch_bundle.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py index 9b07c1e4d19..fff9cbfc07d 100644 --- a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py +++ b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py @@ -607,8 +607,10 @@ class SerialandBatchBundle(Document): precision = row.precision if abs(abs(flt(self.total_qty, precision)) - abs(flt(qty, precision))) > 0.01: + total_qty = frappe.format_value(abs(flt(self.total_qty)), "Float", row) + set_qty = frappe.format_value(abs(flt(row.get(qty_field))), "Float", row) self.throw_error_message( - f"Total quantity {abs(flt(self.total_qty))} in the Serial and Batch Bundle {bold(self.name)} does not match with the quantity {abs(flt(row.get(qty_field)))} for the Item {bold(self.item_code)} in the {self.voucher_type} # {self.voucher_no}" + f"Total quantity {total_qty} in the Serial and Batch Bundle {bold(self.name)} does not match with the quantity {set_qty} for the Item {bold(self.item_code)} in the {self.voucher_type} # {self.voucher_no}" ) def get_qty_field(self, row, qty_field=None) -> str: From eb4a485df6e0953c1552549ffda74caea644ed60 Mon Sep 17 00:00:00 2001 From: Abdeali Chharchhoda Date: Fri, 29 Nov 2024 17:42:11 +0530 Subject: [PATCH 664/734] fix: Added translation for `Account` column (cherry picked from commit de6cbd382f7ff93adff53ad598c2d76361fab4a5) --- .../accounts/report/accounts_receivable/accounts_receivable.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index 86e3dbc5b78..ad6dd096b58 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -1028,7 +1028,7 @@ class ReceivablePayableReport: width=180, ) self.add_column( - label=self.account_type + " Account", + label=_(self.account_type + " Account"), fieldname="party_account", fieldtype="Link", options="Account", From fdda86455a93a25bb2216893da69cc8efe9a07d2 Mon Sep 17 00:00:00 2001 From: Abdeali Chharchhoda Date: Mon, 2 Dec 2024 11:23:10 +0530 Subject: [PATCH 665/734] fix: Translate `Party Account` column label (cherry picked from commit a4f8315602ad5ebfd3805b986f7186648d05313c) --- .../report/accounts_receivable/accounts_receivable.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index ad6dd096b58..49dce0e299b 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -1027,8 +1027,15 @@ class ReceivablePayableReport: options="party_type", width=180, ) + if self.account_type == "Receivable": + label = _("Receivable Account") + elif self.account_type == "Payable": + label = _("Payable Account") + else: + label = _("Party Account") + self.add_column( - label=_(self.account_type + " Account"), + label=label, fieldname="party_account", fieldtype="Link", options="Account", From c8e2c9aa2535b35a9c2a803762ef26a55ef804e2 Mon Sep 17 00:00:00 2001 From: ljain112 Date: Thu, 28 Nov 2024 19:50:01 +0530 Subject: [PATCH 666/734] fix: handle multi currency in common party journal entry (cherry picked from commit e371f68d66700a1641bb36da533385682feac076) # Conflicts: # erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py --- .../sales_invoice/test_sales_invoice.py | 194 ++++++++++++++++++ erpnext/controllers/accounts_controller.py | 67 +++++- 2 files changed, 251 insertions(+), 10 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 90bec018257..1d7359b811f 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -4135,6 +4135,200 @@ class TestSalesInvoice(FrappeTestCase): self.assertEqual(len(actual), 4) self.assertEqual(expected, actual) +<<<<<<< HEAD +======= + @IntegrationTestCase.change_settings("Accounts Settings", {"enable_common_party_accounting": True}) + def test_common_party_with_foreign_currency_jv(self): + from erpnext.accounts.doctype.account.test_account import create_account + from erpnext.accounts.doctype.opening_invoice_creation_tool.test_opening_invoice_creation_tool import ( + make_customer, + ) + from erpnext.accounts.doctype.party_link.party_link import create_party_link + from erpnext.buying.doctype.supplier.test_supplier import create_supplier + from erpnext.setup.utils import get_exchange_rate + + creditors = create_account( + account_name="Creditors USD", + parent_account="Accounts Payable - _TC", + company="_Test Company", + account_currency="USD", + account_type="Payable", + ) + debtors = create_account( + account_name="Debtors USD", + parent_account="Accounts Receivable - _TC", + company="_Test Company", + account_currency="USD", + account_type="Receivable", + ) + + # create a customer + customer = make_customer(customer="_Test Common Party USD") + cust_doc = frappe.get_doc("Customer", customer) + cust_doc.default_currency = "USD" + test_account_details = { + "company": "_Test Company", + "account": debtors, + } + cust_doc.append("accounts", test_account_details) + cust_doc.save() + + # create a supplier + supplier = create_supplier(supplier_name="_Test Common Party USD").name + supp_doc = frappe.get_doc("Supplier", supplier) + supp_doc.default_currency = "USD" + test_account_details = { + "company": "_Test Company", + "account": creditors, + } + supp_doc.append("accounts", test_account_details) + supp_doc.save() + + # create a party link between customer & supplier + create_party_link("Supplier", supplier, customer) + + # create a sales invoice + si = create_sales_invoice( + customer=customer, + currency="USD", + conversion_rate=get_exchange_rate("USD", "INR"), + debit_to=debtors, + do_not_save=1, + ) + si.party_account_currency = "USD" + si.save() + si.submit() + + # check outstanding of sales invoice + si.reload() + self.assertEqual(si.status, "Paid") + self.assertEqual(flt(si.outstanding_amount), 0.0) + + # check creation of journal entry + jv = frappe.get_all( + "Journal Entry Account", + { + "account": si.debit_to, + "party_type": "Customer", + "party": si.customer, + "reference_type": si.doctype, + "reference_name": si.name, + }, + pluck="credit_in_account_currency", + ) + self.assertTrue(jv) + self.assertEqual(jv[0], si.grand_total) + + @IntegrationTestCase.change_settings("Accounts Settings", {"enable_common_party_accounting": True}) + def test_common_party_with_different_currency_in_debtor_and_creditor(self): + from erpnext.accounts.doctype.account.test_account import create_account + from erpnext.accounts.doctype.opening_invoice_creation_tool.test_opening_invoice_creation_tool import ( + make_customer, + ) + from erpnext.accounts.doctype.party_link.party_link import create_party_link + from erpnext.buying.doctype.supplier.test_supplier import create_supplier + from erpnext.setup.utils import get_exchange_rate + + creditors = create_account( + account_name="Creditors INR", + parent_account="Accounts Payable - _TC", + company="_Test Company", + account_currency="INR", + account_type="Payable", + ) + debtors = create_account( + account_name="Debtors USD", + parent_account="Accounts Receivable - _TC", + company="_Test Company", + account_currency="USD", + account_type="Receivable", + ) + + # create a customer + customer = make_customer(customer="_Test Common Party USD") + cust_doc = frappe.get_doc("Customer", customer) + cust_doc.default_currency = "USD" + test_account_details = { + "company": "_Test Company", + "account": debtors, + } + cust_doc.append("accounts", test_account_details) + cust_doc.save() + + # create a supplier + supplier = create_supplier(supplier_name="_Test Common Party INR").name + supp_doc = frappe.get_doc("Supplier", supplier) + supp_doc.default_currency = "INR" + test_account_details = { + "company": "_Test Company", + "account": creditors, + } + supp_doc.append("accounts", test_account_details) + supp_doc.save() + + # create a party link between customer & supplier + create_party_link("Supplier", supplier, customer) + + # create a sales invoice + si = create_sales_invoice( + customer=customer, + currency="USD", + conversion_rate=get_exchange_rate("USD", "INR"), + debit_to=debtors, + do_not_save=1, + ) + si.party_account_currency = "USD" + si.save() + si.submit() + + # check outstanding of sales invoice + si.reload() + self.assertEqual(si.status, "Paid") + self.assertEqual(flt(si.outstanding_amount), 0.0) + + # check creation of journal entry + jv = frappe.get_all( + "Journal Entry Account", + { + "account": si.debit_to, + "party_type": "Customer", + "party": si.customer, + "reference_type": si.doctype, + "reference_name": si.name, + }, + pluck="credit_in_account_currency", + ) + self.assertTrue(jv) + self.assertEqual(jv[0], si.grand_total) + + def test_invoice_remarks(self): + si = frappe.copy_doc(self.globalTestRecords["Sales Invoice"][0]) + si.po_no = "Test PO" + si.po_date = nowdate() + si.save() + si.submit() + self.assertEqual(si.remarks, f"Against Customer Order Test PO dated {format_date(nowdate())}") + + def test_gl_voucher_subtype(self): + si = create_sales_invoice() + gl_entries = frappe.get_all( + "GL Entry", + filters={"voucher_type": "Sales Invoice", "voucher_no": si.name}, + pluck="voucher_subtype", + ) + + self.assertTrue(all([x == "Sales Invoice" for x in gl_entries])) + + si = create_sales_invoice(is_return=1, qty=-1) + gl_entries = frappe.get_all( + "GL Entry", + filters={"voucher_type": "Sales Invoice", "voucher_no": si.name}, + pluck="voucher_subtype", + ) + + self.assertTrue(all([x == "Credit Note" for x in gl_entries])) + +>>>>>>> e371f68d66 (fix: handle multi currency in common party journal entry) def set_advance_flag(company, flag, default_account): frappe.db.set_value( diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index b4b23dd5f4c..bc0cdde94de 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -2465,6 +2465,12 @@ class AccountsController(TransactionBase): secondary_account = get_party_account(secondary_party_type, secondary_party, self.company) primary_account_currency = get_account_currency(primary_account) secondary_account_currency = get_account_currency(secondary_account) + default_currency = erpnext.get_company_currency(self.company) + + # Determine if multi-currency journal entry is needed + multi_currency = ( + primary_account_currency != default_currency or secondary_account_currency != default_currency + ) jv = frappe.new_doc("Journal Entry") jv.voucher_type = "Journal Entry" @@ -2489,7 +2495,7 @@ class AccountsController(TransactionBase): advance_entry.cost_center = self.cost_center or erpnext.get_default_cost_center(self.company) advance_entry.is_advance = "Yes" - # update dimesions + # Update dimensions dimensions_dict = frappe._dict() active_dimensions = get_dimensions()[0] for dim in active_dimensions: @@ -2498,17 +2504,58 @@ class AccountsController(TransactionBase): reconcilation_entry.update(dimensions_dict) advance_entry.update(dimensions_dict) - if self.doctype == "Sales Invoice": - reconcilation_entry.credit_in_account_currency = self.outstanding_amount - advance_entry.debit_in_account_currency = self.outstanding_amount + # Calculate exchange rates if necessary + if multi_currency: + # Exchange rates for primary and secondary accounts + exc_rate_primary_to_default = ( + 1 + if primary_account_currency == default_currency + else get_exchange_rate(primary_account_currency, default_currency, self.posting_date) + ) + exc_rate_secondary_to_default = ( + 1 + if secondary_account_currency == default_currency + else get_exchange_rate(secondary_account_currency, default_currency, self.posting_date) + ) + exc_rate_secondary_to_primary = ( + 1 + if secondary_account_currency == primary_account_currency + else get_exchange_rate( + secondary_account_currency, primary_account_currency, self.posting_date + ) + ) + + # Convert outstanding amount from secondary to primary account currency, if needed + + os_in_default_currency = self.outstanding_amount * exc_rate_secondary_to_default + os_in_primary_currency = self.outstanding_amount * exc_rate_secondary_to_primary + + if self.doctype == "Sales Invoice": + # Calculate credit and debit values for reconciliation and advance entries + reconcilation_entry.credit_in_account_currency = self.outstanding_amount + reconcilation_entry.credit = os_in_default_currency + + advance_entry.debit_in_account_currency = os_in_primary_currency + advance_entry.debit = os_in_default_currency + else: + advance_entry.credit_in_account_currency = os_in_primary_currency + advance_entry.credit = os_in_default_currency + + reconcilation_entry.debit_in_account_currency = self.outstanding_amount + reconcilation_entry.debit = os_in_default_currency + + # Set exchange rates for entries + reconcilation_entry.exchange_rate = exc_rate_secondary_to_default + advance_entry.exchange_rate = exc_rate_primary_to_default else: - advance_entry.credit_in_account_currency = self.outstanding_amount - reconcilation_entry.debit_in_account_currency = self.outstanding_amount - - default_currency = erpnext.get_company_currency(self.company) - if primary_account_currency != default_currency or secondary_account_currency != default_currency: - jv.multi_currency = 1 + if self.doctype == "Sales Invoice": + reconcilation_entry.credit_in_account_currency = self.outstanding_amount + advance_entry.debit_in_account_currency = self.outstanding_amount + else: + advance_entry.credit_in_account_currency = self.outstanding_amount + reconcilation_entry.debit_in_account_currency = self.outstanding_amount + jv.multi_currency = multi_currency jv.append("accounts", reconcilation_entry) jv.append("accounts", advance_entry) From 4c5570ae7d5d04fd3254f3c68e08877530a21997 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 2 Dec 2024 13:46:39 +0530 Subject: [PATCH 667/734] chore: resolve conflict --- .../sales_invoice/test_sales_invoice.py | 114 +----------------- 1 file changed, 1 insertion(+), 113 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 1d7359b811f..d2b8882743a 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -4135,91 +4135,7 @@ class TestSalesInvoice(FrappeTestCase): self.assertEqual(len(actual), 4) self.assertEqual(expected, actual) -<<<<<<< HEAD -======= - @IntegrationTestCase.change_settings("Accounts Settings", {"enable_common_party_accounting": True}) - def test_common_party_with_foreign_currency_jv(self): - from erpnext.accounts.doctype.account.test_account import create_account - from erpnext.accounts.doctype.opening_invoice_creation_tool.test_opening_invoice_creation_tool import ( - make_customer, - ) - from erpnext.accounts.doctype.party_link.party_link import create_party_link - from erpnext.buying.doctype.supplier.test_supplier import create_supplier - from erpnext.setup.utils import get_exchange_rate - - creditors = create_account( - account_name="Creditors USD", - parent_account="Accounts Payable - _TC", - company="_Test Company", - account_currency="USD", - account_type="Payable", - ) - debtors = create_account( - account_name="Debtors USD", - parent_account="Accounts Receivable - _TC", - company="_Test Company", - account_currency="USD", - account_type="Receivable", - ) - - # create a customer - customer = make_customer(customer="_Test Common Party USD") - cust_doc = frappe.get_doc("Customer", customer) - cust_doc.default_currency = "USD" - test_account_details = { - "company": "_Test Company", - "account": debtors, - } - cust_doc.append("accounts", test_account_details) - cust_doc.save() - - # create a supplier - supplier = create_supplier(supplier_name="_Test Common Party USD").name - supp_doc = frappe.get_doc("Supplier", supplier) - supp_doc.default_currency = "USD" - test_account_details = { - "company": "_Test Company", - "account": creditors, - } - supp_doc.append("accounts", test_account_details) - supp_doc.save() - - # create a party link between customer & supplier - create_party_link("Supplier", supplier, customer) - - # create a sales invoice - si = create_sales_invoice( - customer=customer, - currency="USD", - conversion_rate=get_exchange_rate("USD", "INR"), - debit_to=debtors, - do_not_save=1, - ) - si.party_account_currency = "USD" - si.save() - si.submit() - - # check outstanding of sales invoice - si.reload() - self.assertEqual(si.status, "Paid") - self.assertEqual(flt(si.outstanding_amount), 0.0) - - # check creation of journal entry - jv = frappe.get_all( - "Journal Entry Account", - { - "account": si.debit_to, - "party_type": "Customer", - "party": si.customer, - "reference_type": si.doctype, - "reference_name": si.name, - }, - pluck="credit_in_account_currency", - ) - self.assertTrue(jv) - self.assertEqual(jv[0], si.grand_total) - - @IntegrationTestCase.change_settings("Accounts Settings", {"enable_common_party_accounting": True}) + @change_settings("Accounts Settings", {"enable_common_party_accounting": True}) def test_common_party_with_different_currency_in_debtor_and_creditor(self): from erpnext.accounts.doctype.account.test_account import create_account from erpnext.accounts.doctype.opening_invoice_creation_tool.test_opening_invoice_creation_tool import ( @@ -4301,34 +4217,6 @@ class TestSalesInvoice(FrappeTestCase): self.assertTrue(jv) self.assertEqual(jv[0], si.grand_total) - def test_invoice_remarks(self): - si = frappe.copy_doc(self.globalTestRecords["Sales Invoice"][0]) - si.po_no = "Test PO" - si.po_date = nowdate() - si.save() - si.submit() - self.assertEqual(si.remarks, f"Against Customer Order Test PO dated {format_date(nowdate())}") - - def test_gl_voucher_subtype(self): - si = create_sales_invoice() - gl_entries = frappe.get_all( - "GL Entry", - filters={"voucher_type": "Sales Invoice", "voucher_no": si.name}, - pluck="voucher_subtype", - ) - - self.assertTrue(all([x == "Sales Invoice" for x in gl_entries])) - - si = create_sales_invoice(is_return=1, qty=-1) - gl_entries = frappe.get_all( - "GL Entry", - filters={"voucher_type": "Sales Invoice", "voucher_no": si.name}, - pluck="voucher_subtype", - ) - - self.assertTrue(all([x == "Credit Note" for x in gl_entries])) - ->>>>>>> e371f68d66 (fix: handle multi currency in common party journal entry) def set_advance_flag(company, flag, default_account): frappe.db.set_value( From ae93f7f967e2b4da1c76552a794bce118827b57f Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Mon, 2 Dec 2024 14:54:29 +0530 Subject: [PATCH 668/734] fix: set correct unallocated amount in Payment Entry (#43958) * fix: set correct unallocated amount in Payment Entry * fix: add checkbox and other logic fix * fix: patch to set is_exchange_gain_loss in Payment Entry deductions * fix: consider deductions except exch. gain/loss * fix: set exchange gain loss in payment entry * fix: separate function to set exchange gain loss * fix: failing test cases * fix: add cash disc. row first * fix: review changes * fix: changes as per review * fix: failing test cases * fix: review * fix: wait for request to complete before updating exchange gain loss * fix: review --------- Co-authored-by: vishakhdesai Co-authored-by: ruthra kumar (cherry picked from commit 7cc111f7907b47a177dffc4bc40992281d8946b3) # Conflicts: # erpnext/accounts/doctype/payment_entry_deduction/payment_entry_deduction.json # erpnext/patches.txt --- .../test_exchange_rate_revaluation.py | 4 +- .../doctype/payment_entry/payment_entry.js | 242 +++++++++++------- .../doctype/payment_entry/payment_entry.json | 8 +- .../doctype/payment_entry/payment_entry.py | 135 +++++++--- .../payment_entry/test_payment_entry.py | 38 +-- .../payment_entry_deduction.json | 13 + .../payment_entry_deduction.py | 1 + .../test_unreconcile_payment.py | 2 + erpnext/accounts/test/test_utils.py | 8 +- erpnext/patches.txt | 6 + ...e_gain_loss_in_payment_entry_deductions.py | 22 ++ 11 files changed, 304 insertions(+), 175 deletions(-) create mode 100644 erpnext/patches/v15_0/set_is_exchange_gain_loss_in_payment_entry_deductions.py diff --git a/erpnext/accounts/doctype/exchange_rate_revaluation/test_exchange_rate_revaluation.py b/erpnext/accounts/doctype/exchange_rate_revaluation/test_exchange_rate_revaluation.py index 51053f1f68c..3eef6ab3832 100644 --- a/erpnext/accounts/doctype/exchange_rate_revaluation/test_exchange_rate_revaluation.py +++ b/erpnext/accounts/doctype/exchange_rate_revaluation/test_exchange_rate_revaluation.py @@ -188,7 +188,7 @@ class TestExchangeRateRevaluation(AccountsTestMixin, FrappeTestCase): pe = get_payment_entry(si.doctype, si.name) pe.paid_amount = 95 - pe.source_exchange_rate = 84.211 + pe.source_exchange_rate = 84.2105 pe.received_amount = 8000 pe.references = [] pe.save().submit() @@ -229,7 +229,7 @@ class TestExchangeRateRevaluation(AccountsTestMixin, FrappeTestCase): row = next(x for x in je.accounts if x.account == self.debtors_usd) self.assertEqual(flt(row.credit_in_account_currency, precision), 5.0) # in USD row = next(x for x in je.accounts if x.account != self.debtors_usd) - self.assertEqual(flt(row.debit_in_account_currency, precision), 421.06) # in INR + self.assertEqual(flt(row.debit_in_account_currency, precision), 421.05) # in INR # total_debit and total_credit will be 0.0, as JV is posting only to account currency fields self.assertEqual(flt(je.total_debit, precision), 0.0) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index 2d27cccfff8..f2d11ba9ff3 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -324,11 +324,6 @@ frappe.ui.form.on("Payment Entry", { "write_off_difference_amount", frm.doc.difference_amount && frm.doc.party && frm.doc.total_allocated_amount > party_amount ); - - frm.toggle_display( - "set_exchange_gain_loss", - frm.doc.paid_amount && frm.doc.received_amount && frm.doc.difference_amount - ); }, set_dynamic_labels: function (frm) { @@ -1119,36 +1114,34 @@ frappe.ui.form.on("Payment Entry", { }, set_unallocated_amount: function (frm) { - var unallocated_amount = 0; - var total_deductions = frappe.utils.sum( - $.map(frm.doc.deductions || [], function (d) { - return flt(d.amount); - }) - ); + let unallocated_amount = 0; + let deductions_to_consider = 0; + + for (const row of frm.doc.deductions || []) { + if (!row.is_exchange_gain_loss) deductions_to_consider += flt(row.amount); + } + const included_taxes = get_included_taxes(frm); if (frm.doc.party) { if ( frm.doc.payment_type == "Receive" && - frm.doc.base_total_allocated_amount < frm.doc.base_received_amount + total_deductions && - frm.doc.total_allocated_amount < - frm.doc.paid_amount + total_deductions / frm.doc.source_exchange_rate - ) { - unallocated_amount = - (frm.doc.base_received_amount + - total_deductions - - flt(frm.doc.base_total_taxes_and_charges) - - frm.doc.base_total_allocated_amount) / - frm.doc.source_exchange_rate; - } else if ( - frm.doc.payment_type == "Pay" && - frm.doc.base_total_allocated_amount < frm.doc.base_paid_amount - total_deductions && - frm.doc.total_allocated_amount < - frm.doc.received_amount + total_deductions / frm.doc.target_exchange_rate + frm.doc.base_total_allocated_amount < frm.doc.base_paid_amount + deductions_to_consider ) { unallocated_amount = (frm.doc.base_paid_amount + - flt(frm.doc.base_total_taxes_and_charges) - - (total_deductions + frm.doc.base_total_allocated_amount)) / + deductions_to_consider - + frm.doc.base_total_allocated_amount - + included_taxes) / + frm.doc.source_exchange_rate; + } else if ( + frm.doc.payment_type == "Pay" && + frm.doc.base_total_allocated_amount < frm.doc.base_received_amount - deductions_to_consider + ) { + unallocated_amount = + (frm.doc.base_received_amount - + deductions_to_consider - + frm.doc.base_total_allocated_amount - + included_taxes) / frm.doc.target_exchange_rate; } } @@ -1242,77 +1235,85 @@ frappe.ui.form.on("Payment Entry", { }, write_off_difference_amount: function (frm) { - frm.events.set_deductions_entry(frm, "write_off_account"); + frm.events.set_write_off_deduction(frm); }, - set_exchange_gain_loss: function (frm) { - frm.events.set_deductions_entry(frm, "exchange_gain_loss_account"); + base_paid_amount: function (frm) { + frm.events.set_exchange_gain_loss_deduction(frm); }, - set_deductions_entry: function (frm, account) { - if (frm.doc.difference_amount) { - frappe.call({ - method: "erpnext.accounts.doctype.payment_entry.payment_entry.get_company_defaults", - args: { - company: frm.doc.company, - }, - callback: function (r, rt) { - if (r.message) { - const write_off_row = $.map(frm.doc["deductions"] || [], function (t) { - return t.account == r.message[account] ? t : null; - }); + base_received_amount: function (frm) { + frm.events.set_exchange_gain_loss_deduction(frm); + }, - const difference_amount = flt( - frm.doc.difference_amount, - precision("difference_amount") - ); + set_exchange_gain_loss_deduction: async function (frm) { + // wait for allocate_party_amount_against_ref_docs to finish + await frappe.after_ajax(); + const base_paid_amount = frm.doc.base_paid_amount || 0; + const base_received_amount = frm.doc.base_received_amount || 0; + const exchange_gain_loss = flt( + base_paid_amount - base_received_amount, + get_deduction_amount_precision() + ); - const add_deductions = (details) => { - let row = null; - if (!write_off_row.length && difference_amount) { - row = frm.add_child("deductions"); - row.account = details[account]; - row.cost_center = details["cost_center"]; - } else { - row = write_off_row[0]; - } - - if (row) { - row.amount = flt(row.amount) + difference_amount; - } else { - frappe.msgprint(__("No gain or loss in the exchange rate")); - } - refresh_field("deductions"); - }; - - if (!r.message[account]) { - frappe.prompt( - { - label: __("Please Specify Account"), - fieldname: account, - fieldtype: "Link", - options: "Account", - get_query: () => ({ - filters: { - company: frm.doc.company, - }, - }), - }, - (values) => { - const details = Object.assign({}, r.message, values); - add_deductions(details); - }, - __(frappe.unscrub(account)) - ); - } else { - add_deductions(r.message); - } - - frm.events.set_unallocated_amount(frm); - } - }, - }); + if (!exchange_gain_loss) { + frm.events.delete_exchange_gain_loss(frm); + return; } + + const account_fieldname = "exchange_gain_loss_account"; + let row = (frm.doc.deductions || []).find((t) => t.is_exchange_gain_loss); + + if (!row) { + const response = await get_company_defaults(frm.doc.company); + + const account = + response.message?.[account_fieldname] || + (await prompt_for_missing_account(frm, account_fieldname)); + + row = frm.add_child("deductions"); + row.account = account; + row.cost_center = response.message?.cost_center; + row.is_exchange_gain_loss = 1; + } + + row.amount = exchange_gain_loss; + frm.refresh_field("deductions"); + frm.events.set_unallocated_amount(frm); + }, + + delete_exchange_gain_loss: function (frm) { + const exchange_gain_loss_row = (frm.doc.deductions || []).find((row) => row.is_exchange_gain_loss); + + if (!exchange_gain_loss_row) return; + + exchange_gain_loss_row.amount = 0; + frm.get_field("deductions").grid.grid_rows[exchange_gain_loss_row.idx - 1].remove(); + frm.refresh_field("deductions"); + }, + + set_write_off_deduction: async function (frm) { + const difference_amount = flt(frm.doc.difference_amount, get_deduction_amount_precision()); + if (!difference_amount) return; + + const account_fieldname = "write_off_account"; + const response = await get_company_defaults(frm.doc.company); + const write_off_account = + response.message?.[account_fieldname] || + (await prompt_for_missing_account(frm, account_fieldname)); + + if (!write_off_account) return; + + let row = (frm.doc["deductions"] || []).find((t) => t.account == write_off_account); + if (!row) { + row = frm.add_child("deductions"); + row.account = write_off_account; + row.cost_center = response.message?.cost_center; + } + + row.amount = flt(row.amount) + difference_amount; + frm.refresh_field("deductions"); + frm.events.set_unallocated_amount(frm); }, bank_account: function (frm) { @@ -1778,6 +1779,13 @@ frappe.ui.form.on("Advance Taxes and Charges", { }); frappe.ui.form.on("Payment Entry Deduction", { + before_deductions_remove: function (doc, cdt, cdn) { + const row = frappe.get_doc(cdt, cdn); + if (row.is_exchange_gain_loss && row.amount) { + frappe.throw(__("Cannot delete Exchange Gain/Loss row")); + } + }, + amount: function (frm) { frm.events.set_unallocated_amount(frm); }, @@ -1799,3 +1807,53 @@ function set_default_party_type(frm) { if (party_type) frm.set_value("party_type", party_type); } + +function get_included_taxes(frm) { + let included_taxes = 0; + for (const tax of frm.doc.taxes) { + if (!tax.included_in_paid_amount) continue; + + if (tax.add_deduct_tax == "Add") { + included_taxes += tax.base_tax_amount; + } else { + included_taxes -= tax.base_tax_amount; + } + } + + return included_taxes; +} + +function get_company_defaults(company) { + return frappe.call({ + method: "erpnext.accounts.doctype.payment_entry.payment_entry.get_company_defaults", + args: { + company: company, + }, + }); +} + +function prompt_for_missing_account(frm, account) { + return new Promise((resolve) => { + const dialog = frappe.prompt( + { + label: __(frappe.unscrub(account)), + fieldname: account, + fieldtype: "Link", + options: "Account", + get_query: () => ({ + filters: { + company: frm.doc.company, + }, + }), + }, + (values) => resolve(values?.[account]), + __("Please Specify Account") + ); + + dialog.on_hide = () => resolve(""); + }); +} + +function get_deduction_amount_precision() { + return frappe.meta.get_field_precision(frappe.meta.get_field("Payment Entry Deduction", "amount")); +} diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.json b/erpnext/accounts/doctype/payment_entry/payment_entry.json index d420bcca342..69debbec5c7 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.json +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.json @@ -56,7 +56,6 @@ "section_break_34", "total_allocated_amount", "base_total_allocated_amount", - "set_exchange_gain_loss", "column_break_36", "unallocated_amount", "difference_amount", @@ -390,11 +389,6 @@ "print_hide": 1, "read_only": 1 }, - { - "fieldname": "set_exchange_gain_loss", - "fieldtype": "Button", - "label": "Set Exchange Gain / Loss" - }, { "fieldname": "column_break_36", "fieldtype": "Column Break" @@ -801,7 +795,7 @@ "table_fieldname": "payment_entries" } ], - "modified": "2024-05-31 17:07:06.197249", + "modified": "2024-11-07 11:19:19.320883", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Entry", diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 5ebe1dd8754..7e3d8a5833b 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -893,6 +893,7 @@ class PaymentEntry(AccountsController): self.set_amounts_in_company_currency() self.set_total_allocated_amount() self.set_unallocated_amount() + self.set_exchange_gain_loss() self.set_difference_amount() def validate_amounts(self): @@ -988,10 +989,10 @@ class PaymentEntry(AccountsController): if d.exchange_rate is None: d.exchange_rate = 1 - allocated_amount_in_pe_exchange_rate = flt( + allocated_amount_in_ref_exchange_rate = flt( flt(d.allocated_amount) * flt(d.exchange_rate), self.precision("base_paid_amount") ) - d.exchange_gain_loss = base_allocated_amount - allocated_amount_in_pe_exchange_rate + d.exchange_gain_loss = base_allocated_amount - allocated_amount_in_ref_exchange_rate return base_allocated_amount def set_total_allocated_amount(self): @@ -1009,29 +1010,80 @@ class PaymentEntry(AccountsController): def set_unallocated_amount(self): self.unallocated_amount = 0 - if self.party: - total_deductions = sum(flt(d.amount) for d in self.get("deductions")) - included_taxes = self.get_included_taxes() - if ( - self.payment_type == "Receive" - and self.base_total_allocated_amount < self.base_received_amount + total_deductions - and self.total_allocated_amount - < flt(self.paid_amount) + (total_deductions / self.source_exchange_rate) - ): - self.unallocated_amount = ( - self.base_received_amount + total_deductions - self.base_total_allocated_amount - ) / self.source_exchange_rate - self.unallocated_amount -= included_taxes - elif ( - self.payment_type == "Pay" - and self.base_total_allocated_amount < (self.base_paid_amount - total_deductions) - and self.total_allocated_amount - < flt(self.received_amount) + (total_deductions / self.target_exchange_rate) - ): - self.unallocated_amount = ( - self.base_paid_amount - (total_deductions + self.base_total_allocated_amount) - ) / self.target_exchange_rate - self.unallocated_amount -= included_taxes + if not self.party: + return + + deductions_to_consider = sum( + flt(d.amount) for d in self.get("deductions") if not d.is_exchange_gain_loss + ) + included_taxes = self.get_included_taxes() + + if self.payment_type == "Receive" and self.base_total_allocated_amount < ( + self.base_paid_amount + deductions_to_consider + ): + self.unallocated_amount = ( + self.base_paid_amount + + deductions_to_consider + - self.base_total_allocated_amount + - included_taxes + ) / self.source_exchange_rate + elif self.payment_type == "Pay" and self.base_total_allocated_amount < ( + self.base_received_amount - deductions_to_consider + ): + self.unallocated_amount = ( + self.base_received_amount + - deductions_to_consider + - self.base_total_allocated_amount + - included_taxes + ) / self.target_exchange_rate + + def set_exchange_gain_loss(self): + exchange_gain_loss = flt( + self.base_paid_amount - self.base_received_amount, + self.precision("amount", "deductions"), + ) + + exchange_gain_loss_rows = [row for row in self.get("deductions") if row.is_exchange_gain_loss] + exchange_gain_loss_row = exchange_gain_loss_rows.pop(0) if exchange_gain_loss_rows else None + + for row in exchange_gain_loss_rows: + self.remove(row) + + if not exchange_gain_loss: + if exchange_gain_loss_row: + self.remove(exchange_gain_loss_row) + + return + + if not exchange_gain_loss_row: + values = frappe.get_cached_value( + "Company", self.company, ("exchange_gain_loss_account", "cost_center"), as_dict=True + ) + + for fieldname, value in values.items(): + if value: + continue + + label = _(frappe.get_meta("Company").get_label(fieldname)) + return frappe.msgprint( + _("Please set {0} in Company {1} to account for Exchange Gain / Loss").format( + label, get_link_to_form("Company", self.company) + ), + title=_("Missing Default in Company"), + indicator="red" if self.docstatus.is_submitted() else "yellow", + raise_exception=self.docstatus.is_submitted(), + ) + + exchange_gain_loss_row = self.append( + "deductions", + { + "account": values.exchange_gain_loss_account, + "cost_center": values.cost_center, + "is_exchange_gain_loss": 1, + }, + ) + + exchange_gain_loss_row.amount = exchange_gain_loss def set_difference_amount(self): base_unallocated_amount = flt(self.unallocated_amount) * ( @@ -1059,11 +1111,13 @@ class PaymentEntry(AccountsController): def get_included_taxes(self): included_taxes = 0 for tax in self.get("taxes"): - if tax.included_in_paid_amount: - if tax.add_deduct_tax == "Add": - included_taxes += tax.base_tax_amount - else: - included_taxes -= tax.base_tax_amount + if not tax.included_in_paid_amount: + continue + + if tax.add_deduct_tax == "Add": + included_taxes += tax.base_tax_amount + else: + included_taxes -= tax.base_tax_amount return included_taxes @@ -1912,8 +1966,8 @@ class PaymentEntry(AccountsController): def get_matched_payment_request_of_references(references=None): """ Get those `Payment Requests` which are matched with `References`.\n - - Amount must be same. - - Only single `Payment Request` available for this amount. + - Amount must be same. + - Only single `Payment Request` available for this amount. Example: [(reference_doctype, reference_name, allocated_amount, payment_request), ...] """ @@ -2015,7 +2069,7 @@ def get_outstanding_of_references_with_payment_term(references=None): def get_outstanding_of_references_with_no_payment_term(references): """ Fetch outstanding amount of `References` which have no `Payment Term` set.\n - - Fetch outstanding amount from `References` it self. + - Fetch outstanding amount from `References` it self. Note: `None` is used for allocation of `Payment Request` Example: {(reference_doctype, reference_name, None): outstanding_amount, ...} @@ -2829,9 +2883,6 @@ def get_payment_entry( update_accounting_dimensions(pe, doc) if party_account and bank: - pe.set_exchange_rate(ref_doc=doc) - pe.set_amounts() - if discount_amount: base_total_discount_loss = 0 if frappe.db.get_single_value("Accounts Settings", "book_tax_discount_loss"): @@ -2841,7 +2892,8 @@ def get_payment_entry( pe, doc, discount_amount, base_total_discount_loss, party_account_currency ) - pe.set_difference_amount() + pe.set_exchange_rate(ref_doc=doc) + pe.set_amounts() # If PE is created from PR directly, then no need to find open PRs for the references if not created_from_payment_request: @@ -2853,7 +2905,7 @@ def get_payment_entry( def get_open_payment_requests_for_references(references=None): """ Fetch all unpaid Payment Requests for the references. \n - - Each reference can have multiple Payment Requests. \n + - Each reference can have multiple Payment Requests. \n Example: {("Sales Invoice", "SINV-00001"): {"PREQ-00001": 1000, "PREQ-00002": 2000}} """ @@ -3188,13 +3240,14 @@ def set_pending_discount_loss(pe, doc, discount_amount, base_total_discount_loss book_tax_loss = frappe.db.get_single_value("Accounts Settings", "book_tax_discount_loss") account_type = "round_off_account" if book_tax_loss else "default_discount_account" - pe.set_gain_or_loss( - account_details={ + pe.append( + "deductions", + { "account": frappe.get_cached_value("Company", pe.company, account_type), "cost_center": pe.cost_center or frappe.get_cached_value("Company", pe.company, "cost_center"), "amount": discount_amount * positive_negative, - } + }, ) diff --git a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py index 8758110534f..312628d9f97 100644 --- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py @@ -479,16 +479,9 @@ class TestPaymentEntry(FrappeTestCase): self.assertEqual(pe.deductions[0].account, "Write Off - _TC") # Exchange loss - self.assertEqual(pe.difference_amount, 300.0) - - pe.append( - "deductions", - { - "account": "_Test Exchange Gain/Loss - _TC", - "cost_center": "_Test Cost Center - _TC", - "amount": 300.0, - }, - ) + self.assertEqual(pe.deductions[-1].amount, 300.0) + pe.deductions[-1].account = "_Test Exchange Gain/Loss - _TC" + pe.deductions[-1].cost_center = "_Test Cost Center - _TC" pe.insert() pe.submit() @@ -552,16 +545,10 @@ class TestPaymentEntry(FrappeTestCase): pe.reference_no = "1" pe.reference_date = "2016-01-01" - self.assertEqual(pe.difference_amount, 100) + self.assertEqual(pe.deductions[0].amount, 100) + pe.deductions[0].account = "_Test Exchange Gain/Loss - _TC" + pe.deductions[0].cost_center = "_Test Cost Center - _TC" - pe.append( - "deductions", - { - "account": "_Test Exchange Gain/Loss - _TC", - "cost_center": "_Test Cost Center - _TC", - "amount": 100, - }, - ) pe.insert() pe.submit() @@ -654,16 +641,9 @@ class TestPaymentEntry(FrappeTestCase): pe.set_exchange_rate() pe.set_amounts() - self.assertEqual(pe.difference_amount, 500) - - pe.append( - "deductions", - { - "account": "_Test Exchange Gain/Loss - _TC", - "cost_center": "_Test Cost Center - _TC", - "amount": 500, - }, - ) + self.assertEqual(pe.deductions[0].amount, 500) + pe.deductions[0].account = "_Test Exchange Gain/Loss - _TC" + pe.deductions[0].cost_center = "_Test Cost Center - _TC" pe.insert() pe.submit() diff --git a/erpnext/accounts/doctype/payment_entry_deduction/payment_entry_deduction.json b/erpnext/accounts/doctype/payment_entry_deduction/payment_entry_deduction.json index 1c31829f0ea..82e871d0fff 100644 --- a/erpnext/accounts/doctype/payment_entry_deduction/payment_entry_deduction.json +++ b/erpnext/accounts/doctype/payment_entry_deduction/payment_entry_deduction.json @@ -9,6 +9,7 @@ "cost_center", "amount", "column_break_2", + "is_exchange_gain_loss", "description" ], "fields": [ @@ -45,12 +46,24 @@ "fieldname": "description", "fieldtype": "Small Text", "label": "Description" + }, + { + "default": "0", + "depends_on": "eval:doc.is_exchange_gain_loss", + "fieldname": "is_exchange_gain_loss", + "fieldtype": "Check", + "label": "Is Exchange Gain / Loss?", + "read_only": 1 } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], +<<<<<<< HEAD "modified": "2023-03-06 07:11:57.739619", +======= + "modified": "2024-11-05 16:07:47.307971", +>>>>>>> 7cc111f790 (fix: set correct unallocated amount in Payment Entry (#43958)) "modified_by": "Administrator", "module": "Accounts", "name": "Payment Entry Deduction", diff --git a/erpnext/accounts/doctype/payment_entry_deduction/payment_entry_deduction.py b/erpnext/accounts/doctype/payment_entry_deduction/payment_entry_deduction.py index fc67c526b28..ae4134fc27a 100644 --- a/erpnext/accounts/doctype/payment_entry_deduction/payment_entry_deduction.py +++ b/erpnext/accounts/doctype/payment_entry_deduction/payment_entry_deduction.py @@ -18,6 +18,7 @@ class PaymentEntryDeduction(Document): amount: DF.Currency cost_center: DF.Link description: DF.SmallText | None + is_exchange_gain_loss: DF.Check parent: DF.Data parentfield: DF.Data parenttype: DF.Data diff --git a/erpnext/accounts/doctype/unreconcile_payment/test_unreconcile_payment.py b/erpnext/accounts/doctype/unreconcile_payment/test_unreconcile_payment.py index 3d222b22ff8..c058dbfa0b8 100644 --- a/erpnext/accounts/doctype/unreconcile_payment/test_unreconcile_payment.py +++ b/erpnext/accounts/doctype/unreconcile_payment/test_unreconcile_payment.py @@ -262,6 +262,7 @@ class TestUnreconcilePayment(AccountsTestMixin, FrappeTestCase): pe1.paid_from = self.debtors_usd pe1.paid_from_account_currency = "USD" pe1.source_exchange_rate = 75 + pe1.paid_amount = 100 pe1.received_amount = 75 * 100 pe1.save() # Allocate payment against both invoices @@ -279,6 +280,7 @@ class TestUnreconcilePayment(AccountsTestMixin, FrappeTestCase): pe2.paid_from = self.debtors_usd pe2.paid_from_account_currency = "USD" pe2.source_exchange_rate = 75 + pe2.paid_amount = 100 pe2.received_amount = 75 * 100 pe2.save() # Allocate payment against both invoices diff --git a/erpnext/accounts/test/test_utils.py b/erpnext/accounts/test/test_utils.py index 59cbc11794f..5e108dee9b5 100644 --- a/erpnext/accounts/test/test_utils.py +++ b/erpnext/accounts/test/test_utils.py @@ -92,14 +92,14 @@ class TestUtils(unittest.TestCase): payment_entry.deductions = [] payment_entry.save() - # below is the difference between base_received_amount and base_paid_amount - self.assertEqual(payment_entry.difference_amount, -4855.0) + # below is the difference between base_paid_amount and base_received_amount (exchange gain) + self.assertEqual(payment_entry.deductions[0].amount, -4855.0) payment_entry.target_exchange_rate = 62.9 payment_entry.save() - # below is due to change in exchange rate - self.assertEqual(payment_entry.references[0].exchange_gain_loss, -4855.0) + # after changing the exchange rate, there is no exchange gain / loss + self.assertEqual(payment_entry.deductions, []) payment_entry.references = [] self.assertEqual(payment_entry.difference_amount, 0.0) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 2716aa9883b..1828cfa031f 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -380,6 +380,12 @@ erpnext.patches.v15_0.add_disassembly_order_stock_entry_type #1 erpnext.patches.v15_0.set_standard_stock_entry_type erpnext.patches.v15_0.link_purchase_item_to_asset_doc erpnext.patches.v14_0.update_currency_exchange_settings_for_frankfurter +<<<<<<< HEAD erpnext.patches.v15_0.update_task_assignee_email_field_in_asset_maintenance_log erpnext.patches.v15_0.update_sub_voucher_type_in_gl_entries erpnext.patches.v14_0.update_stock_uom_in_work_order_item +======= +erpnext.patches.v15_0.migrate_old_item_wise_tax_detail_data_format +erpnext.patches.v15_0.set_is_exchange_gain_loss_in_payment_entry_deductions +erpnext.patches.v14_0.update_stock_uom_in_work_order_item +>>>>>>> 7cc111f790 (fix: set correct unallocated amount in Payment Entry (#43958)) diff --git a/erpnext/patches/v15_0/set_is_exchange_gain_loss_in_payment_entry_deductions.py b/erpnext/patches/v15_0/set_is_exchange_gain_loss_in_payment_entry_deductions.py new file mode 100644 index 00000000000..9ffe272fd5e --- /dev/null +++ b/erpnext/patches/v15_0/set_is_exchange_gain_loss_in_payment_entry_deductions.py @@ -0,0 +1,22 @@ +import frappe + + +def execute(): + default_exchange_gain_loss_accounts = frappe.get_all( + "Company", + filters={"exchange_gain_loss_account": ["!=", ""]}, + pluck="exchange_gain_loss_account", + ) + + if not default_exchange_gain_loss_accounts: + return + + payment_entry = frappe.qb.DocType("Payment Entry") + payment_entry_deduction = frappe.qb.DocType("Payment Entry Deduction") + + frappe.qb.update(payment_entry_deduction).set(payment_entry_deduction.is_exchange_gain_loss, 1).join( + payment_entry, + ).on(payment_entry.name == payment_entry_deduction.parent).where( + (payment_entry.paid_to_account_currency != payment_entry.paid_from_account_currency) + & (payment_entry_deduction.account.isin(default_exchange_gain_loss_accounts)) + ).run() From c1579789121d8fb93e352a55f45c80aa1c6771b6 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 2 Dec 2024 14:57:54 +0530 Subject: [PATCH 669/734] chore: resolve conflicts --- .../payment_entry_deduction/payment_entry_deduction.json | 4 ---- erpnext/patches.txt | 5 ----- 2 files changed, 9 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry_deduction/payment_entry_deduction.json b/erpnext/accounts/doctype/payment_entry_deduction/payment_entry_deduction.json index 82e871d0fff..e47b51ae028 100644 --- a/erpnext/accounts/doctype/payment_entry_deduction/payment_entry_deduction.json +++ b/erpnext/accounts/doctype/payment_entry_deduction/payment_entry_deduction.json @@ -59,11 +59,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], -<<<<<<< HEAD - "modified": "2023-03-06 07:11:57.739619", -======= "modified": "2024-11-05 16:07:47.307971", ->>>>>>> 7cc111f790 (fix: set correct unallocated amount in Payment Entry (#43958)) "modified_by": "Administrator", "module": "Accounts", "name": "Payment Entry Deduction", diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 1828cfa031f..f53769155bd 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -380,12 +380,7 @@ erpnext.patches.v15_0.add_disassembly_order_stock_entry_type #1 erpnext.patches.v15_0.set_standard_stock_entry_type erpnext.patches.v15_0.link_purchase_item_to_asset_doc erpnext.patches.v14_0.update_currency_exchange_settings_for_frankfurter -<<<<<<< HEAD erpnext.patches.v15_0.update_task_assignee_email_field_in_asset_maintenance_log erpnext.patches.v15_0.update_sub_voucher_type_in_gl_entries erpnext.patches.v14_0.update_stock_uom_in_work_order_item -======= -erpnext.patches.v15_0.migrate_old_item_wise_tax_detail_data_format erpnext.patches.v15_0.set_is_exchange_gain_loss_in_payment_entry_deductions -erpnext.patches.v14_0.update_stock_uom_in_work_order_item ->>>>>>> 7cc111f790 (fix: set correct unallocated amount in Payment Entry (#43958)) From 5c6d9c98122ab0d2a9278455e97c776cf49e8a66 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 2 Dec 2024 15:37:38 +0530 Subject: [PATCH 670/734] refactor(UI): Rearranging fields under new sections (cherry picked from commit 7244754d28762c7c6347812529b8e28ab24475cc) --- erpnext/setup/doctype/company/company.json | 53 ++++++++++++++++++---- 1 file changed, 43 insertions(+), 10 deletions(-) diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json index 4b07037ad3e..271b440fbda 100644 --- a/erpnext/setup/doctype/company/company.json +++ b/erpnext/setup/doctype/company/company.json @@ -48,24 +48,30 @@ "default_bank_account", "default_cash_account", "default_receivable_account", - "round_off_account", - "round_off_for_opening", - "round_off_cost_center", + "default_payable_account", "write_off_account", - "exchange_gain_loss_account", - "unrealized_exchange_gain_loss_account", "unrealized_profit_loss_account", "column_break0", "allow_account_creation_against_child_company", - "default_payable_account", "default_expense_account", "default_income_account", - "default_deferred_revenue_account", - "default_deferred_expense_account", "default_discount_account", "payment_terms", "cost_center", "default_finance_book", + "exchange_gain__loss_section", + "exchange_gain_loss_account", + "column_break_sttp", + "unrealized_exchange_gain_loss_account", + "round_off_section", + "round_off_account", + "round_off_cost_center", + "column_break_jqfo", + "round_off_for_opening", + "deferred_accounting_section", + "default_deferred_revenue_account", + "column_break_dcdl", + "default_deferred_expense_account", "advance_payments_section", "book_advance_payments_in_separate_party_account", "reconcile_on_advance_payment_date", @@ -287,7 +293,7 @@ { "fieldname": "default_settings", "fieldtype": "Section Break", - "label": "Accounts Settings", + "label": "Default Accounts", "oldfieldtype": "Section Break" }, { @@ -808,6 +814,33 @@ "fieldtype": "Link", "label": "Round Off for Opening", "options": "Account" + }, + { + "fieldname": "exchange_gain__loss_section", + "fieldtype": "Section Break", + "label": "Exchange Gain / Loss" + }, + { + "fieldname": "round_off_section", + "fieldtype": "Section Break", + "label": "Round Off" + }, + { + "fieldname": "deferred_accounting_section", + "fieldtype": "Section Break", + "label": "Deferred Accounting" + }, + { + "fieldname": "column_break_sttp", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_jqfo", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_dcdl", + "fieldtype": "Column Break" } ], "icon": "fa fa-building", @@ -815,7 +848,7 @@ "image_field": "company_logo", "is_tree": 1, "links": [], - "modified": "2024-08-02 11:34:46.785377", + "modified": "2024-12-02 15:37:32.723176", "modified_by": "Administrator", "module": "Setup", "name": "Company", From 05795af4716990fb4e3c104dbf4a6721fb7f7afa Mon Sep 17 00:00:00 2001 From: Ninad1306 Date: Wed, 27 Nov 2024 12:05:39 +0530 Subject: [PATCH 671/734] fix: always set sales incoming rate for internal transfers (cherry picked from commit d049c978843ebf3e974aea8559d12d8024fd6ca8) --- erpnext/controllers/buying_controller.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index e9e7ef62670..6020dce0761 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -356,14 +356,14 @@ class BuyingController(SubcontractingController): if not self.is_internal_transfer(): return + self.set_sales_incoming_rate_for_internal_transfer() + allow_at_arms_length_price = frappe.get_cached_value( "Stock Settings", None, "allow_internal_transfer_at_arms_length_price" ) if allow_at_arms_length_price: return - self.set_sales_incoming_rate_for_internal_transfer() - for d in self.get("items"): d.discount_percentage = 0.0 d.discount_amount = 0.0 From 558d49b3d3c1044a255e0c26d89954d7083484f7 Mon Sep 17 00:00:00 2001 From: Ninad1306 Date: Wed, 27 Nov 2024 12:06:11 +0530 Subject: [PATCH 672/734] test: validate buying workflow (cherry picked from commit 94d3fc9fde78220b7924d14b858204785480018a) --- .../tests/test_accounts_controller.py | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/erpnext/controllers/tests/test_accounts_controller.py b/erpnext/controllers/tests/test_accounts_controller.py index b2f8fce3d31..289a955f980 100644 --- a/erpnext/controllers/tests/test_accounts_controller.py +++ b/erpnext/controllers/tests/test_accounts_controller.py @@ -807,6 +807,7 @@ class TestAccountsController(FrappeTestCase): @change_settings("Stock Settings", {"allow_internal_transfer_at_arms_length_price": 1}) def test_16_internal_transfer_at_arms_length_price(self): + from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_inter_company_purchase_invoice from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse prepare_data_for_internal_transfer() @@ -840,6 +841,31 @@ class TestAccountsController(FrappeTestCase): # rate should reset to incoming rate self.assertEqual(si.items[0].rate, 100) + si.update_stock = 0 + si.save() + si.submit() + + pi = make_inter_company_purchase_invoice(si.name) + pi.update_stock = 1 + pi.items[0].rate = arms_length_price + pi.items[0].warehouse = target_warehouse + pi.items[0].from_warehouse = warehouse + pi.save() + + self.assertEqual(pi.items[0].rate, 100) + self.assertEqual(pi.items[0].valuation_rate, 100) + + frappe.db.set_single_value("Stock Settings", "allow_internal_transfer_at_arms_length_price", 1) + pi = make_inter_company_purchase_invoice(si.name) + pi.update_stock = 1 + pi.items[0].rate = arms_length_price + pi.items[0].warehouse = target_warehouse + pi.items[0].from_warehouse = warehouse + pi.save() + + self.assertEqual(pi.items[0].rate, arms_length_price) + self.assertEqual(pi.items[0].valuation_rate, 100) + 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 435280d626e542f64e7265733f6dd478339ffcc5 Mon Sep 17 00:00:00 2001 From: ljain112 Date: Thu, 28 Nov 2024 20:45:17 +0530 Subject: [PATCH 673/734] fix: adjusted incoming rate for zero rated item in purchase receipt (cherry picked from commit 3182c6981c7bb2107b8c2d59b7f0e474f94c0cea) --- .../purchase_invoice/test_purchase_invoice.py | 24 +++++++++++++++++++ .../purchase_receipt/purchase_receipt.py | 2 +- 2 files changed, 25 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 f0b51c32c05..e353435661c 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -1735,6 +1735,30 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin): frappe.db.set_single_value("Buying Settings", "set_landed_cost_based_on_purchase_invoice_rate", 1) + # Cost of Item is zero in Purchase Receipt + pr = make_purchase_receipt(qty=1, rate=0) + + stock_value_difference = frappe.db.get_value( + "Stock Ledger Entry", + {"voucher_type": "Purchase Receipt", "voucher_no": pr.name}, + "stock_value_difference", + ) + self.assertEqual(stock_value_difference, 0) + + pi = create_purchase_invoice_from_receipt(pr.name) + for row in pi.items: + row.rate = 150 + + pi.save() + pi.submit() + + stock_value_difference = frappe.db.get_value( + "Stock Ledger Entry", + {"voucher_type": "Purchase Receipt", "voucher_no": pr.name}, + "stock_value_difference", + ) + self.assertEqual(stock_value_difference, 150) + # Increase the cost of the item pr = make_purchase_receipt(qty=1, rate=100) diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 4c2b6891cea..76ecf0fd596 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -1085,7 +1085,7 @@ def update_billing_percentage(pr_doc, update_modified=True, adjust_incoming_rate if adjust_incoming_rate: adjusted_amt = 0.0 - if item.billed_amt and item.amount: + if item.billed_amt is not None and item.amount is not None: adjusted_amt = flt(item.billed_amt) - flt(item.amount) adjusted_amt = adjusted_amt * flt(pr_doc.conversion_rate) From ebdacc094c47aa334282b5f75cc194e6c25d48ad Mon Sep 17 00:00:00 2001 From: Sugesh393 Date: Fri, 29 Nov 2024 17:39:40 +0530 Subject: [PATCH 674/734] fix: calculate submitted payment entry as paid amount (cherry picked from commit 561a159aec1d71125fa0b1779df5ac1ff111b0da) --- erpnext/accounts/doctype/payment_request/payment_request.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index 27e3aa83092..61bb2932d2b 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -778,6 +778,8 @@ def get_existing_paid_amount(doctype, name): .where(PL.against_voucher_type.eq(doctype)) .where(PL.against_voucher_no.eq(name)) .where(PL.amount < 0) + .where(PL.delinked == 0) + .where(PER.docstatus == 1) .where(PER.payment_request.isnull()) ) response = query.run() From aa090beae0d0a7d3730634c6525e43f681f74653 Mon Sep 17 00:00:00 2001 From: Sugesh393 Date: Fri, 29 Nov 2024 17:44:22 +0530 Subject: [PATCH 675/734] test: add new unit test to validate paid amount in payment request (cherry picked from commit 9bee2d430cb05f21b911d0af5f9c0d1a66615ffc) --- .../payment_request/test_payment_request.py | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/erpnext/accounts/doctype/payment_request/test_payment_request.py b/erpnext/accounts/doctype/payment_request/test_payment_request.py index 4442dbdd7ea..eadb714baa3 100644 --- a/erpnext/accounts/doctype/payment_request/test_payment_request.py +++ b/erpnext/accounts/doctype/payment_request/test_payment_request.py @@ -543,3 +543,30 @@ class TestPaymentRequest(FrappeTestCase): pr = make_payment_request(dt="Sales Invoice", dn=si.name, mute_email=1) self.assertEqual(pr.grand_total, si.outstanding_amount) + + +def test_partial_paid_invoice_with_submitted_payment_entry(self): + pi = make_purchase_invoice(currency="INR", qty=1, rate=5000) + pi.save() + pi.submit() + + pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC") + pe.reference_no = "PURINV0001" + pe.reference_date = frappe.utils.nowdate() + pe.paid_amount = 2500 + pe.references[0].allocated_amount = 2500 + pe.save() + pe.submit() + pe.cancel() + + pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC") + pe.reference_no = "PURINV0002" + pe.reference_date = frappe.utils.nowdate() + pe.paid_amount = 2500 + pe.references[0].allocated_amount = 2500 + pe.save() + pe.submit() + + pi.load_from_db() + pr = make_payment_request(dt="Purchase Invoice", dn=pi.name, mute_email=1) + self.assertEqual(pr.grand_total, pi.outstanding_amount) From 0a9c92fce940ed74dbd1b22ecfbebfc6612ba61a Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 3 Dec 2024 15:09:09 +0530 Subject: [PATCH 676/734] fix: incorrect Gross Margin on project (backport #44461) (#44468) * fix: incorrect Gross Margin on project (#44461) (cherry picked from commit 7de9c14a2ce329ea6134fddf5c13424bc5191657) # Conflicts: # erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py * chore: resolve conflict --------- Co-authored-by: rohitwaghchaure Co-authored-by: ruthra kumar --- .../doctype/sales_invoice/sales_invoice.py | 3 +++ .../doctype/sales_invoice/test_sales_invoice.py | 14 ++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 410e934ab71..1a7ffc3c339 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1755,6 +1755,9 @@ class SalesInvoice(SellingController): def update_project(self): unique_projects = list(set([d.project for d in self.get("items") if d.project])) + if self.project and self.project not in unique_projects: + unique_projects.append(self.project) + for p in unique_projects: project = frappe.get_doc("Project", p) project.update_billed_amount() diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index d2b8882743a..57eb84caaa4 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -4217,6 +4217,20 @@ class TestSalesInvoice(FrappeTestCase): self.assertTrue(jv) self.assertEqual(jv[0], si.grand_total) + def test_total_billed_amount(self): + si = create_sales_invoice(do_not_submit=True) + + project = frappe.new_doc("Project") + project.project_name = "Test Total Billed Amount" + project.save() + + si.project = project.name + si.save() + si.submit() + + doc = frappe.get_doc("Project", project.name) + self.assertEqual(doc.total_billed_amount, si.grand_total) + def set_advance_flag(company, flag, default_account): frappe.db.set_value( From ea57f2b2921f5fb6b06a0be1bac0cac4ddaf248a Mon Sep 17 00:00:00 2001 From: ljain112 Date: Mon, 2 Dec 2024 14:08:21 +0530 Subject: [PATCH 677/734] fix: remove queries (cherry picked from commit a86b223aed6e978154dbe6feb3581bcdacff2d60) --- .../report/gross_profit/gross_profit.py | 29 +++++++------------ 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/erpnext/accounts/report/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py index 5ba1e41b624..852d33c6356 100644 --- a/erpnext/accounts/report/gross_profit/gross_profit.py +++ b/erpnext/accounts/report/gross_profit/gross_profit.py @@ -421,10 +421,10 @@ class GrossProfitGenerator: self.load_invoice_items() self.get_delivery_notes() + self.load_product_bundle() if filters.group_by == "Invoice": self.group_items_by_invoice() - self.load_product_bundle() self.load_non_stock_items() self.get_returned_invoice_items() self.process() @@ -851,6 +851,7 @@ class GrossProfitGenerator: `tabSales Invoice`.project, `tabSales Invoice`.update_stock, `tabSales Invoice`.customer, `tabSales Invoice`.customer_group, `tabSales Invoice`.territory, `tabSales Invoice Item`.item_code, + `tabSales Invoice`.base_net_total as "invoice_base_net_total", `tabSales Invoice Item`.item_name, `tabSales Invoice Item`.description, `tabSales Invoice Item`.warehouse, `tabSales Invoice Item`.item_group, `tabSales Invoice Item`.brand, `tabSales Invoice Item`.so_detail, @@ -911,6 +912,7 @@ class GrossProfitGenerator: """ grouped = OrderedDict() + product_bundels = self.product_bundles.get("Sales Invoice", {}) for row in self.si_list: # initialize list with a header row for each new parent @@ -921,8 +923,7 @@ class GrossProfitGenerator: ) # if item is a bundle, add it's components as seperate rows - if frappe.db.exists("Product Bundle", row.item_code): - bundled_items = self.get_bundle_items(row) + if bundled_items := product_bundels.get(row.parent, {}).get(row.item_code): for x in bundled_items: bundle_item = self.get_bundle_item_row(row, x) grouped.get(row.parent).append(bundle_item) @@ -958,18 +959,11 @@ class GrossProfitGenerator: "item_row": None, "is_return": row.is_return, "cost_center": row.cost_center, - "base_net_amount": frappe.db.get_value("Sales Invoice", row.parent, "base_net_total"), + "base_net_amount": row.invoice_base_net_total, } ) - def get_bundle_items(self, product_bundle): - return frappe.get_all( - "Product Bundle Item", filters={"parent": product_bundle.item_code}, fields=["item_code", "qty"] - ) - def get_bundle_item_row(self, product_bundle, item): - item_name, description, item_group, brand = self.get_bundle_item_details(item.item_code) - return frappe._dict( { "parent_invoice": product_bundle.item_code, @@ -982,23 +976,20 @@ class GrossProfitGenerator: "customer": product_bundle.customer, "customer_group": product_bundle.customer_group, "item_code": item.item_code, - "item_name": item_name, - "description": description, + "item_name": item.item_name, + "description": item.description, "warehouse": product_bundle.warehouse, - "item_group": item_group, - "brand": brand, + "item_group": "", + "brand": "", "dn_detail": product_bundle.dn_detail, "delivery_note": product_bundle.delivery_note, - "qty": (flt(product_bundle.qty) * flt(item.qty)), + "qty": item.total_qty * -1, "item_row": None, "is_return": product_bundle.is_return, "cost_center": product_bundle.cost_center, } ) - def get_bundle_item_details(self, item_code): - return frappe.db.get_value("Item", item_code, ["item_name", "description", "item_group", "brand"]) - def get_stock_ledger_entries(self, item_code, warehouse): if item_code and warehouse: if (item_code, warehouse) not in self.sle: From f165e1732b2160cf6cc65ca52ee2ce0d2a833667 Mon Sep 17 00:00:00 2001 From: ljain112 Date: Mon, 2 Dec 2024 15:20:20 +0530 Subject: [PATCH 678/734] fix: correct buying amount for product bundel (cherry picked from commit 4e6a5893e7a26da863920c782f2719861b5aaecd) --- .../report/gross_profit/gross_profit.py | 36 +++++++++++-------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/erpnext/accounts/report/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py index 852d33c6356..1191f72cb3f 100644 --- a/erpnext/accounts/report/gross_profit/gross_profit.py +++ b/erpnext/accounts/report/gross_profit/gross_profit.py @@ -636,6 +636,7 @@ class GrossProfitGenerator: if packed_item.get("parent_detail_docname") == row.item_row: packed_item_row = row.copy() packed_item_row.warehouse = packed_item.warehouse + packed_item_row.qty = packed_item.total_qty * -1 buying_amount += self.get_buying_amount(packed_item_row, packed_item.item_code) return flt(buying_amount, self.currency_precision) @@ -668,7 +669,9 @@ class GrossProfitGenerator: else: my_sle = self.get_stock_ledger_entries(item_code, row.warehouse) if (row.update_stock or row.dn_detail) and my_sle: - parenttype, parent = row.parenttype, row.parent + parenttype = row.parenttype + parent = row.invoice or row.parent + if row.dn_detail: parenttype, parent = "Delivery Note", row.delivery_note @@ -963,30 +966,33 @@ class GrossProfitGenerator: } ) - def get_bundle_item_row(self, product_bundle, item): + def get_bundle_item_row(self, row, item): return frappe._dict( { - "parent_invoice": product_bundle.item_code, - "indent": product_bundle.indent + 1, + "parent_invoice": row.item_code, + "parenttype": row.parenttype, + "indent": row.indent + 1, "parent": None, "invoice_or_item": item.item_code, - "posting_date": product_bundle.posting_date, - "posting_time": product_bundle.posting_time, - "project": product_bundle.project, - "customer": product_bundle.customer, - "customer_group": product_bundle.customer_group, + "posting_date": row.posting_date, + "posting_time": row.posting_time, + "project": row.project, + "customer": row.customer, + "customer_group": row.customer_group, "item_code": item.item_code, "item_name": item.item_name, "description": item.description, - "warehouse": product_bundle.warehouse, + "warehouse": item.warehouse or row.warehouse, + "update_stock": row.update_stock, "item_group": "", "brand": "", - "dn_detail": product_bundle.dn_detail, - "delivery_note": product_bundle.delivery_note, + "dn_detail": row.dn_detail, + "delivery_note": row.delivery_note, "qty": item.total_qty * -1, - "item_row": None, - "is_return": product_bundle.is_return, - "cost_center": product_bundle.cost_center, + "item_row": row.item_row, + "is_return": row.is_return, + "cost_center": row.cost_center, + "invoice": row.parent, } ) From 4a713f6b5e40472166718cc67b5f975b47bc9de3 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 3 Dec 2024 15:39:17 +0530 Subject: [PATCH 679/734] chore: fix typo (cherry picked from commit fc0122ce760f8c2f14de931bf8f082de8cefa02d) --- erpnext/accounts/report/gross_profit/gross_profit.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py index 1191f72cb3f..c59a3bd2a7a 100644 --- a/erpnext/accounts/report/gross_profit/gross_profit.py +++ b/erpnext/accounts/report/gross_profit/gross_profit.py @@ -915,7 +915,7 @@ class GrossProfitGenerator: """ grouped = OrderedDict() - product_bundels = self.product_bundles.get("Sales Invoice", {}) + product_bundles = self.product_bundles.get("Sales Invoice", {}) for row in self.si_list: # initialize list with a header row for each new parent @@ -926,7 +926,7 @@ class GrossProfitGenerator: ) # if item is a bundle, add it's components as seperate rows - if bundled_items := product_bundels.get(row.parent, {}).get(row.item_code): + if bundled_items := product_bundles.get(row.parent, {}).get(row.item_code): for x in bundled_items: bundle_item = self.get_bundle_item_row(row, x) grouped.get(row.parent).append(bundle_item) From 63de576be61ec2ec30c79cec055b4c8afa2dfd07 Mon Sep 17 00:00:00 2001 From: vishakhdesai Date: Tue, 26 Nov 2024 14:26:02 +0530 Subject: [PATCH 680/734] fix: move validate_total_debit_and_credit from validate to on_submit in Journal Entry (cherry picked from commit 8b5d4c023654157d58b40fce81ab6787d95bc734) --- erpnext/accounts/doctype/journal_entry/journal_entry.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index aeaadae0b30..34a4d14fbbf 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -127,9 +127,6 @@ class JournalEntry(AccountsController): self.set_amounts_in_company_currency() self.validate_debit_credit_amount() self.set_total_debit_credit() - # Do not validate while importing via data import - if not frappe.flags.in_import: - self.validate_total_debit_and_credit() if not frappe.flags.is_reverse_depr_entry: self.validate_against_jv() @@ -185,6 +182,10 @@ class JournalEntry(AccountsController): return self._cancel() def on_submit(self): + # Do not validate while importing via data import + if not frappe.flags.in_import: + self.validate_total_debit_and_credit() + self.validate_cheque_info() self.check_credit_limit() self.make_gl_entries() From 16d0d42afe2f1282c5291cfcb88495348910c015 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 3 Dec 2024 17:42:11 +0530 Subject: [PATCH 681/734] refactor: validate debit and credit on before_submit (cherry picked from commit c3ace82db83dcf2dbb2afabeda2a82aa37d9e5ab) --- erpnext/accounts/doctype/journal_entry/journal_entry.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index 34a4d14fbbf..ef2388a7eaa 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -181,11 +181,12 @@ class JournalEntry(AccountsController): else: return self._cancel() - def on_submit(self): + def before_submit(self): # Do not validate while importing via data import if not frappe.flags.in_import: self.validate_total_debit_and_credit() + def on_submit(self): self.validate_cheque_info() self.check_credit_limit() self.make_gl_entries() From 1d158d58f62087691b8c0f5673f18f9bb21a5e7c Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 4 Dec 2024 04:38:06 +0000 Subject: [PATCH 682/734] chore(release): Bumped to Version 15.45.0 # [15.45.0](https://github.com/frappe/erpnext/compare/v15.44.0...v15.45.0) (2024-12-04) ### Bug Fixes * Add filter for `outstanding_amount` to fetch open PRs ([5999a8e](https://github.com/frappe/erpnext/commit/5999a8e24f908dc84bce88969bdb192c03c2cd08)) * Add translation for showing mandatory fields in error msg ([0e1f5ff](https://github.com/frappe/erpnext/commit/0e1f5ff391b7a6f277149e5a14203f4cde7fac6c)) * added fieldname to avoid fieldname to translate ([17c2734](https://github.com/frappe/erpnext/commit/17c2734042ed0ed2f226ff2adb1ef70dfd63cfd9)) * Added translation for `Account` column ([eb4a485](https://github.com/frappe/erpnext/commit/eb4a485df6e0953c1552549ffda74caea644ed60)) * adjusted incoming rate for zero rated item in purchase receipt ([435280d](https://github.com/frappe/erpnext/commit/435280d626e542f64e7265733f6dd478339ffcc5)) * always set sales incoming rate for internal transfers ([05795af](https://github.com/frappe/erpnext/commit/05795af4716990fb4e3c104dbf4a6721fb7f7afa)) * calculate submitted payment entry as paid amount ([ebdacc0](https://github.com/frappe/erpnext/commit/ebdacc094c47aa334282b5f75cc194e6c25d48ad)) * correct buying amount for product bundel ([f165e17](https://github.com/frappe/erpnext/commit/f165e1732b2160cf6cc65ca52ee2ce0d2a833667)) * Dashboard for `Payment Request` ([0d67c62](https://github.com/frappe/erpnext/commit/0d67c62f43758d453bc59ea0595046f48afa78ab)) * Data Should be Computed in Backend to Maintain Consistent Behaviour ([#44195](https://github.com/frappe/erpnext/issues/44195)) ([8ab9fc7](https://github.com/frappe/erpnext/commit/8ab9fc7f55b7759033f540cd275630a30ace8e5b)) * do not validate stock during inward (backport [#44417](https://github.com/frappe/erpnext/issues/44417)) ([#44427](https://github.com/frappe/erpnext/issues/44427)) ([e607795](https://github.com/frappe/erpnext/commit/e607795baed7544bcaedac031ed595b3a34eec4d)) * handle multi currency in common party journal entry ([c8e2c9a](https://github.com/frappe/erpnext/commit/c8e2c9aa2535b35a9c2a803762ef26a55ef804e2)) * incorrect Gross Margin on project (backport [#44461](https://github.com/frappe/erpnext/issues/44461)) ([#44468](https://github.com/frappe/erpnext/issues/44468)) ([0a9c92f](https://github.com/frappe/erpnext/commit/0a9c92fce940ed74dbd1b22ecfbebfc6612ba61a)) * IndexError in Asset Depreciation Ledger when query result is empty ([c3bc724](https://github.com/frappe/erpnext/commit/c3bc724523aab30585554e24f7e3907ae48f6ea2)) * link cash flow rows and fix summary linking ([633be8d](https://github.com/frappe/erpnext/commit/633be8d06bcbba0a7b08ba3e9172f67a7562343b)) * move validate_total_debit_and_credit from validate to on_submit in Journal Entry ([63de576](https://github.com/frappe/erpnext/commit/63de576be61ec2ec30c79cec055b4c8afa2dfd07)) * number format in the message (backport [#44435](https://github.com/frappe/erpnext/issues/44435)) ([#44438](https://github.com/frappe/erpnext/issues/44438)) ([579d8e2](https://github.com/frappe/erpnext/commit/579d8e293eb898b0bae0da31ca53b8aa46955884)) * precision calculation causing 0.1 discrepancy (backport [#44431](https://github.com/frappe/erpnext/issues/44431)) ([#44436](https://github.com/frappe/erpnext/issues/44436)) ([0d41c23](https://github.com/frappe/erpnext/commit/0d41c23383961a332802de46c2919981e8250a3c)) * remove queries ([ea57f2b](https://github.com/frappe/erpnext/commit/ea57f2b2921f5fb6b06a0be1bac0cac4ddaf248a)) * SABB print for packed items (backport [#44413](https://github.com/frappe/erpnext/issues/44413)) ([#44428](https://github.com/frappe/erpnext/issues/44428)) ([0e39aa3](https://github.com/frappe/erpnext/commit/0e39aa349eda503339299acae94009c6e54b54cb)) * set correct unallocated amount in Payment Entry ([#43958](https://github.com/frappe/erpnext/issues/43958)) ([ae93f7f](https://github.com/frappe/erpnext/commit/ae93f7f967e2b4da1c76552a794bce118827b57f)) * show "Send SMS" only when enabled (backport [#43941](https://github.com/frappe/erpnext/issues/43941)) ([#43970](https://github.com/frappe/erpnext/issues/43970)) ([0fbc60a](https://github.com/frappe/erpnext/commit/0fbc60a20e0958d93c935446f0271d9ee626ecb5)) * source warehouse not set in required items of WO (backport [#44426](https://github.com/frappe/erpnext/issues/44426)) ([#44434](https://github.com/frappe/erpnext/issues/44434)) ([c81b5e3](https://github.com/frappe/erpnext/commit/c81b5e3d9cb8acb6d15b294bb0545f713ba5748d)) * Translate `Party Account` column label ([fdda864](https://github.com/frappe/erpnext/commit/fdda86455a93a25bb2216893da69cc8efe9a07d2)) * typeerror on transaction.js ([173d60f](https://github.com/frappe/erpnext/commit/173d60fb7d9aaab62e7bcd7e1e6ae5ec5f261518)) ### Features * add Company Contact Person in selling transactions (backport [#44362](https://github.com/frappe/erpnext/issues/44362)) ([#44398](https://github.com/frappe/erpnext/issues/44398)) ([70b5b08](https://github.com/frappe/erpnext/commit/70b5b08d588ea4e9132f2ab51846a16d4da04241)) * **Dunning:** separate tab "Address & Contact" ([#44363](https://github.com/frappe/erpnext/issues/44363)) ([e0cb5f9](https://github.com/frappe/erpnext/commit/e0cb5f9ba8dc8a5b23af958b5c988b3499ed4853)) ### Performance Improvements * cache product bundle items at document level ([#44440](https://github.com/frappe/erpnext/issues/44440)) ([1f97979](https://github.com/frappe/erpnext/commit/1f9797905973ef8adc74867adc3a1f052cf5e83b)) * reduce queries during transaction save ([48059a7](https://github.com/frappe/erpnext/commit/48059a7c74422fa2c462aabb041fb642b01a9ae2)) ### Reverts * remove default `Payment Request` indicator color ([9c4b581](https://github.com/frappe/erpnext/commit/9c4b5814a65b9bed086326aa11538f5a9db99938)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 82b86724482..faee116955b 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.44.0" +__version__ = "15.45.0" def get_default_company(user=None): From 106671a414436fe162d492e7619bcf9ff34236d1 Mon Sep 17 00:00:00 2001 From: mahsem <137205921+mahsem@users.noreply.github.com> Date: Wed, 4 Dec 2024 08:35:48 +0100 Subject: [PATCH 683/734] fix: add strings for translation in timesheet.js (#44496) (cherry picked from commit 6585fabdb161513d4c4e3799d44780dba58fc905) --- erpnext/projects/doctype/timesheet/timesheet.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/projects/doctype/timesheet/timesheet.js b/erpnext/projects/doctype/timesheet/timesheet.js index 9aba75b3ce9..168b891e98c 100644 --- a/erpnext/projects/doctype/timesheet/timesheet.js +++ b/erpnext/projects/doctype/timesheet/timesheet.js @@ -58,10 +58,10 @@ frappe.ui.form.on("Timesheet", { } if (frm.doc.docstatus < 1) { - let button = "Start Timer"; + let button = __("Start Timer"); $.each(frm.doc.time_logs || [], function (i, row) { if (row.from_time <= frappe.datetime.now_datetime() && !row.completed) { - button = "Resume Timer"; + button = __("Resume Timer"); } }); From 539c5b7974ffdaf4caf8acb6d4acc00fba626668 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 4 Dec 2024 19:02:26 +0530 Subject: [PATCH 684/734] fix: duplicate required items in the CSV (backport #44498) (#44507) * fix: duplicate required items in the CSV (#44498) (cherry picked from commit b4534e56e4a1b62f6433b031671d62be9c28cf6a) # Conflicts: # erpnext/manufacturing/doctype/production_plan/production_plan.json * chore: fix conflicts --------- Co-authored-by: rohitwaghchaure --- .../production_plan/production_plan.json | 8 ++-- .../production_plan/production_plan.py | 47 +++++++++++-------- 2 files changed, 31 insertions(+), 24 deletions(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.json b/erpnext/manufacturing/doctype/production_plan/production_plan.json index 84bbad58c38..22971d4debd 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.json +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.json @@ -243,7 +243,7 @@ "depends_on": "eval:!doc.__islocal", "fieldname": "download_materials_required", "fieldtype": "Button", - "label": "Download Materials Request Plan" + "label": "Download Required Materials" }, { "fieldname": "get_items_for_mr", @@ -398,7 +398,7 @@ "collapsible": 1, "fieldname": "download_materials_request_plan_section_section", "fieldtype": "Section Break", - "label": "Download Materials Request Plan Section" + "label": "Preview Required Materials" }, { "default": "0", @@ -439,7 +439,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2024-02-27 13:34:20.692211", + "modified": "2024-12-04 11:55:03.108971", "modified_by": "Administrator", "module": "Manufacturing", "name": "Production Plan", @@ -463,4 +463,4 @@ "sort_field": "modified", "sort_order": "ASC", "states": [] -} \ No newline at end of file +} diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index 3f82a75d302..265f99e47d3 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -44,9 +44,7 @@ class ProductionPlan(Document): from erpnext.manufacturing.doctype.material_request_plan_item.material_request_plan_item import ( MaterialRequestPlanItem, ) - from erpnext.manufacturing.doctype.production_plan_item.production_plan_item import ( - ProductionPlanItem, - ) + from erpnext.manufacturing.doctype.production_plan_item.production_plan_item import ProductionPlanItem from erpnext.manufacturing.doctype.production_plan_item_reference.production_plan_item_reference import ( ProductionPlanItemReference, ) @@ -1085,24 +1083,33 @@ def download_raw_materials(doc, warehouses=None): frappe.flags.show_qty_in_stock_uom = 1 items = get_items_for_material_requests(doc, warehouses=warehouses, get_parent_warehouse_data=True) + duplicate_item_wh_list = frappe._dict() + for d in items: - item_list.append( - [ - d.get("item_code"), - d.get("item_name"), - d.get("description"), - d.get("stock_uom"), - d.get("warehouse"), - d.get("required_bom_qty"), - d.get("projected_qty"), - d.get("actual_qty"), - d.get("ordered_qty"), - d.get("planned_qty"), - d.get("reserved_qty_for_production"), - d.get("safety_stock"), - d.get("quantity"), - ] - ) + key = (d.get("item_code"), d.get("warehouse")) + if key in duplicate_item_wh_list: + rm_data = duplicate_item_wh_list[key] + rm_data[12] += d.get("quantity") + continue + + rm_data = [ + d.get("item_code"), + d.get("item_name"), + d.get("description"), + d.get("stock_uom"), + d.get("warehouse"), + d.get("required_bom_qty"), + d.get("projected_qty"), + d.get("actual_qty"), + d.get("ordered_qty"), + d.get("planned_qty"), + d.get("reserved_qty_for_production"), + d.get("safety_stock"), + d.get("quantity"), + ] + + duplicate_item_wh_list[key] = rm_data + item_list.append(rm_data) if not doc.get("for_warehouse"): row = {"item_code": d.get("item_code")} From 07df87ab6972ed3bb23b8ce8029a787a367655cf Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 4 Dec 2024 19:02:45 +0530 Subject: [PATCH 685/734] fix: required by date in the reorder material request (backport #44497) (#44509) fix: required by date in the reorder material request (#44497) (cherry picked from commit 4001166ecc689ee877adf1d9babd4d0332ab4752) Co-authored-by: rohitwaghchaure --- .../doctype/stock_entry/test_stock_entry.py | 40 +++++++++++++++++++ erpnext/stock/reorder_item.py | 2 + 2 files changed, 42 insertions(+) diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py index 469b865dd59..a26940462bf 100644 --- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py @@ -1645,6 +1645,46 @@ class TestStockEntry(FrappeTestCase): mr.cancel() mr.delete() + def test_auto_reorder_level_with_lead_time_days(self): + from erpnext.stock.reorder_item import reorder_item + + item_doc = make_item( + "Test Auto Reorder Item - 002", + properties={"stock_uom": "Kg", "purchase_uom": "Nos", "is_stock_item": 1, "lead_time_days": 2}, + uoms=[{"uom": "Nos", "conversion_factor": 5}], + ) + + if not frappe.db.exists("Item Reorder", {"parent": item_doc.name}): + item_doc.append( + "reorder_levels", + { + "warehouse_reorder_level": 0, + "warehouse_reorder_qty": 10, + "warehouse": "_Test Warehouse - _TC", + "material_request_type": "Purchase", + }, + ) + + item_doc.save(ignore_permissions=True) + + frappe.db.set_single_value("Stock Settings", "auto_indent", 1) + + mr_list = reorder_item() + + frappe.db.set_single_value("Stock Settings", "auto_indent", 0) + mrs = frappe.get_all( + "Material Request Item", + fields=["schedule_date"], + filters={"item_code": item_doc.name, "uom": "Nos"}, + ) + + for mri in mrs: + self.assertEqual(getdate(mri.schedule_date), getdate(add_days(today(), 2))) + + for mr in mr_list: + mr.cancel() + mr.delete() + def test_use_serial_and_batch_fields(self): item = make_item( "Test Use Serial and Batch Item SN Item", diff --git a/erpnext/stock/reorder_item.py b/erpnext/stock/reorder_item.py index ed87906731e..570dc3a3405 100644 --- a/erpnext/stock/reorder_item.py +++ b/erpnext/stock/reorder_item.py @@ -98,6 +98,7 @@ def _reorder_item(): "description": d.description, "stock_uom": d.stock_uom, "purchase_uom": d.purchase_uom, + "lead_time_days": d.lead_time_days, } ), ) @@ -129,6 +130,7 @@ def get_items_for_reorder() -> dict[str, list]: item_table.brand, item_table.variant_of, item_table.has_variants, + item_table.lead_time_days, ) .where( (item_table.disabled == 0) From 8733eda576fe6d27ea64dfb43ee52c2f1215b234 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 5 Dec 2024 09:45:17 +0530 Subject: [PATCH 686/734] fix: inv dimensions fields not creating for standard doctype (backport #44504) (#44515) fix: inv dimensions fields not creating for standard doctype (#44504) (cherry picked from commit 353610ce619d8406c8bd613807cf69c76274dfe0) Co-authored-by: rohitwaghchaure --- .../inventory_dimension/inventory_dimension.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py b/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py index 4f8a166932d..661605bdf5f 100644 --- a/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py +++ b/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py @@ -214,13 +214,10 @@ class InventoryDimension(Document): dimension_fields = [] if self.apply_to_all_doctypes: for doctype in get_inventory_documents(): - if field_exists(doctype[0], self.source_fieldname): - continue - dimension_fields = self.get_dimension_fields(doctype[0]) self.add_transfer_field(doctype[0], dimension_fields) custom_fields.setdefault(doctype[0], dimension_fields) - elif not field_exists(self.document_type, self.source_fieldname): + else: dimension_fields = self.get_dimension_fields() self.add_transfer_field(self.document_type, dimension_fields) @@ -239,8 +236,17 @@ class InventoryDimension(Document): dimension_field["fieldname"] = self.target_fieldname custom_fields["Stock Ledger Entry"] = dimension_field + filter_custom_fields = {} if custom_fields: - create_custom_fields(custom_fields) + for doctype, fields in custom_fields.items(): + if isinstance(fields, dict): + fields = [fields] + + for field in fields: + if not field_exists(doctype, field["fieldname"]): + filter_custom_fields.setdefault(doctype, []).append(field) + + create_custom_fields(filter_custom_fields) def add_transfer_field(self, doctype, dimension_fields): if doctype not in [ From 283043eba7d305bd88d64fe5780a1b65ab3725c2 Mon Sep 17 00:00:00 2001 From: mahsem <137205921+mahsem@users.noreply.github.com> Date: Wed, 4 Dec 2024 08:58:16 +0100 Subject: [PATCH 687/734] fix: add labels for translation in sales_order.js (cherry picked from commit d544328ffe629ed1dfb22a593db4044de8e30131) --- erpnext/selling/doctype/sales_order/sales_order.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index 45f6b364761..de4053458e4 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -146,7 +146,7 @@ frappe.ui.form.on("Sales Order", { target: frm, setters: [ { - label: "Supplier", + label: __("Supplier"), fieldname: "supplier", fieldtype: "Link", options: "Supplier", @@ -783,7 +783,7 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex target: me.frm, setters: [ { - label: "Customer", + label: __("Customer"), fieldname: "party_name", fieldtype: "Link", options: "Customer", @@ -838,7 +838,7 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex } else { const fields = [ { - label: "Items", + label: __("Items"), fieldtype: "Table", fieldname: "items", description: __("Select BOM and Qty for Production"), @@ -1193,7 +1193,7 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex { fieldname: "items_for_po", fieldtype: "Table", - label: "Select Items", + label: __("Select Items"), fields: [ { fieldtype: "Data", From af97d3e341c903c4e1505e00edf36d465a4157a6 Mon Sep 17 00:00:00 2001 From: mahsem <137205921+mahsem@users.noreply.github.com> Date: Thu, 5 Dec 2024 05:29:43 +0100 Subject: [PATCH 688/734] refactor: translatable label on pos payments (#42081) * Use better description in pos_payment.js Use Change Amount instead of Change and To Be Paid in pos_payment.js and be consistent with other strings * change_amount_pos_payment.js (cherry picked from commit 138ffc4e93f8d63f6784d4c48ccb16e825144d8f) --- erpnext/selling/page/point_of_sale/pos_payment.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/selling/page/point_of_sale/pos_payment.js b/erpnext/selling/page/point_of_sale/pos_payment.js index 9f389dfa81f..bea1918fa20 100644 --- a/erpnext/selling/page/point_of_sale/pos_payment.js +++ b/erpnext/selling/page/point_of_sale/pos_payment.js @@ -589,7 +589,7 @@ erpnext.PointOfSale.Payment = class { const remaining = grand_total - doc.paid_amount; const change = doc.change_amount || remaining <= 0 ? -1 * remaining : undefined; const currency = doc.currency; - const label = change ? __("Change") : __("To Be Paid"); + const label = __("Change Amount"); this.$totals.html( `
From ceec5fdb6c094928cb566973547ebe571d655f51 Mon Sep 17 00:00:00 2001 From: mahsem <137205921+mahsem@users.noreply.github.com> Date: Wed, 4 Dec 2024 09:07:09 +0100 Subject: [PATCH 689/734] fix: strings for translation in pos_past_order_summary.js (cherry picked from commit 23c846d4b92dbc121255c8dc98d11de7536e3e4e) --- .../selling/page/point_of_sale/pos_past_order_summary.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/selling/page/point_of_sale/pos_past_order_summary.js b/erpnext/selling/page/point_of_sale/pos_past_order_summary.js index c399005643c..4a2d8911d1a 100644 --- a/erpnext/selling/page/point_of_sale/pos_past_order_summary.js +++ b/erpnext/selling/page/point_of_sale/pos_past_order_summary.js @@ -46,7 +46,7 @@ erpnext.PointOfSale.PastOrderSummary = class { init_email_print_dialog() { const email_dialog = new frappe.ui.Dialog({ - title: "Email Receipt", + title: __("Email Receipt"), fields: [ { fieldname: "email_id", fieldtype: "Data", options: "Email", label: "Email ID", reqd: 1 }, { fieldname: "content", fieldtype: "Small Text", label: "Message (if any)" }, @@ -59,7 +59,7 @@ erpnext.PointOfSale.PastOrderSummary = class { this.email_dialog = email_dialog; const print_dialog = new frappe.ui.Dialog({ - title: "Print Receipt", + title: __("Print Receipt"), fields: [{ fieldname: "print", fieldtype: "Data", label: "Print Preview" }], primary_action: () => { this.print_receipt(); @@ -112,7 +112,7 @@ erpnext.PointOfSale.PastOrderSummary = class { get_discount_html(doc) { if (doc.discount_amount) { return `
-
Discount (${doc.additional_discount_percentage} %)
+
${__("Discount")} (${doc.additional_discount_percentage} %)
${format_currency(doc.discount_amount, doc.currency)}
`; } else { From ffdd7cfa8678ecb4880eb8c68d260673e3cbbc5d Mon Sep 17 00:00:00 2001 From: mahsem <137205921+mahsem@users.noreply.github.com> Date: Wed, 4 Dec 2024 15:40:55 +0100 Subject: [PATCH 690/734] fix: add label strings for translation in pos_controller.js (cherry picked from commit bd77a5557d9b3bac54122cf43d56050bc1ecba5e) --- erpnext/selling/page/point_of_sale/pos_controller.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/selling/page/point_of_sale/pos_controller.js b/erpnext/selling/page/point_of_sale/pos_controller.js index de3259ae2b7..c1127c39cc0 100644 --- a/erpnext/selling/page/point_of_sale/pos_controller.js +++ b/erpnext/selling/page/point_of_sale/pos_controller.js @@ -30,7 +30,7 @@ erpnext.PointOfSale.Controller = class { fieldname: "mode_of_payment", fieldtype: "Link", in_list_view: 1, - label: "Mode of Payment", + label: __("Mode of Payment"), options: "Mode of Payment", reqd: 1, }, @@ -38,7 +38,7 @@ erpnext.PointOfSale.Controller = class { fieldname: "opening_amount", fieldtype: "Currency", in_list_view: 1, - label: "Opening Amount", + label: __("Opening Amount"), options: "company:company_currency", change: function () { dialog.fields_dict.balance_details.df.data.some((d) => { @@ -87,7 +87,7 @@ erpnext.PointOfSale.Controller = class { { fieldname: "balance_details", fieldtype: "Table", - label: "Opening Balance Details", + label: __("Opening Balance Details"), cannot_add_rows: false, in_place_edit: true, reqd: 1, From 71d8dfb401a8a3f533e5e06eedb27cc9a1ae8d27 Mon Sep 17 00:00:00 2001 From: mahsem <137205921+mahsem@users.noreply.github.com> Date: Wed, 4 Dec 2024 10:12:34 +0100 Subject: [PATCH 691/734] fix: add strings for translation in pos_item_cart.js (cherry picked from commit 4b72b60f1a75657c444f210fcb3dd7a055e4d9b8) --- erpnext/selling/page/point_of_sale/pos_item_cart.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/selling/page/point_of_sale/pos_item_cart.js b/erpnext/selling/page/point_of_sale/pos_item_cart.js index b808b4f8828..4ed678e2db0 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_cart.js +++ b/erpnext/selling/page/point_of_sale/pos_item_cart.js @@ -966,13 +966,13 @@ erpnext.PointOfSale.ItemCart = class { if (!res.length) { transaction_container.html( - `
No recent transactions found
` + `
${__("No recent transactions found")}
` ); return; } const elapsed_time = moment(res[0].posting_date + " " + res[0].posting_time).fromNow(); - this.$customer_section.find(".customer-desc").html(`Last transacted ${elapsed_time}`); + this.$customer_section.find(".customer-desc").html(`${__("Last transacted")} ${__(elapsed_time)}`); res.forEach((invoice) => { const posting_datetime = moment(invoice.posting_date + " " + invoice.posting_time).format( @@ -997,7 +997,7 @@ erpnext.PointOfSale.ItemCart = class {
- ${invoice.status} + ${__(invoice.status)}
From d540c99934c5594f16a4e811ea88b0d3a6eca259 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 5 Dec 2024 10:10:34 +0530 Subject: [PATCH 692/734] chore: linter fix (cherry picked from commit 31efaf6dbf8f0139a4dd291ae8abb3e27bdc91ac) --- erpnext/selling/page/point_of_sale/pos_item_cart.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/selling/page/point_of_sale/pos_item_cart.js b/erpnext/selling/page/point_of_sale/pos_item_cart.js index 4ed678e2db0..6342b237f6e 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_cart.js +++ b/erpnext/selling/page/point_of_sale/pos_item_cart.js @@ -972,7 +972,9 @@ erpnext.PointOfSale.ItemCart = class { } const elapsed_time = moment(res[0].posting_date + " " + res[0].posting_time).fromNow(); - this.$customer_section.find(".customer-desc").html(`${__("Last transacted")} ${__(elapsed_time)}`); + this.$customer_section + .find(".customer-desc") + .html(`${__("Last transacted")} ${__(elapsed_time)}`); res.forEach((invoice) => { const posting_datetime = moment(invoice.posting_date + " " + invoice.posting_time).format( From f4aa4c94668a95a04b5a36dde582474df0f0be7b Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 5 Dec 2024 12:51:25 +0530 Subject: [PATCH 693/734] fix: incorrect stock UOM for BOM raw materials (backport #44528) (#44531) fix: incorrect stock UOM for BOM raw materials (#44528) fix: incorrect stock uom for BOM raw materials (cherry picked from commit 5413cf9f1fae48c4af47f5790ef7b5e8d81ebaf7) Co-authored-by: rohitwaghchaure --- erpnext/manufacturing/doctype/bom/bom.py | 18 +++++++++++++++++ erpnext/manufacturing/doctype/bom/test_bom.py | 20 +++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index c3105b8e9ec..0c087470901 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -260,6 +260,24 @@ class BOM(WebsiteGenerator): self.update_cost(update_parent=False, from_child_bom=True, update_hour_rate=False, save=False) self.set_process_loss_qty() self.validate_scrap_items() + self.set_default_uom() + + def set_default_uom(self): + if not self.get("items"): + return + + item_wise_uom = frappe._dict( + frappe.get_all( + "Item", + filters={"name": ("in", [item.item_code for item in self.items])}, + fields=["name", "stock_uom"], + as_list=1, + ) + ) + + for row in self.get("items"): + if row.stock_uom != item_wise_uom.get(row.item_code): + row.stock_uom = item_wise_uom.get(row.item_code) def get_context(self, context): context.parents = [{"name": "boms", "title": _("All BOMs")}] diff --git a/erpnext/manufacturing/doctype/bom/test_bom.py b/erpnext/manufacturing/doctype/bom/test_bom.py index d02b51ca6e7..396a0b107d5 100644 --- a/erpnext/manufacturing/doctype/bom/test_bom.py +++ b/erpnext/manufacturing/doctype/bom/test_bom.py @@ -755,6 +755,26 @@ class TestBOM(FrappeTestCase): self.assertTrue("_Test RM Item 2 Fixed Asset Item" not in items) self.assertTrue("_Test RM Item 3 Manufacture Item" in items) + def test_bom_raw_materials_stock_uom(self): + rm_item = make_item( + properties={"is_stock_item": 1, "valuation_rate": 1000.0, "stock_uom": "Nos"} + ).name + fg_item = make_item(properties={"is_stock_item": 1}).name + + from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom + + bom = make_bom(item=fg_item, raw_materials=[rm_item], do_not_submit=True) + for row in bom.items: + self.assertEqual(row.stock_uom, "Nos") + + frappe.db.set_value("Item", rm_item, "stock_uom", "Kg") + + bom.items[0].qty = 2 + bom.save() + + for row in bom.items: + self.assertEqual(row.stock_uom, "Kg") + def get_default_bom(item_code="_Test FG Item 2"): return frappe.db.get_value("BOM", {"item": item_code, "is_active": 1, "is_default": 1}) From 3771c6eeae15644e459181128f41ebd04a8d0c19 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 5 Dec 2024 13:46:23 +0530 Subject: [PATCH 694/734] fix: consider zero valuation rate for serial nos (backport #44532) (#44534) fix: consider zero valuation rate for serial nos (#44532) (cherry picked from commit 14f2b0ab0e5530395e269213a8594733dca4974a) Co-authored-by: rohitwaghchaure --- erpnext/stock/serial_batch_bundle.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/serial_batch_bundle.py b/erpnext/stock/serial_batch_bundle.py index dd459bb30bc..05b3536c57b 100644 --- a/erpnext/stock/serial_batch_bundle.py +++ b/erpnext/stock/serial_batch_bundle.py @@ -508,7 +508,7 @@ class SerialNoValuation(DeprecatedSerialNoValuation): serial_nos = self.get_serial_nos() for serial_no in serial_nos: incoming_rate = self.get_incoming_rate_from_bundle(serial_no) - if not incoming_rate: + if incoming_rate is None: continue self.stock_value_change += incoming_rate @@ -553,7 +553,7 @@ class SerialNoValuation(DeprecatedSerialNoValuation): query = query.where(timestamp_condition) incoming_rate = query.run() - return flt(incoming_rate[0][0]) if incoming_rate else 0.0 + return flt(incoming_rate[0][0]) if incoming_rate else None def get_serial_nos(self): if self.sle.get("serial_nos"): From 843ff18cf9e45cc88042eb328b30907e888628c8 Mon Sep 17 00:00:00 2001 From: mahsem <137205921+mahsem@users.noreply.github.com> Date: Thu, 5 Dec 2024 08:37:43 +0100 Subject: [PATCH 695/734] fix: add title for translation in asset.js (cherry picked from commit 61439132a4386c1867a08d141e0702e03484cf02) --- erpnext/assets/doctype/asset/asset.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js index c6e76abb0b5..21e307b480c 100644 --- a/erpnext/assets/doctype/asset/asset.js +++ b/erpnext/assets/doctype/asset/asset.js @@ -416,7 +416,7 @@ frappe.ui.form.on("Asset", { } frm.dashboard.render_graph({ - title: "Asset Value", + title: __("Asset Value"), data: { labels: x_intervals, datasets: [ From 99a327155e4a13e5ea8e0ad188a1acd38f90efed Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 5 Dec 2024 15:51:44 +0530 Subject: [PATCH 696/734] fix: update qty in SABB if qty changed in stock reco (backport #44542) (#44546) fix: update qty in SABB if qty changed in stock reco (#44542) (cherry picked from commit 7249cf0001bebe7a33933f9d379065f9cb8d7d61) Co-authored-by: rohitwaghchaure --- .../stock_reconciliation.py | 47 ++++++++++++++++--- 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index f671c11712a..c13b3620517 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -166,6 +166,24 @@ class StockReconciliation(StockController): if not frappe.db.exists("Item", item.item_code): frappe.throw(_("Item {0} does not exist").format(item.item_code)) + item_details = frappe.get_cached_value( + "Item", item.item_code, ["has_serial_no", "has_batch_no"], as_dict=1 + ) + + if not (item_details.has_serial_no or item_details.has_batch_no): + continue + + if ( + not item.use_serial_batch_fields + and not item.reconcile_all_serial_batch + and not item.serial_and_batch_bundle + ): + frappe.throw( + _("Row # {0}: Please add Serial and Batch Bundle for Item {1}").format( + item.idx, frappe.bold(item.item_code) + ) + ) + if not item.reconcile_all_serial_batch and item.serial_and_batch_bundle: bundle = self.get_bundle_for_specific_serial_batch(item) item.current_serial_and_batch_bundle = bundle.name @@ -181,13 +199,6 @@ class StockReconciliation(StockController): if voucher_detail_no and voucher_detail_no != item.name: continue - item_details = frappe.get_cached_value( - "Item", item.item_code, ["has_serial_no", "has_batch_no"], as_dict=1 - ) - - if not (item_details.has_serial_no or item_details.has_batch_no): - continue - if not item.current_serial_and_batch_bundle: serial_and_batch_bundle = frappe.get_doc( { @@ -400,6 +411,28 @@ class StockReconciliation(StockController): item.qty = bundle_doc.total_qty item.valuation_rate = bundle_doc.avg_rate + elif item.serial_and_batch_bundle and item.qty: + self.update_existing_serial_and_batch_bundle(item) + + def update_existing_serial_and_batch_bundle(self, item): + batch_details = frappe.get_all( + "Serial and Batch Entry", + fields=["batch_no", "qty", "name"], + filters={"parent": item.serial_and_batch_bundle, "batch_no": ("is", "set")}, + ) + + if batch_details and len(batch_details) == 1: + batch = batch_details[0] + if abs(batch.qty) == abs(item.qty): + return + + update_values = { + "qty": item.qty, + "stock_value_difference": flt(item.valuation_rate) * flt(item.qty), + } + + frappe.db.set_value("Serial and Batch Entry", batch.name, update_values) + def remove_items_with_no_change(self): """Remove items if qty or rate is not changed""" self.difference_amount = 0.0 From 5ef063c63471092f568ee5fdead5ae0f2f52a74d Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 5 Dec 2024 15:52:04 +0530 Subject: [PATCH 697/734] fix: not able to make PO for returned qty from material request (backport #44540) (#44547) fix: not able to make PO for returned qty from material request (#44540) (cherry picked from commit 024c44208786dc3da59f6cdabba125ceb6f7c8d6) Co-authored-by: rohitwaghchaure --- .../doctype/material_request/material_request.js | 16 ++++++++-------- .../doctype/material_request/material_request.py | 8 ++++++-- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js index f04acc85ed5..c7485470cf0 100644 --- a/erpnext/stock/doctype/material_request/material_request.js +++ b/erpnext/stock/doctype/material_request/material_request.js @@ -107,6 +107,14 @@ frappe.ui.form.on("Material Request", { if (flt(frm.doc.per_received, precision) < 100) { frm.add_custom_button(__("Stop"), () => frm.events.update_status(frm, "Stopped")); + + if (frm.doc.material_request_type === "Purchase") { + frm.add_custom_button( + __("Purchase Order"), + () => frm.events.make_purchase_order(frm), + __("Create") + ); + } } if (flt(frm.doc.per_ordered, precision) < 100) { @@ -149,14 +157,6 @@ frappe.ui.form.on("Material Request", { ); } - if (frm.doc.material_request_type === "Purchase") { - frm.add_custom_button( - __("Purchase Order"), - () => frm.events.make_purchase_order(frm), - __("Create") - ); - } - if (frm.doc.material_request_type === "Purchase") { frm.add_custom_button( __("Request for Quotation"), diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py index 7bf3ca4d728..23d289170db 100644 --- a/erpnext/stock/doctype/material_request/material_request.py +++ b/erpnext/stock/doctype/material_request/material_request.py @@ -378,7 +378,9 @@ def set_missing_values(source, target_doc): def update_item(obj, target, source_parent): target.conversion_factor = obj.conversion_factor - target.qty = flt(flt(obj.stock_qty) - flt(obj.ordered_qty)) / target.conversion_factor + + qty = obj.received_qty or obj.ordered_qty + target.qty = flt(flt(obj.stock_qty) - flt(qty)) / target.conversion_factor target.stock_qty = target.qty * target.conversion_factor if getdate(target.schedule_date) < getdate(nowdate()): target.schedule_date = None @@ -430,7 +432,9 @@ def make_purchase_order(source_name, target_doc=None, args=None): filtered_items = args.get("filtered_children", []) child_filter = d.name in filtered_items if filtered_items else True - return d.ordered_qty < d.stock_qty and child_filter + qty = d.received_qty or d.ordered_qty + + return qty < d.stock_qty and child_filter doclist = get_mapped_doc( "Material Request", From 63b1df38a81877168ea71047c9b29db144c4d8a9 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 5 Dec 2024 16:01:04 +0530 Subject: [PATCH 698/734] fix: variant qty while making work order from BOM (backport #44548) (#44551) fix: variant qty while making work order from BOM (#44548) (cherry picked from commit 1571dff3ef4c89c91e155967c0a187e8f855fe28) Co-authored-by: rohitwaghchaure --- erpnext/manufacturing/doctype/bom/bom.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js index 6267ee4d029..5cfd170ab13 100644 --- a/erpnext/manufacturing/doctype/bom/bom.js +++ b/erpnext/manufacturing/doctype/bom/bom.js @@ -364,7 +364,7 @@ frappe.ui.form.on("BOM", { dialog.fields_dict.items.df.data.push({ item_code: d.item_code, variant_item_code: "", - qty: d.qty, + qty: (d.qty / frm.doc.quantity) * (dialog.fields_dict.qty.value || 1), source_warehouse: d.source_warehouse, operation: d.operation, }); From 06ac21dd856a5b6ecfb37ccf20c3d83fa4d79c05 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 5 Dec 2024 22:57:09 +0530 Subject: [PATCH 699/734] chore: Ignore stock validation for non stock invoices (backport #44549) (#44554) chore: Ignore stock validation for non stock invoices (#44549) * chore: Ignore stock validation for non stock invoices * chore: Ignore stock validation for non stock invoices (cherry picked from commit 1ac292285e217c77d2c9f9ce37e4c018c2f5e849) Co-authored-by: Deepesh Garg --- erpnext/controllers/sales_and_purchase_return.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py index 7f9a5a35a73..696d404d16d 100644 --- a/erpnext/controllers/sales_and_purchase_return.py +++ b/erpnext/controllers/sales_and_purchase_return.py @@ -24,6 +24,10 @@ def validate_return(doc): if doc.return_against: validate_return_against(doc) + + if doc.doctype in ("Sales Invoice", "Purchase Invoice") and not doc.update_stock: + return + validate_returned_items(doc) From 6980d1e142d25a3151ea5b12625a9f6e62e39352 Mon Sep 17 00:00:00 2001 From: mahsem <137205921+mahsem@users.noreply.github.com> Date: Thu, 5 Dec 2024 16:40:00 +0100 Subject: [PATCH 700/734] fix: add labels for translation in sales_order_analysis.py (cherry picked from commit 8a554a55382cf41855a0e22af89a55c8ec79a5e7) --- .../selling/report/sales_order_analysis/sales_order_analysis.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py b/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py index 70b021a9cab..8fcf29bd7a6 100644 --- a/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py +++ b/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py @@ -206,7 +206,7 @@ def prepare_data(data, so_elapsed_time, filters): def prepare_chart_data(pending, completed): - labels = ["Amount to Bill", "Billed Amount"] + labels = [_("Amount to Bill"), _("Billed Amount")] return { "data": {"labels": labels, "datasets": [{"values": [pending, completed]}]}, From 773e03f84f9514b2d7558be14ea257b9596d4474 Mon Sep 17 00:00:00 2001 From: mahsem <137205921+mahsem@users.noreply.github.com> Date: Thu, 5 Dec 2024 16:20:31 +0100 Subject: [PATCH 701/734] fix: add strings for translation payment_terms_status_for_sales_order.py (cherry picked from commit 7d244051c8dd231abbd3bcd9717d95f7046ab3fc) --- .../payment_terms_status_for_sales_order.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.py b/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.py index cf61a0e35f3..1b57a6d7390 100644 --- a/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.py +++ b/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.py @@ -270,11 +270,11 @@ def prepare_chart(s_orders): "labels": [term.payment_term for term in s_orders], "datasets": [ { - "name": "Payment Amount", + "name": _("Payment Amount"), "values": [x.base_payment_amount for x in s_orders], }, { - "name": "Paid Amount", + "name": _("Paid Amount"), "values": [x.paid_amount for x in s_orders], }, ], From 43fc8bf74e758ceb8c0a7170fff6fdc3c7ce0b9f Mon Sep 17 00:00:00 2001 From: mahsem <137205921+mahsem@users.noreply.github.com> Date: Thu, 5 Dec 2024 16:13:07 +0100 Subject: [PATCH 702/734] fix: add string for translation in delayed_tasks_summary.py (cherry picked from commit 84b54f549abc28b5b776c2833977044aa876fd34) --- .../report/delayed_tasks_summary/delayed_tasks_summary.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.py b/erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.py index 766e40e319c..dc3da259662 100644 --- a/erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.py +++ b/erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.py @@ -77,7 +77,7 @@ def get_chart_data(data): charts = { "data": { "labels": [_("On Track"), _("Delayed")], - "datasets": [{"name": "Delayed", "values": [on_track, delay]}], + "datasets": [{"name": _("Delayed"), "values": [on_track, delay]}], }, "type": "percentage", "colors": ["#84D5BA", "#CB4B5F"], From dd3a747af461a01b4fdafb877320460a85a864fe Mon Sep 17 00:00:00 2001 From: mahsem <137205921+mahsem@users.noreply.github.com> Date: Thu, 5 Dec 2024 09:17:35 +0100 Subject: [PATCH 703/734] fix: add labels for translation in quality_inspection_summary.py (cherry picked from commit 6ff4704345fcf299f959f61f05341da76680db69) --- .../quality_inspection_summary/quality_inspection_summary.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.py b/erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.py index 38e05852ee8..7e0fcf14cc6 100644 --- a/erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.py +++ b/erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.py @@ -42,7 +42,7 @@ def get_data(filters): def get_chart_data(periodic_data, columns): - labels = ["Rejected", "Accepted"] + labels = [_("Rejected"), _("Accepted")] status_wise_data = {"Accepted": 0, "Rejected": 0} @@ -53,7 +53,7 @@ def get_chart_data(periodic_data, columns): datasets.append( { - "name": "Qty Wise Chart", + "name": _("Qty Wise Chart"), "values": [status_wise_data.get("Rejected"), status_wise_data.get("Accepted")], } ) From 7a6cb0f157adb8a0e582e2d2c6806a2acc2e8a32 Mon Sep 17 00:00:00 2001 From: mahsem <137205921+mahsem@users.noreply.github.com> Date: Thu, 5 Dec 2024 09:02:05 +0100 Subject: [PATCH 704/734] fix: add labels for translation in production_analytics.py (cherry picked from commit 9b09116576f7645b9bdfc4e8be3c1c90c7516935) --- .../production_analytics/production_analytics.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/manufacturing/report/production_analytics/production_analytics.py b/erpnext/manufacturing/report/production_analytics/production_analytics.py index c02c1e6fcd3..dc2b9ad62f3 100644 --- a/erpnext/manufacturing/report/production_analytics/production_analytics.py +++ b/erpnext/manufacturing/report/production_analytics/production_analytics.py @@ -131,11 +131,11 @@ def get_chart_data(periodic_data, columns): pending.append(periodic_data.get("Pending").get(d)) completed.append(periodic_data.get("Completed").get(d)) - datasets.append({"name": "All Work Orders", "values": all_data}) - datasets.append({"name": "Not Started", "values": not_start}) - datasets.append({"name": "Overdue", "values": overdue}) - datasets.append({"name": "Pending", "values": pending}) - datasets.append({"name": "Completed", "values": completed}) + datasets.append({"name": _("All Work Orders"), "values": all_data}) + datasets.append({"name": _("Not Started"), "values": not_start}) + datasets.append({"name": _("Overdue"), "values": overdue}) + datasets.append({"name": _("Pending"), "values": pending}) + datasets.append({"name": _("Completed"), "values": completed}) chart = {"data": {"labels": labels, "datasets": datasets}} chart["type"] = "line" From f06827c6fbdbbd74cf3c93a843a206e384cfdf5b Mon Sep 17 00:00:00 2001 From: mahsem <137205921+mahsem@users.noreply.github.com> Date: Thu, 5 Dec 2024 08:41:02 +0100 Subject: [PATCH 705/734] fix: add labels for translation in purchase_order_analysis.py (cherry picked from commit 342a398bec707688749c79b515462b5ea4bde734) --- .../report/purchase_order_analysis/purchase_order_analysis.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py index 1a250acd4d2..6d2034d1878 100644 --- a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py +++ b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py @@ -180,7 +180,7 @@ def prepare_data(data, filters): def prepare_chart_data(pending, completed): - labels = ["Amount to Bill", "Billed Amount"] + labels = [_("Amount to Bill"), _("Billed Amount")] return { "data": {"labels": labels, "datasets": [{"values": [pending, completed]}]}, From 31c2b818bdaf1e7b1dae39a045d8b2fdbd00d4e6 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 6 Dec 2024 11:57:48 +0100 Subject: [PATCH 706/734] fix(Bank Transaction): error in party matching should not block submitting (backport #44416) (#44574) fix(Bank Transaction): error in party matching should not block submitting (#44416) (cherry picked from commit 72256565bb2caf02f8e87b3ee0fbd0ee233c4f40) Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com> --- .../bank_transaction/bank_transaction.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py index 4354f238a42..c13dbe445f1 100644 --- a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py +++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py @@ -208,13 +208,17 @@ class BankTransaction(Document): if self.party_type and self.party: return - result = AutoMatchParty( - bank_party_account_number=self.bank_party_account_number, - bank_party_iban=self.bank_party_iban, - bank_party_name=self.bank_party_name, - description=self.description, - deposit=self.deposit, - ).match() + result = None + try: + result = AutoMatchParty( + bank_party_account_number=self.bank_party_account_number, + bank_party_iban=self.bank_party_iban, + bank_party_name=self.bank_party_name, + description=self.description, + deposit=self.deposit, + ).match() + except Exception: + frappe.log_error(title=_("Error in party matching for Bank Transaction {0}").format(self.name)) if not result: return From 03ae9e27be5acca32d3e15f8434a3f6062dc8c30 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 6 Dec 2024 21:50:49 +0530 Subject: [PATCH 707/734] fix: BOM name issue (backport #44575) (#44579) fix: BOM name issue (#44575) fix: bom name issue (cherry picked from commit b7a3c6b6ca902138007daff2343abe14f91157f7) Co-authored-by: rohitwaghchaure --- erpnext/manufacturing/doctype/bom/bom.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index 0c087470901..2f48a498ec3 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -176,9 +176,14 @@ class BOM(WebsiteGenerator): search_key = f"{self.doctype}-{self.item}%" existing_boms = frappe.get_all( - "BOM", filters={"name": ("like", search_key), "amended_from": ["is", "not set"]}, pluck="name" + "BOM", filters={"name": search_key, "amended_from": ["is", "not set"]}, pluck="name" ) + if not existing_boms: + existing_boms = frappe.get_all( + "BOM", filters={"name": ("like", search_key), "amended_from": ["is", "not set"]}, pluck="name" + ) + if existing_boms: index = self.get_next_version_index(existing_boms) else: From 0b268279cfbe720917bd73ff952771e1eaae05b4 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 6 Dec 2024 22:35:37 +0530 Subject: [PATCH 708/734] fix: BOM for variant items (backport #44580) (#44584) fix: BOM for variant items (#44580) (cherry picked from commit 93e9517f5d6b023cba91d93cb77340691dd24ac0) Co-authored-by: rohitwaghchaure --- erpnext/manufacturing/doctype/bom/bom.js | 56 ++++++++++++------- .../doctype/work_order/work_order.py | 18 +++++- 2 files changed, 50 insertions(+), 24 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js index 5cfd170ab13..d8024a428a3 100644 --- a/erpnext/manufacturing/doctype/bom/bom.js +++ b/erpnext/manufacturing/doctype/bom/bom.js @@ -182,25 +182,30 @@ frappe.ui.form.on("BOM", { }, make_work_order(frm) { - frm.events.setup_variant_prompt(frm, "Work Order", (frm, item, data, variant_items) => { - frappe.call({ - method: "erpnext.manufacturing.doctype.work_order.work_order.make_work_order", - args: { - bom_no: frm.doc.name, - item: item, - qty: data.qty || 0.0, - project: frm.doc.project, - variant_items: variant_items, - }, - freeze: true, - callback(r) { - if (r.message) { - let doc = frappe.model.sync(r.message)[0]; - frappe.set_route("Form", doc.doctype, doc.name); - } - }, - }); - }); + frm.events.setup_variant_prompt( + frm, + "Work Order", + (frm, item, data, variant_items, use_multi_level_bom) => { + frappe.call({ + method: "erpnext.manufacturing.doctype.work_order.work_order.make_work_order", + args: { + bom_no: frm.doc.name, + item: item, + qty: data.qty || 0.0, + project: frm.doc.project, + variant_items: variant_items, + use_multi_level_bom: use_multi_level_bom, + }, + freeze: true, + callback(r) { + if (r.message) { + let doc = frappe.model.sync(r.message)[0]; + frappe.set_route("Form", doc.doctype, doc.name); + } + }, + }); + } + ); }, make_variant_bom(frm) { @@ -248,6 +253,13 @@ frappe.ui.form.on("BOM", { }; }, }); + + fields.push({ + fieldtype: "Check", + label: __("Use Multi-Level BOM"), + fieldname: "use_multi_level_bom", + default: 1, + }); } if (!skip_qty_field) { @@ -285,6 +297,7 @@ frappe.ui.form.on("BOM", { fieldname: "items", fieldtype: "Table", label: __("Raw Materials"), + depends_on: "eval:!doc.use_multi_level_bom", fields: [ { fieldname: "item_code", @@ -347,14 +360,15 @@ frappe.ui.form.on("BOM", { (data) => { let item = data.item || frm.doc.item; let variant_items = data.items || []; + let use_multi_level_bom = data.use_multi_level_bom || 0; variant_items.forEach((d) => { - if (!d.variant_item_code) { + if (!d.variant_item_code && !use_multi_level_bom) { frappe.throw(__("Select variant item code for the template item {0}", [d.item_code])); } }); - callback(frm, item, data, variant_items); + callback(frm, item, data, variant_items, use_multi_level_bom); }, __(title), __("Create") diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index 1d0df26800d..e5219d7cb3e 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -1299,7 +1299,7 @@ def get_item_details(item, project=None, skip_bom_info=False, throw=True): @frappe.whitelist() -def make_work_order(bom_no, item, qty=0, project=None, variant_items=None): +def make_work_order(bom_no, item, qty=0, project=None, variant_items=None, use_multi_level_bom=None): if not frappe.has_permission("Work Order", "write"): frappe.throw(_("Not permitted"), frappe.PermissionError) @@ -1309,12 +1309,13 @@ def make_work_order(bom_no, item, qty=0, project=None, variant_items=None): wo_doc.production_item = item wo_doc.update(item_details) wo_doc.bom_no = bom_no + wo_doc.use_multi_level_bom = cint(use_multi_level_bom) if flt(qty) > 0: wo_doc.qty = flt(qty) wo_doc.get_items_and_operations_from_bom() - if variant_items: + if variant_items and not wo_doc.use_multi_level_bom: add_variant_item(variant_items, wo_doc, bom_no, "required_items") return wo_doc @@ -1358,7 +1359,18 @@ def add_variant_item(variant_items, wo_doc, bom_no, table_name="items"): args["amount"] = flt(args.get("required_qty")) * flt(args.get("rate")) args["uom"] = item_data.stock_uom - wo_doc.append(table_name, args) + + existing_row = get_template_rm_item(wo_doc, item.get("item_code")) + if existing_row: + existing_row.update(args) + else: + wo_doc.append(table_name, args) + + +def get_template_rm_item(wo_doc, item_code): + for row in wo_doc.required_items: + if row.item_code == item_code: + return row @frappe.whitelist() From 3c50cfef4e432f039979f8d389a240b95da39e09 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 5 Dec 2024 16:01:04 +0530 Subject: [PATCH 709/734] fix: variant qty while making work order from BOM (backport #44548) (#44551) fix: variant qty while making work order from BOM (#44548) (cherry picked from commit 1571dff3ef4c89c91e155967c0a187e8f855fe28) Co-authored-by: rohitwaghchaure (cherry picked from commit 63b1df38a81877168ea71047c9b29db144c4d8a9) --- erpnext/manufacturing/doctype/bom/bom.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js index 6267ee4d029..5cfd170ab13 100644 --- a/erpnext/manufacturing/doctype/bom/bom.js +++ b/erpnext/manufacturing/doctype/bom/bom.js @@ -364,7 +364,7 @@ frappe.ui.form.on("BOM", { dialog.fields_dict.items.df.data.push({ item_code: d.item_code, variant_item_code: "", - qty: d.qty, + qty: (d.qty / frm.doc.quantity) * (dialog.fields_dict.qty.value || 1), source_warehouse: d.source_warehouse, operation: d.operation, }); From 27b63beb18c4a67a8043d8cfd5c0635427b47cec Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 6 Dec 2024 21:50:49 +0530 Subject: [PATCH 710/734] fix: BOM name issue (backport #44575) (#44579) fix: BOM name issue (#44575) fix: bom name issue (cherry picked from commit b7a3c6b6ca902138007daff2343abe14f91157f7) Co-authored-by: rohitwaghchaure (cherry picked from commit 03ae9e27be5acca32d3e15f8434a3f6062dc8c30) --- erpnext/manufacturing/doctype/bom/bom.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index c3105b8e9ec..f8523c88933 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -176,9 +176,14 @@ class BOM(WebsiteGenerator): search_key = f"{self.doctype}-{self.item}%" existing_boms = frappe.get_all( - "BOM", filters={"name": ("like", search_key), "amended_from": ["is", "not set"]}, pluck="name" + "BOM", filters={"name": search_key, "amended_from": ["is", "not set"]}, pluck="name" ) + if not existing_boms: + existing_boms = frappe.get_all( + "BOM", filters={"name": ("like", search_key), "amended_from": ["is", "not set"]}, pluck="name" + ) + if existing_boms: index = self.get_next_version_index(existing_boms) else: From 2a2d8da6286f0db21f2c9498f5eda730a86edb5a Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 6 Dec 2024 22:35:37 +0530 Subject: [PATCH 711/734] fix: BOM for variant items (backport #44580) (#44584) fix: BOM for variant items (#44580) (cherry picked from commit 93e9517f5d6b023cba91d93cb77340691dd24ac0) Co-authored-by: rohitwaghchaure (cherry picked from commit 0b268279cfbe720917bd73ff952771e1eaae05b4) --- erpnext/manufacturing/doctype/bom/bom.js | 56 ++++++++++++------- .../doctype/work_order/work_order.py | 18 +++++- 2 files changed, 50 insertions(+), 24 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js index 6267ee4d029..63c114970ea 100644 --- a/erpnext/manufacturing/doctype/bom/bom.js +++ b/erpnext/manufacturing/doctype/bom/bom.js @@ -182,25 +182,30 @@ frappe.ui.form.on("BOM", { }, make_work_order(frm) { - frm.events.setup_variant_prompt(frm, "Work Order", (frm, item, data, variant_items) => { - frappe.call({ - method: "erpnext.manufacturing.doctype.work_order.work_order.make_work_order", - args: { - bom_no: frm.doc.name, - item: item, - qty: data.qty || 0.0, - project: frm.doc.project, - variant_items: variant_items, - }, - freeze: true, - callback(r) { - if (r.message) { - let doc = frappe.model.sync(r.message)[0]; - frappe.set_route("Form", doc.doctype, doc.name); - } - }, - }); - }); + frm.events.setup_variant_prompt( + frm, + "Work Order", + (frm, item, data, variant_items, use_multi_level_bom) => { + frappe.call({ + method: "erpnext.manufacturing.doctype.work_order.work_order.make_work_order", + args: { + bom_no: frm.doc.name, + item: item, + qty: data.qty || 0.0, + project: frm.doc.project, + variant_items: variant_items, + use_multi_level_bom: use_multi_level_bom, + }, + freeze: true, + callback(r) { + if (r.message) { + let doc = frappe.model.sync(r.message)[0]; + frappe.set_route("Form", doc.doctype, doc.name); + } + }, + }); + } + ); }, make_variant_bom(frm) { @@ -248,6 +253,13 @@ frappe.ui.form.on("BOM", { }; }, }); + + fields.push({ + fieldtype: "Check", + label: __("Use Multi-Level BOM"), + fieldname: "use_multi_level_bom", + default: 1, + }); } if (!skip_qty_field) { @@ -285,6 +297,7 @@ frappe.ui.form.on("BOM", { fieldname: "items", fieldtype: "Table", label: __("Raw Materials"), + depends_on: "eval:!doc.use_multi_level_bom", fields: [ { fieldname: "item_code", @@ -347,14 +360,15 @@ frappe.ui.form.on("BOM", { (data) => { let item = data.item || frm.doc.item; let variant_items = data.items || []; + let use_multi_level_bom = data.use_multi_level_bom || 0; variant_items.forEach((d) => { - if (!d.variant_item_code) { + if (!d.variant_item_code && !use_multi_level_bom) { frappe.throw(__("Select variant item code for the template item {0}", [d.item_code])); } }); - callback(frm, item, data, variant_items); + callback(frm, item, data, variant_items, use_multi_level_bom); }, __(title), __("Create") diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index 1d0df26800d..e5219d7cb3e 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -1299,7 +1299,7 @@ def get_item_details(item, project=None, skip_bom_info=False, throw=True): @frappe.whitelist() -def make_work_order(bom_no, item, qty=0, project=None, variant_items=None): +def make_work_order(bom_no, item, qty=0, project=None, variant_items=None, use_multi_level_bom=None): if not frappe.has_permission("Work Order", "write"): frappe.throw(_("Not permitted"), frappe.PermissionError) @@ -1309,12 +1309,13 @@ def make_work_order(bom_no, item, qty=0, project=None, variant_items=None): wo_doc.production_item = item wo_doc.update(item_details) wo_doc.bom_no = bom_no + wo_doc.use_multi_level_bom = cint(use_multi_level_bom) if flt(qty) > 0: wo_doc.qty = flt(qty) wo_doc.get_items_and_operations_from_bom() - if variant_items: + if variant_items and not wo_doc.use_multi_level_bom: add_variant_item(variant_items, wo_doc, bom_no, "required_items") return wo_doc @@ -1358,7 +1359,18 @@ def add_variant_item(variant_items, wo_doc, bom_no, table_name="items"): args["amount"] = flt(args.get("required_qty")) * flt(args.get("rate")) args["uom"] = item_data.stock_uom - wo_doc.append(table_name, args) + + existing_row = get_template_rm_item(wo_doc, item.get("item_code")) + if existing_row: + existing_row.update(args) + else: + wo_doc.append(table_name, args) + + +def get_template_rm_item(wo_doc, item_code): + for row in wo_doc.required_items: + if row.item_code == item_code: + return row @frappe.whitelist() From a5cc30741703857556efe0f201a7bb91231eee0b Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sat, 7 Dec 2024 12:58:34 +0530 Subject: [PATCH 712/734] fix: BOM name issue (backport #44586) (#44589) fix: BOM name issue (#44586) (cherry picked from commit d871e21a40943b3e9d52e2dd7233a128fa0f0ff6) Co-authored-by: rohitwaghchaure --- erpnext/manufacturing/doctype/bom/bom.js | 14 +++++----- erpnext/manufacturing/doctype/bom/bom.py | 34 ++++++++++-------------- 2 files changed, 21 insertions(+), 27 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js index d8024a428a3..36ada87111a 100644 --- a/erpnext/manufacturing/doctype/bom/bom.js +++ b/erpnext/manufacturing/doctype/bom/bom.js @@ -253,15 +253,15 @@ frappe.ui.form.on("BOM", { }; }, }); - - fields.push({ - fieldtype: "Check", - label: __("Use Multi-Level BOM"), - fieldname: "use_multi_level_bom", - default: 1, - }); } + fields.push({ + fieldtype: "Check", + label: __("Use Multi-Level BOM"), + fieldname: "use_multi_level_bom", + default: 1, + }); + if (!skip_qty_field) { fields.push({ fieldtype: "Float", diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index 2f48a498ec3..730ff494801 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -179,15 +179,7 @@ class BOM(WebsiteGenerator): "BOM", filters={"name": search_key, "amended_from": ["is", "not set"]}, pluck="name" ) - if not existing_boms: - existing_boms = frappe.get_all( - "BOM", filters={"name": ("like", search_key), "amended_from": ["is", "not set"]}, pluck="name" - ) - - if existing_boms: - index = self.get_next_version_index(existing_boms) - else: - index = 1 + index = self.get_index_for_bom(existing_boms) prefix = self.doctype suffix = "%.3i" % index # convert index to string (1 -> "001") @@ -205,21 +197,23 @@ class BOM(WebsiteGenerator): name = f"{prefix}-{truncated_item_name}-{suffix}" if frappe.db.exists("BOM", name): - conflicting_bom = frappe.get_doc("BOM", name) + existing_boms = frappe.get_all( + "BOM", filters={"name": ("like", search_key), "amended_from": ["is", "not set"]}, pluck="name" + ) - if conflicting_bom.item != self.item: - msg = _("A BOM with name {0} already exists for item {1}.").format( - frappe.bold(name), frappe.bold(conflicting_bom.item) - ) - - frappe.throw( - _("{0}{1} Did you rename the item? Please contact Administrator / Tech support").format( - msg, "
" - ) - ) + index = self.get_index_for_bom(existing_boms) + suffix = "%.3i" % index + name = f"{prefix}-{self.item}-{suffix}" self.name = name + def get_index_for_bom(self, existing_boms): + index = 1 + if existing_boms: + index = self.get_next_version_index(existing_boms) + + return index + @staticmethod def get_next_version_index(existing_boms: list[str]) -> int: # split by "/" and "-" From 408a09233bfae90082a06ab21cf38ceaf7a9c7cb Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sat, 7 Dec 2024 12:58:34 +0530 Subject: [PATCH 713/734] fix: BOM name issue (backport #44586) (#44589) fix: BOM name issue (#44586) (cherry picked from commit d871e21a40943b3e9d52e2dd7233a128fa0f0ff6) Co-authored-by: rohitwaghchaure (cherry picked from commit a5cc30741703857556efe0f201a7bb91231eee0b) # Conflicts: # erpnext/manufacturing/doctype/bom/bom.py --- erpnext/manufacturing/doctype/bom/bom.js | 7 ++++++ erpnext/manufacturing/doctype/bom/bom.py | 28 ++++++++++++++---------- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js index 6267ee4d029..92e4715590b 100644 --- a/erpnext/manufacturing/doctype/bom/bom.js +++ b/erpnext/manufacturing/doctype/bom/bom.js @@ -250,6 +250,13 @@ frappe.ui.form.on("BOM", { }); } + fields.push({ + fieldtype: "Check", + label: __("Use Multi-Level BOM"), + fieldname: "use_multi_level_bom", + default: 1, + }); + if (!skip_qty_field) { fields.push({ fieldtype: "Float", diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index c3105b8e9ec..41ea6f80f10 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -179,10 +179,14 @@ class BOM(WebsiteGenerator): "BOM", filters={"name": ("like", search_key), "amended_from": ["is", "not set"]}, pluck="name" ) +<<<<<<< HEAD if existing_boms: index = self.get_next_version_index(existing_boms) else: index = 1 +======= + index = self.get_index_for_bom(existing_boms) +>>>>>>> a5cc307417 (fix: BOM name issue (backport #44586) (#44589)) prefix = self.doctype suffix = "%.3i" % index # convert index to string (1 -> "001") @@ -200,21 +204,23 @@ class BOM(WebsiteGenerator): name = f"{prefix}-{truncated_item_name}-{suffix}" if frappe.db.exists("BOM", name): - conflicting_bom = frappe.get_doc("BOM", name) + existing_boms = frappe.get_all( + "BOM", filters={"name": ("like", search_key), "amended_from": ["is", "not set"]}, pluck="name" + ) - if conflicting_bom.item != self.item: - msg = _("A BOM with name {0} already exists for item {1}.").format( - frappe.bold(name), frappe.bold(conflicting_bom.item) - ) - - frappe.throw( - _("{0}{1} Did you rename the item? Please contact Administrator / Tech support").format( - msg, "
" - ) - ) + index = self.get_index_for_bom(existing_boms) + suffix = "%.3i" % index + name = f"{prefix}-{self.item}-{suffix}" self.name = name + def get_index_for_bom(self, existing_boms): + index = 1 + if existing_boms: + index = self.get_next_version_index(existing_boms) + + return index + @staticmethod def get_next_version_index(existing_boms: list[str]) -> int: # split by "/" and "-" From 3b57767d367ac4b54042b0ff73c038395f223520 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Sat, 7 Dec 2024 07:35:56 +0000 Subject: [PATCH 714/734] chore(release): Bumped to Version 15.45.1 ## [15.45.1](https://github.com/frappe/erpnext/compare/v15.45.0...v15.45.1) (2024-12-07) ### Bug Fixes * BOM for variant items (backport [#44580](https://github.com/frappe/erpnext/issues/44580)) ([#44584](https://github.com/frappe/erpnext/issues/44584)) ([2a2d8da](https://github.com/frappe/erpnext/commit/2a2d8da6286f0db21f2c9498f5eda730a86edb5a)) * BOM name issue (backport [#44575](https://github.com/frappe/erpnext/issues/44575)) ([#44579](https://github.com/frappe/erpnext/issues/44579)) ([27b63be](https://github.com/frappe/erpnext/commit/27b63beb18c4a67a8043d8cfd5c0635427b47cec)) * variant qty while making work order from BOM (backport [#44548](https://github.com/frappe/erpnext/issues/44548)) ([#44551](https://github.com/frappe/erpnext/issues/44551)) ([3c50cfe](https://github.com/frappe/erpnext/commit/3c50cfef4e432f039979f8d389a240b95da39e09)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index faee116955b..c0a4cea763c 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.45.0" +__version__ = "15.45.1" def get_default_company(user=None): From a204abcaa59f0502110536fa1cb9e8268dde7b1b Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Sat, 7 Dec 2024 13:10:40 +0530 Subject: [PATCH 715/734] chore: fix conflicts --- erpnext/manufacturing/doctype/bom/bom.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index 41ea6f80f10..480a98f8916 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -179,14 +179,7 @@ class BOM(WebsiteGenerator): "BOM", filters={"name": ("like", search_key), "amended_from": ["is", "not set"]}, pluck="name" ) -<<<<<<< HEAD - if existing_boms: - index = self.get_next_version_index(existing_boms) - else: - index = 1 -======= index = self.get_index_for_bom(existing_boms) ->>>>>>> a5cc307417 (fix: BOM name issue (backport #44586) (#44589)) prefix = self.doctype suffix = "%.3i" % index # convert index to string (1 -> "001") From 0cd495a128015d14add5a66c619d6880af5dd465 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Sat, 7 Dec 2024 08:18:12 +0000 Subject: [PATCH 716/734] chore(release): Bumped to Version 15.45.2 ## [15.45.2](https://github.com/frappe/erpnext/compare/v15.45.1...v15.45.2) (2024-12-07) ### Bug Fixes * BOM name issue (backport [#44586](https://github.com/frappe/erpnext/issues/44586)) ([#44589](https://github.com/frappe/erpnext/issues/44589)) ([408a092](https://github.com/frappe/erpnext/commit/408a09233bfae90082a06ab21cf38ceaf7a9c7cb)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index c0a4cea763c..6eedda1f463 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.45.1" +__version__ = "15.45.2" def get_default_company(user=None): From 11fc560a88a66308d927c104d87c28ae5226a7ad Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Sun, 8 Dec 2024 14:44:19 +0530 Subject: [PATCH 717/734] fix: duplicate buttons --- erpnext/manufacturing/doctype/bom/bom.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js index 9d862e0bbdf..36ada87111a 100644 --- a/erpnext/manufacturing/doctype/bom/bom.js +++ b/erpnext/manufacturing/doctype/bom/bom.js @@ -253,13 +253,6 @@ frappe.ui.form.on("BOM", { }; }, }); - - fields.push({ - fieldtype: "Check", - label: __("Use Multi-Level BOM"), - fieldname: "use_multi_level_bom", - default: 1, - }); } fields.push({ From 6f80af0355ef1b26434b111e122b523487dd57c7 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Sun, 8 Dec 2024 09:23:09 +0000 Subject: [PATCH 718/734] chore(release): Bumped to Version 15.45.3 ## [15.45.3](https://github.com/frappe/erpnext/compare/v15.45.2...v15.45.3) (2024-12-08) ### Bug Fixes * duplicate buttons ([11fc560](https://github.com/frappe/erpnext/commit/11fc560a88a66308d927c104d87c28ae5226a7ad)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 6eedda1f463..cf0568e6cc6 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.45.2" +__version__ = "15.45.3" def get_default_company(user=None): From 608eaaa482507cd5724e4b05ee0dc99e0b6f5004 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sun, 8 Dec 2024 16:06:41 +0530 Subject: [PATCH 719/734] fix: BOM has not attr required items (backport #44598) (#44600) fix: BOM has not attr required items (#44598) (cherry picked from commit 42d238da1425b5c8a72308c2522a9336d4ea89fb) Co-authored-by: rohitwaghchaure --- erpnext/manufacturing/doctype/bom/bom.js | 28 +++++++++++++++---- .../doctype/work_order/work_order.py | 4 ++- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js index 36ada87111a..3a0bcf6b47e 100644 --- a/erpnext/manufacturing/doctype/bom/bom.js +++ b/erpnext/manufacturing/doctype/bom/bom.js @@ -255,12 +255,14 @@ frappe.ui.form.on("BOM", { }); } - fields.push({ - fieldtype: "Check", - label: __("Use Multi-Level BOM"), - fieldname: "use_multi_level_bom", - default: 1, - }); + if (!skip_qty_field) { + fields.push({ + fieldtype: "Check", + label: __("Use Multi-Level BOM"), + fieldname: "use_multi_level_bom", + default: 1, + }); + } if (!skip_qty_field) { fields.push({ @@ -306,6 +308,13 @@ frappe.ui.form.on("BOM", { fieldtype: "Link", in_list_view: 1, reqd: 1, + get_query() { + return { + filters: { + has_variants: 1, + }, + }; + }, }, { fieldname: "variant_item_code", @@ -326,6 +335,13 @@ frappe.ui.form.on("BOM", { }, }; }, + change() { + let doc = this.doc; + if (!doc.qty) { + doc.qty = 1.0; + this.grid.set_value("qty", 1.0, doc); + } + }, }, { fieldname: "qty", diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index e5219d7cb3e..9af3403ffa3 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -1360,7 +1360,9 @@ def add_variant_item(variant_items, wo_doc, bom_no, table_name="items"): args["amount"] = flt(args.get("required_qty")) * flt(args.get("rate")) args["uom"] = item_data.stock_uom - existing_row = get_template_rm_item(wo_doc, item.get("item_code")) + existing_row = ( + get_template_rm_item(wo_doc, item.get("item_code")) if table_name == "required_items" else None + ) if existing_row: existing_row.update(args) else: From 2a17ecdc3949380215b03e4ae296de40efc2c11d Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sun, 8 Dec 2024 16:07:06 +0530 Subject: [PATCH 720/734] fix: currency symbol in SCO and SCR (backport #44577) (#44594) * fix: currency symbol in SCO and SCR (#44577) (cherry picked from commit 8806d17ef1daa0a581aec5f4f20489346802cc19) # Conflicts: # erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.json # erpnext/subcontracting/doctype/subcontracting_order_item/subcontracting_order_item.json # erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json # erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json * chore: fix conflicts * chore: fix conflicts * chore: fix conflicts * chore: fix conflicts --------- Co-authored-by: rohitwaghchaure --- .../subcontracting_order.json | 21 ++++++++------- .../subcontracting_order_item.json | 11 +++++--- .../subcontracting_order_item.py | 3 +++ .../subcontracting_receipt.json | 27 ++++++++++--------- .../subcontracting_receipt_item.json | 9 ++++--- .../subcontracting_receipt_item.py | 1 + 6 files changed, 42 insertions(+), 30 deletions(-) diff --git a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.json b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.json index b8bda832983..206e3135dfb 100644 --- a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.json +++ b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.json @@ -294,7 +294,7 @@ "fieldname": "total", "fieldtype": "Currency", "label": "Total", - "options": "currency", + "options": "Company:company:default_currency", "read_only": 1 }, { @@ -353,6 +353,7 @@ "fieldname": "total_additional_costs", "fieldtype": "Currency", "label": "Total Additional Costs", + "options": "Company:company:default_currency", "print_hide_if_no_value": 1, "read_only": 1 }, @@ -450,21 +451,21 @@ "options": "Project" }, { - "fieldname": "tab_other_info", - "fieldtype": "Tab Break", - "label": "Other Info" + "fieldname": "tab_other_info", + "fieldtype": "Tab Break", + "label": "Other Info" }, { - "fieldname": "tab_connections", - "fieldtype": "Tab Break", - "label": "Connections", - "show_dashboard": 1 + "fieldname": "tab_connections", + "fieldtype": "Tab Break", + "label": "Connections", + "show_dashboard": 1 } ], "icon": "fa fa-file-text", "is_submittable": 1, "links": [], - "modified": "2024-01-03 20:56:04.670380", + "modified": "2024-12-06 15:21:49.924146", "modified_by": "Administrator", "module": "Subcontracting", "name": "Subcontracting Order", @@ -519,4 +520,4 @@ "timeline_field": "supplier", "title_field": "supplier_name", "track_changes": 1 -} \ No newline at end of file +} 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 1ca90c31654..502a28b3ec6 100644 --- a/erpnext/subcontracting/doctype/subcontracting_order_item/subcontracting_order_item.json +++ b/erpnext/subcontracting/doctype/subcontracting_order_item/subcontracting_order_item.json @@ -185,7 +185,7 @@ "fieldtype": "Currency", "in_list_view": 1, "label": "Rate", - "options": "currency", + "options": "Company:company:default_currency", "read_only": 1, "reqd": 1 }, @@ -199,7 +199,7 @@ "fieldtype": "Currency", "in_list_view": 1, "label": "Amount", - "options": "currency", + "options": "Company:company:default_currency", "read_only": 1, "reqd": 1 }, @@ -269,6 +269,7 @@ "fieldname": "service_cost_per_qty", "fieldtype": "Currency", "label": "Service Cost Per Qty", + "options": "Company:company:default_currency", "read_only": 1, "reqd": 1 }, @@ -277,6 +278,7 @@ "fieldname": "additional_cost_per_qty", "fieldtype": "Currency", "label": "Additional Cost Per Qty", + "options": "Company:company:default_currency", "read_only": 1 }, { @@ -284,6 +286,7 @@ "fieldtype": "Currency", "label": "Raw Material Cost Per Qty", "no_copy": 1, + "options": "Company:company:default_currency", "read_only": 1 }, { @@ -384,7 +387,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2023-11-30 15:29:43.744618", + "modified": "2024-12-06 15:23:05.252346", "modified_by": "Administrator", "module": "Subcontracting", "name": "Subcontracting Order Item", @@ -397,4 +400,4 @@ "sort_order": "DESC", "states": [], "track_changes": 1 -} \ No newline at end of file +} diff --git a/erpnext/subcontracting/doctype/subcontracting_order_item/subcontracting_order_item.py b/erpnext/subcontracting/doctype/subcontracting_order_item/subcontracting_order_item.py index fcd143c1dd9..7a426f91cb0 100644 --- a/erpnext/subcontracting/doctype/subcontracting_order_item/subcontracting_order_item.py +++ b/erpnext/subcontracting/doctype/subcontracting_order_item/subcontracting_order_item.py @@ -26,8 +26,11 @@ class SubcontractingOrderItem(Document): include_exploded_items: DF.Check item_code: DF.Link item_name: DF.Data + job_card: DF.Link | None manufacturer: DF.Link | None manufacturer_part_no: DF.Data | None + material_request: DF.Link | None + material_request_item: DF.Data | None page_break: DF.Check parent: DF.Data parentfield: DF.Data diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json index ad03171f29a..b8bd95bcbca 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json @@ -336,7 +336,7 @@ "fieldname": "total", "fieldtype": "Currency", "label": "Total", - "options": "currency", + "options": "Company:company:default_currency", "read_only": 1 }, { @@ -618,6 +618,7 @@ "fieldname": "total_additional_costs", "fieldtype": "Currency", "label": "Total Additional Costs", + "options": "Company:company:default_currency", "print_hide_if_no_value": 1, "read_only": 1 }, @@ -656,27 +657,27 @@ "fieldtype": "Column Break" }, { - "fieldname": "tab_other_info", - "fieldtype": "Tab Break", - "label": "Other Info" + "fieldname": "tab_other_info", + "fieldtype": "Tab Break", + "label": "Other Info" }, { - "collapsible": 1, - "fieldname": "order_status_section", - "fieldtype": "Section Break", - "label": "Order Status" + "collapsible": 1, + "fieldname": "order_status_section", + "fieldtype": "Section Break", + "label": "Order Status" }, { - "fieldname": "tab_connections", - "fieldtype": "Tab Break", - "label": "Connections", - "show_dashboard": 1 + "fieldname": "tab_connections", + "fieldtype": "Tab Break", + "label": "Connections", + "show_dashboard": 1 } ], "in_create": 1, "is_submittable": 1, "links": [], - "modified": "2024-05-28 15:02:13.517969", + "modified": "2024-12-06 15:24:38.384232", "modified_by": "Administrator", "module": "Subcontracting", "name": "Subcontracting Receipt", 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 75e263e2c1c..23a7e69669d 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json +++ b/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json @@ -207,7 +207,7 @@ "fieldtype": "Currency", "in_list_view": 1, "label": "Rate", - "options": "currency", + "options": "Company:company:default_currency", "print_width": "100px", "read_only": 1, "width": "100px" @@ -217,7 +217,7 @@ "fieldtype": "Currency", "in_list_view": 1, "label": "Amount", - "options": "currency", + "options": "Company:company:default_currency", "read_only": 1 }, { @@ -231,6 +231,7 @@ "fieldtype": "Currency", "label": "Raw Material Cost Per Qty", "no_copy": 1, + "options": "Company:company:default_currency", "read_only": 1 }, { @@ -239,6 +240,7 @@ "fieldname": "service_cost_per_qty", "fieldtype": "Currency", "label": "Service Cost Per Qty", + "options": "Company:company:default_currency", "read_only": 1, "reqd": 1 }, @@ -248,6 +250,7 @@ "fieldname": "additional_cost_per_qty", "fieldtype": "Currency", "label": "Additional Cost Per Qty", + "options": "Company:company:default_currency", "read_only": 1 }, { @@ -582,7 +585,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2024-03-29 15:42:43.425544", + "modified": "2024-12-06 15:23:58.680169", "modified_by": "Administrator", "module": "Subcontracting", "name": "Subcontracting Receipt Item", diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.py b/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.py index 1a4ce5b977a..69f7ae73e7a 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.py @@ -28,6 +28,7 @@ class SubcontractingReceiptItem(Document): is_scrap_item: DF.Check item_code: DF.Link item_name: DF.Data | None + job_card: DF.Link | None manufacturer: DF.Link | None manufacturer_part_no: DF.Data | None page_break: DF.Check From 971e474640de208d023d3a69979613395cb9d387 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sun, 8 Dec 2024 16:06:41 +0530 Subject: [PATCH 721/734] fix: BOM has not attr required items (backport #44598) (#44600) fix: BOM has not attr required items (#44598) (cherry picked from commit 42d238da1425b5c8a72308c2522a9336d4ea89fb) Co-authored-by: rohitwaghchaure (cherry picked from commit 608eaaa482507cd5724e4b05ee0dc99e0b6f5004) --- erpnext/manufacturing/doctype/bom/bom.js | 28 +++++++++++++++---- .../doctype/work_order/work_order.py | 4 ++- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js index 36ada87111a..3a0bcf6b47e 100644 --- a/erpnext/manufacturing/doctype/bom/bom.js +++ b/erpnext/manufacturing/doctype/bom/bom.js @@ -255,12 +255,14 @@ frappe.ui.form.on("BOM", { }); } - fields.push({ - fieldtype: "Check", - label: __("Use Multi-Level BOM"), - fieldname: "use_multi_level_bom", - default: 1, - }); + if (!skip_qty_field) { + fields.push({ + fieldtype: "Check", + label: __("Use Multi-Level BOM"), + fieldname: "use_multi_level_bom", + default: 1, + }); + } if (!skip_qty_field) { fields.push({ @@ -306,6 +308,13 @@ frappe.ui.form.on("BOM", { fieldtype: "Link", in_list_view: 1, reqd: 1, + get_query() { + return { + filters: { + has_variants: 1, + }, + }; + }, }, { fieldname: "variant_item_code", @@ -326,6 +335,13 @@ frappe.ui.form.on("BOM", { }, }; }, + change() { + let doc = this.doc; + if (!doc.qty) { + doc.qty = 1.0; + this.grid.set_value("qty", 1.0, doc); + } + }, }, { fieldname: "qty", diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index e5219d7cb3e..9af3403ffa3 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -1360,7 +1360,9 @@ def add_variant_item(variant_items, wo_doc, bom_no, table_name="items"): args["amount"] = flt(args.get("required_qty")) * flt(args.get("rate")) args["uom"] = item_data.stock_uom - existing_row = get_template_rm_item(wo_doc, item.get("item_code")) + existing_row = ( + get_template_rm_item(wo_doc, item.get("item_code")) if table_name == "required_items" else None + ) if existing_row: existing_row.update(args) else: From 3c8abe41e799043db43cfcad7d9c49439521ccbc Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Sun, 8 Dec 2024 11:01:12 +0000 Subject: [PATCH 722/734] chore(release): Bumped to Version 15.45.4 ## [15.45.4](https://github.com/frappe/erpnext/compare/v15.45.3...v15.45.4) (2024-12-08) ### Bug Fixes * BOM has not attr required items (backport [#44598](https://github.com/frappe/erpnext/issues/44598)) ([#44600](https://github.com/frappe/erpnext/issues/44600)) ([971e474](https://github.com/frappe/erpnext/commit/971e474640de208d023d3a69979613395cb9d387)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index cf0568e6cc6..bb8bf11b7ea 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.45.3" +__version__ = "15.45.4" def get_default_company(user=None): From 47e1ed1eff0a14fef46881ac42843b43d5ce43d0 Mon Sep 17 00:00:00 2001 From: mahsem <137205921+mahsem@users.noreply.github.com> Date: Fri, 6 Dec 2024 14:59:16 +0100 Subject: [PATCH 723/734] fix: add docstatus for translation (cherry picked from commit dda272220bcc65768ef0adcdb48d23baba576481) --- erpnext/templates/includes/issue_row.html | 2 +- erpnext/templates/includes/projects/project_row.html | 2 +- erpnext/templates/includes/transaction_row.html | 2 +- erpnext/templates/pages/timelog_info.html | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/templates/includes/issue_row.html b/erpnext/templates/includes/issue_row.html index a04f558509f..b55712ab189 100644 --- a/erpnext/templates/includes/issue_row.html +++ b/erpnext/templates/includes/issue_row.html @@ -18,7 +18,7 @@ {% if doc.status == "Open" %} {{ doc.priority }} {% else %} - {{ doc.status }} + {{ _(doc.status) }} {%- endif -%} diff --git a/erpnext/templates/includes/projects/project_row.html b/erpnext/templates/includes/projects/project_row.html index 686637a2014..ccb306afcdb 100644 --- a/erpnext/templates/includes/projects/project_row.html +++ b/erpnext/templates/includes/projects/project_row.html @@ -20,7 +20,7 @@ {% else %} - {{ doc.status }} + {{ _(doc.status) }} {% endif %} {% if doc["_assign"] %} diff --git a/erpnext/templates/includes/transaction_row.html b/erpnext/templates/includes/transaction_row.html index a498ba0eefa..061c9bd1796 100644 --- a/erpnext/templates/includes/transaction_row.html +++ b/erpnext/templates/includes/transaction_row.html @@ -8,7 +8,7 @@
- {{doc.status}} + {{ _(doc.status) }}
diff --git a/erpnext/templates/pages/timelog_info.html b/erpnext/templates/pages/timelog_info.html index be13826444c..9f9445661a0 100644 --- a/erpnext/templates/pages/timelog_info.html +++ b/erpnext/templates/pages/timelog_info.html @@ -38,7 +38,7 @@ - + From 2560beb69581ef74551e892e54410cde0fb13a00 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 9 Dec 2024 12:58:20 +0530 Subject: [PATCH 724/734] fix: prevent set_payment_schedule on return documents --- erpnext/stock/doctype/delivery_note/delivery_note.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index 0bc6b28fe68..2b4dad137c2 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -1046,7 +1046,7 @@ def make_sales_invoice(source_name, target_doc=None, args=None): automatically_fetch_payment_terms = cint( frappe.db.get_single_value("Accounts Settings", "automatically_fetch_payment_terms") ) - if automatically_fetch_payment_terms: + if automatically_fetch_payment_terms and not doc.is_return: doc.set_payment_schedule() return doc From cce1cc66747cdf3f4a8fe210b53298c41417526f Mon Sep 17 00:00:00 2001 From: ljain112 Date: Wed, 4 Dec 2024 20:40:08 +0530 Subject: [PATCH 725/734] fix: correct color for draft in list view (cherry picked from commit 143acf23303a1cf8a3dff7ec60be694974badb4a) --- .../accounts/doctype/payment_request/payment_request_list.js | 2 +- erpnext/accounts/doctype/sales_invoice/sales_invoice_list.js | 2 +- erpnext/stock/doctype/pick_list/pick_list_list.js | 2 +- .../doctype/subcontracting_order/subcontracting_order_list.js | 2 +- .../subcontracting_receipt/subcontracting_receipt_list.js | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/payment_request/payment_request_list.js b/erpnext/accounts/doctype/payment_request/payment_request_list.js index 1027385aaaf..6e4aada66c6 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request_list.js +++ b/erpnext/accounts/doctype/payment_request/payment_request_list.js @@ -1,7 +1,7 @@ const INDICATORS = { "Partially Paid": "orange", Cancelled: "red", - Draft: "gray", + Draft: "red", Failed: "red", Initiated: "green", Paid: "blue", diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice_list.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice_list.js index f971f68a454..3371a63cca2 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice_list.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice_list.js @@ -15,7 +15,7 @@ frappe.listview_settings["Sales Invoice"] = { ], get_indicator: function (doc) { const status_colors = { - Draft: "grey", + Draft: "red", Unpaid: "orange", Paid: "green", Return: "gray", diff --git a/erpnext/stock/doctype/pick_list/pick_list_list.js b/erpnext/stock/doctype/pick_list/pick_list_list.js index 9cdbfe18720..eca6eece785 100644 --- a/erpnext/stock/doctype/pick_list/pick_list_list.js +++ b/erpnext/stock/doctype/pick_list/pick_list_list.js @@ -4,7 +4,7 @@ frappe.listview_settings["Pick List"] = { get_indicator: function (doc) { const status_colors = { - Draft: "grey", + Draft: "red", Open: "orange", Completed: "green", Cancelled: "red", diff --git a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order_list.js b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order_list.js index 895ffdbf88c..f5486bbd59d 100644 --- a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order_list.js +++ b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order_list.js @@ -4,7 +4,7 @@ frappe.listview_settings["Subcontracting Order"] = { get_indicator: function (doc) { const status_colors = { - Draft: "grey", + Draft: "red", Open: "orange", "Partially Received": "yellow", Completed: "green", diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt_list.js b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt_list.js index be6c0d0b18f..5f5a6db01e0 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt_list.js +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt_list.js @@ -4,7 +4,7 @@ frappe.listview_settings["Subcontracting Receipt"] = { get_indicator: function (doc) { const status_colors = { - Draft: "grey", + Draft: "red", Return: "gray", "Return Issued": "grey", Completed: "green", From 243bf542c43de476c8d0c26c34bd45d31e72f905 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 9 Dec 2024 14:56:10 +0530 Subject: [PATCH 726/734] fix: description overwrite on qty change (backport #44606) (#44609) fix: description overwrite on qty change (#44606) (cherry picked from commit 9ad79625e02772a6c72b5eacfd7ebf5f38706675) Co-authored-by: rohitwaghchaure --- erpnext/stock/doctype/material_request/material_request.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js index c7485470cf0..09c01a0ee88 100644 --- a/erpnext/stock/doctype/material_request/material_request.js +++ b/erpnext/stock/doctype/material_request/material_request.js @@ -259,18 +259,21 @@ frappe.ui.form.on("Material Request", { }, callback: function (r) { const d = item; - const allow_to_change_fields = [ + let allow_to_change_fields = [ "actual_qty", "projected_qty", "min_order_qty", "item_name", - "description", "stock_uom", "uom", "conversion_factor", "stock_qty", ]; + if (overwrite_warehouse) { + allow_to_change_fields.push("description"); + } + if (!r.exc) { $.each(r.message, function (key, value) { if (!d[key] || allow_to_change_fields.includes(key)) { From 04b07a9e1c0235d5a8515973f55c2676532dfcc3 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 9 Dec 2024 19:11:49 +0530 Subject: [PATCH 727/734] fix: not able to make sales return entry (backport #44605) (#44613) fix: not able to make sales return entry (#44605) (cherry picked from commit 314c7b8d2afa6abda3386b5f8f7dcf2ce659ffc0) Co-authored-by: rohitwaghchaure --- .../doctype/serial_and_batch_bundle/serial_and_batch_bundle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py index fff9cbfc07d..4bd8f5e8465 100644 --- a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py +++ b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py @@ -223,7 +223,7 @@ class SerialandBatchBundle(Document): else: valuation_rate = valuation_details["batches"].get(row.batch_no) - row.incoming_rate = valuation_rate + row.incoming_rate = flt(valuation_rate) row.stock_value_difference = flt(row.qty) * flt(row.incoming_rate) if save: From bdb60a51c3232dc14b052822463a43b67b16ed8f Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 9 Dec 2024 20:30:45 +0530 Subject: [PATCH 728/734] fix: do not allow to inward same serial nos multiple times (backport #44617) (#44619) * fix: do not allow to inward same serial nos multiple times (#44617) (cherry picked from commit 616bb383c5434e43b32099f5e21d679c0bb0baae) # Conflicts: # erpnext/patches.txt * chore: fix conflicts --------- Co-authored-by: rohitwaghchaure --- erpnext/patches.txt | 1 + .../v15_0/enable_allow_existing_serial_no.py | 6 ++ .../purchase_receipt/test_purchase_receipt.py | 36 ++++++++++ .../serial_and_batch_bundle.py | 68 +++++++++++++++++-- .../stock_settings/stock_settings.json | 11 ++- .../doctype/stock_settings/stock_settings.py | 1 + 6 files changed, 114 insertions(+), 9 deletions(-) create mode 100644 erpnext/patches/v15_0/enable_allow_existing_serial_no.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index f53769155bd..a916478d4b3 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -384,3 +384,4 @@ erpnext.patches.v15_0.update_task_assignee_email_field_in_asset_maintenance_log erpnext.patches.v15_0.update_sub_voucher_type_in_gl_entries erpnext.patches.v14_0.update_stock_uom_in_work_order_item erpnext.patches.v15_0.set_is_exchange_gain_loss_in_payment_entry_deductions +erpnext.patches.v15_0.enable_allow_existing_serial_no diff --git a/erpnext/patches/v15_0/enable_allow_existing_serial_no.py b/erpnext/patches/v15_0/enable_allow_existing_serial_no.py new file mode 100644 index 00000000000..e13adc2b187 --- /dev/null +++ b/erpnext/patches/v15_0/enable_allow_existing_serial_no.py @@ -0,0 +1,6 @@ +import frappe + + +def execute(): + if frappe.get_all("Company", filters={"country": "India"}, limit=1): + frappe.db.set_single_value("Stock Settings", "allow_existing_serial_no", 1) diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index 4b8d5101f43..86d1a6948de 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -3948,6 +3948,42 @@ class TestPurchaseReceipt(FrappeTestCase): self.assertEqual(return_pr.per_billed, 100) self.assertEqual(return_pr.status, "Completed") + def test_do_not_allow_to_inward_same_serial_no_multiple_times(self): + from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry + + frappe.db.set_single_value("Stock Settings", "allow_existing_serial_no", 0) + + item_code = make_item( + "Test Do Not Allow INWD Item 123", {"has_serial_no": 1, "serial_no_series": "SN-TDAISN-.#####"} + ).name + + pr = make_purchase_receipt(item_code=item_code, qty=1, rate=100, use_serial_batch_fields=1) + serial_no = get_serial_nos_from_bundle(pr.items[0].serial_and_batch_bundle)[0] + + status = frappe.db.get_value("Serial No", serial_no, "status") + self.assertTrue(status == "Active") + + make_stock_entry( + item_code=item_code, + source=pr.items[0].warehouse, + qty=1, + serial_no=serial_no, + use_serial_batch_fields=1, + ) + + status = frappe.db.get_value("Serial No", serial_no, "status") + self.assertFalse(status == "Active") + + pr = make_purchase_receipt( + item_code=item_code, qty=1, rate=100, use_serial_batch_fields=1, do_not_submit=1 + ) + pr.items[0].serial_no = serial_no + pr.save() + + self.assertRaises(frappe.exceptions.ValidationError, pr.submit) + + frappe.db.set_single_value("Stock Settings", "allow_existing_serial_no", 1) + 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/serial_and_batch_bundle/serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py index 4bd8f5e8465..a1f3135b70b 100644 --- a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py +++ b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py @@ -89,6 +89,10 @@ class SerialandBatchBundle(Document): self.validate_serial_and_batch_no() self.validate_duplicate_serial_and_batch_no() self.validate_voucher_no() + + if self.docstatus == 0: + self.allow_existing_serial_nos() + if self.type_of_transaction == "Maintenance": return @@ -102,6 +106,42 @@ class SerialandBatchBundle(Document): self.set_incoming_rate() self.calculate_qty_and_amount() + def allow_existing_serial_nos(self): + if self.type_of_transaction == "Outward" or not self.has_serial_no: + return + + if frappe.db.get_single_value("Stock Settings", "allow_existing_serial_no"): + return + + if self.voucher_type not in ["Purchase Receipt", "Purchase Invoice", "Stock Entry"]: + return + + if self.voucher_type == "Stock Entry" and frappe.get_cached_value( + "Stock Entry", self.voucher_no, "purpose" + ) in ["Material Transfer", "Send to Subcontractor", "Material Transfer for Manufacture"]: + return + + serial_nos = [d.serial_no for d in self.entries if d.serial_no] + + data = frappe.get_all( + "Serial and Batch Entry", + filters={"serial_no": ("in", serial_nos), "docstatus": 1, "qty": ("<", 0)}, + fields=["serial_no", "parent"], + ) + + note = "

Note:
" + for row in data: + frappe.throw( + _( + "You can't process the serial number {0} as it has already been used in the SABB {1}. {2} if you want to inward same serial number multiple times then enabled 'Allow existing Serial No to be Manufactured/Received again' in the {3}" + ).format( + row.serial_no, + get_link_to_form("Serial and Batch Bundle", row.parent), + note, + get_link_to_form("Stock Settings", "Stock Settings"), + ) + ) + def reset_serial_batch_bundle(self): if self.is_new() and self.amended_from: for field in ["is_cancelled", "is_rejected"]: @@ -136,7 +176,12 @@ class SerialandBatchBundle(Document): return serial_nos = [d.serial_no for d in self.entries if d.serial_no] - kwargs = {"item_code": self.item_code, "warehouse": self.warehouse} + kwargs = { + "item_code": self.item_code, + "warehouse": self.warehouse, + "check_serial_nos": True, + "serial_nos": serial_nos, + } if self.voucher_type == "POS Invoice": kwargs["ignore_voucher_nos"] = [self.voucher_no] @@ -177,6 +222,7 @@ class SerialandBatchBundle(Document): "posting_date": self.posting_date, "posting_time": self.posting_time, "serial_nos": serial_nos, + "check_serial_nos": True, } ) @@ -1683,7 +1729,7 @@ def get_serial_nos_based_on_posting_date(kwargs, ignore_serial_nos): serial_nos = set() data = get_stock_ledgers_for_serial_nos(kwargs) - bundle_wise_serial_nos = get_bundle_wise_serial_nos(data) + bundle_wise_serial_nos = get_bundle_wise_serial_nos(data, kwargs) for d in data: if d.serial_and_batch_bundle: if sns := bundle_wise_serial_nos.get(d.serial_and_batch_bundle): @@ -1707,16 +1753,21 @@ def get_serial_nos_based_on_posting_date(kwargs, ignore_serial_nos): return serial_nos -def get_bundle_wise_serial_nos(data): +def get_bundle_wise_serial_nos(data, kwargs): bundle_wise_serial_nos = defaultdict(list) bundles = [d.serial_and_batch_bundle for d in data if d.serial_and_batch_bundle] if not bundles: return bundle_wise_serial_nos + filters = {"parent": ("in", bundles), "docstatus": 1, "serial_no": ("is", "set")} + + if kwargs.get("check_serial_nos") and kwargs.get("serial_nos"): + filters["serial_no"] = ("in", kwargs.get("serial_nos")) + bundle_data = frappe.get_all( "Serial and Batch Entry", fields=["serial_no", "parent"], - filters={"parent": ("in", bundles), "docstatus": 1, "serial_no": ("is", "set")}, + filters=filters, ) for d in bundle_data: @@ -2277,6 +2328,8 @@ def get_ledgers_from_serial_batch_bundle(**kwargs) -> list[frappe._dict]: def get_stock_ledgers_for_serial_nos(kwargs): + from erpnext.stock.utils import get_combine_datetime + stock_ledger_entry = frappe.qb.DocType("Stock Ledger Entry") query = ( @@ -2287,15 +2340,16 @@ def get_stock_ledgers_for_serial_nos(kwargs): stock_ledger_entry.serial_and_batch_bundle, ) .where(stock_ledger_entry.is_cancelled == 0) + .orderby(stock_ledger_entry.posting_datetime) ) if kwargs.get("posting_date"): if kwargs.get("posting_time") is None: kwargs.posting_time = nowtime() - timestamp_condition = CombineDatetime( - stock_ledger_entry.posting_date, stock_ledger_entry.posting_time - ) <= CombineDatetime(kwargs.posting_date, kwargs.posting_time) + timestamp_condition = stock_ledger_entry.posting_datetime <= get_combine_datetime( + kwargs.posting_date, kwargs.posting_time + ) query = query.where(timestamp_condition) diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.json b/erpnext/stock/doctype/stock_settings/stock_settings.json index 069e7da41cb..e542a1582e3 100644 --- a/erpnext/stock/doctype/stock_settings/stock_settings.json +++ b/erpnext/stock/doctype/stock_settings/stock_settings.json @@ -49,12 +49,13 @@ "do_not_use_batchwise_valuation", "auto_create_serial_and_batch_bundle_for_outward", "pick_serial_and_batch_based_on", + "naming_series_prefix", "column_break_mhzc", "disable_serial_no_and_batch_selector", "use_naming_series", - "naming_series_prefix", "use_serial_batch_fields", "do_not_update_serial_batch_on_creation_of_auto_bundle", + "allow_existing_serial_no", "stock_planning_tab", "auto_material_request", "auto_indent", @@ -460,6 +461,12 @@ "fieldname": "over_picking_allowance", "fieldtype": "Percent", "label": "Over Picking Allowance" + }, + { + "default": "1", + "fieldname": "allow_existing_serial_no", + "fieldtype": "Check", + "label": "Allow existing Serial No to be Manufactured/Received again" } ], "icon": "icon-cog", @@ -467,7 +474,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2024-07-29 14:55:19.093508", + "modified": "2024-12-09 17:52:36.030456", "modified_by": "Administrator", "module": "Stock", "name": "Stock Settings", diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.py b/erpnext/stock/doctype/stock_settings/stock_settings.py index 229ff944750..b7a317cd66a 100644 --- a/erpnext/stock/doctype/stock_settings/stock_settings.py +++ b/erpnext/stock/doctype/stock_settings/stock_settings.py @@ -25,6 +25,7 @@ class StockSettings(Document): action_if_quality_inspection_is_not_submitted: DF.Literal["Stop", "Warn"] action_if_quality_inspection_is_rejected: DF.Literal["Stop", "Warn"] + allow_existing_serial_no: DF.Check allow_from_dn: DF.Check allow_from_pr: DF.Check allow_internal_transfer_at_arms_length_price: DF.Check From 98bd5f20c4e20edb766ad4b0b4df23fda5552be9 Mon Sep 17 00:00:00 2001 From: ljain112 Date: Thu, 5 Dec 2024 15:18:44 +0530 Subject: [PATCH 729/734] fix: restrict advance payment entries with flag where "book_advance_payments_in_separate_party_account" is true --- .../payment_reconciliation.py | 6 ++--- erpnext/controllers/accounts_controller.py | 26 ++++++++++++++----- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index 68e9eef711a..db4a4b0f268 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -153,10 +153,7 @@ class PaymentReconciliation(Document): self.add_payment_entries(non_reconciled_payments) def get_payment_entries(self): - if self.default_advance_account: - party_account = [self.receivable_payable_account, self.default_advance_account] - else: - party_account = [self.receivable_payable_account] + party_account = [self.receivable_payable_account] order_doctype = "Sales Order" if self.party_type == "Customer" else "Purchase Order" condition = frappe._dict( @@ -187,6 +184,7 @@ class PaymentReconciliation(Document): self.party, party_account, order_doctype, + default_advance_account=self.default_advance_account, against_all_orders=True, limit=self.payment_limit, condition=condition, diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index bc0cdde94de..e1a7d5803c9 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -2916,6 +2916,7 @@ def get_advance_payment_entries( party_account, order_doctype, order_list=None, + default_advance_account=None, include_unallocated=True, against_all_orders=False, limit=None, @@ -2929,6 +2930,7 @@ def get_advance_payment_entries( party_type, party, party_account, + default_advance_account, limit, condition, ) @@ -2952,6 +2954,7 @@ def get_advance_payment_entries( party_type, party, party_account, + default_advance_account, limit, condition, ) @@ -2967,6 +2970,7 @@ def get_common_query( party_type, party, party_account, + default_advance_account, limit, condition, ): @@ -2988,14 +2992,22 @@ def get_common_query( .where(payment_entry.docstatus == 1) ) - if payment_type == "Receive": - q = q.select((payment_entry.paid_from_account_currency).as_("currency")) - q = q.select(payment_entry.paid_from) - q = q.where(payment_entry.paid_from.isin(party_account)) + field = "paid_from" if payment_type == "Receive" else "paid_to" + + q = q.select((payment_entry[f"{field}_account_currency"]).as_("currency")) + q = q.select(payment_entry[field]) + account_condition = payment_entry[field].isin(party_account) + if default_advance_account: + q = q.where( + account_condition + | ( + (payment_entry[field] == default_advance_account) + & (payment_entry.book_advance_payments_in_separate_party_account == 1) + ) + ) + else: - q = q.select((payment_entry.paid_to_account_currency).as_("currency")) - q = q.select(payment_entry.paid_to) - q = q.where(payment_entry.paid_to.isin(party_account)) + q = q.where(account_condition) if payment_type == "Receive": q = q.select((payment_entry.source_exchange_rate).as_("exchange_rate")) From 5cd39941ee9d8067e606df169b1d773fa46cc580 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 10 Dec 2024 10:57:20 +0530 Subject: [PATCH 730/734] fix: 'Use Multi-Level BOM' checkbox default value (backport #44618) (#44620) fix: 'Use Multi-Level BOM' checkbox default value (#44618) (cherry picked from commit f5c038cd1bb129609a048a62fe61bd20505abe32) Co-authored-by: rohitwaghchaure --- erpnext/manufacturing/doctype/bom/bom.js | 16 +++++++--------- erpnext/manufacturing/doctype/bom/bom.py | 17 +++++++++++++++++ 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js index 3a0bcf6b47e..4b52859cabd 100644 --- a/erpnext/manufacturing/doctype/bom/bom.js +++ b/erpnext/manufacturing/doctype/bom/bom.js @@ -255,15 +255,6 @@ frappe.ui.form.on("BOM", { }); } - if (!skip_qty_field) { - fields.push({ - fieldtype: "Check", - label: __("Use Multi-Level BOM"), - fieldname: "use_multi_level_bom", - default: 1, - }); - } - if (!skip_qty_field) { fields.push({ fieldtype: "Float", @@ -291,6 +282,13 @@ frappe.ui.form.on("BOM", { cur_dialog.refresh(); }, }); + + fields.push({ + fieldtype: "Check", + label: __("Use Multi-Level BOM"), + fieldname: "use_multi_level_bom", + default: frm.doc?.__onload.use_multi_level_bom, + }); } var has_template_rm = frm.doc.items.filter((d) => d.has_variants === 1) || []; diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index 730ff494801..78b95f2c80c 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -214,6 +214,23 @@ class BOM(WebsiteGenerator): return index + def onload(self): + super().onload() + + self.set_onload_for_muulti_level_bom() + + def set_onload_for_muulti_level_bom(self): + use_multi_level_bom = frappe.db.get_value( + "Property Setter", + {"field_name": "use_multi_level_bom", "doc_type": "Work Order", "property": "default"}, + "value", + ) + + if use_multi_level_bom is None: + use_multi_level_bom = 1 + + self.set_onload("use_multi_level_bom", cint(use_multi_level_bom)) + @staticmethod def get_next_version_index(existing_boms: list[str]) -> int: # split by "/" and "-" From 815c6b423497f59d0bafa58483ca6b63b432a56b Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 5 Dec 2024 22:57:09 +0530 Subject: [PATCH 731/734] chore: Ignore stock validation for non stock invoices (backport #44549) (#44554) chore: Ignore stock validation for non stock invoices (#44549) * chore: Ignore stock validation for non stock invoices * chore: Ignore stock validation for non stock invoices (cherry picked from commit 1ac292285e217c77d2c9f9ce37e4c018c2f5e849) Co-authored-by: Deepesh Garg (cherry picked from commit 06ac21dd856a5b6ecfb37ccf20c3d83fa4d79c05) --- erpnext/controllers/sales_and_purchase_return.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py index 7f9a5a35a73..696d404d16d 100644 --- a/erpnext/controllers/sales_and_purchase_return.py +++ b/erpnext/controllers/sales_and_purchase_return.py @@ -24,6 +24,10 @@ def validate_return(doc): if doc.return_against: validate_return_against(doc) + + if doc.doctype in ("Sales Invoice", "Purchase Invoice") and not doc.update_stock: + return + validate_returned_items(doc) From ef5a5643dca2c5ee8f008088eb9bcc77079e72e9 Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Tue, 10 Dec 2024 12:54:02 +0530 Subject: [PATCH 732/734] fix: precision check for salvage value --- erpnext/assets/doctype/asset/asset.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index c788d5265a7..05d575ac822 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -410,6 +410,9 @@ class Asset(AccountsController): ) def validate_asset_finance_books(self, row): + row.expected_value_after_useful_life = flt( + row.expected_value_after_useful_life, self.precision("gross_purchase_amount") + ) if flt(row.expected_value_after_useful_life) >= flt(self.gross_purchase_amount): frappe.throw( _("Row {0}: Expected Value After Useful Life must be less than Gross Purchase Amount").format( @@ -430,7 +433,10 @@ class Asset(AccountsController): self.opening_accumulated_depreciation = 0 self.opening_number_of_booked_depreciations = 0 else: - depreciable_amount = flt(self.gross_purchase_amount) - flt(row.expected_value_after_useful_life) + depreciable_amount = flt( + flt(self.gross_purchase_amount) - flt(row.expected_value_after_useful_life), + self.precision("gross_purchase_amount"), + ) if flt(self.opening_accumulated_depreciation) > depreciable_amount: frappe.throw( _("Opening Accumulated Depreciation must be less than or equal to {0}").format( From 7e0ff851ab1a46479927b0a3e097aab5fa368ad8 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 11 Dec 2024 15:40:56 +0530 Subject: [PATCH 733/734] refactor: consider against voucher when not filtered on voucher_no --- .../report/general_ledger/general_ledger.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py index 04df22c9c9c..69e3d241f12 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.py +++ b/erpnext/accounts/report/general_ledger/general_ledger.py @@ -35,9 +35,6 @@ def execute(filters=None): if filters.get("party"): filters.party = frappe.parse_json(filters.get("party")) - if filters.get("voucher_no") and not filters.get("group_by"): - filters.group_by = "Group by Voucher (Consolidated)" - validate_filters(filters, account_details) validate_party(filters) @@ -373,16 +370,21 @@ def get_data_with_opening_closing(filters, account_details, accounting_dimension if acc_dict.entries: # opening data.append({"debit_in_transaction_currency": None, "credit_in_transaction_currency": None}) - if filters.get("group_by") != "Group by Voucher": + if (not filters.get("group_by") and not filters.get("voucher_no")) or ( + filters.get("group_by") and filters.get("group_by") != "Group by Voucher" + ): data.append(acc_dict.totals.opening) data += acc_dict.entries # totals - data.append(acc_dict.totals.total) + if filters.get("group_by") or not filters.voucher_no: + data.append(acc_dict.totals.total) # closing - if filters.get("group_by") != "Group by Voucher": + if (not filters.get("group_by") and not filters.get("voucher_no")) or ( + filters.get("group_by") and filters.get("group_by") != "Group by Voucher" + ): data.append(acc_dict.totals.closing) data.append({"debit_in_transaction_currency": None, "credit_in_transaction_currency": None}) From f88dc263d5746f1f3f9b9dafd75d050390df23a1 Mon Sep 17 00:00:00 2001 From: venkat102 Date: Wed, 11 Dec 2024 15:55:22 +0530 Subject: [PATCH 734/734] fix: update free item qty while adding same item in seperate row --- erpnext/accounts/doctype/pricing_rule/utils.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index 087e2bffa4b..551eaa3d1ce 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -651,8 +651,17 @@ def get_product_discount_rule(pricing_rule, item_details, args=None, doc=None): qty = pricing_rule.free_qty or 1 if pricing_rule.is_recursive: - transaction_qty = (args.get("qty") if args else doc.total_qty) - pricing_rule.apply_recursion_over - if transaction_qty: + transaction_qty = sum( + [ + row.qty + for row in doc.items + if not row.is_free_item + and row.item_code == args.item_code + and row.pricing_rules == args.pricing_rules + ] + ) + transaction_qty = transaction_qty - pricing_rule.apply_recursion_over + if transaction_qty and transaction_qty > 0: qty = flt(transaction_qty) * qty / pricing_rule.recurse_for if pricing_rule.round_free_qty: qty = (flt(transaction_qty) // pricing_rule.recurse_for) * (pricing_rule.free_qty or 1)
{d.batch_no}{d.serial_no}{abs(d.qty)}
{d.batch_no}{d.serial_no}{abs(d.qty)}
{d.batch_no}{abs(d.qty)}